Compare commits

...
Author SHA1 Message Date
ajnart acb58c60a3 style: auto-fix lint and format 2026-08-25 05:44:48 +00:00
ajnart 5f84ce590b fix(mcp): return valid string for void tool results
JSON.stringify(undefined) returns the value undefined, not a string,
so void-returning MCP tools produced content blocks with text: undefined
causing clients to reject the call with -32602 despite the action running.

Serialize to 'null' as a fallback so the text field is always a string.
2026-08-25 07:43:22 +02:00
3 changed files with 28 additions and 1 deletions

No files matched your search

@@ -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);
@@ -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");
});
});
@@ -0,0 +1,3 @@
export function serializeMcpToolResult(result: unknown): string {
return JSON.stringify(result) ?? "null";
}