From f58efea16437b79837075a3f63bc1572f3693ccc Mon Sep 17 00:00:00 2001 From: Ben Senescu <44480372+bensenescu@users.noreply.github.com> Date: Fri, 8 May 2026 16:17:28 -0400 Subject: [PATCH] Improve MCP structured result visibility (#167) --- src/server/mcp/formatters.test.ts | 37 +++++++++++++++++++++++++++ src/server/mcp/formatters.ts | 17 ++++++++---- src/server/mcp/tools/save-keywords.ts | 7 +++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/server/mcp/formatters.test.ts b/src/server/mcp/formatters.test.ts index 2c20680..9aae6b0 100644 --- a/src/server/mcp/formatters.test.ts +++ b/src/server/mcp/formatters.test.ts @@ -44,4 +44,41 @@ describe("mcpResponse", () => { }); expect(result.structuredContent).toEqual({ foo: "bar" }); }); + + it("mirrors metadata into structuredContent for clients that hide _meta", () => { + const result = mcpResponse({ + text: "hi", + meta: { + url: "https://app.openseo.so/p/1", + projectId: "1", + creditsRemaining: 100, + }, + structuredContent: { foo: "bar" }, + }); + + expect(result.structuredContent).toEqual({ + foo: "bar", + meta: { + url: "https://app.openseo.so/p/1", + projectId: "1", + creditsRemaining: 100, + }, + }); + expect(result._meta).toEqual({ + url: "https://app.openseo.so/p/1", + projectId: "1", + creditsRemaining: 100, + }); + }); + + it("uses metadata as structuredContent when no data payload is provided", () => { + const result = mcpResponse({ + text: "hi", + meta: { url: "https://app.openseo.so" }, + }); + + expect(result.structuredContent).toEqual({ + meta: { url: "https://app.openseo.so" }, + }); + }); }); diff --git a/src/server/mcp/formatters.ts b/src/server/mcp/formatters.ts index 4039228..f5a7762 100644 --- a/src/server/mcp/formatters.ts +++ b/src/server/mcp/formatters.ts @@ -17,15 +17,22 @@ export function mcpResponse(opts: { const result: CallToolResult = { content: [{ type: "text", text: opts.text }], }; - if (opts.structuredContent) { - result.structuredContent = opts.structuredContent; - } + let meta: Record | undefined; if (opts.meta) { - // Drop undefined keys so the wire payload stays clean. - const meta: Record = {}; + meta = {}; for (const [key, value] of Object.entries(opts.meta)) { if (value !== undefined) meta[key] = value; } + } + if (opts.structuredContent) { + result.structuredContent = + meta && Object.keys(meta).length > 0 + ? { ...opts.structuredContent, meta } + : opts.structuredContent; + } else if (meta && Object.keys(meta).length > 0) { + result.structuredContent = { meta }; + } + if (meta) { if (Object.keys(meta).length > 0) { result._meta = meta; } diff --git a/src/server/mcp/tools/save-keywords.ts b/src/server/mcp/tools/save-keywords.ts index ff2e2d4..a796921 100644 --- a/src/server/mcp/tools/save-keywords.ts +++ b/src/server/mcp/tools/save-keywords.ts @@ -46,6 +46,13 @@ export const saveKeywordsTool = { args.projectId, `/p/${args.projectId}/saved`, ), + structuredContent: { + projectId: args.projectId, + savedCount: args.keywords.length, + keywords: args.keywords, + locationCode: args.locationCode ?? DEFAULT_LOCATION_CODE, + languageCode: args.languageCode ?? DEFAULT_LANGUAGE_CODE, + }, }); }), };