Skip to content
Merged
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
77 changes: 77 additions & 0 deletions app/(app)/projects/[id]/social/api-tokens/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,83 @@ export default async function ApiTokensPage({
</div>

<ApiTokensClient tokens={(tokens ?? []) as TokenRow[]} />

<McpServerSection />
</div>
);
}

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 (
<section className="card space-y-4 p-5">
<div>
<h2 className="text-lg font-semibold">MCP server</h2>
<p className="mt-1 text-sm text-[var(--color-muted)]">
The same token also authenticates the CrawlProof{" "}
<a href="https://modelcontextprotocol.io" target="_blank" rel="noreferrer" className="underline">
MCP
</a>{" "}
server, so an AI agent can write and publish promo posts to your socials. Endpoint:{" "}
<code>{mcpUrl}</code>
</p>
</div>

<div>
<div className="text-xs uppercase tracking-wider text-[var(--color-muted)]">
Add to your agent (Claude Desktop, Cursor, …)
</div>
<pre className="mt-1 overflow-x-auto rounded border border-[var(--color-border)] bg-[var(--color-bg-deep)] p-3 text-xs">
{agentConfig}
</pre>
</div>

<div>
<div className="text-xs uppercase tracking-wider text-[var(--color-muted)]">
List the tools (curl)
</div>
<pre className="mt-1 overflow-x-auto rounded border border-[var(--color-border)] bg-[var(--color-bg-deep)] p-3 text-xs">
{curlList}
</pre>
</div>

<div>
<div className="text-xs uppercase tracking-wider text-[var(--color-muted)]">
Write &amp; post in one call (curl)
</div>
<pre className="mt-1 overflow-x-auto rounded border border-[var(--color-border)] bg-[var(--color-bg-deep)] p-3 text-xs">
{curlPromote}
</pre>
</div>

<p className="text-xs text-[var(--color-muted)]">
Set <code>CRAWLPROOF_MCP_TOKEN</code> to a token above. Tools:{" "}
<code>list_accounts</code>, <code>generate_promo_post</code>, <code>post_to_socials</code>,{" "}
<code>promote_url</code>. The <code>Accept: application/json, text/event-stream</code> header
is required. Cookie-auth platforms report <code>queued</code> — the post lands shortly after.
</p>
</section>
);
}
14 changes: 11 additions & 3 deletions app/api/mcp/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Loading