Skip to content

Commit e8ca89d

Browse files
author
Charlie Tonneslan
committed
fix(memory): use plain JSON Schema for outputSchema instead of Zod
Per the MCP spec, outputSchema must be a plain JSON Schema object. The memory server was passing Zod schema objects directly, which causes serialization failures when tools/list attempts to convert schemas to JSON-RPC responses. inputSchema still uses Zod for runtime validation (correct usage). outputSchema now uses plain JSON Schema objects for documentation and type hints (spec-compliant). Fixes #3622
1 parent f424458 commit e8ca89d

1 file changed

Lines changed: 73 additions & 18 deletions

File tree

src/memory/index.ts

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,28 @@ const RelationSchema = z.object({
252252
relationType: z.string().describe("The type of the relation")
253253
});
254254

255+
// JSON Schema equivalents for outputSchema (MCP spec requires plain JSON Schema, not Zod)
256+
const EntityJsonSchema = {
257+
type: "object" as const,
258+
properties: {
259+
name: { type: "string" as const },
260+
entityType: { type: "string" as const },
261+
observations: { type: "array" as const, items: { type: "string" as const } },
262+
},
263+
required: ["name", "entityType", "observations"] as const,
264+
};
265+
266+
const RelationJsonSchema = {
267+
type: "object" as const,
268+
properties: {
269+
from: { type: "string" as const },
270+
to: { type: "string" as const },
271+
relationType: { type: "string" as const },
272+
},
273+
required: ["from", "to", "relationType"] as const,
274+
};
275+
276+
255277
// The server instance and tools exposed to Claude
256278
const server = new McpServer({
257279
name: "memory-server",
@@ -268,7 +290,10 @@ server.registerTool(
268290
entities: z.array(EntitySchema)
269291
},
270292
outputSchema: {
271-
entities: z.array(EntitySchema)
293+
type: "object" as const,
294+
properties: {
295+
entities: { type: "array" as const, items: EntityJsonSchema },
296+
},
272297
}
273298
},
274299
async ({ entities }) => {
@@ -290,7 +315,10 @@ server.registerTool(
290315
relations: z.array(RelationSchema)
291316
},
292317
outputSchema: {
293-
relations: z.array(RelationSchema)
318+
type: "object" as const,
319+
properties: {
320+
relations: { type: "array" as const, items: RelationJsonSchema },
321+
},
294322
}
295323
},
296324
async ({ relations }) => {
@@ -315,10 +343,19 @@ server.registerTool(
315343
}))
316344
},
317345
outputSchema: {
318-
results: z.array(z.object({
319-
entityName: z.string(),
320-
addedObservations: z.array(z.string())
321-
}))
346+
type: "object" as const,
347+
properties: {
348+
results: {
349+
type: "array" as const,
350+
items: {
351+
type: "object" as const,
352+
properties: {
353+
entityName: { type: "string" as const },
354+
addedObservations: { type: "array" as const, items: { type: "string" as const } },
355+
},
356+
},
357+
},
358+
},
322359
}
323360
},
324361
async ({ observations }) => {
@@ -340,8 +377,11 @@ server.registerTool(
340377
entityNames: z.array(z.string()).describe("An array of entity names to delete")
341378
},
342379
outputSchema: {
343-
success: z.boolean(),
344-
message: z.string()
380+
type: "object" as const,
381+
properties: {
382+
success: { type: "boolean" as const },
383+
message: { type: "string" as const },
384+
},
345385
}
346386
},
347387
async ({ entityNames }) => {
@@ -366,8 +406,11 @@ server.registerTool(
366406
}))
367407
},
368408
outputSchema: {
369-
success: z.boolean(),
370-
message: z.string()
409+
type: "object" as const,
410+
properties: {
411+
success: { type: "boolean" as const },
412+
message: { type: "string" as const },
413+
},
371414
}
372415
},
373416
async ({ deletions }) => {
@@ -389,8 +432,11 @@ server.registerTool(
389432
relations: z.array(RelationSchema).describe("An array of relations to delete")
390433
},
391434
outputSchema: {
392-
success: z.boolean(),
393-
message: z.string()
435+
type: "object" as const,
436+
properties: {
437+
success: { type: "boolean" as const },
438+
message: { type: "string" as const },
439+
},
394440
}
395441
},
396442
async ({ relations }) => {
@@ -410,8 +456,11 @@ server.registerTool(
410456
description: "Read the entire knowledge graph",
411457
inputSchema: {},
412458
outputSchema: {
413-
entities: z.array(EntitySchema),
414-
relations: z.array(RelationSchema)
459+
type: "object" as const,
460+
properties: {
461+
entities: { type: "array" as const, items: EntityJsonSchema },
462+
relations: { type: "array" as const, items: RelationJsonSchema },
463+
},
415464
}
416465
},
417466
async () => {
@@ -433,8 +482,11 @@ server.registerTool(
433482
query: z.string().describe("The search query to match against entity names, types, and observation content")
434483
},
435484
outputSchema: {
436-
entities: z.array(EntitySchema),
437-
relations: z.array(RelationSchema)
485+
type: "object" as const,
486+
properties: {
487+
entities: { type: "array" as const, items: EntityJsonSchema },
488+
relations: { type: "array" as const, items: RelationJsonSchema },
489+
},
438490
}
439491
},
440492
async ({ query }) => {
@@ -456,8 +508,11 @@ server.registerTool(
456508
names: z.array(z.string()).describe("An array of entity names to retrieve")
457509
},
458510
outputSchema: {
459-
entities: z.array(EntitySchema),
460-
relations: z.array(RelationSchema)
511+
type: "object" as const,
512+
properties: {
513+
entities: { type: "array" as const, items: EntityJsonSchema },
514+
relations: { type: "array" as const, items: RelationJsonSchema },
515+
},
461516
}
462517
},
463518
async ({ names }) => {

0 commit comments

Comments
 (0)