Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
5 changes: 5 additions & 0 deletions src/resources/changelog.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
3 changes: 3 additions & 0 deletions src/tools/web-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown> = {
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;
Expand Down
14 changes: 14 additions & 0 deletions tests/test-live.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MassiveClient } from "../src/client.js";
import { webFetchHandler } from "../src/tools/web-fetch.js";

interface UserRow {
email: string;
Expand Down Expand Up @@ -30,6 +31,19 @@ async function main(): Promise<void> {
}),
);

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<string>("/search", {
Expand Down
5 changes: 2 additions & 3 deletions tests/tools/web-fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand Down