diff --git a/app/(app)/projects/[id]/social/api-tokens/page.tsx b/app/(app)/projects/[id]/social/api-tokens/page.tsx index ec5e3adf..1fab2733 100644 --- a/app/(app)/projects/[id]/social/api-tokens/page.tsx +++ b/app/(app)/projects/[id]/social/api-tokens/page.tsx @@ -59,6 +59,83 @@ export default async function ApiTokensPage({ + + ); } + +function McpServerSection() { + const site = process.env.NEXT_PUBLIC_SITE_URL ?? "https://crawlproof.com"; + const mcpUrl = `${site}/api/mcp`; + const agentConfig = `{ + "mcpServers": { + "crawlproof": { + "url": "${mcpUrl}", + "headers": { "Authorization": "Bearer crp_YOUR_TOKEN" } + } + } +}`; + // Note the Accept header — the Streamable-HTTP transport requires both types. + const curlList = `curl -s ${mcpUrl} \\ + -H "Authorization: Bearer $CRAWLPROOF_MCP_TOKEN" \\ + -H "Content-Type: application/json" \\ + -H "Accept: application/json, text/event-stream" \\ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'`; + const curlPromote = `curl -s ${mcpUrl} \\ + -H "Authorization: Bearer $CRAWLPROOF_MCP_TOKEN" \\ + -H "Content-Type: application/json" \\ + -H "Accept: application/json, text/event-stream" \\ + -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{ + "name":"promote_url","arguments":{"url":"https://example.com"}}}'`; + + return ( +
+
+

MCP server

+

+ The same token also authenticates the CrawlProof{" "} + + MCP + {" "} + server, so an AI agent can write and publish promo posts to your socials. Endpoint:{" "} + {mcpUrl} +

+
+ +
+
+ Add to your agent (Claude Desktop, Cursor, …) +
+
+          {agentConfig}
+        
+
+ +
+
+ List the tools (curl) +
+
+          {curlList}
+        
+
+ +
+
+ Write & post in one call (curl) +
+
+          {curlPromote}
+        
+
+ +

+ Set CRAWLPROOF_MCP_TOKEN to a token above. Tools:{" "} + list_accounts, generate_promo_post, post_to_socials,{" "} + promote_url. The Accept: application/json, text/event-stream header + is required. Cookie-auth platforms report queued — the post lands shortly after. +

+
+ ); +} diff --git a/app/api/mcp/route.ts b/app/api/mcp/route.ts index 94e856d6..ef4bb8e6 100644 --- a/app/api/mcp/route.ts +++ b/app/api/mcp/route.ts @@ -13,9 +13,17 @@ import { registerPromoteTools } from "@/lib/mcp/promote"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; -const handler = createMcpHandler((server) => { - registerPromoteTools(server); -}); +const handler = createMcpHandler( + (server) => { + registerPromoteTools(server); + }, + {}, + // The route is mounted at /api/mcp, so mcp-handler must derive its endpoint + // from basePath "/api" (streamable HTTP → /api/mcp). Without this it looks for + // "/mcp" and returns 404 on every real MCP call. maxDuration covers a + // promote_url that generates + posts across several accounts. + { basePath: "/api", maxDuration: 120 }, +); // Resolve the crp_ bearer token to a CrawlProof user; stash the user id on the // AuthInfo so tool handlers can scope to the caller. Returning undefined (with diff --git a/docs/mcp.md b/docs/mcp.md index b8ddf9a4..69110b93 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -40,6 +40,28 @@ For clients that only speak stdio, bridge with `mcp-remote`: } ``` +## Raw curl + +The Streamable-HTTP transport **requires** `Accept: application/json, text/event-stream`. + +```bash +# list tools +curl -s https://crawlproof.com/api/mcp \ + -H "Authorization: Bearer $CRAWLPROOF_MCP_TOKEN" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' + +# write + post in one call +curl -s https://crawlproof.com/api/mcp \ + -H "Authorization: Bearer $CRAWLPROOF_MCP_TOKEN" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"promote_url","arguments":{"url":"https://example.com"}}}' +``` + +Responses come back as SSE frames (`event: message` / `data: {…}`). + ## Tools (module: `promote`) | Tool | What it does |