mcp-server
Manages the setup and protocol handling for the deep-research MCP server, enabling external agents to query research pipelines.
Install
mkdir -p .claude/skills/mcp-server && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/19039" && unzip -o skill.zip -d .claude/skills/mcp-server && rm skill.zipInstalls to .claude/skills/mcp-server
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Load when working on the MCP server surface — the site exposed AS a tool other agents (Claude, Cursor, any MCP client) can call — or when touching src/mcp.js, the POST /mcp route, the deep_research tool, its input schema, the JSON-RPC 2.0 / Streamable-HTTP protocol handling (initialize / tools/list / tools/call / notifications/initialized), or debugging an MCP client that can't connect or call the tool. Covers the file-layout rule (pure protocol helpers static, pipeline dynamic-imported), how a tool call reuses chat.js's per-request setup (quota gate, model routing, usage/billing recording), how to add or change a tool, the shared seams (model-routing.js, billing.js), and the validation ladder (mcp.test.js → live JSON-RPC probe).Key capabilities
- →Expose deep-research pipeline as an MCP server
- →Handle JSON-RPC 2.0 over Streamable HTTP
- →Implement `initialize`, `tools/list`, `tools/call`, and `notifications/initialized` methods
- →Ensure MCP inherits site access control and quota gates
- →Record usage and billing for MCP calls
- →Add or change deep_research tool schema
How it works
The skill exposes the deep-research pipeline as an MCP server via a single POST to `/mcp`, handling JSON-RPC 2.0 requests to initialize, list tools, and call the `deep_research` tool, while ensuring access control and usage recording.
Inputs & outputs
When to use mcp-server
- →Debugging MCP tool calls
- →Adding new search tools
- →Managing JSON-RPC protocols
- →Integrating research agents
About this skill
The MCP server — DeepResearch as a tool (POST /mcp)
What this is and why it exists
src/mcp.js exposes the whole deep-research pipeline as an MCP server.
Its headline tool is deep_research — question in, cited/validated/
source-diverse answer out — callable by any MCP client (Claude, Cursor, an
agent SDK). Alongside it the server re-exposes DistillSDK's four manifest
tools (sdk_list_modules, sdk_show_module, sdk_plan, sdk_validate,
via SDK_MCP_TOOLS) so an external agent can plan against the SDK without
shelling into the execution sandbox. Five tools total; the pipeline one is
the reason the server exists.
This is the ONE place the pipeline points outward: the architecture
roadmap (docs/ARCHITECTURE-ROADMAP.md §3) argues MCP belongs on the
outbound edge (DeepResearch as a tool other agents compose with), NOT as
internal plumbing — internal tool selection stays deterministic and in the
Worker's hands (invariant 1). So this server adds a transport over the
existing pipeline; it does not hand control flow to a model.
Transport is modern Streamable HTTP: JSON-RPC 2.0 over a single POST to
/mcp. The protocol surface is tiny, so it's hand-rolled — no
dependency (same minimal-deps stance as the rest of the repo). It
implements exactly the methods a minimal server needs:
initialize→initializeResult()(reportsPROTOCOL_VERSION,SERVER_INFO,capabilities: { tools: {} })tools/list→toolsListResult(config)(ALL_MCP_TOOLS=DEEP_RESEARCH_TOOLSDK_MCP_TOOLS, filtered by the account's exposure config)
tools/call→handleToolCall()→runDeepResearch()notifications/initialized→ no-op ack (a notification has noid, so it returns no response body)
Getting in: the two auth paths and the mcp. host
The route is wired in src/index.js twice, deliberately, and both sides ask
the same isMcpEndpoint(url, method) predicate (src/mcp-config.js) so they
can never disagree about what the endpoint is:
- Below the identity gate (
routeApi) — a signed-in session or the break-glass Basic header, exactly as before. - Above it — an MCP key (
src/mcp-key.js), the bearer credential an external client carries because it has no cookie jar.src/mcp-api.js'sresolveMcpKeyIdentityresolves it, and the router consults that function for this endpoint and nothing else. Its three outcomes matter:null(no key — fall through to the gate),{identity, config}, and{error}— a key that was PRESENTED and refused must come back as a 401 JSON-RPC error, never the sign-in HTML, or the client reports a transport failure and its user hunts the wrong problem.
An MCP key is never a login, and that is structural, not a promise:
identify() reads a Basic header and the dr_session cookie, so an
mck1. bearer cannot satisfy it in any position (test-pinned in
src/mcp-key.test.js, alongside the cross-family forgery matrix). It is
also deliberately not the Se/rver token — that family's closed
upstream-only vocabulary exists to protect Se/cure, whereas a key acts for a
Se/rver account inside the trust boundary. One key per account; minting
rotates, revoking rewrites the stored jti the token must match. The token
is returned once at mint and never stored (only jti + a six-character
hint), so "I lost it" is answered by minting again, not by reading it back.
Production also serves the endpoint on mcp.deepresearch.se (a
custom_domain route in wrangler.toml, provisioned through the Workers
custom-domains API — PUT /accounts/<id>/workers/domains with
{environment, hostname, service, zone_id}, which creates the DNS record and
the certificate; the host answered within ~20 s).
Declare it in
wrangler.tomlor lose it. Observed 2026-07-26: the domain was created by API, an unrelated merge tomainauto-deployed from awrangler.tomlthat did not list it, and the deploy removed the domain — the host went unreachable until it was re-created. The API call and the config entry are not alternatives; a domain missing fromroutesdoes not survive the next deploy.
Same Worker, same code path. On that host the BARE
ORIGIN answers too — clients disagree about whether the configured URL
includes the path, and a wrong-URL 404 is the commonest way an MCP setup
fails — and a GET serves the public setup page public/connect/
(allowlisted in src/assets.js). src/canonical.js leaves the host alone:
it only rewrites http → https and strips www..
What is exposed: per-account configuration
src/mcp-config.js is a pure leaf owning WHAT the surface offers, edited in
Settings → MCP server (public/js/account-mcp.js, one level below the
gear icon's Settings, the same treatment as LLM sharing) through
GET/PUT /api/mcp/config and POST/DELETE /api/mcp/key.
MCP_TOOL_CATALOGmirrors the served tool list exactly —src/mcp-config.test.jsfails the build when they drift, so a tool cannot ship on this surface without a switch to turn it off. Adding a tool means adding a catalog entry in the same change.- The config is read at CALL time and lives on the ACCOUNT, not in the token: narrowing takes effect on the next call for every outstanding key, with nothing to re-issue. The config endpoints are behind the identity gate, so a key holder can see the effects and never change them.
tools/listis filtered by it andtools/callENFORCES it (a client that cached an older listing still cannot reach a switched-off tool — reported as unknown-tool, since from the caller's side it does not exist).resolveResearchArgsreconciles adeep_researchcall's arguments with the account's defaults and override policy beforerunDeepResearchsees them. Asymmetry worth keeping: a caller may always DECLINE web search, and can never switch it back on.
Defaults are "everything exposed, site defaults, no key" — byte-for-byte the behaviour that existed before the configuration did, so an account that never opens the screen sees no change.
The load-bearing file-layout rule
src/mcp.test.js must unit-test the protocol without loading the
pipeline. So the module is split by import weight:
- Top of file — PURE, statically importable:
parseJsonRpc, the envelope builders (jsonRpcResult,jsonRpcError,toolResult),initializeResult,toolsListResult, the RPC error-code constants, andDEEP_RESEARCH_TOOL(the tool schema). The only static imports are leaf modules:http.js(jsonResponse),model-routing.js(resolveJsonModel) andmcp-config.js(the exposure seam) — none of which pulls the pipeline graph in. - Inside
tools/call— DYNAMICimport(): the pipeline and its deps (pipeline.js,berget.js,budget.js,validation.js,providers.js,config.js,quota.js,billing.js) are imported insiderunDeepResearch, so importing the module (as the test does) never drags inpipeline.js/berget.js/etc.
Keep this rule. New pure protocol logic goes at the top; anything that
needs the pipeline stays behind the dynamic import. mcp.test.js asserts
the module loads without the pipeline — breaking the split fails the suite.
How a tool call runs (runDeepResearch)
tools/call for deep_research mirrors src/chat.js's per-request setup
without editing chat.js — it deliberately re-does the same steps so a
change to the chat path doesn't silently change MCP:
- Guard
BERGET_API_TOKEN; build a single-turn[{role:"user", content:question}]conversation andvalidateMessagesit. - Resolve the model against the catalog (fail-soft to default if the
catalog is unreachable), honoring
args.modelthen the admin default.resolveJsonModel(catalog, model, DEFAULT_MODEL)picks the fixed JSON planning model — the SAME split-routing decisionchat.jsuses (shared viamodel-routing.js, invariant 3). - Budget:
clampBudget(args.time_budget_s ?? 120)thenMath.min(…, config.max_time_budget_s)— chat.js's exact two-step clamp. - Quota gate — the same one
/api/chatenforces. Admins (isSecretAdmin/role === "admin") are never blocked; every regular user is checked against their four-window budget BEFORE any spend. This is load-bearing: without it,/mcpwould be an unmetered bypass of the quota/api/chatapplies — each call runs the full pipeline for real Berget + Exa money. - Run
runPipeline, collect the streamed answer into one string, append the Sources list (withSources). - Record usage (
recordUsage) with the split-billing totals (summarizeSpend/exaCostfrom the sharedbilling.js) so MCP spend shows up in the usage bars and admin cost totals just like chat spend.
The tool's input schema (DEEP_RESEARCH_TOOL.inputSchema): required
question; optional time_budget_s (default 120, clamped 15–600), model
(Berget id; JSON phases stay on the reliable model regardless), web_search
(default true; false = answer directly, no search provider contacted).
Adding or changing a tool
- Change the deep_research schema: edit
DEEP_RESEARCH_TOOLat the top, read the new arg inrunDeepResearchwith a fail-soft default, and update thetools/listassertion inmcp.test.js. Keep descriptions written for an LLM caller (they're what the client model sees). - Add ANOTHER tool: add its schema constant at the top, put it in
ALL_MCP_TOOLS, add itsMCP_TOOL_CATALOGentry insrc/mcp-config.jsin the same change (the mirror test fails otherwise — and an account needs a way to switch it off), and branch onparsed.params.nameinhandleToolCall— which already dispatches thesdk_*family bySDK_TOOL_NAMESmembership before falling through todeep_research; anything matching neither is method-not-found. Any heavy work its handler needs stays behind a dynamic import. Ask whether
Content truncated.
When not to use it
- →When the pipeline should point inward instead of outward
- →When introducing model-driven tool selection on the inbound side
- →When adding a tool that is not a genuinely distinct outward capability
Limitations
- →The architecture argues MCP belongs on the outbound edge, NOT as internal plumbing
- →The module is split by import weight: pure protocol logic at top, pipeline behind dynamic import
- →Never introduce model-driven tool selection on the inbound side
How it compares
This skill provides a hand-rolled, minimal-dependency MCP server that exposes a deep-research pipeline as a tool for other agents, ensuring strict protocol adherence and shared access control, unlike a generic API endpoint.
Compared to similar skills
mcp-server side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| mcp-server (this skill) | 0 | 14d | No flags | Advanced |
| mcp-builder | 136 | 3mo | Review | Advanced |
| mcp-integration | 21 | 8mo | Review | Intermediate |
| opencode-orchestrator-creator | 8 | 8mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
mcp-builder
anthropics
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
mcp-integration
anthropics
This skill should be used when the user asks to "add MCP server", "integrate MCP", "configure MCP in plugin", "use .mcp.json", "set up Model Context Protocol", "connect external service", mentions "${CLAUDE_PLUGIN_ROOT} with MCP", or discusses MCP server types (SSE, stdio, HTTP, WebSocket). Provides comprehensive guidance for integrating Model Context Protocol servers into Claude Code plugins for external tool and service integration.
opencode-orchestrator-creator
IgorWarzocha
Creates universal OpenCode orchestrator folder structure with specialized agent that can manage swarm servers via curl commands
claude-opus-4-5-migration
anthropics
Migrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5. Use when the user wants to update their codebase, prompts, or API calls to use Opus 4.5. Handles model string updates and prompt adjustments for known Opus 4.5 behavioral differences. Does NOT migrate Haiku 4.5.
mcp-management
mrgoonie
Manage Model Context Protocol (MCP) servers - discover, analyze, and execute tools/prompts/resources from configured MCP servers. Use when working with MCP integrations, need to discover available MCP capabilities, filter MCP tools for specific tasks, execute MCP tools programmatically, access MCP prompts/resources, or implement MCP client functionality. Supports intelligent tool selection, multi-server management, and context-efficient capability discovery.
n8n-mcp-orchestrator
manutej
Expert MCP (Model Context Protocol) orchestration with n8n workflow automation. Master bidirectional MCP integration, expose n8n workflows as AI agent tools, consume MCP servers in workflows, build agentic systems, orchestrate multi-agent workflows, and create production-ready AI-powered automation pipelines with Claude Code integration.