diff --git a/apps/nextjs/src/app/api/mcp/[transport]/route.ts b/apps/nextjs/src/app/api/mcp/[transport]/route.ts index 364fe90c5..f6a303a86 100644 --- a/apps/nextjs/src/app/api/mcp/[transport]/route.ts +++ b/apps/nextjs/src/app/api/mcp/[transport]/route.ts @@ -14,6 +14,7 @@ import { db } from "@homarr/db"; import { getPackageVersion } from "~/versions/package-reader"; import { extractMcpTools } from "../_extract-tools"; +import { serializeMcpToolResult } from "../_serialize-result"; const logger = createLogger({ module: "mcpRoute" }); @@ -203,7 +204,7 @@ const mcpHandler = createMcpHandler( const input = args && Object.keys(args).length > 0 ? args : undefined; const result = await procedure(input); return { - content: [{ type: "text" as const, text: JSON.stringify(result) }], + content: [{ type: "text" as const, text: serializeMcpToolResult(result) }], }; } catch (error) { const message = sanitizeErrorMessage(error); diff --git a/apps/nextjs/src/app/api/mcp/_serialize-result.spec.ts b/apps/nextjs/src/app/api/mcp/_serialize-result.spec.ts new file mode 100644 index 000000000..8ee8df795 --- /dev/null +++ b/apps/nextjs/src/app/api/mcp/_serialize-result.spec.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; + +import { serializeMcpToolResult } from "./_serialize-result"; + +describe("serializeMcpToolResult", () => { + it("serializes objects as JSON strings", () => { + expect(serializeMcpToolResult({ boardId: "1" })).toBe('{"boardId":"1"}'); + }); + + it("serializes primitive values", () => { + expect(serializeMcpToolResult("hello")).toBe('"hello"'); + expect(serializeMcpToolResult(42)).toBe("42"); + expect(serializeMcpToolResult(true)).toBe("true"); + }); + + it("serializes null as the string null", () => { + expect(serializeMcpToolResult(null)).toBe("null"); + }); + + it("serializes void results as the string null so MCP content text is always a string", () => { + expect(serializeMcpToolResult(undefined)).toBe("null"); + }); +}); diff --git a/apps/nextjs/src/app/api/mcp/_serialize-result.ts b/apps/nextjs/src/app/api/mcp/_serialize-result.ts new file mode 100644 index 000000000..f49492062 --- /dev/null +++ b/apps/nextjs/src/app/api/mcp/_serialize-result.ts @@ -0,0 +1,3 @@ +export function serializeMcpToolResult(result: unknown): string { + return JSON.stringify(result) ?? "null"; +}