mcp

package
v1.16.0 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 20 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")
	// ErrEmptyBearer — a bound OAuth provider returned a nil error AND
	// an empty access token. Defence-in-depth on the fail-closed
	// surface: the call aborts rather than proceeding unauthenticated
	// on a connection the operator declared per-identity-authorized.
	ErrEmptyBearer = errors.New("mcp: oauth provider returned an empty bearer token")
)

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.

View Source
var ErrOAuthBinding = errors.New("mcp: invalid oauth_provider binding")

ErrOAuthBinding — a connection's `oauth_provider` binding is invalid: it names an unregistered provider, sits on a stdio transport, or conflicts with a static `Authorization` header. Callers compare with errors.Is.

View Source
var ErrRedirectToUnlistedHost = errors.New("mcp: redirect target host is not in the bound provider's allowed_downstream_hosts")

ErrRedirectToUnlistedHost is the typed sentinel the MCP bearer client refuses a redirect with when the redirect target host is not in the bound provider's downstream-sink allow-list. 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
	// Owner is the (tenant, agent) reconcile-view tag stamped on the
	// registry entry for a RUNTIME-ADDED connection. The boot loader leaves it
	// zero (boot-declared servers are untagged and never reconciled); the
	// runtime-add attach path sets a non-zero owner so the run-start reconcile
	// view scopes to it. It is a reconcile-view filter, never a dispatch or
	// isolation key.
	Owner auth.Owner
	// OAuthProviders is the declared OAuth-provider registry (keyed by the
	// non-secret provider NAME) Attach resolves a connection's
	// `oauth_provider` binding against. Populated by the runtime assembler
	// from its constructed provider map (and the devstack twin). A binding
	// naming a provider absent from this map fails the attach loud, listing
	// the registered names (§4.4 factory-error convention). Nil / empty is
	// valid when no connection binds a provider. The driver depends ONLY on
	// the `auth.OAuthProvider` interface — no concrete driver import (§13).
	OAuthProviders map[string]auth.OAuthProvider
	// OAuthProviderSet is the runtime provider SET a RUNTIME-ADDED connection's
	// `oauth_provider` binding resolves against, so a Protocol-installed
	// (owner-tagged) provider is bindable in addition to the boot map. When set
	// it TAKES PRECEDENCE over OAuthProviders (the set is seeded from the same
	// boot map at assembly, so boot providers stay resolvable). Optional — nil
	// leaves resolution on the OAuthProviders map (the boot catalog path). The
	// driver depends only on the narrow resolver interface (bare-name Get +
	// Names for the fail-loud message) — no concrete import.
	OAuthProviderSet OAuthProviderResolver
}

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 AuthChallenge added in v1.13.0

type AuthChallenge struct {
	// Scheme is the challenge auth scheme (e.g. "Bearer").
	Scheme string
	// ResourceMetadataURL is the RFC 9728 `resource_metadata` pointer, when
	// the challenge carried one.
	ResourceMetadataURL string
	// Realm is the optional `realm` challenge parameter.
	Realm string
	// Error is the optional RFC 6750 §3.1 `error` challenge parameter (e.g.
	// "insufficient_scope", "invalid_token"). Empty when the challenge
	// carried none.
	Error string
	// Scope is the optional RFC 6750 §3.1 `scope` challenge parameter — the
	// space-delimited scopes the downstream requires. Empty when absent.
	Scope string
	// Raw is the verbatim header value (provenance / debugging).
	Raw string
	// CapturedAt is the wall-clock instant the challenge was observed.
	CapturedAt time.Time
}

AuthChallenge is a parsed `WWW-Authenticate` Bearer challenge captured off a `401` response from an MCP HTTP server. It is inert data recorded on the connection's registry state.

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

	// OAuthProvider, when non-nil, binds this connection to a per-identity
	// OAuth credential source: every identity-stamped per-call RPC resolves a
	// fresh bearer via Token(ctx, source) and injects Authorization on THAT
	// request only. Nil leaves the connection on its static Headers (the
	// unbound default). Resolved once at construction from the operator's
	// non-secret provider NAME (config `oauth_provider`) against the declared
	// provider registry — the name selects an acquisition strategy; the
	// secret stays on the provider. Immutable after construction: no per-call
	// transport state mutates (the token rides the call's ctx). A connect-time
	// request (initialize / discovery) predates any per-call identity and so
	// carries only the static Headers — a documented limitation.
	OAuthProvider auth.OAuthProvider

	// MetaAnnotations is a static, non-secret set of operator-declared
	// key/values merged verbatim into the `_meta` map on every
	// identity-stamped per-call RPC, so a deployment can carry its own
	// attribution vocabulary to a shared server. Reserved keys (the triple
	// keys, `agent_id`, `traceparent`, `tracestate`, and any
	// `io.modelcontextprotocol/`-prefixed key) are rejected at config /
	// attach validation and can never shadow the triple or agent provenance
	// (those are stamped last). Read once at construction; immutable
	// thereafter.
	MetaAnnotations map[string]string

	// OnAuthChallenge, when non-nil, is invoked whenever an MCP HTTP call to
	// this server answers `401` with a `WWW-Authenticate` Bearer challenge —
	// the MCP authorization spec's OAuth step-up. The callback records the
	// advertised OAuth requirement on the connection's registry state so an
	// operator can inspect it. Capture is pure observation: it never
	// retries, never attaches credentials, and never alters the call's error
	// semantics. Optional; nil disables challenge capture (stdio connections
	// never set it — the challenge is an HTTP-auth construct). Read once at
	// construction; immutable thereafter.
	OnAuthChallenge func(AuthChallenge)

	// OnScopeShortfall, when non-nil, is invoked whenever an MCP HTTP call
	// answers `403` with a `WWW-Authenticate` marking
	// `error="insufficient_scope"` (RFC 6750 §3.1) — a downstream step-up
	// scope shortfall. The callback records the parsed shortfall on the
	// connection's registry state so an operator can inspect the last
	// observed required-vs-granted gap. Best-effort observability: it never
	// retries, never widens a binding, and never alters the call's error
	// semantics (the per-call error enrichment rides a request-scoped ctx
	// slot instead). Optional; nil disables the registry-side record (stdio
	// connections never set it). Read once at construction; immutable
	// thereafter.
	OnScopeShortfall func(ScopeShortfall)

	// ToolOAuthProviders are per-tool OAuth-provider overrides keyed by the
	// MCP server-side tool name (mirroring ToolPolicies' shape). A tool named
	// here binds THAT provider for its CallTool RPCs only; an unlisted tool
	// falls back to OAuthProvider (the connection-level binding). Each entry
	// was resolved + validated against the same binding rules as
	// OAuthProvider at attach time (unknown name / stdio transport /
	// static-Authorization conflict / downstream-host allow-list). Scope is
	// CallTool ONLY — resource/prompt paths keep resolving OAuthProvider.
	// Read-only after New; the resolved provider is read per-call from the
	// call's own tool name, so concurrent invocations of different tools
	// never share or race this map (the concurrent-reuse contract).
	ToolOAuthProviders map[string]auth.OAuthProvider
}

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 OAuthProviderResolver added in v1.14.0

type OAuthProviderResolver interface {
	// Get resolves a provider by bare name; the bool reports presence.
	Get(name string) (auth.OAuthProvider, bool)
	// Names returns every resolvable provider name, sorted, for a fail-loud
	// error message.
	Names() []string
}

OAuthProviderResolver is the narrow bare-name resolution seam Attach uses to resolve a connection's `oauth_provider` binding — satisfied by `auth.ProviderSet` (the runtime provider set) and by the boot map adapter. Bare-name resolution across every session; Names feeds the fail-loud "registered: …" message.

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) Deregister added in v1.11.0

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

Deregister removes the named server from the Registry and closes its transport gracefully — the physical inverse of Register. It is the registry leg of detach-on-reconcile: when a run-start reconciliation finds a server that is attached but no longer declared by the agent's active config revision (a removed connection, or a rollback past an add), it deregisters the server so observability surfaces (mcp.servers.list) no longer list it and the subprocess / connection drains.

The map entry is deleted under the write lock (so the server vanishes from the Registry atomically), then the provider's Close runs OUTSIDE the lock (a transport close can block on session teardown and must not stall concurrent reads). An unknown name returns ErrServerNotFound. Idempotent in effect: a second Deregister of the same name returns ErrServerNotFound, which the reconcile caller treats as already-detached.

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) OAuthDiscoveryTarget added in v1.13.0

func (r *Registry) OAuthDiscoveryTarget(name string) (challenge *AuthChallenge, serverURL string, allowedOrigins []string, err error)

OAuthDiscoveryTarget returns the inputs the on-demand discovery walker needs for a server: the captured challenge (nil when none seen), the server URL, and the per-connection cross-origin allowance list. An unknown name returns ErrServerNotFound. The returned slices/pointers are copies — the caller may read them without holding the registry lock.

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) RecordAuthChallenge added in v1.13.0

func (r *Registry) RecordAuthChallenge(name string, ch AuthChallenge)

RecordAuthChallenge records a captured `WWW-Authenticate` Bearer challenge on a server's state. It is invoked from the HTTP transport's challenge-capture callback whenever an MCP call answers `401`. Pure observation: it records inert, server-supplied data and never alters transport state or call semantics. An unknown name is a no-op (the connection may have been deregistered mid-flight) — recording a challenge is best-effort observability, never a hard failure on the call path.

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) RecordOAuthRequirement added in v1.13.0

func (r *Registry) RecordOAuthRequirement(name string, req *auth.OAuthRequirement) error

RecordOAuthRequirement records the discovered OAuth requirement chain on a server's state. Invoked by the on-demand discovery orchestrator after a probe walks the chain. An unknown name returns ErrServerNotFound.

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) RecordScopeShortfall added in v1.16.0

func (r *Registry) RecordScopeShortfall(name string, sf ScopeShortfall)

RecordScopeShortfall records a captured downstream insufficient-scope step-up on a server's state (mirrors RecordAuthChallenge for the 403 path). It is invoked from the HTTP transport's shortfall-capture callback whenever an MCP call answers `403` + `error="insufficient_scope"`. Pure observation: it records inert, server-supplied data and never alters transport state or call semantics. An unknown name is a no-op (the connection may have been deregistered mid-flight) — recording is best-effort observability, never a hard failure on the call path.

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) RuntimeAddedSources added in v1.14.0

func (r *Registry) RuntimeAddedSources(owner auth.Owner) []string

RuntimeAddedSources returns the source ids of the runtime-added servers whose owner tag equals owner — the OWNER-SCOPED reconcile VIEW. Boot-declared (zero-owner) servers and every OTHER owner's runtime-adds are excluded, so a run-start reconcile for one owner enumerates only its own runtime-added set and can never detach a boot server or another owner's connection. A zero owner returns nil (a reconcile with no owner has nothing of its own to reconcile — it never falls back to the whole registry). The result is a fresh sorted slice, safe to retain.

This is the ONLY owner-aware read on the Registry: the bare-name read / dispatch paths (ListServers, GetServer, OAuthDiscoveryTarget, ReadResource) stay process-global and untouched, so boot servers remain visible to every session regardless of the reconciling owner.

func (*Registry) SetOAuthDiscoveryOrigins added in v1.14.0

func (r *Registry) SetOAuthDiscoveryOrigins(ctx context.Context, name string, origins []string) (prev []string, err error)

SetOAuthDiscoveryOrigins FULL-REPLACES a server's OAuth-discovery cross-origin allowance list on the live registry and returns the prior set so the caller can compute the granted / revoked delta. It is the live half of the `agent_config.set_mcp_discovery_origins` write: the very next discovery walk reads the new set via OAuthDiscoveryTarget, so a grant lets a previously refused authorization-server hop through and a revoke refuses it.

Revoke is symmetric: dropping an origin also PRUNES the recorded OAuth requirement's authorization-server entries whose provenance origin (SourceURL) is no longer allowed — by building a FRESH requirement and swapping the stored pointer under the lock. The registry hands the requirement out BY POINTER (GetServer returns it directly), so mutating the pointee in place would be a data race against a concurrent reader; the swap leaves any reader holding the prior (immutable) pointer with a consistent value. Origin matching reuses the discovery walker's exported origin normaliser (auth.OriginOf), so a port-differing origin never spuriously matches.

The registry stays PROCESS-GLOBAL bare-name — identity is mandatory for AUTHORIZATION (a caller with no identity triple is refused), NOT for keying. An unknown name returns ErrServerNotFound.

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.

func (*Registry) SourceIDs added in v1.11.0

func (r *Registry) SourceIDs() []string

SourceIDs returns the source ids of every currently-registered server — boot-declared AND runtime-added, across every owner. It is the PROCESS-GLOBAL enumeration (the deployment-shared attached set), NOT the owner-scoped reconcile view: the run-start reconcile uses Registry.RuntimeAddedSources instead so one owner's reconcile never sees (and never detaches) a boot server or another owner's runtime-add. Identity-free (a process-local read of the attached set, not an identity-scoped projection like ListServers); the result is a fresh sorted slice, safe to retain.

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 ScopeShortfall added in v1.16.0

type ScopeShortfall struct {
	// RequiredScopes is the parsed `scope` challenge parameter.
	RequiredScopes []string
	// GrantedScopes is the binding's most-recently-granted scope set
	// (populated by the reader that resolved the token, not the transport).
	GrantedScopes []string
	// DownstreamResource is the host the challenge came from.
	DownstreamResource string
	// Origin is the scheme://host[:port] the challenge was observed on.
	Origin string
	// WWWAuthenticate is the verbatim challenge header value.
	WWWAuthenticate string
	// ToolName is the server-side tool name the call targeted (populated by
	// the reader that owns the tool name, not the transport).
	ToolName string
	// CapturedAt is the wall-clock instant the shortfall was observed.
	CapturedAt time.Time
}

ScopeShortfall is a parsed downstream insufficient-scope step-up captured off a `403` whose `WWW-Authenticate` carried `error="insufficient_scope"` (RFC 6750 §3.1). It is inert, server-supplied data — the connection view's LastScopeShortfall record and the per-call error enrichment both read it.

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
	// OAuthDiscoveryAllowedOrigins is the explicit per-connection cross-origin
	// allowance list for OAuth-requirement discovery fetches. Empty
	// leaves the authorization-server hop needs-allowance (partial discovery).
	OAuthDiscoveryAllowedOrigins []string
	// Owner is the (tenant, agent) reconcile-view tag for a RUNTIME-ADDED
	// server. Boot-declared servers leave it zero (untagged) — the
	// owner-scoped reconcile view never enumerates a zero-owner entry. The
	// runtime-add attach path stamps a non-zero owner (fail-closed there when
	// either component is missing); nothing about resolution or dispatch reads
	// it (those stay bare-name and process-global).
	Owner auth.Owner
}

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
	// OAuthRequirement is the OAuth requirement advertised by the server and
	// discovered on demand — the verbatim RFC 9728RFC 8414 chain
	// plus provenance. Nil when no discovery has run. Populated only on the
	// DETAIL read (GetServer); the list projection leaves it nil so the hot
	// list row stays compact (§4.3-recorded — the requirement rides get/probe,
	// not list). It is inert, server-supplied, UNVERIFIED data.
	OAuthRequirement *auth.OAuthRequirement
	// LastScopeShortfall is the most recent downstream insufficient-scope
	// step-up (a `403` + `WWW-Authenticate` marking
	// `error="insufficient_scope"`) observed on this connection. Nil when
	// none seen. Populated only on the DETAIL read (GetServer), mirroring how
	// OAuthRequirement rides get — the list row stays compact. Inert,
	// server-supplied data; the operator acts on it, the runtime never does.
	LastScopeShortfall *ScopeShortfall
}

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