fix: convert MCP tool schemas from JSON Schema to Zod for SDK v1.29.0
All checks were successful
CI / ci (push) Successful in 28s
CI / ci (pull_request) Successful in 25s
CI / e2e (push) Successful in 1m2s
CI / e2e (pull_request) Successful in 1m3s

The MCP SDK v1.29.0 changed server.tool() to require Zod schemas
(raw shapes) instead of plain JSON Schema objects. The old format
triggered "expected a Zod schema or ToolAnnotations" errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 20:54:20 +02:00
parent 0a40d7627f
commit 68f6647f76
5 changed files with 89 additions and 169 deletions

View File

@@ -1,3 +1,4 @@
import { z } from "zod";
import type { db as prodDb } from "../../../db/index.ts";
import {
createCandidate,
@@ -31,14 +32,12 @@ export const threadToolDefinitions = [
description:
"List research threads. Threads are the recommended way to evaluate gear purchases — each thread tracks multiple candidates for a single gear slot, making it easy to compare options before committing.",
inputSchema: {
type: "object" as const,
properties: {
includeResolved: {
type: "boolean",
description:
"Include resolved threads (default: false, only active threads)",
},
},
includeResolved: z
.boolean()
.optional()
.describe(
"Include resolved threads (default: false, only active threads)",
),
},
},
{
@@ -46,11 +45,7 @@ export const threadToolDefinitions = [
description:
"Get a thread with all its candidates for detailed comparison.",
inputSchema: {
type: "object" as const,
properties: {
id: { type: "number", description: "Thread ID" },
},
required: ["id"],
id: z.number().describe("Thread ID"),
},
},
{
@@ -58,15 +53,8 @@ export const threadToolDefinitions = [
description:
"Start a new research thread for a gear slot. This is the preferred workflow: create a thread, add candidates with pros/cons/prices, compare them, then resolve the thread to add the winner to your collection.",
inputSchema: {
type: "object" as const,
properties: {
name: {
type: "string",
description: "Thread name (e.g. 'Handlebar bag')",
},
categoryId: { type: "number", description: "Category ID" },
},
required: ["name", "categoryId"],
name: z.string().describe("Thread name (e.g. 'Handlebar bag')"),
categoryId: z.number().describe("Category ID"),
},
},
{
@@ -74,35 +62,24 @@ export const threadToolDefinitions = [
description:
"Resolve a research thread by picking the winning candidate. The winner is automatically added to the gear collection as a new item, and the thread is marked as resolved.",
inputSchema: {
type: "object" as const,
properties: {
threadId: { type: "number", description: "Thread ID" },
candidateId: {
type: "number",
description: "ID of the winning candidate",
},
},
required: ["threadId", "candidateId"],
threadId: z.number().describe("Thread ID"),
candidateId: z.number().describe("ID of the winning candidate"),
},
},
{
name: "add_candidate",
description: "Add a candidate option to a research thread for comparison.",
inputSchema: {
type: "object" as const,
properties: {
threadId: { type: "number", description: "Thread ID" },
name: { type: "string", description: "Candidate name" },
categoryId: { type: "number", description: "Category ID" },
weightGrams: { type: "number", description: "Weight in grams" },
priceCents: { type: "number", description: "Price in cents" },
notes: { type: "string", description: "Notes" },
productUrl: { type: "string", description: "Product URL" },
imageFilename: { type: "string", description: "Image filename" },
pros: { type: "string", description: "Pros of this candidate" },
cons: { type: "string", description: "Cons of this candidate" },
},
required: ["threadId", "name", "categoryId"],
threadId: z.number().describe("Thread ID"),
name: z.string().describe("Candidate name"),
categoryId: z.number().describe("Category ID"),
weightGrams: z.number().optional().describe("Weight in grams"),
priceCents: z.number().optional().describe("Price in cents"),
notes: z.string().optional().describe("Notes"),
productUrl: z.string().optional().describe("Product URL"),
imageFilename: z.string().optional().describe("Image filename"),
pros: z.string().optional().describe("Pros of this candidate"),
cons: z.string().optional().describe("Cons of this candidate"),
},
},
{
@@ -110,36 +87,28 @@ export const threadToolDefinitions = [
description:
"Update a candidate's details (name, price, pros, cons, etc.).",
inputSchema: {
type: "object" as const,
properties: {
id: { type: "number", description: "Candidate ID" },
name: { type: "string", description: "Candidate name" },
weightGrams: { type: "number", description: "Weight in grams" },
priceCents: { type: "number", description: "Price in cents" },
categoryId: { type: "number", description: "Category ID" },
notes: { type: "string", description: "Notes" },
productUrl: { type: "string", description: "Product URL" },
imageFilename: { type: "string", description: "Image filename" },
imageSourceUrl: { type: "string", description: "Image source URL" },
status: {
type: "string",
description: "Status: researching, ordered, or arrived",
},
pros: { type: "string", description: "Pros" },
cons: { type: "string", description: "Cons" },
},
required: ["id"],
id: z.number().describe("Candidate ID"),
name: z.string().optional().describe("Candidate name"),
weightGrams: z.number().optional().describe("Weight in grams"),
priceCents: z.number().optional().describe("Price in cents"),
categoryId: z.number().optional().describe("Category ID"),
notes: z.string().optional().describe("Notes"),
productUrl: z.string().optional().describe("Product URL"),
imageFilename: z.string().optional().describe("Image filename"),
imageSourceUrl: z.string().optional().describe("Image source URL"),
status: z
.string()
.optional()
.describe("Status: researching, ordered, or arrived"),
pros: z.string().optional().describe("Pros"),
cons: z.string().optional().describe("Cons"),
},
},
{
name: "remove_candidate",
description: "Remove a candidate from a research thread.",
inputSchema: {
type: "object" as const,
properties: {
id: { type: "number", description: "Candidate ID to remove" },
},
required: ["id"],
id: z.number().describe("Candidate ID to remove"),
},
},
];