From 915f5121c25316558799503708fd71450754dade Mon Sep 17 00:00:00 2001 From: Volodymyr Rudenko Date: Thu, 11 Jun 2026 14:03:22 +0200 Subject: [PATCH] fix(web_fetch): include page body in structuredContent (MASS-739) web_fetch returned the page body only in the text content block, while structuredContent carried just {format, url, bytes} metadata. Clients that show the model structuredContent instead of the content blocks (e.g. Claude Cowork) received no page body. Include the body as structuredContent.body so the two representations are functionally equivalent per the MCP spec, matching the other tools. Also adds a handler-level live check (test:live) asserting structuredContent.body matches the text content, and bumps the version to 0.2.2 with a changelog entry. Co-Authored-By: Claude Fable 5 --- manifest.json | 2 +- package.json | 2 +- src/resources/changelog.md | 5 +++++ src/tools/web-fetch.ts | 3 +++ tests/test-live.ts | 14 ++++++++++++++ tests/tools/web-fetch.test.ts | 5 ++--- 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/manifest.json b/manifest.json index 48ad504..532fa34 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": "0.2", "name": "massive-mcp", "display_name": "Massive Web Render", - "version": "0.2.1", + "version": "0.2.2", "description": "Real-time web access for AI agents — fetch URLs, search Google, query AI chatbots.", "long_description": "Official MCP server for the Massive Web Render API.\n\nFour tools: web_fetch (any URL → Markdown/HTML, with JS rendering, captchas, and geo-targeting), web_search (parsed Google SERP with AI overview + people-also-asked), ai_chat_completion (ChatGPT/Gemini/Perplexity/Copilot answers with structured sources), account_status (credits remaining).\n\nThree reference resources surface inside MCP clients (Connectors panel / @-menu): docs://massive/pricing, docs://massive/geotargeting, docs://massive/changelog. Four starter prompts ship out of the box.\n\nPay-as-you-go pricing — see https://joinmassive.com/pricing. MIT-licensed; source at https://github.com/joinmassive/mcp-server. Compatible with Claude Desktop, Claude Code, Cursor, Continue, Cody, Windsurf, and any MCP-compatible client.", "author": { "name": "Massive Computing, Inc.", "url": "https://joinmassive.com" }, diff --git a/package.json b/package.json index d4f6361..2abc517 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@joinmassive/mcp-server", - "version": "0.2.1", + "version": "0.2.2", "description": "Official MCP server for the Massive Web Render API. Real-time web access for AI agents — fetch any URL, search Google, query AI chatbots — with JS rendering, captcha solving, and 195+ country geo-targeting.", "license": "MIT", "type": "module", diff --git a/src/resources/changelog.md b/src/resources/changelog.md index ee44906..1df6c8d 100644 --- a/src/resources/changelog.md +++ b/src/resources/changelog.md @@ -1,5 +1,10 @@ # Changelog +## v0.2.2 (2026-06-11) + +### Fixed +- **`web_fetch` body missing in clients that read `structuredContent`** (e.g. Claude Cowork): the page body was returned only in the text content block, while `structuredContent` carried just `{format, url, bytes}` metadata. Clients that show the model `structuredContent` instead of the content blocks received no page body. The body is now included as `structuredContent.body`, making the two representations functionally equivalent per the MCP spec (MASS-739). + ## v0.2.1 (2026-05-05) Polish on top of v0.2.0 — visual fixes for Claude Desktop and a runtime API migration. No new tool capabilities. diff --git a/src/tools/web-fetch.ts b/src/tools/web-fetch.ts index 5758468..c426cf0 100644 --- a/src/tools/web-fetch.ts +++ b/src/tools/web-fetch.ts @@ -59,10 +59,13 @@ export async function webFetchHandler(input: Input, client: MassiveClient): Prom difficulty: parsed.difficulty, }); + // `body` must be present here too: some clients (e.g. Claude Cowork) show the + // model only structuredContent, others only the text content blocks (MASS-739). const structured: Record = { format: parsed.format, url: parsed.url, bytes: Buffer.byteLength(body, "utf8"), + body, }; if (parsed.country) structured.country = parsed.country; if (parsed.city) structured.city = parsed.city; diff --git a/tests/test-live.ts b/tests/test-live.ts index bcf58df..f00aab0 100644 --- a/tests/test-live.ts +++ b/tests/test-live.ts @@ -1,4 +1,5 @@ import { MassiveClient } from "../src/client.js"; +import { webFetchHandler } from "../src/tools/web-fetch.js"; interface UserRow { email: string; @@ -30,6 +31,19 @@ async function main(): Promise { }), ); + results.push( + await run("web_fetch handler: structuredContent.body matches text content (MASS-739)", async () => { + const result = await webFetchHandler( + { url: "https://example.com", format: "markdown", difficulty: "low" }, + client, + ); + if (result.isError) return false; + const text = result.content[0]?.type === "text" ? result.content[0].text : ""; + const sc = result.structuredContent as { body?: string } | undefined; + return Boolean(sc?.body) && sc?.body === text && text.toLowerCase().includes("example domain"); + }), + ); + results.push( await run("web_search", async () => { const html = await client.get("/search", { diff --git a/tests/tools/web-fetch.test.ts b/tests/tools/web-fetch.test.ts index 1f01b57..d6b730a 100644 --- a/tests/tools/web-fetch.test.ts +++ b/tests/tools/web-fetch.test.ts @@ -27,9 +27,8 @@ describe("webFetchHandler", () => { format: "markdown", url: "https://example.com", bytes: 6, + body: "# Page", }); - // Body must NOT be duplicated into structuredContent — already in content[0].text - expect(result.structuredContent).not.toHaveProperty("content"); }); it("forwards optional country, city, device and echoes them in structured output", async () => { @@ -53,8 +52,8 @@ describe("webFetchHandler", () => { country: "DE", city: "Berlin", device: "iphone-15", + body: "ok", }); - expect(result.structuredContent).not.toHaveProperty("content"); }); it("returns an MCP error result on upstream failure", async () => {