Engineers verifying a remote MCP server before handing it to users.
Whether your server's authentication behaves correctly against a real client rather than against curl.
The control gap
Inspector is usually used to list tools and call one. That exercises the least interesting half of a remote server. The half that fails in production is discovery, consent, scope enforcement, and audience checking, and Inspector can drive all of it because it is a real MCP client with an OAuth implementation. Testing with a hand-made bearer token skips exactly the paths that break.
What good looks like
A recorded session showing the 401 challenge, the discovery documents, the consent screen, the successful call, and the deliberate failures, all produced by a client rather than by a script.
- Connect with no credential first, and read the challenge Inspector reports rather than assuming it exists.
- Complete the OAuth flow from a clean profile so registration and consent are exercised, not replayed.
- Test a narrowed scope explicitly, and confirm a write tool fails with an insufficient-scope response.
- Point Inspector at one endpoint using a token obtained for another, and confirm the rejection.
A production workflow
- Start Inspector and enter the MCP endpoint URL with authentication left empty.
- Observe the 401 and the resource_metadata pointer, then let Inspector follow discovery and registration.
- Complete consent, list tools, and call one read-only tool.
- Repeat with a reduced scope and with a foreign token, recording both failures.
Copy this
Run these in order. The interesting output is the failures in steps 4 and 5, because a server that passes 1 to 3 can still fail both.
npx @modelcontextprotocol/inspector
1. Transport: Streamable HTTP
URL: https://endram.com/mcp/billing-tools
Auth: none
EXPECT: 401, and a resource_metadata URL in the reported challenge
2. Let Inspector follow discovery
EXPECT: it fetches /.well-known/oauth-protected-resource/mcp/billing-tools
then /.well-known/oauth-authorization-server
then registers dynamically and opens the consent screen
3. Consent, then List Tools, then call one read-only tool
EXPECT: success, and a decision record on the server naming your user
4. Reconnect requesting scope "mcp:connect tools:read" only,
then call a state-changing tool
EXPECT: 403 with WWW-Authenticate insufficient_scope, scope="tools:call"
5. Reuse the token from step 3 against a different endpoint on the same host
EXPECT: 401 invalid_token, because the audience does not match
Server-side, each of the five steps should have produced a log line.
A step that produced nothing is a gap in evidence, not a pass.Step 5 is worth automating. It is the check that most often regresses quietly, because adding a second MCP endpoint to an existing deployment is exactly when a shared audience gets introduced.
Evidence to require
- The Inspector session output for each step, kept with the release it tested.
- Server-side records for the same five steps, matched by timestamp.
- The scope set granted at consent, compared with the scope set requested.
- The rejection reason recorded for the foreign-audience attempt.
Buyer checklist
- Has anyone connected to this server with an unauthenticated client and read the actual challenge?
- Does a reduced-scope consent genuinely block writes, or only hide the tools?
- Is there a server-side record for every failed attempt, not only successful calls?
- Would a second endpoint added next quarter share this one's audience?
Practical answers
Common implementation questions
Can Inspector replace a real client for acceptance testing?
For the protocol and authentication surface, yes. It performs discovery, registration, and the code flow the way a shipping client does. It does not tell you how a specific client renders consent or handles elicitation.
Should Inspector be exposed on a shared host?
No. Run it locally and bind to loopback. It is a debugging client holding live credentials, and it has no business being reachable from the network.
What if the server has no OAuth yet?
Inspector still connects with a static header, which is fine for early development. Just be clear that a passing session in that mode has tested the tools and none of the authorization behaviour.