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 () => {