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 {
|
function textResult(data: unknown): ToolResult {
|
||||||
return {
|
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,
|
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>;
|
} satisfies Record<string, ToolDefinition>;
|
||||||
|
|
||||||
function rpcResult(id: JsonRpcId, result: unknown, init?: ResponseInit) {
|
function rpcResult(id: JsonRpcId, result: unknown, init?: ResponseInit) {
|
||||||
|
|||||||
Reference in New Issue
Block a user