Documentation
¶
Overview ¶
Package scout is a Model Context Protocol client for onboarding and validating remote MCP servers over Streamable HTTP with OAuth 2.1.
The Client drives the full authorization state machine: it contacts the server, honours a 401 challenge by discovering protected-resource and authorization-server metadata, registers a client identity, obtains a token with either the client-credentials (B2B) or authorization-code with PKCE (B2C) grant, and completes the MCP initialize handshake.
Index ¶
- Variables
- func Unauthorized(err error) (auth.Challenge, bool)
- type AuthConfig
- type AuthMode
- type CallToolResult
- type Client
- func (c *Client) AllowedOrigins() []string
- func (c *Client) Call(ctx context.Context, method string, params, result any) error
- func (c *Client) CallTool(ctx context.Context, name string, args any) (*CallToolResult, error)
- func (c *Client) ClientCredentialsSource(d *Discovery) (*auth.ClientCredentialsSource, error)
- func (c *Client) CompleteAuthorization(ctx context.Context, code, state string) (*ConnectResult, error)
- func (c *Client) CompleteAuthorizationFrom(ctx context.Context, code, state, iss string) (*ConnectResult, error)
- func (c *Client) Config() Config
- func (c *Client) Connect(ctx context.Context) (*ConnectResult, error)
- func (c *Client) Discover(ctx context.Context, challenge auth.Challenge) (*Discovery, error)
- func (c *Client) Discoverer() *auth.Discoverer
- func (c *Client) GetPrompt(ctx context.Context, name string, args map[string]string) (*GetPromptResult, error)
- func (c *Client) HTTPClient() *http.Client
- func (c *Client) Initialize(ctx context.Context) (*InitializeResult, error)
- func (c *Client) LastConnect() *ConnectResult
- func (c *Client) ListPrompts(ctx context.Context) ([]Prompt, error)
- func (c *Client) ListResourceTemplates(ctx context.Context) ([]ResourceTemplate, error)
- func (c *Client) ListResources(ctx context.Context) ([]Resource, error)
- func (c *Client) ListTools(ctx context.Context) ([]Tool, error)
- func (c *Client) Negotiate(ctx context.Context) (*Negotiation, error)
- func (c *Client) Negotiation() *Negotiation
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) ReadResource(ctx context.Context, uri string) (*ReadResourceResult, error)
- func (c *Client) Register(ctx context.Context, d *Discovery) (*auth.Registration, error)
- func (c *Client) Resume(ctx context.Context, src auth.TokenSource) (*ConnectResult, error)
- func (c *Client) ServerInfo() *InitializeResult
- func (c *Client) SetTokenSource(src auth.TokenSource)
- func (c *Client) StartAuthorization(d *Discovery) (string, string, error)
- func (c *Client) TokenSource() auth.TokenSource
- func (c *Client) Transport() *transport.Streamable
- type ClientCapabilities
- type Config
- type ConnectResult
- type Content
- type DiscoverResult
- type Discovery
- type Era
- type GetPromptResult
- type Implementation
- type InitializeResult
- type Negotiation
- type Overrides
- type Prompt
- type PromptArgument
- type PromptMessage
- type ReadResourceResult
- type Resource
- type ResourceContents
- type ResourceTemplate
- type ServerCapabilities
- type Status
- type Tool
- type ToolAnnotations
Constants ¶
This section is empty.
Variables ¶
var ErrIssuerMismatch = errors.New("scout: authorization response came from the wrong issuer")
ErrIssuerMismatch reports an authorization response whose iss parameter names a different authorization server than the one the request went to.
var ErrResourceMismatch = errors.New("scout: protected resource metadata does not identify this endpoint")
ErrResourceMismatch reports a protected-resource metadata document whose "resource" does not identify the endpoint it was fetched for. RFC 9728 requires the client to verify this binding: without it, a resource can hand out metadata for somebody else's API and collect tokens minted for it.
var SessionVersions = []string{transport.V20251125, transport.V20250618, transport.V20250326}
SessionVersions are the handshake revisions scout offers, newest first.
var StatelessVersions = []string{transport.V20260728}
StatelessVersions are the stateless revisions scout offers, newest first.
var SupportedProtocolVersions = []string{"2025-11-25", "2025-06-18", "2025-03-26"}
SupportedProtocolVersions lists the protocol versions this client can speak, newest first. The first entry is offered on initialize; a server may answer with any listed version.
Functions ¶
Types ¶
type AuthConfig ¶
type AuthConfig struct {
Mode AuthMode
// Token is the pre-issued bearer token for AuthBearer.
Token string
// Registration controls how a client identity is obtained.
Registration auth.RegistrationOptions
// RedirectURI is required for AuthAuthorizationCode.
RedirectURI string
// Scope requested on the first token request. When empty, the scope
// from the WWW-Authenticate challenge (or PRM scopes_supported) is used.
Scope string
// Extra parameters sent on token requests (client credentials) or the
// authorization request (authorization code). Use it for server
// extensions such as a tenant profile identifier.
Extra url.Values
// TokenAuthMethod overrides the token endpoint auth method.
TokenAuthMethod string
// Overrides bypass discovery for the endpoints given.
Overrides Overrides
// StepUp, when set, is consulted on insufficient_scope; defaults to
// re-requesting the token with the required scope.
StepUp auth.StepUpFunc
}
AuthConfig configures how the client authorizes.
type AuthMode ¶
type AuthMode string
AuthMode selects how the client authorizes.
const ( // AuthNone connects without credentials and fails on a 401. AuthNone AuthMode = "none" // AuthBearer sends a pre-issued token handed to the operator out of // band. No discovery or token exchange happens. AuthBearer AuthMode = "bearer" // AuthClientCredentials is the B2B flow: a confidential client // exchanges its own credentials for a token. AuthClientCredentials AuthMode = "client_credentials" // AuthAuthorizationCode is the B2C flow: an end user consents in a // browser and the client redeems the code with PKCE. AuthAuthorizationCode AuthMode = "authorization_code" )
type CallToolResult ¶
type CallToolResult struct {
Content []Content `json:"content"`
StructuredContent json.RawMessage `json:"structuredContent,omitempty"`
IsError bool `json:"isError,omitempty"`
}
CallToolResult is the result of tools/call. IsError marks a tool-level failure (as opposed to a protocol error, which is returned as a Go error). StructuredContent, when present, must validate against the tool's OutputSchema.
func (*CallToolResult) Text ¶
func (r *CallToolResult) Text() string
Text concatenates all text content blocks.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an MCP client bound to one server.
func (*Client) AllowedOrigins ¶
AllowedOrigins lists the origins this client may send credentials to.
func (*Client) CallTool ¶
CallTool invokes a tool. A tool-level failure is reported through CallToolResult.IsError, not as an error.
func (*Client) ClientCredentialsSource ¶
func (c *Client) ClientCredentialsSource(d *Discovery) (*auth.ClientCredentialsSource, error)
ClientCredentialsSource builds the B2B token source from a completed discovery. It does not fetch a token until first use.
func (*Client) CompleteAuthorization ¶
func (c *Client) CompleteAuthorization(ctx context.Context, code, state string) (*ConnectResult, error)
CompleteAuthorization finishes the authorization-code flow with the code and state received on the redirect URI, then runs Initialize.
It is shorthand for CompleteAuthorizationFrom with no issuer, and is kept for callers whose redirect handler does not surface the iss parameter.
func (*Client) CompleteAuthorizationFrom ¶
func (c *Client) CompleteAuthorizationFrom(ctx context.Context, code, state, iss string) (*ConnectResult, error)
CompleteAuthorizationFrom finishes the authorization-code flow with the code, state and iss received on the redirect URI, then runs Initialize.
iss is the RFC 9207 issuer identifier. When the authorization server advertised authorization_response_iss_parameter_supported, or simply sent one, it must match the issuer the code was requested from: that is what stops a mix-up attack where a malicious authorization server relays a code minted by an honest one.
func (*Client) Connect ¶
func (c *Client) Connect(ctx context.Context) (*ConnectResult, error)
Connect runs the authorization state machine and the MCP handshake.
initialize ──200──▶ connected
│401
▼
Discover (PRM: hint, then well-known → AS metadata) ─▶ Register
│
├─ client_credentials ─▶ token ─▶ Initialize ─▶ connected
└─ authorization_code ─▶ StatusAuthorizationRequired
(CompleteAuthorization finishes it)
Each step is also exported so a caller can run them one at a time.
func (*Client) Discover ¶
Discover resolves protected-resource and authorization-server metadata for the challenge, honouring Overrides. It does not register a client.
func (*Client) Discoverer ¶
func (c *Client) Discoverer() *auth.Discoverer
Discoverer exposes the metadata fetcher.
func (*Client) GetPrompt ¶
func (c *Client) GetPrompt(ctx context.Context, name string, args map[string]string) (*GetPromptResult, error)
GetPrompt renders one prompt with arguments.
func (*Client) HTTPClient ¶
HTTPClient returns the client used for discovery and token requests: it carries tracing and fixed headers but no bearer token.
func (*Client) Initialize ¶
func (c *Client) Initialize(ctx context.Context) (*InitializeResult, error)
Initialize starts a fresh session: it clears any session state, sends initialize, records the negotiated protocol version, and sends the initialized notification.
func (*Client) LastConnect ¶
func (c *Client) LastConnect() *ConnectResult
LastConnect returns the most recent ConnectResult.
func (*Client) ListPrompts ¶
ListPrompts returns every prompt, following pagination.
func (*Client) ListResourceTemplates ¶
func (c *Client) ListResourceTemplates(ctx context.Context) ([]ResourceTemplate, error)
ListResourceTemplates returns every resource template, following pagination.
func (*Client) ListResources ¶
ListResources returns every resource, following pagination.
func (*Client) Negotiate ¶
func (c *Client) Negotiate(ctx context.Context) (*Negotiation, error)
Negotiate decides which era the server speaks and configures the transport for it.
The order follows the specification's backward-compatibility rule: try a stateless request first, and on 400 read the body before concluding anything. A modern server explains itself with a JSON-RPC error — an unsupported version, a missing capability, a header mismatch — and should be retried or corrected, not abandoned. Only an empty or unrecognised body means the server predates the revision and wants an initialize handshake.
func (*Client) Negotiation ¶
func (c *Client) Negotiation() *Negotiation
Negotiation returns the result of the last Negotiate call, or nil.
func (*Client) ReadResource ¶
ReadResource reads one resource by URI.
func (*Client) Register ¶
Register obtains a client identity for the discovered server and records it on d.
func (*Client) Resume ¶
func (c *Client) Resume(ctx context.Context, src auth.TokenSource) (*ConnectResult, error)
Resume connects using a previously obtained token source (for example a stored refresh token) without re-running discovery.
func (*Client) ServerInfo ¶
func (c *Client) ServerInfo() *InitializeResult
ServerInfo returns the initialize result, or nil before Connect.
func (*Client) SetTokenSource ¶
func (c *Client) SetTokenSource(src auth.TokenSource)
SetTokenSource installs a token source (for example one built by the caller from a stored refresh token).
func (*Client) StartAuthorization ¶
StartAuthorization begins the authorization-code flow and returns the URL the user must visit plus the state to verify on redirect.
func (*Client) TokenSource ¶
func (c *Client) TokenSource() auth.TokenSource
TokenSource returns the active token source, or nil when unauthenticated.
func (*Client) Transport ¶
func (c *Client) Transport() *transport.Streamable
Transport exposes the underlying Streamable HTTP transport.
type ClientCapabilities ¶
type ClientCapabilities struct {
Roots *struct {
ListChanged bool `json:"listChanged,omitempty"`
} `json:"roots,omitempty"`
Sampling *struct{} `json:"sampling,omitempty"`
Elicitation *struct{} `json:"elicitation,omitempty"`
}
ClientCapabilities advertised on initialize.
type Config ¶
type Config struct {
// Endpoint is the MCP server URL (the Streamable HTTP endpoint).
Endpoint string
// HTTPClient supplies the base transport and timeouts. Its Transport
// is wrapped with tracing, fixed headers and token handling.
HTTPClient *http.Client
// Headers are sent verbatim on every request to the MCP server and, once
// discovery has validated it, the authorization server (API keys, tenant
// selectors, basic auth). They are never sent to an origin neither the
// operator nor a validated discovery document named.
Headers map[string]string
ClientInfo Implementation
Auth AuthConfig
// URLPolicy governs which discovered URLs may be fetched or credentialed.
// The zero value is strict: https only, public hosts only.
URLPolicy auth.URLPolicy
// AllowResourceMismatch permits a protected-resource metadata document
// whose "resource" does not match the endpoint. RFC 9728 requires the
// client to check this binding; skipping it invites a token mix-up.
AllowResourceMismatch bool
}
Config configures a Client.
type ConnectResult ¶
type ConnectResult struct {
Status Status
AuthorizationURL string // set when Status == StatusAuthorizationRequired
State string // OAuth state to verify on the redirect
Initialize *InitializeResult
Discovery *Discovery // populated when an auth flow ran
}
ConnectResult describes the outcome of Connect.
type Content ¶
type Content struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
Data string `json:"data,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Resource json.RawMessage `json:"resource,omitempty"`
}
Content is one content block in a tool result.
type DiscoverResult ¶
type DiscoverResult struct {
ResultType string `json:"resultType,omitempty"`
ServerInfo Implementation `json:"serverInfo"`
Capabilities ServerCapabilities `json:"capabilities"`
Instructions string `json:"instructions,omitempty"`
// Extensions the server advertises, by reverse-DNS identifier.
Extensions []string `json:"extensions,omitempty"`
}
DiscoverResult is what server/discover returned. The RPC is optional in the 2026-07-28 revision, so its absence is not a failure.
type Discovery ¶
type Discovery struct {
Challenge auth.Challenge
PRM *auth.ProtectedResourceMetadata
PRMSource string // URL the PRM was fetched from ("" when overridden)
Server *auth.ServerMetadata
// Registration is nil until Register has run.
Registration *auth.Registration
Resource string
Scope string
Overridden bool
}
Discovery is what the authorization step learned about the server.
type Era ¶
type Era string
Era is which generation of the protocol a server speaks.
const ( // EraStateless is 2026-07-28 and later: no handshake, no session, every // request carrying its own protocol metadata. EraStateless Era = "stateless" // EraSession is 2025-03-26 through 2025-11-25: an initialize handshake // establishes connection state carried by a session header. EraSession Era = "session" // EraUnknown means detection has not run or could not decide. EraUnknown Era = "unknown" )
type GetPromptResult ¶
type GetPromptResult struct {
Description string `json:"description,omitempty"`
Messages []PromptMessage `json:"messages"`
}
GetPromptResult is the result of prompts/get.
type Implementation ¶
type Implementation struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Version string `json:"version"`
}
Implementation identifies a client or server.
type InitializeResult ¶
type InitializeResult struct {
ProtocolVersion string `json:"protocolVersion"`
Capabilities ServerCapabilities `json:"capabilities"`
ServerInfo Implementation `json:"serverInfo"`
Instructions string `json:"instructions,omitempty"`
}
InitializeResult is the server's answer to initialize.
type Negotiation ¶
type Negotiation struct {
Era Era `json:"era"`
// Version is the protocol version in use.
Version string `json:"version"`
// Attempted lists the versions offered, newest first.
Attempted []string `json:"attempted,omitempty"`
// ServerSupported is what the server said it supports, when it told us.
ServerSupported []string `json:"server_supported,omitempty"`
// Reason is the plain-language account of how the era was decided.
Reason string `json:"reason,omitempty"`
// Discovered is the server/discover result, when the server answered it.
Discovered *DiscoverResult `json:"discovered,omitempty"`
}
Negotiation records how the era was decided, so a report can say what was tried rather than only what was concluded.
type Overrides ¶
Overrides pins authorization server endpoints when the server does not publish discovery metadata. Any field left empty is discovered.
type Prompt ¶
type Prompt struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Arguments []PromptArgument `json:"arguments,omitempty"`
}
Prompt is one entry from prompts/list.
type PromptArgument ¶
type PromptArgument struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
}
PromptArgument describes one prompt parameter.
type PromptMessage ¶
PromptMessage is one message in a prompts/get result.
type ReadResourceResult ¶
type ReadResourceResult struct {
Contents []ResourceContents `json:"contents"`
}
ReadResourceResult is the result of resources/read.
type Resource ¶
type Resource struct {
URI string `json:"uri"`
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Size int64 `json:"size,omitempty"`
Annotations json.RawMessage `json:"annotations,omitempty"`
}
Resource is one entry from resources/list.
type ResourceContents ¶
type ResourceContents struct {
URI string `json:"uri"`
MimeType string `json:"mimeType,omitempty"`
Text string `json:"text,omitempty"`
Blob string `json:"blob,omitempty"`
}
ResourceContents is one item in a resources/read result.
type ResourceTemplate ¶
type ResourceTemplate struct {
URITemplate string `json:"uriTemplate"`
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
MimeType string `json:"mimeType,omitempty"`
}
ResourceTemplate is one entry from resources/templates/list.
type ServerCapabilities ¶
type ServerCapabilities struct {
Tools *struct {
ListChanged bool `json:"listChanged,omitempty"`
} `json:"tools,omitempty"`
Resources *struct {
Subscribe bool `json:"subscribe,omitempty"`
ListChanged bool `json:"listChanged,omitempty"`
} `json:"resources,omitempty"`
Prompts *struct {
ListChanged bool `json:"listChanged,omitempty"`
} `json:"prompts,omitempty"`
Logging *struct{} `json:"logging,omitempty"`
}
ServerCapabilities as reported by the server on initialize.
type Tool ¶
type Tool struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
InputSchema json.RawMessage `json:"inputSchema"`
OutputSchema json.RawMessage `json:"outputSchema,omitempty"`
Annotations *ToolAnnotations `json:"annotations,omitempty"`
}
Tool is one entry from tools/list.
func (Tool) IsDestructive ¶
IsDestructive reports the effective destructiveHint (default true). A read-only tool is never destructive.
func (Tool) IsReadOnly ¶
IsReadOnly reports the effective readOnlyHint (default false).
type ToolAnnotations ¶
type ToolAnnotations struct {
Title string `json:"title,omitempty"`
ReadOnlyHint *bool `json:"readOnlyHint,omitempty"`
DestructiveHint *bool `json:"destructiveHint,omitempty"`
IdempotentHint *bool `json:"idempotentHint,omitempty"`
OpenWorldHint *bool `json:"openWorldHint,omitempty"`
}
ToolAnnotations are hints about tool behaviour. All are advisory; the spec defaults are readOnlyHint=false, destructiveHint=true, idempotentHint=false, openWorldHint=true.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package auth implements the MCP authorization flow: RFC 9110 challenge parsing, RFC 9728 protected-resource discovery, RFC 8414 authorization server metadata, client registration (Client ID Metadata Documents with RFC 7591 dynamic registration as fallback), and OAuth 2.1 token acquisition with RFC 8707 resource indicators and PKCE.
|
Package auth implements the MCP authorization flow: RFC 9110 challenge parsing, RFC 9728 protected-resource discovery, RFC 8414 authorization server metadata, client registration (Client ID Metadata Documents with RFC 7591 dynamic registration as fallback), and OAuth 2.1 token acquisition with RFC 8707 resource indicators and PKCE. |
|
Package cmd provides scout's command-line interface.
|
Package cmd provides scout's command-line interface. |
|
scout
command
Command scout is the entry point for the scout CLI.
|
Command scout is the entry point for the scout CLI. |
|
Package diagnostics exercises a connected MCP server the way an agent would and produces an observability report with a quality score.
|
Package diagnostics exercises a connected MCP server the way an agent would and produces an observability report with a quality score. |
|
internal
|
|
|
config
Package config reads scout's configuration file.
|
Package config reads scout's configuration file. |
|
creds
Package creds models the credentials an operator is handed for an MCP server: a bearer token, an API key header, HTTP basic, OAuth client credentials, or an interactive authorization-code login.
|
Package creds models the credentials an operator is handed for an MCP server: a bearer token, an API key header, HTTP basic, OAuth client credentials, or an interactive authorization-code login. |
|
diag
Package diag carries scout's diagnostic output: what it is doing and why something was skipped, as distinct from the results it produces.
|
Package diag carries scout's diagnostic output: what it is doing and why something was skipped, as distinct from the results it produces. |
|
engine
Package engine is the one place a scout run is configured and started.
|
Package engine is the one place a scout run is configured and started. |
|
hostile
Package hostile provides MCP servers that misbehave on purpose.
|
Package hostile provides MCP servers that misbehave on purpose. |
|
otlp
Package otlp exports a finished run as OpenTelemetry traces.
|
Package otlp exports a finished run as OpenTelemetry traces. |
|
probe
Package probe runs scout's step-by-step diagnostic against one MCP server.
|
Package probe runs scout's step-by-step diagnostic against one MCP server. |
|
report
Package report turns a probe session into a document: a scored, step-by-step account of what was observed, with the telemetry attached.
|
Package report turns a probe session into a document: a scored, step-by-step account of what was observed, with the telemetry attached. |
|
telemetry
Package telemetry records every HTTP exchange scout makes, with connection-level timings from net/http/httptrace, TLS details, redacted headers and optionally bodies.
|
Package telemetry records every HTTP exchange scout makes, with connection-level timings from net/http/httptrace, TLS details, redacted headers and optionally bodies. |
|
tui
Package tui provides scout's Bubble Tea terminal user interface: a calm live progress view shown while a check runs, and the interactive tool selector.
|
Package tui provides scout's Bubble Tea terminal user interface: a calm live progress view shown while a check runs, and the interactive tool selector. |
|
web
Package web is scout's third surface: a local application that starts runs, streams their progress and renders their reports in a browser.
|
Package web is scout's third surface: a local application that starts runs, streams their progress and renders their reports in a browser. |
|
Package trace carries a per-run trace identifier through context and stamps it on every outgoing HTTP request so a single onboarding run can be followed across the network boundary.
|
Package trace carries a per-run trace identifier through context and stamps it on every outgoing HTTP request so a single onboarding run can be followed across the network boundary. |
|
Package transport implements the MCP Streamable HTTP transport: JSON-RPC over a single HTTP endpoint, with responses delivered either as a JSON body or as a Server-Sent Events stream, plus session and protocol-version header handling.
|
Package transport implements the MCP Streamable HTTP transport: JSON-RPC over a single HTTP endpoint, with responses delivered either as a JSON body or as a Server-Sent Events stream, plus session and protocol-version header handling. |