@openrouter/mcp connects to a remote Model Context Protocol server (Streamable HTTP or SSE) and exposes its tools as first-class callModel tools. You authenticate once, spread the returned tools into callModel({ tools }), and the SDK handles discovery, invocation, progress events, cancellation, resources, and elicitation.
stdio MCP servers are intentionally out of scope — this package targets remote servers you can reach over HTTP.When to use it
Reach for@openrouter/mcp when you want to plug an existing MCP server (Linear, GitHub, an internal tool server) into an Agent SDK loop without hand-rolling the protocol. It complements your own tool() definitions — MCP tools and locally-defined tools can live in the same tools array, and the SDK keeps the two apart with a source discriminant so your typed tool results stay typed.
If you only need a hosted MCP server for your editor or coding assistant, see the OpenRouter MCP server guide instead.
Installation
Quick start
CallcreateMCPTools() once, spread mcp.tools into callModel, and close the handle when you’re done:
createMCPTools() returns a handle:
mcp.tools— an array of Agent SDK tools, one per MCP tool the server advertises. Spread them directly intocallModel({ tools }).mcp.serialize()— capture the current tool list (and, optionally, credentials) as JSON for cache reuse.mcp.close()— disconnect from the server. Call it when you’re done with the handle.
Authentication
Auth is supplied once and reused for tool discovery and every tool call. Choose the shape that matches how your MCP server expects to be called:OAuthClientProvider over caching a static bearer token when the server supports OAuth. The transport refreshes through the provider automatically, so tokens stay valid without you re-issuing them.
Combining with your own tools
MCP tools coexist with your owntool() definitions. Every tool the wrapper produces is marked with the _mcp brand, so the Agent SDK can tell the two apart and preserve type safety on your typed results:
The source discriminant
ToolExecutionResult (and the streaming tool.result event) carry source: 'client' | 'mcp':
source: 'client'— your own tools.resultis typed from the tool’soutputSchema.source: 'mcp'— a wrapped MCP tool.resultisunknownbecause the MCP server describes its output at runtime.
source === 'client' recovers precise, schema-derived results for every locally-defined tool. Without it, a single untyped MCP tool would collapse the entire result union to unknown.
isMcpTool() and markMcp()
Two helpers let you interact with the brand directly:
isMcpTool(tool)— runtime type guard for the_mcpbrand. Useful in lifecycle hooks or custom stop conditions that need to treat MCP tools differently.markMcp(tool)— add the brand to an already-built client tool.@openrouter/mcpuses this internally on every wrapped tool (including the synthetic resource tools); you rarely need to call it yourself.
Caching and rehydration
Every call tocreateMCPTools() performs an initialize + tools/list round-trip. For cold-start-heavy workloads (serverless, edge, per-request handlers) you can persist a snapshot and rebuild the handle without touching the network.
Manual serialization
Using an MCPCacheStore
Pass a store and key to createMCPTools() and it will rehydrate on hit and connect + write on miss automatically:
MCPCacheStore (a small get / set / delete interface) to back the cache with Redis, a database, or any KV store you already run.
Streaming, progress, and cancellation
Wrapped MCP tools behave like generator tools. Progress notifications the server emits during a tool call surface astool.progress events on the same streams your own tools use:
AbortSignal to createMCPTools({ signal }) and every in-flight tool call is cancelled when the signal aborts. Individual callModel invocations can also be aborted through their own signal.
autoRefreshOnListChanged (on by default) keeps mcp.tools in sync when the server emits tools/list_changed — the next callModel step sees the updated list without you reconnecting.
Resources and elicitation
Resources as synthetic tools
Resources as synthetic tools
When the MCP server advertises resources,
@openrouter/mcp exposes them as two synthetic tools the model can call directly:list_resources— enumerate available resources.read_resource— fetch a resource by URI.
resources: false to hide them, or resources: { mode: 'synthetic-tools' } to configure future modes.Handling elicitation requests
Handling elicitation requests
Some MCP servers pause a tool call to ask the caller for structured input (If you omit the handler, requests are auto-declined so the tool call fails cleanly rather than hanging.
elicitation/create). Pass an onElicitation handler to answer them:Multiple servers
Spread the tools from more than one handle to compose several MCP servers into a single agent. UsetoolNamePrefix to keep names unambiguous:
Options
Related
- Tools — define your own tools with
tool()and the Zod schema helpers. - Streaming — consume
tool.resultandtool.progressevents, including thesourcediscriminant. - OpenRouter MCP server — the hosted MCP endpoint for your editor or coding assistant.