scout

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: GPL-3.0 Imports: 12 Imported by: 0

README

scout logo

scout

Test any Model Context Protocol server and find out, in plain language, whether it is ready for your agents — and exactly what to fix if it is not.

Build Status Go Reference golangci-lint Code Coverage OpenSSF Scorecard Documentation Release Version License Minimum Go version


Contents

Getting started

  • Install — mise, Homebrew, Arch, Nix, Go, or from source
  • Quick Start — diagnose a server in one command

Features & Capabilities

Reference & Operational

Project


Install

mise (macOS / Linux)

mise use -g github:sebastienrousseau/scout

This installs the latest released scout binary and keeps it managed with the rest of your mise tools.

Homebrew (macOS / Linux)

brew install sebastienrousseau/tap/scout

The tap ships a formula, so the same line works with Homebrew on Linux and installs the manpages and completions alongside the binary. The .deb/.rpm packages and the tarballs attached to each release are the alternatives, as are mise and the Go toolchain.

Arch Linux (AUR)

yay -S scout-bin    # or: paru -S scout-bin

Nix (any platform)

nix run github:sebastienrousseau/scout -- --help   # run without installing
nix profile install github:sebastienrousseau/scout # install

The flake ships the binary with its manpages and shell completions, and nix develop gives a shell with every tool the project's CI gates need.

Go toolchain

go install github.com/sebastienrousseau/scout/cmd/scout@latest

Installs into $(go env GOPATH)/bin (or $GOBIN when set). Note that a binary built this way reports scout version dev: the real version is stamped by the release pipeline through -ldflags, which go install does not apply. Use a release artefact if you need version to be meaningful.

Build from source

Requires Go 1.26.8+:

git clone https://github.com/sebastienrousseau/scout.git
cd scout
make install            # installs /usr/local/bin/scout, manpages, completions

make install PREFIX=$HOME/.local for a home-directory install.

Platform Prerequisites

macOS
brew install go
Ubuntu / Debian / WSL2
sudo apt install golang
Fedora / RHEL
sudo dnf install golang

Quick Start

Point scout check at a server's Streamable HTTP endpoint. It runs nine checks, shows a calm live checklist while it works, and then prints a plain-language verdict you can read at a glance:

# An open server
scout check https://mcp.example.com/mcp

# A server that handed you a bearer token
MCP_TOKEN=… scout check https://mcp.example.com/mcp --token-env MCP_TOKEN

# A server that handed you OAuth client credentials and a tenant parameter
scout check https://mcp.example.com/mcp --auth client-credentials \
    --client-id acme --client-secret-env ACME_SECRET --param profile_id=tenant-1

# Keep everything: text, Markdown, JSON, NDJSON events and a HAR archive
scout check https://mcp.example.com/mcp --report-dir ./scout-out

A finished run reads like this:

  http://mcp.example.com/mcp
  acme-mcp 1.4.0 · MCP 2025-11-25 · open, no sign-in

  Ready, with room to improve   98 / 100   Excellent

  Agents can connect to this server and use its tools. 3 things are worth
  improving, but none of them blocks adoption.

What to improve
  1  Unknown method returns -32601
     HTTP 400 instead of a JSON-RPC error
     → answer 200 with a JSON-RPC error object
  2  Tools declare outputSchema
     None declare outputSchema
     → add outputSchema and return structuredContent so results are machine-checkable
  3  Tool latency profile
     search and find_symbol take up to 17s at p95
     → slow tools make agents time out or retry; cache or bound the work

How it scores
  connectivity    ●●●●●   100
  authorization   ●●●●●   100
  protocol        ●●●●●    95
  catalog         ●●●●●    95
  execution       ●●●●●   100
  performance     ●●●●●    95
  6 of 6 areas tested

The headline is for anyone — a plain verdict, a score out of 100, and the things worth fixing in order. --verbose adds the per-check detail for developers; --report-dir DIR saves the full report and every request.

One binary, one base command, and every operation is a subcommand of it:

Command Does
scout check <endpoint> Run all nine phases and write the scored report
scout connect <endpoint> Only net, discovery, auth and handshake — is it reachable and do the credentials work
scout tools <endpoint> Connect and audit the tool, resource and prompt catalog without invoking anything
scout call <endpoint> <tool> Invoke one tool with --arg field=value or --json and time it
scout login <endpoint> Authorize as a user in the browser (PKCE) and store the token for later runs
scout config init Write a commented configuration file listing every setting

The exit status is 0 when nothing failed, 2 when any finding failed, and 1 on a scout error, so a pipeline can gate on it.

A run against a local server looks like this:

ok    Network and TLS  [0.7ms]
      info  Endpoint uses HTTPS: plain http to loopback 127.0.0.1 (acceptable for local servers)
      ok    Hostname resolves
            1 address(es) in 0.0ms
      ok    TCP connection
            connected in 0.5ms

ok    Authorization discovery  [2.7ms]
      info  Unauthenticated initialize: 200 OK without credentials: the server is open

warn  Protocol conformance  [2.7ms]
      ok    ping
      warn  Unknown method returns -32601
            HTTP 400 instead of a JSON-RPC error
            → answer 200 with a JSON-RPC error object
      ok    Response id matches request id
…
Score
      connectivity   100.0  (weight 10)
      authorization  100.0  (weight 20)
      protocol        95.0  (weight 20)
            -5 protocol.unknown_method: HTTP 400 instead of a JSON-RPC error
      total           97.5  grade A · 6 of 6 categories assessed

Features

Feature Description
Nine phases, in order Network and TLS, OAuth discovery, credentials, the initialize handshake, protocol conformance, catalog audit, safe execution with content validation, latency and concurrency, session and token recovery.
Your credentials, every kind Bearer token, API-key headers, HTTP basic, OAuth 2.1 client credentials with extra parameters, pinned endpoints for servers without discovery, or an interactive user login with PKCE.
Evidence-backed findings Every finding cites the requests that produced it as req#N, matching the seq in the NDJSON telemetry and the entry in the HAR file. Nothing passes without a request that showed it.
Full telemetry DNS, connect, TLS and time-to-first-byte per request from httptrace, TLS version and cipher, certificate expiry, redacted headers, byte counts, JSON-RPC method and error code.
Structural redaction Tokens, client secrets, API keys, OAuth codes and cookies are masked by name in headers, URLs, forms and JSON bodies, and a token issued during the run is masked wherever it appears afterwards.
Read-only by default Only tools that declare readOnlyHint are invoked. Unannotated tools are destructive by the MCP specification's default and are skipped; mutations are an explicit opt-in.
Honest scoring Six weighted categories, each deduction named with the finding behind it, and the report says how many categories were actually assessed.
Zero configuration Flags cover everything; a config file with profiles is there when you test the same servers repeatedly.

Architecture

A single run builds one HTTP client whose transport is wrapped by the telemetry recorder, then wraps it again for the MCP client with tracing, fixed headers and the bearer transport. A second, bare transport carries nothing but the trace header, so the requests that must arrive unauthenticated — first contact and the invalid-token probe — really do. Phases run in order; a phase that finds the server unreachable, or that credentials are missing for a protected server, marks every later phase as skipped with that reason.

graph TD
    A[User Shell] --> B{scout check}
    B --> C[Resolve credentials<br/>flags › profile › defaults › env]
    C --> D[net: DNS, TCP, TLS]
    D -- unreachable --> Z[Report: later phases skipped]
    D --> E[discovery: unauthenticated initialize]
    E -- 200 --> H
    E -- 401 --> F[PRM → AS metadata → registration path]
    F --> G[auth: token with your credentials<br/>+ invalid-token probe]
    G -- rejected --> Z
    G --> H[handshake: initialize, capabilities, session]
    H --> I[protocol: ping, -32601, id echo,<br/>malformed JSON, bogus session]
    I --> J[catalog: tools, resources, prompts audit]
    J --> K{Policy}
    K -- readOnlyHint --> L[execution: generated or supplied args,<br/>outputSchema validation, missing-arg test]
    K -- unannotated / destructive --> M[skipped, reported]
    L --> N[performance: p50/p95, cold vs warm,<br/>bounded burst, 429 + Retry-After]
    N --> O[resilience: lost session, invalidated token]
    O --> P[Score + report<br/>text · md · json · ndjson · html · sarif · junit · har]
    M --> N

Every request, whichever phase made it, passes through the same recorder, so the report's telemetry section and the HAR file are complete by construction.


The nine phases

Phase What happens Examples of findings
net URL scheme, DNS, TCP, TLS handshake plain HTTP to a public host, TLS 1.2 only, certificate expiring in 9 days
discovery unauthenticated first contact; on 401, the challenge, RFC 9728 protected-resource metadata (hint, then path-aware and root well-known), RFC 8414/OIDC server metadata, PKCE, grants, registration path no WWW-Authenticate, PRM resource differs from endpoint, S256 not advertised, no DCR or CIMD
auth token acquisition with your credentials (static, CIMD, or dynamic registration), token shape, and whether the server rejects a made-up token no expires_in, granted scope narrower than requested, server accepts any bearer token
handshake initialize: protocol version, serverInfo, capabilities, instructions, session id empty serverInfo.version, no capabilities declared
protocol ping, unknown method, id echo, malformed JSON, invalid params, unknown tool, missing Accept, GET stream, bogus session id, bad protocol-version header unknown method answered with HTTP 400, unknown session accepted
catalog tools, resources, templates, prompts: unique names, descriptions, inputSchema shape, annotations, outputSchema, absolute URIs; capabilities vs what actually lists 3 tools unannotated, tools listed without the capability
execution invoke what the policy allows with generated or supplied arguments; validate structuredContent against outputSchema; omit a required argument and expect rejection; read resources; render prompts search returns hits as a string, lax accepts a call with x missing
performance ping baseline, repeat-call p50/p95/max per tool, cold vs warm, bounded parallel burst, 429 and Retry-After p95 above 2s, errors under 4 workers
resilience lose the session and recover, invalidate the token and recover server ignores unknown session ids

Run a subset with --phases net,discovery,auth or --skip-phases performance. scout connect and scout tools are shorthands for the connection phases and the catalog.


Interactive TUI Mode

On a terminal, scout check shows a calm live checklist while it works — the wordmark, the endpoint, and each of the nine checks with a spinner on the one in flight and a plain result as it finishes:

  scout
  http://mcp.example.com/mcp
  no credentials

  ✓  Connectivity    reachable over TLS   4ms
  ✓  Authorization   open server         92ms
  –  Credentials     not needed
  ✓  Handshake       acme-mcp 1.4.0      21ms
  ⠋  Protocol        checking…
  ○  Catalog
  ○  Execution
  ○  Performance
  ○  Resilience

  4 of 9 phases   ·   press q to stop

Press q at any time to stop. When the run finishes, the checklist stays put and the report is printed below it, in your terminal's own scrollback. There is nothing to scroll inside — it is just text you can select, copy and page through as usual. Piped or redirected, the same run prints one ✓ [PASS] net: 3 ok line per check instead, so logs stay greppable.

By passing the -i or --interactive flag, you can pick the tools to exercise before anything runs:

scout check -i https://mcp.example.com/mcp --token-env MCP_TOKEN

The selector connects, lists the tools with their kind (read-only, mutating, destructive) and whether the default policy would run them, and preselects the ones it would. Selecting a mutating or destructive tool is an explicit opt-in for that tool only.

Keybindings

  • [space] — Toggle selection of the current tool.
  • [ctrl+a] — Select all currently filtered tools.
  • [ctrl+n] — Deselect all currently filtered tools.
  • [/] — Enter command / filter mode.
  • [enter] — Confirm selection and start the run.
  • [esc] — Exit without running.

In-Session Commands

Press / inside the TUI to enter Command Mode. Commands support prefix-based autocompletion (press [tab] or [right-arrow] to autocomplete):

  • /sort <field> — Sort tools. Fields:
    • name — Alphabetical sort by tool name.
    • kind — Sort by kind (destructive, mutating, read-only).
    • policy — Sort by whether the default policy allows the tool.
    • read-only / mutating / destructive — Bring that kind to the top.
  • /all — Select all filtered tools.
  • /none — Deselect all filtered tools.
  • /exit / /quit — Cancel and exit silently.
  • /help — Display the in-session help panel overlay.

SCOUT_SHOW_LOGO=0 replaces the flame with a plain title in both views.


Credentials

Whatever you were handed, there is a flag for it, an environment variable, and a config-profile setting with the same name.

You were given Use
a bearer token --token, --token-env NAME, or SCOUT_TOKEN
an API key or tenant header --header "X-API-Key: …" (repeatable)
a username and password --basic user:pass or SCOUT_BASIC
an OAuth client id and secret --auth client-credentials --client-id … --client-secret-env NAME (or SCOUT_CLIENT_ID, SCOUT_CLIENT_SECRET)
extra token parameters (tenant, profile, audience) --param key=value (repeatable)
a scope --scope "a b" (default: what the server's challenge asks for)
a token endpoint but no discovery --token-url … [--auth-url …] [--resource …]
a hosted Client ID Metadata Document --client-metadata-url https://…
a user login scout login <endpoint> then --auth authorization-code

--auth auto (the default) picks the mode from what is set. An explicit --client-id wins over dynamic registration: an operator who was handed a client id chose it deliberately. The report records where each credential came from — flag, environment variable, profile — never its value.


Safety

  • Read-only by default. Only tools with readOnlyHint: true are invoked. Tools without annotations are destructive by the MCP specification's default and are skipped; the catalog phase warns about them so the server author can fix the annotations.
  • Mutations are opt-in. --allow-mutations also runs tools that mutate but declare destructiveHint: false. --allow-destructive runs everything and is meant for a tenant you are willing to lose.
  • Narrow the set. --only NAME and --deny NAME pick tools; --arg tool.field=value supplies real arguments instead of generated ones, and a tool given real arguments is not put through the missing-argument test.
  • Throttled. Requests are capped at --rps (default 2). The parallel burst respects it unless you pass --allow-load to test the server's rate limiting for real.
  • One adversarial request. The invalid-token probe sends a single request with an obviously made-up bearer token. Nothing else adversarial is sent, and nothing is fuzzed against a server you do not own.

Safety in the other direction

Those rules protect the server. These protect you, because the server you point scout at is by definition one you have not vetted yet.

  • Credentials are bound to the origin you named. A bearer token, API-key header or HTTP basic credential is sent to that origin and nowhere else. A redirect leaving it is refused, not followed: an HTTP round-tripper sits below the redirect handler, so a client that attaches credentials without this check re-attaches them on every hop, and one 307 is enough to collect them.
  • Discovered endpoints are checked before they are contacted. Everything after your own endpoint is chosen by the server: authorization_servers comes from the resource, token_endpoint and registration_endpoint from the authorization server, and the resource_metadata hint from a response header. Each must be HTTPS and must resolve to a public address, so a server cannot aim your client secret at a plaintext host or at an address inside the network your CI runner sits in. Loopback is always allowed, and an endpoint that is itself private relaxes the check for its own network.
  • The metadata must be about your endpoint. A protected-resource document naming a different resource is refused: RFC 9728 requires that binding, and a mismatch is the shape of a token mix-up.
  • The authorization response must come from the right issuer. The RFC 9207 iss on the redirect is checked against the issuer the code was requested from.
  • A misbehaving server gets a finding, not a crash. scout is tested against servers that misbehave on purpose — acknowledging a request with 202, redirecting mid-session, streaming without end, advertising a schema that overflows an integer — and every one must produce a finding or a typed error rather than a panic, a hang, or an unbounded allocation.

Understanding your report

Every run answers one question first: is this server ready for agents?

  • Ready for agents — everything works. Ship it.
  • Ready, with room to improve — agents can use it today; the listed items make it better, none blocks adoption.
  • Not ready for agents — an agent will hit a real problem here. Fix the numbered items before you roll it out to your fleet.
  • Couldn't finish the check — scout could not reach or sign in to the server. The one thing to fix is shown; nothing else was tested.

The score out of 100 is a weighted read across six areas — connectivity, authorization, protocol, catalog, execution and performance. The five-dot meter beside each area shows where the points went. Only areas that were actually tested count, and the report says how many, so a high score on a partial run can't be mistaken for a clean bill of health.

What to improve lists each issue as a plain problem and a one-line fix, worst first. That is the whole executive summary. Developers add --verbose for the per-check detail: every check, the tools and resources that were called, the speed measurements, and a req#N reference tying each finding to a recorded request.

Formats and telemetry

--output text (the default, coloured on a terminal) prints the report above to your terminal's scrollback, so you scroll it the normal way. --output md is a shareable Markdown document, --output json the full structured report (add --events to embed every request), and --output ndjson a stream you can pipe into jq.

Two formats exist for machines that will not read anything else: --output sarif is SARIF 2.1.0 for GitHub code scanning — every rule carries the check's documentation link — and --output junit is JUnit XML, so a run lands beside the unit tests in your CI panel. Neither is richer than json; each is the same findings in the shape one reader insists on.

--report-dir DIR writes all of them plus:

  • telemetry.ndjson, one line per request: phase, label, method, URL, status, DNS/connect/TLS/TTFB/total timings, TLS version and cipher, certificate expiry, redacted headers, byte counts, JSON-RPC method and error, and with --capture-bodies the redacted bodies.
  • telemetry.har, the same as an HTTP Archive 1.2 you can open in any browser's devtools.

Findings cite requests as req#N; N is the seq field in the NDJSON and the entry index in the HAR.

Redaction is structural, not best-effort: Authorization, cookies and key-like headers are masked by name; code, state, client_secret and friends are masked in URLs and forms; access_token, refresh_token, client_secret and similar are masked inside JSON bodies and their values registered, so a token issued mid-run is masked wherever it appears afterwards. Content types are not trusted — a body that starts with { is treated as JSON, because token endpoints answer text/plain often enough.


Scoring

Six categories, weighted: connectivity 10, authorization 20, protocol 20, catalog 15, execution 20, performance 15. Each starts at 100; a critical failure zeroes it, a major one costs 40, a minor one 15, and every warning 5. The total is the weighted mean over the categories whose phases actually ran, and the report says how many that was, so a partial run cannot pass for a full one. Every deduction is listed with the finding that caused it.

Grades are coarse labels for dashboards: A at 90 and above, B at 75, C at 60, D at 40, F below.


Library use

The CLI is built on public packages you can use directly:

client, _ := scout.New(scout.Config{
    Endpoint: "https://mcp.example.com/mcp",
    Auth:     scout.AuthConfig{Mode: scout.AuthClientCredentials, Extra: url.Values{"profile_id": {"t1"}}},
})
res, err := client.Connect(ctx)      // 401 → discovery → registration → token → initialize
tools, _ := client.ListTools(ctx)
out, _ := client.CallTool(ctx, "search", map[string]any{"q": "invoices"})

auth exposes discovery, registration and the token sources individually; transport is the Streamable HTTP layer with raw access for conformance probes; diagnostics holds the safety policy, the schema-driven argument generator, the validator and a standalone read-only runner; trace carries the run's trace id.


Usage & Flags

Positional Arguments

scout check <endpoint>
  • <endpoint> — The server's Streamable HTTP URL (Required, unless --profile supplies it).

Credential Options

Option Default Description
--auth auto Credential mode: auto, none, bearer, client-credentials, authorization-code
--token Pre-issued bearer token (or SCOUT_TOKEN)
--token-env Read the bearer token from this environment variable
--header Extra header sent on every request, "Name: value" (repeatable)
--basic HTTP basic credentials as user:password (or SCOUT_BASIC)
--client-id OAuth client id (or SCOUT_CLIENT_ID)
--client-secret OAuth client secret (or SCOUT_CLIENT_SECRET; prefer --client-secret-env)
--client-secret-env Read the client secret from this environment variable
--client-metadata-url https URL of a Client ID Metadata Document to use as client id
--scope Scope to request (default: what the server challenge asks for)
--param Extra token/authorization request parameter key=value (repeatable)
--token-url Token endpoint, bypassing discovery
--auth-url Authorization endpoint, bypassing discovery (with --token-url)
--resource RFC 8707 resource indicator override
--redirect-port 8976 Loopback port for the authorization-code redirect
--token-auth-method Token endpoint auth: client_secret_basic, client_secret_post or none

Policy Options

Option Default Description
--allow-mutations off Also invoke tools that mutate but are not destructive
--allow-destructive off Also invoke destructive and unannotated tools (dangerous)
--only Restrict execution to this tool (repeatable)
--deny Never invoke this tool (repeatable)
--arg Argument override tool.field=value (repeatable; JSON parsed when possible)
--insecure-allow-http-auth off Allow a discovered OAuth endpoint served over plain http
--insecure-allow-private-hosts off Allow a discovered OAuth endpoint that resolves inside your network
--allow-resource-mismatch off Continue when the protected-resource metadata names a different endpoint

The three overrides above each switch off a check that exists because everything past your own endpoint is chosen by the server under test. Leave them off unless you know why you are turning one on; see Safety.

Pacing Options

Option Default Description
--samples 5 Repeat calls per tool in the performance phase
--concurrency 4 Workers in the parallel burst (0 disables)
--rps 2 Max requests per second; 0 or negative disables throttling
--timeout 30s Per-call timeout
--seed 1 Seed for generated arguments
--fill-optional off Also populate optional schema properties
--allow-load off Run the burst unthrottled to test the server's rate limiting
--max-resources 25 Max resources to read
--max-prompts 25 Max prompts to render

Output Options

Option Short Default Description
--interactive -i off Pick the tools to exercise in the selector before the run
--output text Output format: text, json, md, ndjson, html, sarif, junit
--report-dir Write report.{txt,md,json,html,sarif,junit.xml}, telemetry.ndjson and telemetry.har here
--otlp-endpoint Export the finished run as OpenTelemetry traces to an OTLP/HTTP collector
--otlp-header Extra header on the OTLP export, Name: value (repeatable)
--log-format human Diagnostic format on stderr: human or json
--capture-bodies off Record request/response bodies in telemetry (redacted, capped)
--events off Embed every telemetry event in JSON output
--verbose -v off Show evidence references and full info findings
--no-color off Disable ANSI colour
--phases all Run only these phases (comma-separated)
--skip-phases Skip these phases (comma-separated)
--config ~/.config/scout/config.json Configuration file
--profile Profile from the configuration file supplying the endpoint and settings
--log-level info Diagnostic verbosity on stderr: error, warn, info, debug

Diagnostics

Results go to stdout in the format --output selects. Diagnostics — which phase is running, what failed, why something was skipped — go to stderr, so --output json stays pipeable no matter how noisy the run is.

--log-level controls how much of that stderr you get. SCOUT_LOG_LEVEL sets the same thing for a whole shell session. SCOUT_SHOW_LOGO=0 drops the flame from the selector and version.

# Why was that tool skipped? Turn the detail up.
scout check https://mcp.example.com/mcp --log-level debug

# Machine-readable results, quiet stderr, both at once.
scout check https://mcp.example.com/mcp --output json --log-level error > report.json

# For a bug report: full detail, everything captured.
SCOUT_LOG_LEVEL=debug scout check https://mcp.example.com/mcp --report-dir ./out 2> diagnostics.log

Operational Commands

scout connect https://mcp.example.com/mcp --token-env MCP_TOKEN
scout tools https://mcp.example.com/mcp --output md
scout call https://mcp.example.com/mcp search --arg q=invoices --arg limit=5
scout login https://mcp.example.com/mcp
scout config validate

connect stops after the handshake, tools after the catalog audit, call invokes one tool and prints its result with timing, and login runs the PKCE flow and stores the token (0600) under ~/.config/scout/tokens.json for --auth authorization-code runs.


Configuration file

~/.config/scout/config.json (or $XDG_CONFIG_HOME/scout/config.json, or SCOUT_CONFIG). Keys are flag names, so the file has no schema of its own and picks up new flags automatically. scout config init writes a commented template listing every setting.

{
  "defaults": { "rps": 4, "report-dir": "./scout-reports" },
  "profiles": {
    "prod": {
      "endpoint": "https://mcp.example.com/mcp",
      "settings": {
        "auth": "client-credentials",
        "client-id": "acme",
        "client-secret-env": "ACME_SECRET",
        "param": ["profile_id=tenant-1"],
        "deny": ["send_email"]
      }
    }
  }
}
scout check --profile prod

Precedence, highest first: an explicit flag, the selected profile, the defaults block, the flag's built-in default. Environment variables are consulted for secrets only. Keep secrets in the environment and reference them with the *-env settings. A setting that names no flag is an error, not a silent no-op.


Coming from another tool

Migration guides live in docs/migrating/: from MCP Inspector, from a curl script, or from an online playground.

Each says what carries over, what is genuinely different, and what scout will not do — nothing there touches the server, and scout connect shows you the handshake before anything else runs.


Examples

To inspect the package layout and programmatically drive scout modules, see the self-contained, copy-pasteable Go code examples in the examples directory:

  1. Connect and call — Connect with OAuth client credentials through the public scout and auth packages and invoke one tool.
  2. Safe diagnostics — Run the library's read-only diagnostics runner against an open server and print the quality score with its deductions.

Troubleshooting

Error Message Cause Solution
server requires authorization and no credentials were supplied The server answered 401 and --auth resolved to none. Pass --token-env, --client-id/--client-secret-env, or run scout login.
--auth client-credentials needs --client-id Client-credentials mode with nothing to identify the client. Supply --client-id, --client-metadata-url, or SCOUT_CLIENT_ID.
token endpoint invalid_target The authorization server rejected the RFC 8707 resource indicator. Pass --resource with the value the server expects.
no stored token for this endpoint --auth authorization-code without a prior login. Run scout login <endpoint> first.
credentials rejected at initialize The token was issued but the MCP server did not accept it. Check audience/resource, scope and expiry; --log-level debug shows the challenge.
unknown setting "rsp" A config key does not match any flag name. Settings are named after flags; see scout check --help.

Frequently Asked Questions

  • Does it test stdio servers?
    No. scout speaks Streamable HTTP. Put a stdio-to-HTTP bridge in front of a stdio server, or run it in HTTP mode if it has one.
  • Why were my tools skipped?
    They declare no annotations, or destructiveHint is true. The MCP specification's default for an unannotated tool is destructive, and scout honours it. Add readOnlyHint: true to the tools that are, or opt in with --allow-mutations / --allow-destructive on a tenant you control.
  • Why did a tool return isError and count as a warning, not a failure?
    Generated arguments are representative, not real; a server that rejects "probe" as a repository name is behaving correctly. Give it real values with --arg tool.field=value and the call becomes a proper test.
  • Can I run it inside cron or CI?
    Yes. The command is non-interactive, stdout carries the report in the format you chose, and the exit status is 2 when any finding failed.
  • Where do the secrets go?
    Nowhere. They are registered with the redactor before the first request and masked in every event, body and report. The token store is written with mode 0600, and a store that is readable by anyone else is refused rather than read.
  • Can a server under test steal my token?
    Not by asking for it. Credentials are bound to the origin you named, so a redirect pointing somewhere else is refused rather than followed, and the OAuth endpoints a server advertises are checked for HTTPS and for pointing at a public host before scout will talk to them. That is what --insecure-allow-http-auth and --insecure-allow-private-hosts turn off, which is why they say insecure.
  • Are the *_ms fields in the JSON milliseconds?
    Yes. scout.schema_version in the report says which format version you are reading; pin it if you build on the JSON.

THE ARCHITECTSebastien Rousseau
THE ENGINEEUXIS ᛫ Enterprise Unified Execution Intelligence System


Documentation

The four entry points, identical across every repo in the family:

  • User Manual — the rendered manual: getting started, credentials, the nine phases, reports, configuration
  • API reference — the Go packages the CLI is built on
  • Developer docs — toolchain, task map, reproducing every CI gate locally
  • Ecosystem map — the surfaces, the published artefacts, the lockstep version rule
Document Covers
docs/architecture.md How a run is put together: the engine, the phases, the transports
docs/checks.md Every check scout runs, generated from the source and gated in CI
docs/adr/ Decision records for the choices that get questioned later
docs/security-model.md Threat model, the credential boundary, what scout will and will not send
docs/packaging.md Addressed to distribution maintainers: licence grant, toolchain policy, offline tests
pkg/VERIFY.md Verifying a release: checksums, keyless cosign identity, SBOM
SECURITY.md Disclosure policy, supported versions, response SLA
CONTRIBUTING.md Signed-commit and DCO policy, PR guidelines, the local test recipe
CHANGELOG.md Per-release notes following Keep a Changelog 1.1.0
SUPPORT.md Where to ask, and what to expect

Once installed, man scout works offline, and every subcommand has its own page (man scout-check).


When not to use scout

scout is opinionated, and the opinions do not suit everyone.

  • You want a load test. The parallel burst is bounded on purpose, and the default throttle is two requests per second. Use a load-testing tool for capacity questions; use scout to learn whether the server behaves under modest concurrency and rate-limits politely.
  • You need full JSON Schema validation. The validator covers the structural core — types, required, properties, items, enum, const, bounds, oneOf/anyOf/allOf — and resolves local $ref/$defs pointers, which is what pydantic, zod and the official SDKs emit. It does not fetch a $ref that points at another document (it reports one as unchecked rather than passing it over), and it does not check pattern or format. A contract that leans on those needs a full validator.
  • Your server is stdio-only. scout tests the Streamable HTTP transport. A bridge can expose a stdio server over HTTP, but the transport findings then describe the bridge.
  • You need the Tasks or Apps extensions checked. scout diagnoses the core protocol on both the handshake revisions and the stateless 2026-07-28 one, but it does not yet exercise the optional extensions.
  • You want an agent to exercise the server. scout's execution phase is deterministic: generated or supplied arguments, one call per tool. The diagnostics.Model interface exists for a model-driven probe, but no vendor adapter ships in this module.
  • You need Windows without WSL. Binaries are published for Windows, but the experience is less tested than on macOS and Linux.

Requirements & toolchain policy

Go The go directive in go.mod — currently 1.26.8
Network Outbound HTTPS to the server under test and its authorization server

The Go floor is stated in exactly one place, go.mod, and CI sets GOTOOLCHAIN=auto so it cannot disagree with a workflow input.

Policy for raising it. The floor may rise in any release when a standard-library fix or language feature justifies it, and the reason is recorded in that release's CHANGELOG entry. scout makes no distro-LTS compatibility promise — an aspirational claim without a table mapping distro toolchains to the floor would be worse than none. Packagers should check go.mod on every version bump rather than assume the floor held.


Stability guarantees

scout is pre-1.0 and follows SemVer, with the patch digit moving for everything until 1.0.

The breaking axis is behaviour, not signatures. For a tool whose output is consumed by pipelines and whose requests reach other people's servers, a change to what it sends or reports is breaking even when no flag or function signature moves. Specifically, these are treated as breaking:

  • A change to what --output json / ndjson emits, beyond added fields
  • A change to a finding id, or to the status a given observation produces
  • A change to the score weights or deductions
  • A change to an exit code
  • A safety refusal becoming permissive: any case where scout used to decline to invoke a tool and now proceeds
  • A new request that reaches the server under test without a flag to disable it

Added fields, new flags with inert defaults, new findings, and new refusals are not breaking.

Deprecation window. A deprecated flag keeps working for at least one minor release after the release that announces it, and warns on stderr — never on stdout, which carries the selected output format.


Security & hardening

Reporting. Do not open a public issue. Follow the private process in SECURITY.md; the response SLA is stated there.

Posture. scout runs with credentials the operator supplied against servers the operator chose, so the threat model is about limiting blast radius, not crossing a privilege boundary. Full detail in docs/security-model.md.

  • Secrets are redacted at the recorder, structurally. Headers by name, URL and form parameters by name, JSON keys by name with their values registered, so nothing that was ever a secret reaches a report, a log or a HAR file. The token store is written with mode 0600.
  • Nothing destructive without a flag. Only readOnlyHint tools run by default; unannotated tools are treated as destructive per the specification; mutations and destructive tools each need their own opt-in.
  • One adversarial request, and throttled. The invalid-token probe is the only request designed to be rejected. Everything is capped at --rps unless --allow-load is given.
  • Unauthenticated probes are really unauthenticated. A second, bare transport carries no credentials of any kind, so a server that accepts any token cannot hide behind the real one.
  • Memory safety comes from Go; there is no CGO anywhere (CGO_ENABLED=0), so released binaries are static and free of libc coupling.

Fuzzing. Fuzz targets cover the parsing boundaries — the WWW-Authenticate challenge parser, the SSE response reader, the JSON Schema validator and the argument generator. A committed seed corpus under each package's testdata/fuzz/ replays on every go test, and the targets run for a fixed duration on every push. scout is not enrolled in OSS-Fuzz.

Supply chain. Releases are signed with keyless cosign, carry SLSA build provenance and a CycloneDX SBOM, and are built with -trimpath so two builds of a commit are byte-identical. Every GitHub Action is pinned by commit SHA and the container base by digest. govulncheck runs on every push.


License

Licensed under the GNU General Public License v3.0.

Back to Top

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

Constants

This section is empty.

Variables

View Source
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.

View Source
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.

SessionVersions are the handshake revisions scout offers, newest first.

View Source
var StatelessVersions = []string{transport.V20260728}

StatelessVersions are the stateless revisions scout offers, newest first.

View Source
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

func Unauthorized

func Unauthorized(err error) (auth.Challenge, bool)

Unauthorized extracts the Bearer challenge from a 401 transport error.

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 New

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

New builds a Client. It does not contact the server.

func (*Client) AllowedOrigins

func (c *Client) AllowedOrigins() []string

AllowedOrigins lists the origins this client may send credentials to.

func (*Client) Call

func (c *Client) Call(ctx context.Context, method string, params, result any) error

Call sends an arbitrary JSON-RPC request on the current session.

func (*Client) CallTool

func (c *Client) CallTool(ctx context.Context, name string, args any) (*CallToolResult, error)

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) Config

func (c *Client) Config() Config

Config returns the configuration the client was built with.

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

func (c *Client) Discover(ctx context.Context, challenge auth.Challenge) (*Discovery, error)

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

func (c *Client) HTTPClient() *http.Client

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

func (c *Client) ListPrompts(ctx context.Context) ([]Prompt, error)

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

func (c *Client) ListResources(ctx context.Context) ([]Resource, error)

ListResources returns every resource, following pagination.

func (*Client) ListTools

func (c *Client) ListTools(ctx context.Context) ([]Tool, error)

ListTools returns every tool, following pagination cursors.

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) Ping

func (c *Client) Ping(ctx context.Context) error

Ping sends the MCP ping request.

func (*Client) ReadResource

func (c *Client) ReadResource(ctx context.Context, uri string) (*ReadResourceResult, error)

ReadResource reads one resource by URI.

func (*Client) Register

func (c *Client) Register(ctx context.Context, d *Discovery) (*auth.Registration, error)

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

func (c *Client) StartAuthorization(d *Discovery) (string, string, error)

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.

func (*Discovery) Endpoint

func (d *Discovery) Endpoint(authMethod string) auth.Endpoint

Endpoint returns the token-layer view of the discovery.

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

type Overrides struct {
	AuthorizationURL string
	TokenURL         string
	Resource         string
}

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

type PromptMessage struct {
	Role    string  `json:"role"`
	Content Content `json:"content"`
}

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 Status

type Status string

Status of a Connect call.

const (
	// StatusConnected means initialize succeeded.
	StatusConnected Status = "connected"
	// StatusAuthorizationRequired means the user must visit AuthorizationURL
	// and the caller must then call CompleteAuthorization.
	StatusAuthorizationRequired Status = "authorization_required"
)

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

func (t Tool) IsDestructive() bool

IsDestructive reports the effective destructiveHint (default true). A read-only tool is never destructive.

func (Tool) IsReadOnly

func (t Tool) IsReadOnly() bool

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.
cmd
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.

Jump to

Keyboard shortcuts

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