Reach a protected server
Find out why a server refused you, walk the OAuth discovery chain including the step that failed, get a token with dynamic registration and PKCE, or present an AAuth agent identity.
A refusal is not one problem. "No OAuth here" and "it advertises OAuth but its authorization server could not be read" look identical from a client, and have completely different fixes. The auth lab exists to tell them apart.
What came back, and why
mcpg inspector auth https://gw.example/mcp
auth sends one deliberately credential-free request and reports:
- the status and the
WWW-Authenticatechallenge; - every step of the RFC 9728 → RFC 8414 discovery walk, including the one that failed;
- a one-line verdict naming the next action.
A half-walked chain reads as a half-walked chain. That is the whole point — it needs no connected session, because the case you care about is the one where connecting failed.
It also reports AAuth posture, which lives in a separate channel:
AAuth-Requirement, Signature-Error, and the target's
/.well-known/aauth-resource.json.
In the web UI this is the first diagnose tab; in the TUI, the diagnose screen.
Getting a token
Once auth has found an authorization server, login walks the rest:
mcpg inspector login https://gw.example/mcp
It registers a client dynamically (RFC 7591 — which is how MCP clients work, since you cannot pre-register everywhere), opens a browser for an authorization-code grant with PKCE, catches the redirect on loopback, and prints the access token on stdout. Pass it straight back:
TOKEN="$(mcpg inspector login https://gw.example/mcp)"
mcpg inspector list https://gw.example/mcp tools --bearer "$TOKEN"
Useful flags:
| Flag | What it does |
|---|---|
--client-id | Use a client you registered yourself instead of registering one |
--scope | Override what to ask for; the default is whatever the challenge named |
--no-browser | Print the URL instead of opening one — for SSH sessions |
The token is audience-bound to this server via RFC 8707 resource indicators, so it is not replayable at another resource that trusts the same authorization server.
AAuth: an agent identity
AAuth gives an agent a portable cryptographic identity
— aauth:local@domain bound to an Ed25519 key — and signs every request with
it, so a captured credential is useless without the key. mcpg verifies it
through the dev.mcpg.identity.aauth plugin; the inspector can present it.
There is no registration step. Trust is domain control plus a published key, so getting an identity means generating one and serving two static documents:
mcpg inspector aauth-keygen \
--agent aauth:inspector@yourdomain.example \
--publish ./site
That writes ./site/.well-known/aauth-agent.json and ./site/.well-known/jwks.json,
and prints the private seed. Serve the two documents under
https://yourdomain.example, then sign with:
mcpg inspector list https://gw.example/mcp tools \
--aauth-key "$MCPG_INSPECTOR_AAUTH_KEY" \
--aauth-agent aauth:inspector@yourdomain.example \
--aauth-issuer https://yourdomain.example
--aauth-key reads MCPG_INSPECTOR_AAUTH_KEY, so the seed need not appear in
a process listing. If you already have an agent token from an agent provider,
pass it with --aauth-token and the token's own sub names the agent.
The inspector implements the identity-based access mode — the agent token
is the credential — which is the mode mcpg verifies. The modes that require a
person server and a human approval are reported by auth but not driven.
Two details that decide whether it works
Content-Digestis signed by default. Every MCP call isPOST /mcp, so without it the signature would say nothing about which call was made.EdDSAvsEd25519. The JOSEalgis the fully-specifiedEd25519. The polymorphicEdDSAis forbidden by the draft and refused by mcpg, so there is no switch for it. Do not confuse it with the RFC 9421alginsideSignature-Input, a different registry spelled lowercaseed25519— this is the most common interop failure in AAuth.
Keeping credentials off the command line
A command line is readable by every process on the box. Where a credential is involved, the inspector takes it from the environment instead:
MCPG_INSPECTOR_AAUTH_KEYfor the AAuth seed;MCPG_INSPECTOR_TOKENwhen attaching a TUI to a running inspector;- the gateway's own token when it supervises a sidecar, which is why
--inspectorneeds nothing pasted.
Next steps
- Debug a server — now that you can connect.
- Run it for a team — per-user workspaces and the hosted profile.
- Authorization — what the gateway does with the identity you present.