Skip to main content
MCP (Model Context Protocol) tools allow your agent to connect to external MCP servers and use their tools. This enables your agent to interact with various services and resources through standardized MCP tool interfaces.

Connecting to an MCP Server

To use MCP tools, you first need to create an MCP client using the mcpclient.NewClient function.:

Parameters

  • ctx: The context for the connection
  • endpoint: The SSE endpoint URL of the MCP server (e.g., "http://localhost:9001/sse")

Custom Headers

You can also configure the MCP Client to send custom HTTP headers while interacting with the MCP server.

Filtering Tools

You can optionally restrict which tools are exposed to the agent using the WithToolFilter(...) option. Only the named tools are surfaced; all others are dropped.

Deferred (Lazy-Loaded) Tools

Use WithDeferredTools(...) to mark tools as deferred. Deferred tools are not added to the LLM’s tool list upfront; instead they are discovered and activated on demand via the ToolSearch meta-tool. This keeps the prompt small when an MCP server exposes a large number of tools.
Pass "*" to defer every tool the server exposes, which saves keeping a list in sync with a server whose catalog changes:
See Deferred Tools for how the activation flow works and when to defer a tool.

Schema Caching

By default, the client connects to the MCP server to fetch tool schemas on every ListTools call. You can cache schemas to avoid repeated round-trips:
  • WithCacheTTL(ttl time.Duration) sets how long cached schemas remain valid.
  • WithSchemaCache(cache SchemaCache) injects a SchemaCache implementation. When set, ListTools() checks the cache before connecting to the MCP server. Back it with Redis (or a similar shared store) to share cached schemas across multiple pods.

Approval-Required Tools

Use WithApprovalRequiredTools(...) to mark specific MCP tools as requiring human approval before they run. See the Human in the Loop page for the full approval flow.

Transport

You can set the transport to be used for the connection. Supported transports are SSE and Streamable HTTP. If not specified it defaults to SSE
On streamable-http, the client also opens the post-init server→client SSE stream. Some servers don’t support that standalone GET stream, and the client then hangs waiting on a stream that never opens. Turn it off for those servers:

Tool Annotations

MCP tools carry whatever behavioural hints their server declared — read-only, destructive, idempotent, open-world — and the SDK converts them into the same agents.ToolAnnotations shape a local function tool uses. Nothing extra is needed to pick them up, and one permission policy can read both:
The MCP wire format carries readOnlyHint and idempotentHint as bare booleans, so an absent hint arrives as false — which is the spec’s default for both. destructiveHint and openWorldHint stay unset when the server said nothing, leaving the conservative defaults (destructive, open world) to the Is* helpers. See Tool Annotations for the full table and how to declare them on your own tools.
Hints are self-reported. Never let a hint from an untrusted MCP server widen what a tool is allowed to do.

Progress Notifications

When a tool call is streaming, the client attaches a progress token so the server can send notifications/progress for the duration of that call. Those notifications arrive on the run’s chunk stream as tool.progress chunks, exactly like progress emitted by a function tool:
CallID ties each update back to the in-flight call. Total is optional — 0 means unknown. Progress is a live side stream: under the durable runtimes it is published directly rather than journaled, so it is never replayed.

Elicitation

An MCP server can ask the user for something mid-call rather than answering: a form to fill in, or a URL to visit. The SDK maps that onto the same pause the agent already uses for human-in-the-loop approvals — the run ends with an interrupt, and the answer arrives on a later request. The pause is surfaced as an Interrupt whose Mode says what is being asked:
For form and URL modes, Elicitations carries the server’s own request — its message, and for a form the RequestedSchema describing the fields to collect.

Answering a form

Resume the same way as an approval, but attach the filled form as the resolution’s Content. It must match the RequestedSchema the pause advertised:
An approval carrying content becomes an MCP accept; a rejection becomes a decline, and its content is dropped rather than delivered — a rejected form has no answer to pass on.
The request id and the server’s own opaque state are carried across the pause in thread state, so the resuming call answers the server’s original question rather than provoking a fresh one. That is what lets a server resume from where it left off instead of redoing the work that preceded the question.
Only one input request per call is supported: one interrupt carries one answer, so a second question in the same call would have nowhere to put its reply.

Complete Example

Here’s a complete example of an agent using MCP tools: