Skip to main content
@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

Call createMCPTools() 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 into callModel({ 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:
Prefer an 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 own tool() 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. result is typed from the tool’s outputSchema.
  • source: 'mcp' — a wrapped MCP tool. result is unknown because the MCP server describes its output at runtime.
Narrowing on 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 _mcp brand. 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/mcp uses this internally on every wrapped tool (including the synthetic resource tools); you rarely need to call it yourself.

Caching and rehydration

Every call to createMCPTools() 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:
Implement MCPCacheStore (a small get / set / delete interface) to back the cache with Redis, a database, or any KV store you already run.
cacheCredentials is false by default. When enabled, snapshots contain bearer tokens or headers — treat the store as a secret store and namespace cache keys by principal in multi-tenant setups.

Streaming, progress, and cancellation

Wrapped MCP tools behave like generator tools. Progress notifications the server emits during a tool call surface as tool.progress events on the same streams your own tools use:
Pass an 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

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.
These are on by default. Set resources: false to hide them, or resources: { mode: 'synthetic-tools' } to configure future modes.
Some MCP servers pause a tool call to ask the caller for structured input (elicitation/create). Pass an onElicitation handler to answer them:
If you omit the handler, requests are auto-declined so the tool call fails cleanly rather than hanging.

Multiple servers

Spread the tools from more than one handle to compose several MCP servers into a single agent. Use toolNamePrefix to keep names unambiguous:

Options

  • Tools — define your own tools with tool() and the Zod schema helpers.
  • Streaming — consume tool.result and tool.progress events, including the source discriminant.
  • OpenRouter MCP server — the hosted MCP endpoint for your editor or coding assistant.