mcp

package
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 27, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

attach.go — the exported boot-time MCP server attachment helper (absorbs cmd/harbor's attachDevMCPServer from INCLUDING the config→ToolPolicy projection that the devstack mirror had silently dropped).

Attach wires ONE configured MCP server into a running stack: it projects the operator-facing policy YAML onto the driver's runtime ToolPolicy fields, spawns the transport, opens the MCP session, discovers tools, registers each ToolDescriptor on the tool catalog, surfaces the live Provider on the Registry (with its configured per-server policy + seeded discovery stats), and threads the Provider's Close into the caller's closer chain so stack teardown drains the subprocess. Fail-loud on every step: a misconfigured / unreachable MCP server must not boot silently (CLAUDE.md §13).

Package mcp is Harbor's Model Context Protocol (MCP) southbound driver. It implements `tools.ToolProvider` against a remote MCP server, exposing the server's tools / resources / prompts as Harbor `Tool` entries (RFC §6.4). Three wire transports are supported (stdio, SSE, streamable-HTTP) with auto-detect.

Concurrent reuse: a constructed *Provider is safe to share across N concurrent goroutines after Connect returns. All per-call state lives on the goroutine stack + the request `ctx`; descriptor fields are immutable after Discover.

Identity (RFC §4): the (tenant, user, session) triple is forwarded to the remote MCP server in the request's `_meta` map so trust signals flow across the seam.

Reliability shell: every Invoke runs inside `tools.RunWithPolicy` so timeout / retry / classifier behaviour is identical to the in-process driver.

Index

Constants

View Source
const EventTypeMCPAppAvailable events.EventType = "mcp.app_available"

EventTypeMCPAppAvailable is the canonical event the runtime emits when an invoked MCP tool declares an interactive MCP App via the `_meta.ui.resourceUri` slot on its tool DEFINITION (the spec-conformant placement in the `io.modelcontextprotocol/ui` dialect, captured at discovery). It is the discovery signal a Protocol client consumes off the event stream to mount the inline MCP App renderer in the chat surface for the run's turn — without it, a planner-initiated call to a tool carrying a `ui://` app reference reaches no surface and the renderer never activates. The payload is SafePayload by construction: it carries the server source id, the `ui://` resource URI, the display-mode hint (empty unless the server supplied one — the renderer defaults to inline), the default-deny raw-HTML trust posture, and the actor identity quadruple — no upstream MCP content bytes and no caller-controlled argument data.

View Source
const EventTypeMCPResourceOffloaded events.EventType = "mcp.resource_offloaded"

EventTypeMCPResourceOffloaded is the canonical event the runtime emits when an MCP resource read (an MCP App `ui://` document fetch, or an app-tool-call result) meets the heavy-content threshold and is routed through the ArtifactStore by reference instead of inlined. It is the loud bypass record the context-window safety net requires — heavy content is never inlined past the threshold and never silently truncated; the offload is observable. The payload is SafePayload by construction: it carries the artifact reference id, the resource URI or tool name, the byte size, and the actor identity quadruple — no upstream MCP content bytes.

View Source
const EventTypeMCPResourceUpdated events.EventType = "mcp.resource_updated"

EventTypeMCPResourceUpdated is the canonical event type emitted when the remote MCP server pushes a resource-update notification for a URI the driver previously subscribed to. The payload is SafePayload by construction — the URI is operator- trust-equivalent (it originates from an operator-configured MCP server) and the source ID is operator-supplied.

View Source
const ResourceMIMEType = "text/html;profile=mcp-app"

ResourceMIMEType is the single canonical media type an MCP App (`io.modelcontextprotocol/ui`) UI document carries. It mirrors the ext-apps SDK's `RESOURCE_MIME_TYPE` constant; the host advertises it in the `mimeTypes` UI capability so a conformant server gates its `ui://` tool registration on it.

Variables

View Source
var (
	// ErrInvalidConfig — operator-side misconfiguration: missing URL
	// for HTTP-flavoured transports, missing Command for stdio,
	// unknown transport mode, etc.
	ErrInvalidConfig = errors.New("mcp: invalid config")
	// ErrTransportFailed — every candidate transport failed at
	// Connect time. The wrapped causes preserve the per-transport
	// failure messages.
	ErrTransportFailed = errors.New("mcp: transport failed")
	// ErrNotConnected — Discover / Invoke / SubscribeResource called
	// before Connect (or after Close).
	ErrNotConnected = errors.New("mcp: provider not connected")
	// ErrMCPToolError — the server returned a CallToolResult with
	// IsError == true. The wrapped message carries the rendered
	// text body.
	ErrMCPToolError = errors.New("mcp: server returned tool error")
	// ErrSchemaInvalid — the server-advertised InputSchema failed to
	// compile; the descriptor is rejected at Discover time so the
	// catalog never holds a Tool whose Validate is broken.
	ErrSchemaInvalid = errors.New("mcp: invalid tool input schema")
	// ErrIdentityMissing — the per-invocation ctx had no identity
	// triple. AGENTS.md §6 rule 9: identity is mandatory; the MCP
	// driver fails closed rather than dispatching to a remote server
	// with an empty `_meta` block. Mirrors the HTTP and A2A drivers.
	ErrIdentityMissing = errors.New("mcp: identity missing from ctx")
)

Sentinel errors. Callers compare with errors.Is.

View Source
var (
	// ErrServerNotFound — the named server is not registered.
	ErrServerNotFound = fmt.Errorf("mcp: server not found")
	// ErrRegistryIdentityMissing — the read ctx had no identity triple.
	// Identity is mandatory (AGENTS.md §6 rule 9); the read fails closed.
	ErrRegistryIdentityMissing = fmt.Errorf("mcp: identity missing from ctx")
)

Sentinel errors. Callers compare with errors.Is.

Functions

func Attach added in v1.3.0

func Attach(ctx context.Context, ms config.MCPServerConfig, deps AttachDeps) error

Attach wires one configured MCP server (config.MCPServerConfig) into the supplied catalog + registry. See the file doc for the full lifecycle. Every error is wrapped with the failing step so the boot path's `mcp[<name>]: ...` prefix pins the offending server.

func IsUIResourceURI added in v1.4.0

func IsUIResourceURI(uri string) bool

IsUIResourceURI reports whether uri carries the reserved `ui://` scheme — the distinct recognition the MCP Apps extension requires so an ordinary file:// / https:// resource is never mistaken for an app.

func IsValidTransportMode

func IsValidTransportMode(s string) bool

IsValidTransportMode is the exported helper used by `internal/config` to validate the raw string from YAML.

func ProjectToolPolicies added in v1.3.0

func ProjectToolPolicies(ms config.MCPServerConfig) (tools.ToolPolicy, map[string]tools.ToolPolicy, error)

ProjectToolPolicies converts an MCPServerConfig's operator-facing policy YAML into the driver's runtime ToolPolicy fields: the per-server default and the per-tool override map (keyed by the MCP server-side tool name). The config package owns the single config→policy translation seam (config.ToolPolicyConfig.ToToolPolicy); this helper performs only the trivial primitive→tools.ToolPolicy copy. It lives next to the driver (— promoted from cmd/harbor, where it was stranded because internal/config cannot import internal/tools). Any projection error (e.g. an unknown retry_on class) is returned so the boot path fails loud (CLAUDE.md §5).

A nil ms.Policy yields a zero-valued default policy, so the driver applies tools.DefaultPolicy() per-field at dispatch — preserving the no-policy behaviour exactly.

func ToolCallID added in v1.4.1

func ToolCallID(runID, serverID, tool string, args json.RawMessage) string

ToolCallID mints the stable, collision-free identifier for one tool invocation that declared an app. It is a deterministic content hash of the run / server / tool / args — NOT a counter or any mutable Provider field (the Provider is a compiled artifact, immutable after construction; per-call identity rides ctx, CLAUDE.md §5). Two invocations with distinct (run, server, tool, args) tuples never collide; the same tuple within a run is idempotent by construction (it re-derives the same id and overwrites the same captured slot).

Types

type AppAvailablePayload added in v1.4.0

type AppAvailablePayload struct {
	events.SafeSealed
	// Identity scopes the discovery to the (tenant, user, session) triple
	// the tool ran under; its RunID is the turn-correlation key.
	Identity identity.Quadruple
	// ServerID is the MCP server (source id) hosting the app — the value a
	// client passes to mcp.servers.read_resource to fetch the document.
	ServerID tools.ToolSourceID
	// ToolCallID is the stable per-invocation id (a content hash, not a
	// counter) the client passes to mcp.apps.tool_context to fetch the tool
	// context — the input + lowered result — that produced this app. Safe
	// by construction: an opaque hash, never caller content.
	ToolCallID string
	// ToolName is the server-side tool name that declared the app — display
	// metadata only. Safe by construction: a tool name, never content.
	ToolName string
	// ResourceURI is the `ui://`-scheme URI of the app's UI document.
	ResourceURI string
	// DisplayMode is the display-mode hint (one of inline / fullscreen /
	// pip), or empty when the server stated none. The tool-definition
	// binding carries no mode in the canonical dialect, so this is empty on
	// the golden path and the renderer defaults to inline; a server MAY
	// supply a per-result hint that wins over the binding.
	DisplayMode string
	// RawHTMLTrusted is the raw-HTML trust posture carried on the
	// discovery. The driver emits the default-deny posture; a client
	// reconciles the full per-server trust via mcp.servers.get.
	RawHTMLTrusted bool
	// OccurredAt is the wall-clock instant the app was discovered.
	OccurredAt time.Time
}

AppAvailablePayload is the typed payload for EventTypeMCPAppAvailable. SafePayload: no caller-controlled MCP content survives on the payload — only the server source id, the `ui://` resource URI, the per-result display-mode hint, the default-deny raw-HTML trust posture, and the actor identity quadruple (its RunID correlates the discovery to the turn that produced it).

type AppRef added in v1.4.0

type AppRef struct {
	// ResourceURI is the `ui://`-scheme URI of the app's UI document.
	// The host fetches the document via a resource read scoped to the
	// request identity triple. Its canonical source is the tool
	// definition's `_meta.ui.resourceUri`.
	ResourceURI string
	// PreferredDisplayMode is an optional display-mode hint (one of
	// inline / fullscreen / pip), or empty when none was stated. The tool
	// definition's `_meta.ui` carries no display mode in the canonical
	// dialect, so this is empty on the golden path; a server MAY supply a
	// per-result hint on the CallToolResult `_meta.ui` slot, which the
	// host merges over the binding. When empty, the renderer defaults to
	// inline. It is a hint only; the host reconciles it against the
	// server's negotiated capability set.
	PreferredDisplayMode string
	// ToolCallID is the stable, per-invocation identifier minted at the
	// tool-call site (a content hash of the run / server / tool / args).
	// It correlates a discovered app to the captured tool context — the
	// input + lowered result that produced it — so a Protocol client can
	// fetch that context via mcp.apps.tool_context. Empty until the
	// invocation path stamps it; it is NOT parsed from the server's
	// `_meta` (a result never carries it).
	ToolCallID string
}

AppRef is the host-side reference to an MCP App — the interactive HTML UI an MCP tool declares via the official `io.modelcontextprotocol/ui` (ext-apps) extension. The canonical dialect binds the UI resource to the tool DEFINITION: the `ui://` resource URI rides the tool's `_meta.ui.resourceUri` slot, captured at discovery. It is recognised distinctly from ordinary content: ONLY a `ui://`-scheme URI is treated as an app. An ordinary `file://` / `https://` resource reference is never promoted to an AppRef.

Concurrent reuse: AppRef is a value type with no mutable state after construction.

type AttachDeps added in v1.3.0

type AttachDeps struct {
	// Catalog receives one ToolDescriptor per discovered tool.
	Catalog tools.ToolCatalog
	// Registry receives the live Provider registration so observability
	// surfaces (the Console MCP Connections page) can project it.
	Registry *Registry
	// Bus carries the driver's mcp.* events. Mandatory — the driver's
	// own constructor validates it (mcp.resource_updated emission).
	Bus events.EventBus
	// Logger receives the per-server attachment Info line. Optional.
	Logger *slog.Logger
	// DefaultIdentity is the FALLBACK identity stamped on server-pushed
	// events that arrive without an inflight call (transport-side
	// notifications — Item 1). Per-call subscriptions
	// stamp the inflight caller's ctx-resident identity via the
	// driver's pushIdentity helper; this default only covers
	// transport-level events.
	DefaultIdentity identity.Identity
	// Closers is the caller's ordered closer chain. Attach appends the
	// Provider's Close immediately after a successful Connect so a
	// later Discover/Register failure still drains the live subprocess.
	Closers *[]func(context.Context) error
	// HostDisplayModes lists the MCP App (`io.modelcontextprotocol/ui`)
	// display modes the host can render. Projected onto the Provider's
	// Config.HostDisplayModes so the provider advertises the UI extension
	// during the initialize handshake. The boot loader sources this once
	// from the deployment-level `tools.mcp_app_host.display_modes` config
	// (defaulting to inline); empty leaves the SDK's default advertisement
	// untouched. This is the programmatic seam an embedder sets without YAML.
	HostDisplayModes []string
	// ToolContext is the optional MCP Apps tool-context capturer. When set,
	// the Provider persists the input + lowered result behind a declared
	// `ui://` app so the host can deliver it to the rendered app. A nil
	// capturer leaves tool-context delivery unwired (the host read returns
	// not-found). Optional.
	ToolContext ToolContextCapturer
}

AttachDeps bundles the collaborators Attach wires the server into. Catalog, Registry, Closers, and Bus are mandatory — a nil Bus fails loud at mcp.New (Config.validate rejects it; the driver publishes mcp.resource_updated). Only Logger is optional (a nil Logger silences the attachment log line — test stacks omit it).

type AudioRef

type AudioRef struct {
	Data     []byte
	MIMEType string
}

AudioRef is the lowered form of an MCP AudioContent.

type CapturedToolContext added in v1.4.1

type CapturedToolContext struct {
	// ServerID is the MCP server (source id) the tool belongs to. Paired
	// with ToolCallID it forms the lookup key the host reads back through.
	ServerID tools.ToolSourceID
	// ToolCallID is the stable, collision-free per-invocation id minted by
	// ToolCallID (the content hash). It is the same value carried on the
	// discovery event and projected onto the app reference.
	ToolCallID string
	// Tool is the server-side tool name (display metadata only — the
	// lookup keys on ServerID + ToolCallID, never the tool name).
	Tool string
	// Input is the raw JSON argument object the tool was invoked with.
	Input json.RawMessage
	// Result is the JSON-encoded lowered tool result.
	Result json.RawMessage
	// IsError reports whether the tool returned a server-side error result.
	IsError bool
}

CapturedToolContext is the input a ToolContextCapturer persists when an invoked MCP tool declared an interactive app. It is the "Data Delivery" half of the MCP Apps lifecycle: the input arguments and the lowered result that produced the app, keyed by the stable ToolCallID so a Protocol client (the rendered app) can fetch them later. The capture rides the SAME ctx the tool call ran under, so the persisted record is scoped to the caller's identity triple.

type Config

type Config struct {
	// Name is the unique source ID prefix. Empty rejects with
	// ErrInvalidConfig.
	Name string
	// TransportMode selects the wire transport. Empty defaults to
	// TransportAuto.
	TransportMode MCPTransportMode
	// URL is the endpoint for SSE / streamable-HTTP transports.
	// Required for those modes.
	URL string
	// Command is the argv-form subprocess command for the stdio
	// transport. [0] is the binary; [1:] are args. Required for
	// stdio. NEVER shell-form — the driver enforces this in
	// transport_stdio.go.
	Command []string
	// Headers are operator-supplied HTTP headers added to every
	// SSE / streamable-HTTP request (auth tokens, custom auth).
	// "URL connections require explicit headers for auth (no
	// implicit env passthrough)" — a settled security rule.
	Headers map[string]string
	// KeepAlive is the ping interval for the MCP session; zero
	// disables. The SDK's KeepAlive runs the underlying ping/pong.
	KeepAlive time.Duration
	// Logger is the per-provider slog logger. nil → a discard
	// logger; runtime never panics on absent Logger.
	Logger *slog.Logger
	// Bus is the event bus used to publish `mcp.resource_updated`
	// notifications. Required.
	Bus events.EventBus
	// ToolContext captures the tool context (input + lowered result) behind
	// a declared MCP App so the host can deliver it to the rendered app
	// (the MCP Apps "Data Delivery" lifecycle). Optional — a nil capturer
	// means tool-context delivery is not wired (the host's tool-context
	// read then returns not-found). When set, the driver calls it from the
	// tool-invocation path whenever a result declares a `ui://` app; a
	// Capture error is logged loudly but never fails the tool call.
	ToolContext ToolContextCapturer
	// DefaultPolicy is the ToolPolicy applied to descriptors built
	// from this provider. Zero-valued → tools.DefaultPolicy().
	DefaultPolicy tools.ToolPolicy
	// ToolPolicies are per-tool ToolPolicy overrides keyed by the
	// MCP server-side tool name (NOT the `<source>_<tool>` Harbor
	// name). When a discovered tool's name is present here, its
	// descriptor uses the override instead of DefaultPolicy; a tool
	// absent from the map falls back to DefaultPolicy.
	// Per-tool overrides apply to TOOLS only — MCP resources and
	// prompts always run under DefaultPolicy (the per-server default).
	//
	// Concurrent reuse: the map is read-only after New — it is
	// never mutated per-run. buildToolDescriptor only reads it, and
	// the resolved ToolPolicy is copied by value into each descriptor
	// at Discover time, so concurrent invocations of different tools
	// never share or race this map.
	ToolPolicies map[string]tools.ToolPolicy
	// DefaultIdentity is the fallback identity stamped on
	// transport-side events (notifications that arrive without an
	// inflight call). Required so the bus's ValidateEvent does not
	// reject the event when the SDK-supplied ctx carries no triple.
	//
	// the role narrows. For events the
	// SDK delivers WITH a populated ctx (per-call notifications
	// originating from an inflight tool / resource subscription),
	// the driver prefers `identity.From(ctx)` over this cached
	// default — `pushIdentity(ctx, cfg)` is the single helper that
	// implements the preference. The DefaultIdentity remains the
	// fallback for genuine transport-level events (a server-pushed
	// `notifications/resources/updated` arriving outside any
	// inflight call) where the ctx has no triple to read.
	DefaultIdentity identity.Identity

	// HostDisplayModes lists the MCP App (`io.modelcontextprotocol/ui`)
	// display modes this host can render. When non-empty, the provider
	// advertises the UI extension during the MCP initialize handshake with
	// these modes (filtered against the closed valid-mode set), so a server
	// can tailor the app references it returns to what the host actually
	// renders. Empty leaves the SDK's default capability advertisement
	// untouched (no UI extension) — the behaviour for an embedder that does
	// not opt in. The boot loader sources this from the deployment-level
	// `tools.mcp_app_host.display_modes` config (defaulting to inline), but
	// a programmatic embedder may set it directly. Read once at construction;
	// immutable thereafter.
	HostDisplayModes []string
}

Config is the operator-supplied configuration for one MCP attachment. Operator-facing fields map 1:1 to the `config.MCPServerConfig` yaml shape; the runtime entry point (cmd/harbor wiring, future phase) is responsible for the projection.

type ContentKind

type ContentKind string

ContentKind discriminates a ContentPart.

const (
	ContentKindImage    ContentKind = "image"
	ContentKindAudio    ContentKind = "audio"
	ContentKindLink     ContentKind = "link"
	ContentKindEmbedded ContentKind = "embedded"
)

The ContentKind values, one per MCP content-part shape.

type ContentPart

type ContentPart struct {
	Kind     ContentKind
	Image    *ImageRef
	Audio    *AudioRef
	Link     *LinkRef
	Embedded *EmbeddedRef
}

ContentPart is the discriminated union of non-text content shapes. Exactly one of Image / Audio / Link / Embedded is set; Kind names which.

type Cursor

type Cursor struct {
	// NextPageToken is the cursor for the next page, or empty when the
	// page is the last.
	NextPageToken string
}

Cursor is the opaque pagination cursor a paged read returns.

type DiscoveryResult

type DiscoveryResult struct {
	DiscoveryID   string
	ToolCount     int
	ResourceCount int
	PromptCount   int
}

DiscoveryResult is the outcome of a RefreshDiscovery call.

type EmbeddedRef

type EmbeddedRef struct {
	URI      string
	MIMEType string
	Text     string
	Blob     []byte
}

EmbeddedRef is the lowered form of an MCP EmbeddedResource (a resource embedded directly in the tool call result).

type HealthBucket

type HealthBucket struct {
	StartMs   int64
	LatencyMs int64
}

HealthBucket is one handshake-latency sparkline bucket.

type HealthSnapshot

type HealthSnapshot struct {
	HandshakeLatencyBuckets []HealthBucket
	ReconnectHistory        []ReconnectEntry
	TransportErrorRate      float64
}

HealthSnapshot is the Health read result.

type ImageRef

type ImageRef struct {
	Data     []byte
	MIMEType string
}

ImageRef is the lowered form of an MCP ImageContent.

type LinkRef

type LinkRef struct {
	URI         string
	Name        string
	Title       string
	Description string
	MIMEType    string
}

LinkRef is the lowered form of an MCP ResourceLink (a pointer to a resource the server hosts; the client may follow it with ReadResource if desired).

type ListFilter

type ListFilter struct {
	// State filters to servers in any of the given states. Empty = all.
	State []ServerState
	// Transport filters to servers on any of the given transports.
	Transport []string
	// HasOAuth, when set, filters on OAuth-binding presence.
	HasOAuth *bool
	// HasRecentError, when set, filters on recent-error presence.
	HasRecentError *bool
	// NamePrefix filters to servers whose name has the prefix.
	NamePrefix string
	// PageToken is the opaque cursor from a prior page.
	PageToken string
	// PageSize is the requested max row count (clamped by the Registry).
	PageSize int
}

ListFilter is the filter shape ListServers applies.

type MCPToolValue

type MCPToolValue struct {
	// Text concatenates every TextContent block in encounter order.
	Text string
	// Parts is the ordered, typed slice of every non-text content
	// block. Empty when the response is pure text.
	Parts []ContentPart
	// StructuredContent is the MCP `structuredContent` field on
	// servers that support typed JSON output (mcpsdk.ToolHandlerFor).
	// nil when absent.
	StructuredContent any
	// AppRef is the MCP Apps reference for an invoked tool that declared
	// an interactive UI. The canonical `io.modelcontextprotocol/ui`
	// (ext-apps) dialect binds the `ui://` UI resource to the tool
	// DEFINITION (`_meta.ui.resourceUri` on the tool, captured at
	// discovery); a server MAY additionally place a per-result hint on the
	// CallToolResult `_meta.ui` slot. The value here is the reconciled
	// reference — the tool-definition binding, merged with any per-result
	// hint — set on the result by `Provider.callTool`. `lowerCallToolResult`
	// alone populates it only from the per-result `_meta`; the binding is
	// merged in by `callTool`, which holds the discovery-time binding. It is
	// nil for ordinary (non-app) tools. AppRef is excluded from the JSON
	// wire form (`json:"-"`) so it never reaches the LLM-facing observation —
	// it is a host-side projection consumed by the app-tool-call proxy and
	// the discovery event, not planner context.
	AppRef *AppRef `json:"-"`
}

MCPToolValue is the typed shape returned from `Invoke` when the remote MCP server returns a CallToolResult. Heterogeneous parts preserve the wire ordering so downstream consumers (LLM context assembly, audit) can reconstruct the server's response.

func (MCPToolValue) MarshalJSON added in v1.2.0

func (v MCPToolValue) MarshalJSON() ([]byte, error)

MarshalJSON renders the value LLM-edge-friendly. The text-only degenerate case (Parts + StructuredContent both empty) emits just the raw Text — most MCP tools return their result as a TextContent block carrying JSON-as-string, and the default struct marshal produces a `{"Text": "<escaped JSON>"}` wrapper that doubles the encoding. When Text is itself well-formed JSON, MarshalJSON emits the JSON value directly so the LLM reads a clean structure; otherwise the text rides as a JSON string. Audit / observability consumers that need the typed shape can re-derive it from the underlying CallToolResult on the bus.

When StructuredContent is set, it wins (it's the MCP-server-typed projection). When Parts are non-empty, the wrapper carries the non-text shape verbatim — there is no clean unwrap for mixed- content responses, so the default struct render applies.

type MCPTransportMode

type MCPTransportMode string

MCPTransportMode selects the wire transport for one MCP attachment. Mirrors the settled transport design ("`MCPTransportMode = Auto | SSE | StreamableHTTP`"). Stdio is the implicit fourth mode: selected when `Auto` sees a `Command` but no `URL`.

const (
	// TransportAuto inspects Config and picks: streamable-HTTP first
	// if URL is set; on connect failure fall back to SSE; if Command
	// is set with no URL, stdio.
	TransportAuto MCPTransportMode = "auto"
	// TransportSSE selects the SDK's SSEClientTransport. URL must
	// be set.
	TransportSSE MCPTransportMode = "sse"
	// TransportStreamableHTTP selects the SDK's
	// StreamableClientTransport. URL must be set.
	TransportStreamableHTTP MCPTransportMode = "streamable_http"
	// TransportStdio selects the SDK's CommandTransport. Command
	// (argv form) must be set.
	TransportStdio MCPTransportMode = "stdio"
)

type ProbeResult

type ProbeResult struct {
	OK        bool
	LatencyMs int64
	Error     string
}

ProbeResult is the outcome of a Probe call.

type PromptArgView

type PromptArgView struct {
	Name        string
	Description string
	Required    bool
}

PromptArgView is one declared prompt argument.

type PromptView

type PromptView struct {
	Name        string
	Description string
	Arguments   []PromptArgView
}

PromptView is one advertised prompt.

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider implements tools.ToolProvider against a remote MCP server. Safe for N concurrent goroutines after Connect; per-call state lives on the call's ctx, never on the Provider.

Concurrent reuse contract:

  • `session` is set once by Connect under mu; subsequent reads are guarded by mu.RLock so Invoke / Discover / Close races are safe.
  • `closed` flips to true on Close; subsequent Invoke / Discover return ErrNotConnected.
  • The resource-update goroutine reads `session` once at Connect time and exits when the session closes.

func New

func New(cfg Config) (*Provider, error)

New constructs a Provider. The Provider is NOT connected; the caller MUST call Connect before Discover / Invoke / SubscribeResource.

func (*Provider) Close

func (p *Provider) Close(ctx context.Context) error

Close shuts the session down idempotently and joins any in-flight SDK goroutines. Safe to call multiple times.

func (*Provider) Connect

func (p *Provider) Connect(ctx context.Context) error

Connect establishes the MCP session. Calling Connect twice without an interleaving Close returns the existing session (Connect is idempotent on the second call only when the first succeeded).

Auto-mode fallback: when TransportMode is TransportAuto and the URL is set, the Provider tries streamable-HTTP first. On a non-cancellation failure of `client.Connect` (which covers both transport-Connect and the MCP initialize handshake), it retries with SSE.

func (*Provider) Discover

func (p *Provider) Discover(ctx context.Context) ([]tools.ToolDescriptor, error)

Discover returns one ToolDescriptor per remote tool, plus one per resource (rendered as a `__resource.<uri>` tool) and one per prompt (`__prompt.<name>`). All descriptors carry Transport = TransportMCP and Source = p.source.

func (*Provider) DisplayModes added in v1.4.0

func (p *Provider) DisplayModes() []string

DisplayModes returns the MCP App display modes THIS HOST can render — the deployment's configured host modes (`Config.HostDisplayModes`, sourced from `tools.mcp_app_host.display_modes`), filtered against the closed valid-mode set with duplicates removed and order preserved. It is NOT a server read: display modes are not a spec capability field — they ride the `ui/initialize` host-context `availableDisplayModes` the host dictates — so the Registry / Console column reports what the host renders, not a value scraped off the server's capabilities.

Concurrent reuse: the configured modes are immutable after construction; DisplayModes reads no per-call or session state.

func (*Provider) ReadResource added in v1.4.0

func (p *Provider) ReadResource(ctx context.Context, uri string) (content []byte, mimeType string, err error)

ReadResource fetches a single resource's content from the remote MCP server under the request identity triple. It is the runtime-side leg of the `mcp.servers.read_resource` Protocol method: the Console reads an MCP App's `ui://` UI document through it (the URI is validated to the `ui://` scheme by the caller; ReadResource itself is scheme- agnostic so the same path serves any resource read).

The returned bytes are the first resource-content item's payload — Text rendered as UTF-8 bytes, or the raw Blob when the server sent a binary resource. The MIME type is the content item's declared type. Identity is mandatory: a ctx without a full (tenant, user, session) triple fails closed with ErrIdentityMissing (the `_meta` builder rejects it) — never a read with an empty `_meta` block.

Concurrent reuse: ReadResource holds no per-call state on the Provider; identity + the URI ride the call.

func (*Provider) SelectedMode

func (p *Provider) SelectedMode() MCPTransportMode

SelectedMode reports the transport mode that succeeded at Connect time. Empty before Connect.

func (*Provider) SourceID

func (p *Provider) SourceID() tools.ToolSourceID

SourceID returns the source ID under which this provider's descriptors are stamped. Implements tools.ToolProvider.

func (*Provider) SubscribeResource

func (p *Provider) SubscribeResource(ctx context.Context, uri string) error

SubscribeResource registers a server-side resource subscription. Updates received via the SDK's ResourceUpdatedHandler are published as `mcp.resource_updated` on the configured event bus (see Provider.onResourceUpdated).

type ReconnectEntry

type ReconnectEntry struct {
	OccurredAt time.Time
	Reason     string
}

ReconnectEntry is one reconnect-history entry.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is the process-local MCP-server read API. It is a compiled artifact — built once at construction; the provider set is write-once after Register; per-server stats are guarded by mu.

func NewRegistry

func NewRegistry(opts ...RegistryOption) *Registry

NewRegistry builds an empty Registry. Servers are added via Register.

func (*Registry) GetServer

func (r *Registry) GetServer(ctx context.Context, name string) (*ServerView, error)

GetServer returns the per-server detail view. Identity is mandatory.

func (*Registry) Health

func (r *Registry) Health(ctx context.Context, name string, window time.Duration) (*HealthSnapshot, error)

Health returns the per-server handshake-latency sparkline + reconnect history + transport-error rate. The window argument bounds the reconnect-history slice. Identity is mandatory.

func (*Registry) ListPrompts

func (r *Registry) ListPrompts(ctx context.Context, name string) ([]PromptView, error)

ListPrompts returns the advertised prompts for a server. Identity is mandatory.

func (*Registry) ListResources

func (r *Registry) ListResources(ctx context.Context, name string) ([]ResourceView, error)

ListResources returns the advertised resources for a server. It runs a Discover and projects the synthetic resource descriptors. Identity is mandatory.

func (*Registry) ListServers

func (r *Registry) ListServers(ctx context.Context, f ListFilter) ([]ServerView, *Cursor, error)

ListServers returns the filtered, paginated server list. The view shapes are projection-only; no per-call state lives on the Registry. Identity is mandatory.

func (*Registry) Probe

func (r *Registry) Probe(ctx context.Context, name string) (*ProbeResult, error)

Probe runs a transport round-trip (a Discover acting as a tools/list ping) and returns the latency. Identity is mandatory.

func (*Registry) ReadResource added in v1.4.0

func (r *Registry) ReadResource(ctx context.Context, name, uri string) (content []byte, mimeType string, err error)

ReadResource fetches a single resource's content from the named MCP server under the request identity triple — the runtime-side leg of the `mcp.servers.read_resource` Protocol method. Identity is mandatory: a ctx without a full triple fails closed with ErrRegistryIdentityMissing. An unknown server name returns ErrServerNotFound.

func (*Registry) RecordDiscovery

func (r *Registry) RecordDiscovery(name string, descs []tools.ToolDescriptor) error

RecordDiscovery seeds the per-server stats from an already-fetched descriptor slice without re-calling provider.Discover. The boot-time dev attach path uses this so the Console MCP-page wire surface (`mcp.servers.list`) reports the actual tool count + a real `last_discovery_at`, not zero values.

Pre-RecordDiscovery the boot-time path called Register() with initial-zero stats; the only API that updated stats was RefreshDiscovery, which re-runs the network call. Operators saw `tool_count: 0` and `last_discovery_at: 0001-01-01T00:00:00Z` on every just-booted Runtime because the boot-time discovery never reached the registry's stats — its result went straight to the tool catalog. a walkthrough fix.

RecordDiscovery is a no-network counterpart to RefreshDiscovery: caller already has the descriptors (from a previous provider.Discover at boot), so the method just classifies them + writes the stats. State is set to Online (the descriptors arrived successfully) and recentLatencyMs is set to 0 (the boot-time latency is not threaded through; a follow-up RefreshDiscovery from the Console will populate it).

Identity is NOT required — this is a server-side seeding gesture from the boot path, not a Protocol-edge read.

func (*Registry) RecordReconnect

func (r *Registry) RecordReconnect(name, reason string)

RecordReconnect appends a reconnect-history entry. The runtime wires this to the transport-reconnect path; tests call it directly.

func (*Registry) RefreshDiscovery

func (r *Registry) RefreshDiscovery(ctx context.Context, name string) (*DiscoveryResult, error)

RefreshDiscovery re-runs the named server's discovery and updates the per-server counts + state. Identity is mandatory.

func (*Registry) Register

func (r *Registry) Register(reg ServerRegistration) error

Register adds a server to the Registry. Re-registering the same name replaces the prior entry (the dev hot-reload path re-registers).

func (*Registry) SetRawHTMLTrust

func (r *Registry) SetRawHTMLTrust(ctx context.Context, name string, trusted bool) (prev bool, err error)

SetRawHTMLTrust persists the per-server raw-HTML trust flag in the runtime-side mirror (the legitimate carve-out for a preference with audit consequences). It returns the prior value so a caller can detect a no-op toggle. Identity is mandatory.

type RegistryOption

type RegistryOption func(*Registry)

RegistryOption configures a Registry at construction.

func WithRegistryClock

func WithRegistryClock(now func() time.Time) RegistryOption

WithRegistryClock overrides the Registry's wall clock — tests inject a deterministic clock so latency / timestamps are stable.

type ResourceOffloadedPayload added in v1.4.0

type ResourceOffloadedPayload struct {
	events.SafeSealed
	// Identity scopes the offload to the (tenant, user, session) triple
	// the read ran under.
	Identity identity.Quadruple
	// ArtifactID is the content-addressed reference the heavy content
	// was stored under.
	ArtifactID string
	// Source identifies what was offloaded: the resource URI for a
	// `read_resource`, or the tool name for an app-tool-call result.
	Source string
	// SizeBytes is the length of the offloaded content.
	SizeBytes int64
	// OccurredAt is the wall-clock instant the offload happened.
	OccurredAt time.Time
}

ResourceOffloadedPayload is the typed payload for EventTypeMCPResourceOffloaded. SafePayload: no caller-controlled MCP content survives on the payload — only the reference id, the source identifier (resource URI or tool name), the byte size, and the actor identity quadruple.

type ResourceUpdatedPayload

type ResourceUpdatedPayload struct {
	events.SafeSealed
	Identity   identity.Quadruple
	Source     tools.ToolSourceID
	URI        string
	OccurredAt time.Time
}

ResourceUpdatedPayload is the typed payload for EventTypeMCPResourceUpdated. SafePayload: no caller-controlled bytes survive on the payload.

  • Identity scopes the event to the (tenant, user, session) triple under which the resource subscription was registered.
  • Source is the originating MCP attachment's source ID, so subscribers can route by provider.
  • URI is the resource URI the server reported as updated; this may be a sub-resource of the URI the client actually subscribed to.
  • OccurredAt is the wall-clock time the driver received the notification.

type ResourceView

type ResourceView struct {
	URI       string
	MimeType  string
	SizeBytes int64
	Name      string
	Title     string
}

ResourceView is one advertised resource.

type ServerRegistration

type ServerRegistration struct {
	// Provider is the live MCP provider. Required.
	Provider serverProvider
	// Transport is the wire transport string ("stdio" / "http+sse" /
	// "streamable-http" / "websocket"). Required.
	Transport string
	// URLOrCommand is the transport-prefixed endpoint or argv command.
	URLOrCommand string
	// Policy is the server's ToolPolicy. Zero-valued → DefaultPolicy.
	Policy tools.ToolPolicy
	// ContentShapes lists the canonical content shapes the tools return.
	ContentShapes []string
	// OAuthBindingCount is the configured OAuth binding count.
	OAuthBindingCount int
	// InitialState is the server's starting state. Zero-valued →
	// ServerStateOffline.
	InitialState ServerState
}

ServerRegistration is the operator-supplied static descriptor for one MCP server attachment the Registry tracks.

type ServerState

type ServerState string

ServerState mirrors the canonical state chip the Console renders. The V1 set is closed.

const (
	// ServerStateOnline — transport connected, last discovery / probe
	// succeeded.
	ServerStateOnline ServerState = "online"
	// ServerStateReconnecting — transport dropped, re-establishing.
	ServerStateReconnecting ServerState = "reconnecting"
	// ServerStateOffline — transport down (never connected / closed).
	ServerStateOffline ServerState = "offline"
	// ServerStateAuthPending — server needs an incomplete OAuth binding.
	ServerStateAuthPending ServerState = "auth_pending"
	// ServerStateError — last discovery / probe failed.
	ServerStateError ServerState = "error"
)

The canonical MCP server states.

type ServerView

type ServerView struct {
	// Name is the unique server / source id.
	Name string
	// Transport is the wire transport string.
	Transport string
	// URLOrCommand is the transport-prefixed endpoint or argv command.
	URLOrCommand string
	// State is the canonical state chip.
	State ServerState
	// LastDiscoveryAt is the last successful discovery instant (zero
	// when discovery has never run).
	LastDiscoveryAt time.Time
	// ToolCount / ResourceCount / PromptCount are the advertised counts.
	ToolCount     int
	ResourceCount int
	PromptCount   int
	// RecentLatencyMs is the most recent observed handshake / probe
	// latency.
	RecentLatencyMs int64
	// ErrorRatePerMin is the transport-error rate over the window.
	ErrorRatePerMin float64
	// OAuthBindingCount is the number of OAuth bindings configured.
	OAuthBindingCount int
	// RawHTMLTrusted reports the per-server raw-HTML trust flag.
	RawHTMLTrusted bool
	// DisplayModes lists the advertised MCP-Apps DisplayMode values.
	DisplayModes []string
	// ContentShapes lists the canonical content shapes the server's
	// tools return.
	ContentShapes []string
	// Policy is the read-only ToolPolicy projection.
	Policy tools.ToolPolicy
}

ServerView is the per-server projection the Registry returns. It is a flat shape — no MCP-SDK type crosses the package boundary.

type ToolContextCapturer added in v1.4.1

type ToolContextCapturer interface {
	Capture(ctx context.Context, in CapturedToolContext) error
}

ToolContextCapturer persists the tool context behind a declared MCP App so the host can deliver it to the rendered app. It is an optional seam on the MCP Config: when set, the driver calls Capture from the tool-invocation path whenever a result declares a `ui://` app. The implementation lives in the runtime (over the StateStore + ArtifactStore — heavy-aware), wired at boot; the driver holds only this narrow interface so it never imports the runtime concretes.

Capture is best-effort relative to the tool call: a Capture error is surfaced to the caller (the invocation path logs it loudly and never silently swallows it, CLAUDE.md §13), but it does not fail the tool call itself — the planner still receives the tool result.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL