feat: add missing MCP tools and fix undefined content response
- Add time_entries_update, time_entries_delete, time_entries_get_summary tools - Fix textResult to use `data ?? null` so JSON.stringify always returns a string — procedures returning undefined (e.g. getRunning with no timer, businesses.getById not found) were dropping the text field from the MCP content item, causing client SDK validation failures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -234,7 +234,7 @@ function parseInvoiceItems(items: z.infer<typeof invoiceItemSchema>[]) {
|
||||
|
||||
function textResult(data: unknown): ToolResult {
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
||||
content: [{ type: "text", text: JSON.stringify(data ?? null, null, 2) }],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -509,6 +509,67 @@ const tools = {
|
||||
endedAt: input.endedAt ? parseDate(input.endedAt, "endedAt") : undefined,
|
||||
}),
|
||||
}),
|
||||
time_entries_update: defineTool({
|
||||
description: "Update an existing time entry by ID. All fields are optional except id.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string" },
|
||||
description: { type: "string", maxLength: 500 },
|
||||
clientId: { type: "string" },
|
||||
startedAt: { type: "string", format: "date-time" },
|
||||
endedAt: { type: "string", format: "date-time" },
|
||||
hours: { type: "number", minimum: 0 },
|
||||
rate: { type: "number", minimum: 0 },
|
||||
notes: { type: "string", maxLength: 500 },
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
schema: z.object({
|
||||
id: z.string(),
|
||||
description: z.string().max(500).optional(),
|
||||
clientId: z.string().optional().or(z.literal("")),
|
||||
startedAt: dateString.optional(),
|
||||
endedAt: dateString.optional(),
|
||||
hours: z.number().min(0).optional(),
|
||||
rate: z.number().min(0).optional(),
|
||||
notes: z.string().max(500).optional().or(z.literal("")),
|
||||
}),
|
||||
handler: async (input, caller) =>
|
||||
caller.timeEntries.update({
|
||||
...input,
|
||||
startedAt: input.startedAt ? parseDate(input.startedAt, "startedAt") : undefined,
|
||||
endedAt: input.endedAt ? parseDate(input.endedAt, "endedAt") : undefined,
|
||||
}),
|
||||
}),
|
||||
time_entries_delete: defineTool({
|
||||
description: "Delete a time entry by ID.",
|
||||
inputSchema: jsonSchemas.id,
|
||||
schema: z.object({ id: z.string() }),
|
||||
handler: async (input, caller) => caller.timeEntries.delete(input),
|
||||
}),
|
||||
time_entries_get_summary: defineTool({
|
||||
description:
|
||||
"Get total hours, total earnings, and entry count for the authenticated user, optionally filtered by date range.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
from: { type: "string", format: "date-time" },
|
||||
to: { type: "string", format: "date-time" },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
schema: z.object({
|
||||
from: dateString.optional(),
|
||||
to: dateString.optional(),
|
||||
}),
|
||||
handler: async (input, caller) =>
|
||||
caller.timeEntries.getSummary({
|
||||
from: input.from ? parseDate(input.from, "from") : undefined,
|
||||
to: input.to ? parseDate(input.to, "to") : undefined,
|
||||
}),
|
||||
}),
|
||||
} satisfies Record<string, ToolDefinition>;
|
||||
|
||||
function rpcResult(id: JsonRpcId, result: unknown, init?: ResponseInit) {
|
||||
|
||||
Reference in New Issue
Block a user