sondahub / MCP OAuth test server
MCP server with OAuth, for testing the authorization flow
The sondahub MCP server, behind OAuth 2.1 as the MCP specification lays it out: a 401 that points to protected-resource metadata, an authorization server with dynamic client registration, PKCE, tokens bound to the resource, and scopes that tell reads from writes. Free, with people to sign in as.
Connect a client
- Server
- https://api.sondahub.com/mcp/secure
- Transport
- Streamable HTTP, stateless
- Scopes
- mcp:read, mcp:write
- Sign in as
- sonda / probe, or any directory user / probe
A client that implements MCP authorization needs nothing but the address: it meets the 401, discovers the rest, registers itself, opens the consent screen and comes back with a token.
claude mcp add --transport http sondahub-secure https://api.sondahub.com/mcp/secure
npx @modelcontextprotocol/inspector # Transport: Streamable HTTP · URL: https://api.sondahub.com/mcp/secure — it runs the OAuth flow when the 401 comes back
The flow, step by step
What a client does, one request at a time — for building one, or for finding where yours stops.
1. Call without a token
curl -i -X POST https://api.sondahub.com/mcp/secure \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.sondahub.com/.well-known/oauth-protected-resource/mcp/secure", scope="mcp:read"
2. Read the metadata
curl https://api.sondahub.com/.well-known/oauth-protected-resource/mcp/secure
curl https://api.sondahub.com/.well-known/oauth-authorization-server
3. Register
curl -X POST https://api.sondahub.com/v1/utils/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name":"My MCP client","redirect_uris":["http://127.0.0.1:33418/callback"],"token_endpoint_auth_method":"none"}'
4. Authorize, with PKCE and the resource
Open this in a browser with your client_id; sign in, allow, and the browser lands on your redirect URI with a code (and iss, RFC 9207):
https://api.sondahub.com/v1/utils/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A33418%2Fcallback&scope=mcp%3Aread%20mcp%3Awrite&state=xyz&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.sondahub.com%2Fmcp%2Fsecure
5. Exchange the code
curl https://api.sondahub.com/v1/utils/oauth/token \ -d grant_type=authorization_code -d client_id=CLIENT_ID \ -d code=THE_CODE -d redirect_uri=http://127.0.0.1:33418/callback \ -d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk \ -d resource=https://api.sondahub.com/mcp/secure
6. Call the server
curl https://api.sondahub.com/mcp/secure \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
A token minted for another audience answers 401 invalid_token naming the audience it has and the one it needs — the mistake clients make most. For a quick token without a browser: the password grant with resource=https://api.sondahub.com/mcp/secure and scope=mcp:read, as on the OAuth page.
Questions
Which version of the MCP authorization spec does it follow?
The one of protocol 2025-11-25 and 2025-06-18: the resource server answers 401 with WWW-Authenticate naming its protected-resource metadata (RFC 9728), which names the authorization server; that server publishes RFC 8414 metadata with a registration endpoint (RFC 7591), requires PKCE with S256 of public clients (a client the spec says must use it either way), and binds tokens to the resource (RFC 8707). Clients built for the older spec, which fetch /.well-known/oauth-authorization-server from the server's origin, find it there too.
What do the scopes allow?
mcp:read calls every read-only tool, lists and reads resources and prompts. Write tools need mcp:write; without it they answer 403 insufficient_scope with a WWW-Authenticate naming the scopes to ask for — the step-up a good client handles by asking again.
Who is the user?
Whoever signs in on the consent screen: the playground user sonda / probe, or anyone in the Identity directory with the password probe. The whoami tool answers with the token's claims, so a client can show who it is acting as.
Does registration store my client?
No. The client_id is the registration, signed, so it works forever and on any copy of the hub. Redirect URIs you registered are enforced on every authorization request.