Documentation
¶
Overview ¶
Package mcp implements a client for the Model Context Protocol (MCP): a JSON-RPC 2.0 based protocol that lets an AI application discover and invoke tools exposed by an external server process.
Index ¶
- func Tools(ctx context.Context, c *Client) ([]ai.Tool, error)
- type CapabilityError
- type Client
- func (c *Client) CallTool(ctx context.Context, name string, args json.RawMessage) (*ToolResult, error)
- func (c *Client) Close() error
- func (c *Client) Complete(ctx context.Context, ref CompletionRef, argName, argValue string) (*Completion, error)
- func (c *Client) GetPrompt(ctx context.Context, name string, args map[string]string) (string, []PromptMessage, error)
- func (c *Client) Initialize(ctx context.Context) error
- func (c *Client) ListPrompts(ctx context.Context) ([]Prompt, error)
- func (c *Client) ListResourceTemplates(ctx context.Context) ([]ResourceTemplate, error)
- func (c *Client) ListResources(ctx context.Context) ([]Resource, error)
- func (c *Client) ListTools(ctx context.Context) ([]ToolDef, error)
- func (c *Client) ProtocolVersion() string
- func (c *Client) ReadResource(ctx context.Context, uri string) ([]ResourceContents, error)
- func (c *Client) SetElicitationHandler(h ElicitationHandler)
- func (c *Client) SetNotificationHandler(h NotificationHandler)
- func (c *Client) SetRoots(roots []Root)
- func (c *Client) SetSamplingHandler(h SamplingHandler)
- type Completion
- type CompletionRef
- type CreateMessageRequest
- type CreateMessageResult
- type ElicitationHandler
- type ElicitationRequest
- type ElicitationResult
- type HTTPOption
- type NotificationHandler
- type Prompt
- type PromptArgument
- type PromptMessage
- type PromptPart
- type RPCError
- type Resource
- type ResourceContents
- type ResourceTemplate
- type Root
- type SamplingHandler
- type SamplingMessage
- type TokenProvider
- type TokenProviderFunc
- type ToolContent
- type ToolDef
- type ToolResult
- type Transport
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type CapabilityError ¶ added in v0.2.0
type CapabilityError struct {
Capability string
}
CapabilityError is returned when a method requires a server capability (as advertised in the "initialize" response) that the server did not declare. It is returned before any request is sent for that method.
func (*CapabilityError) Error ¶ added in v0.2.0
func (e *CapabilityError) Error() string
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a JSON-RPC 2.0 client over a Transport, specialised for MCP. A single background goroutine reads incoming messages and dispatches responses to the waiting call by id. It is safe for concurrent use.
func (*Client) CallTool ¶
func (c *Client) CallTool(ctx context.Context, name string, args json.RawMessage) (*ToolResult, error)
CallTool issues tools/call with the given arguments (a JSON object; a nil args is sent as {}). Every content part is preserved, in order, in ToolResult.Content; parts of type "text" are additionally concatenated in order into ToolResult.Text for convenience. It returns a *CapabilityError without sending any request if the server's "initialize" response didn't advertise the "tools" capability.
func (*Client) Close ¶
Close shuts down the receive loop, abandons any pending calls with a "mcp: client closed" error, and closes the underlying transport (via closeWith, which performs the actual transport.Close call — see its doc). It also waits (up to closeDrainGrace) for any in-flight server-request dispatch goroutines (dispatchWG) to finish before returning, so that in the normal case — a well-behaved ElicitationHandler that respects the ctx it's given — no dispatch goroutine outlives Close. A handler that ignores ctx and blocks indefinitely cannot make Close hang forever: after closeDrainGrace, Close returns anyway (see that constant's doc for why leaving such a goroutine running is safe).
Ordering matters: closeWith cancels c.ctx (so a ctx-respecting handler in a dispatch goroutine exits promptly) and closes the transport (so recvLoop's blocked Receive call returns an error and recvLoop exits, closing loopDone). Only after <-c.loopDone — meaning recvLoop has definitely stopped and will spawn no further dispatch goroutines — is it safe to wait on dispatchWG: sync.WaitGroup forbids a positive-delta Add racing with a Wait call that could observe a zero counter, and waiting immediately after closeWith (while recvLoop might still be spawning new dispatch goroutines) would risk exactly that race.
func (*Client) Complete ¶ added in v0.2.0
func (c *Client) Complete(ctx context.Context, ref CompletionRef, argName, argValue string) (*Completion, error)
Complete issues "completion/complete" to request argument autocompletion suggestions for argName/argValue against ref (a prompt or resource template). It returns a *CapabilityError without sending any request if the server's "initialize" response did not advertise the "completions" capability.
func (*Client) GetPrompt ¶ added in v0.2.0
func (c *Client) GetPrompt(ctx context.Context, name string, args map[string]string) (string, []PromptMessage, error)
GetPrompt issues prompts/get for name with the given arguments (may be nil), returning the server's description and the rendered messages with content parts flattened into PromptPart slices.
func (*Client) Initialize ¶
Initialize performs the MCP handshake: an "initialize" request, followed by a "notifications/initialized" notification once the server replies. It sends the latest supported protocol version (supportedProtocolVersions[0]) and accepts any version the server returns that is in supportedProtocolVersions, rejecting the handshake only if the returned version is outside that set. The negotiated version is stored on the Client (see ProtocolVersion). The server's advertised capabilities are likewise stored on the Client so later calls (e.g. ListResources, ListPrompts) can gate on them.
func (*Client) ListPrompts ¶ added in v0.2.0
ListPrompts issues prompts/list, transparently paginating via nextCursor. It returns a *CapabilityError without sending any request if the server's "initialize" response did not advertise the "prompts" capability.
func (*Client) ListResourceTemplates ¶ added in v0.2.0
func (c *Client) ListResourceTemplates(ctx context.Context) ([]ResourceTemplate, error)
ListResourceTemplates issues resources/templates/list, transparently paginating via nextCursor. Same capability gate as ListResources.
func (*Client) ListResources ¶ added in v0.2.0
ListResources issues resources/list, transparently paginating via nextCursor until the server stops returning one. It returns a *CapabilityError without sending any request if the server's "initialize" response did not advertise the "resources" capability.
func (*Client) ListTools ¶
ListTools issues tools/list, transparently paginating via nextCursor until the server stops returning one. It returns a *CapabilityError without sending any request if the server's "initialize" response didn't advertise the "tools" capability.
func (*Client) ProtocolVersion ¶ added in v0.2.1
ProtocolVersion returns the MCP protocol version negotiated during Initialize (one of supportedProtocolVersions). It is empty until Initialize has completed successfully.
func (*Client) ReadResource ¶ added in v0.2.0
ReadResource issues resources/read for uri. A resource contents entry carrying "text" decodes into ResourceContents.Text; one carrying a base64 "blob" decodes into ResourceContents.Blob.
func (*Client) SetElicitationHandler ¶ added in v0.2.0
func (c *Client) SetElicitationHandler(h ElicitationHandler)
SetElicitationHandler installs h as the handler invoked for "elicitation/create" requests from the server. Call this before Initialize: the "elicitation" capability is only declared to the server when a handler has been set.
func (*Client) SetNotificationHandler ¶ added in v0.3.0
func (c *Client) SetNotificationHandler(h NotificationHandler)
SetNotificationHandler installs h as the handler invoked for server-initiated notifications. Call this before Initialize so no notification sent early in the session is missed. With no handler installed (the default), incoming notifications are silently dropped.
Delivery is best-effort: if the bounded dispatch pool (shared with server-initiated requests) is saturated when a notification arrives, that notification is dropped rather than queued or blocking recvLoop, matching the fire-and-forget semantics of JSON-RPC notifications.
func (*Client) SetRoots ¶ added in v0.3.0
SetRoots installs the fixed set of roots reported to "roots/list" requests from the server and causes Initialize to declare the "roots" capability (listChanged: false — v1 has no dynamic root updates). Call before Initialize: the "roots" capability is only declared to the server when roots have been set.
func (*Client) SetSamplingHandler ¶ added in v0.3.0
func (c *Client) SetSamplingHandler(h SamplingHandler)
SetSamplingHandler installs h as the handler invoked for "sampling/createMessage" requests from the server. Call this before Initialize: the "sampling" capability is only declared to the server when a handler has been set.
type Completion ¶ added in v0.2.0
type Completion struct {
Values []string
Total int // server's total count when provided
HasMore bool
}
Completion is the server's suggested completions for one argument.
type CompletionRef ¶ added in v0.2.0
type CompletionRef struct {
Type string // "ref/prompt" or "ref/resource"
Name string // prompt name (ref/prompt)
URI string // resource template URI (ref/resource)
}
CompletionRef identifies what argument completion is being requested for: either a prompt (Type "ref/prompt", Name set) or a resource template (Type "ref/resource", URI set).
type CreateMessageRequest ¶ added in v0.3.0
type CreateMessageRequest struct {
Messages []SamplingMessage
SystemPrompt string
MaxTokens int
ModelPreferences json.RawMessage // passed through verbatim; nil if absent
}
CreateMessageRequest is the payload of a server-initiated "sampling/createMessage" request: the server is asking the client to run an LLM completion on its behalf.
type CreateMessageResult ¶ added in v0.3.0
type CreateMessageResult struct {
Role string // typically "assistant"
Content json.RawMessage // wire content object
Model string
StopReason string
}
CreateMessageResult is the client's reply to a CreateMessageRequest.
type ElicitationHandler ¶ added in v0.2.0
type ElicitationHandler func(ctx context.Context, req ElicitationRequest) (ElicitationResult, error)
ElicitationHandler is called when the server sends an "elicitation/create" request. A nil handler installed on the Client causes it to auto-respond with Action "decline" and not declare the "elicitation" capability during Initialize.
Implementations must respect ctx: it is cancelled when the Client is closed, and a handler that ignores cancellation and blocks indefinitely (e.g. on an unrelated channel, a UI prompt, or a stuck downstream call) will not hang Client.Close forever, but it will delay it — Close waits up to a short grace period (see closeDrainGrace) for in-flight handlers to finish before giving up and returning anyway.
type ElicitationRequest ¶ added in v0.2.0
type ElicitationRequest struct {
Message string
RequestedSchema json.RawMessage // JSON schema of the requested object
}
ElicitationRequest is the payload of a server-initiated "elicitation/create" request: the server is asking the client to gather structured input from the user mid-session.
type ElicitationResult ¶ added in v0.2.0
type ElicitationResult struct {
Action string // "accept" | "decline" | "cancel"
Content map[string]any // set when Action == "accept"
}
ElicitationResult is the client's reply to an ElicitationRequest.
type HTTPOption ¶ added in v0.2.0
type HTTPOption func(*httpTransport)
HTTPOption configures the Streamable HTTP transport constructed by NewStreamableHTTPTransportWithOptions.
func WithAuthHeader ¶ added in v0.2.0
func WithAuthHeader(name string) HTTPOption
WithAuthHeader sends the TokenProvider's token under the header named name (the raw token value, no "Bearer " prefix) instead of the default Authorization header. It has no effect without a TokenProvider.
func WithHTTPClientOpt ¶ added in v0.2.0
func WithHTTPClientOpt(c *http.Client) HTTPOption
WithHTTPClientOpt overrides the *http.Client used to send requests (default http.DefaultClient).
func WithHTTPRetry ¶ added in v0.2.0
func WithHTTPRetry(maxRetries int) HTTPOption
WithHTTPRetry enables retrying Send on transient failures: HTTP 429/503 responses (always safe — the server either rejected or didn't process the request) and a conservative allowlist of pre-delivery connection errors: connection-refused and DNS resolution failures, plus any error surfaced during the dial phase (a *net.OpError with Op == "dial"). These all prove the request bytes never reached the server, so retrying cannot cause a side-effecting call (e.g. tools/call) to run twice.
A generic client.Do error that isn't on that allowlist (e.g. "connection reset by peer" while reading the response, which can happen *after* the server has already processed a POST) is deliberately NOT retried: the transport cannot tell whether the server received and acted on the request, and retrying could double-execute a non-idempotent tool call. If your deployment needs broader retry coverage, wrap the *http.Client (WithHTTPClientOpt) with your own idempotency-aware retry logic instead.
maxRetries is the number of retries after the initial attempt (0, the default, disables retrying entirely). Retries use capped exponential backoff, honoring a Retry-After response header when present (seconds or an HTTP-date), and respect ctx cancellation while backing off. 4xx responses other than 429, and any failure once response bytes (JSON or SSE) have begun being consumed, are never retried. Each retry attempt re-invokes the configured TokenProvider, if any, for a fresh token — the transport does not retry 401s itself; refreshing credentials on auth failure is the TokenProvider's job.
No head-of-line blocking: httpTransport self-serializes (see SelfSerializes), so Client does not hold its write-serialization slot across Send. A call's retry backoff here (up to ~10s per attempt, capped by defaultRetryMaxDelay) therefore does NOT block any other concurrent call, or any server-initiated request reply, on the same Client — each Send (including its retries) runs fully independently.
func WithTokenProvider ¶ added in v0.2.0
func WithTokenProvider(tp TokenProvider) HTTPOption
WithTokenProvider configures tp to supply a fresh bearer token on every request (see TokenProvider's doc for details). It overrides any static Authorization header.
type NotificationHandler ¶ added in v0.3.0
type NotificationHandler func(method string, params json.RawMessage)
NotificationHandler is called for each server-initiated notification (a JSON-RPC message with a method but no id, e.g. "notifications/message" or "notifications/resources/updated"). params is the raw, undecoded params object from the wire; the handler is responsible for unmarshaling it into whatever shape the given method implies.
The handler runs on a dispatch goroutine spawned by recvLoop, bounded by the same dispatchSem used for server-initiated requests (see maxConcurrentServerDispatch), and therefore may run concurrently with other dispatched notifications, server requests, or in-flight calls. Since a notification owes no reply, a handler that blocks does not delay any response to the server, but it does hold a dispatch slot: a handler that never returns will eventually starve delivery of further notifications and server requests once the bound is exhausted.
type Prompt ¶ added in v0.2.0
type Prompt struct {
Name string
Title string
Description string
Arguments []PromptArgument
}
Prompt describes one prompt template exposed by the server.
type PromptArgument ¶ added in v0.2.0
PromptArgument describes one argument a Prompt accepts.
type PromptMessage ¶ added in v0.2.0
type PromptMessage struct {
Role string // "user" / "assistant"
Content []PromptPart
}
PromptMessage is one message in a prompt's rendered conversation.
type PromptPart ¶ added in v0.2.0
type PromptPart struct {
Type string
Text string
Resource *ResourceContents // for "resource" parts
Data []byte // decoded, for "image"/"audio" parts
MimeType string
}
PromptPart is one content part of a PromptMessage. Type is always set; which of Text, Resource, or Data is populated depends on Type ("text", "resource", "image", "audio"). Content types the client doesn't recognize are preserved with Type set and no error.
type RPCError ¶
RPCError is returned when the server replies with a JSON-RPC error object. It is errors.As-able.
type ResourceContents ¶ added in v0.2.0
ResourceContents is the body of one resource, as returned by ReadResource or embedded in a prompt message. Exactly one of Text or Blob is set, depending on whether the server sent "text" or a base64 "blob".
type ResourceTemplate ¶ added in v0.2.0
type ResourceTemplate struct {
URITemplate string
Name string
Title string
Description string
MimeType string
}
ResourceTemplate describes one RFC 6570 URI template exposed by the server, from which concrete resource URIs can be constructed.
type Root ¶ added in v0.3.0
Root is a filesystem root the client exposes to the server (MCP roots capability). URI must be a file:// URI per the MCP spec.
type SamplingHandler ¶ added in v0.3.0
type SamplingHandler func(ctx context.Context, req CreateMessageRequest) (CreateMessageResult, error)
SamplingHandler is called when the server sends a "sampling/createMessage" request. A nil handler installed on the Client causes "sampling/createMessage" requests to be rejected with a JSON-RPC -32601 "Method not found" error and the "sampling" capability is not declared during Initialize.
Implementations must respect ctx: it is cancelled when the Client is closed, and a handler that ignores cancellation and blocks indefinitely will not hang Client.Close forever, but it will delay it — Close waits up to a short grace period (see closeDrainGrace) for in-flight handlers to finish before giving up and returning anyway.
type SamplingMessage ¶ added in v0.3.0
type SamplingMessage struct {
Role string // "user" | "assistant"
Content json.RawMessage // wire content object: {"type":"text","text":...} etc.
}
SamplingMessage is one message in a server-initiated "sampling/createMessage" request or its reply: a role and a wire content object (e.g. {"type":"text","text":...}), preserved verbatim.
type TokenProvider ¶ added in v0.2.0
TokenProvider supplies a bearer token per request, enabling refresh and rotation: Token is called fresh on every Send (and on every retry attempt), and its result is sent as "Authorization: Bearer <token>" unless a custom header has been configured via WithAuthHeader, in which case the raw token is sent under that header instead. A TokenProvider overrides any static Authorization header configured via headers/ NewStreamableHTTPTransport.
Because httpTransport self-serializes (see SelfSerializes), Client does not hold a client-wide lock across Send, so a TokenProvider backing an httpTransport used by a Client with multiple concurrent in-flight calls must support Token being called concurrently by more than one goroutine.
type TokenProviderFunc ¶ added in v0.2.0
TokenProviderFunc adapts a function to the TokenProvider interface.
type ToolContent ¶ added in v0.3.0
type ToolContent struct {
Type string // "text", "image", "audio", "resource", ...
Text string // set for "text"
Data string // base64 payload for "image"/"audio"
MimeType string // media type for binary parts
Raw json.RawMessage // the full wire part, for forward compatibility
}
ToolContent is one content part of a CallTool result, preserved verbatim from the wire response.
type ToolDef ¶
type ToolDef struct {
Name string
Description string
InputSchema json.RawMessage
}
ToolDef describes one tool exposed by the server.
type ToolResult ¶
type ToolResult struct {
Text string // concatenated text content parts
Content []ToolContent // every content part, in wire order, preserved verbatim
IsError bool
}
ToolResult is the outcome of a CallTool invocation.
type Transport ¶
type Transport interface {
Send(ctx context.Context, msg json.RawMessage) error
Receive(ctx context.Context) (json.RawMessage, error)
Close() error
}
Transport moves one JSON-RPC message each way. Implementations are safe for one concurrent reader and one concurrent writer (i.e. Send may be called concurrently with Receive, but Send is expected to be called by at most one goroutine at a time, as is Receive) — UNLESS the implementation also implements selfSerializingTransport and reports SelfSerializes() == true, in which case concurrent Send calls from multiple goroutines are required to be safe (see that interface's doc).
func NewStdioTransport ¶
NewStdioTransport launches cmd (argv form) and speaks newline-delimited JSON-RPC over its stdin/stdout (MCP stdio framing: one JSON object per line). Env entries are appended to the child's environment. The child's stderr is passed through to os.Stderr. Close closes the child's stdin, waits briefly for it to exit on its own, and kills it if it hasn't.
cmd is trusted developer configuration, executed verbatim; callers passing user-influenced input are responsible for validating it.
func NewStreamableHTTPTransport ¶
NewStreamableHTTPTransport speaks the MCP Streamable HTTP transport: each Send POSTs the JSON-RPC message to url (Content-Type application/json, Accept "application/json, text/event-stream"); responses arrive either as a direct application/json body or as an SSE stream (text/event-stream) whose events carry JSON-RPC messages — both are fed to Receive in order. The Mcp-Session-Id response header, when present, is captured and echoed on subsequent requests. headers are added to every request (e.g. Authorization).
For an SSE response, Send hands the body off to a per-response drain goroutine that enqueues messages as they arrive (in arrival order) and returns as soon as the response headers are read — it does not block until the stream ends. This matters because Client serializes all Sends behind one mutex: draining synchronously would hold that mutex, and the MCP spec only says a server SHOULD close the SSE stream after sending its response (2025-03-26), not that it MUST, so a server that keeps the stream open would otherwise wedge every subsequent call. Per response, only one goroutine drains, so ordering within that response's messages is preserved; Close closes any still-open response bodies, which unblocks their drain goroutines' reads and lets them exit.
Close marks the transport closed, closes any response bodies still being drained, unblocks any blocked Receive, and — if a session id was captured — issues a best-effort DELETE carrying that session id to let the server free session state promptly (see terminateSession's doc). That DELETE is sent synchronously from Close and is bounded by its own 5-second timeout, so against a stalled or unreachable server Close (and therefore mcp.Client.Close, which calls it) can block for up to ~5 seconds; against a session-less transport (no session id ever captured) Close returns immediately.
Known deviations from the full 2025-03-26 Streamable HTTP transport spec (v1 is scoped to tools-only MCP clients, which don't need the rest):
- There is no standalone GET request opening a server-initiated SSE channel, so server-initiated requests/notifications outside of a POST response are not supported.
url is trusted developer configuration and is not SSRF-filtered — MCP servers legitimately live on localhost/private addresses; callers exposing URL choice to untrusted input must validate it themselves.
func NewStreamableHTTPTransportWithOptions ¶ added in v0.2.0
func NewStreamableHTTPTransportWithOptions(url string, opts ...HTTPOption) Transport
NewStreamableHTTPTransportWithOptions is the options-taking form of NewStreamableHTTPTransport: it speaks the same MCP Streamable HTTP transport (see NewStreamableHTTPTransport's doc for the full protocol description), configured by opts. Use WithTokenProvider/WithAuthHeader for per-request bearer auth, WithHTTPRetry to opt into retrying transient failures, and WithHTTPClientOpt to supply a custom *http.Client.