Why an MCP Server Should Advertise OAuth Discovery

What the WWW-Authenticate header and /.well-known/oauth-protected-resource actually do, how an agent walks the discovery chain from a 401 to a working token, and why a server that skips this step quietly turns away every client that could have become a user.

An agent arrives at your MCP server. It has no credentials. It has no account. Nobody has pasted an API key into a config file on its behalf. What happens next decides whether that agent becomes a user or a bounced request in your logs.

If your server answers with a bare 401 Unauthorized and an empty body, the answer is: nothing happens next. The agent has hit a wall with no door in it. It cannot ask a human. It cannot guess your login URL. It stops.

If your server answers with a 401 that carries a WWW-Authenticate header pointing at a metadata document, the agent has a path. It reads the document, finds your authorization server, registers itself, runs a browser consent flow with its human in the loop, and comes back with a token scoped to your resource. That whole sequence is machine-readable from end to end. No documentation reading. No support ticket. No copy-paste.

This piece explains the mechanics of that handshake, why each piece exists, and what breaks when you leave one out.

The problem OAuth discovery solves

Traditional API auth assumes a human developer. The human reads the docs, finds the signup page, clicks through a dashboard, generates a key, and pastes it into an environment variable. Every step of that is a human step.

An MCP client is not a human developer. It is a program that was handed a URL. Often it was handed that URL seconds ago by a user who typed it into a settings panel and expected the connection to work. There is no docs-reading step available. The only thing the client can do is make a request and read what comes back.

So the protocol has to carry the onboarding instructions in the response itself. That is the entire design goal. The MCP authorization specification builds on existing OAuth 2.1 practice rather than inventing anything, and the relevant standards are RFC 9728 for protected resource metadata, RFC 8414 for authorization server metadata and RFC 7591 for dynamic client registration.

Step one: the 401 that actually says something

When an unauthenticated request arrives, your server should return 401 with a header shaped like this:

WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"

That header is the whole entry point. It tells the client two things at once. First, this resource uses bearer tokens. Second, here is exactly where to read about how to get one.

Without the resource_metadata parameter, a client is left probing. Some clients will try well-known paths anyway. Many will not. A 401 with no header is indistinguishable from a broken server, and clients are right to treat it that way.

Step two: the protected resource metadata document

The URL in that header returns a small JSON document. The important fields are:

{
  "resource": "https://mcp.example.com/mcp",
  "authorization_servers": ["https://auth.example.com"],
  "scopes_supported": ["filings:read", "signals:read"],
  "bearer_methods_supported": ["header"]
}

resource is the canonical identifier of your server. authorization_servers is the list of issuers that can mint tokens for it. Everything else is a hint.

One detail trips people up. RFC 9728 constructs the well-known URL by inserting the well-known segment before the resource path, not by appending. For a resource at https://mcp.example.com/mcp, the metadata lives at https://mcp.example.com/.well-known/oauth-protected-resource/mcp. Many clients also try the plain root path as a fallback. Serving both is cheap insurance and costs you one route.

The separation here matters. Your MCP server is the resource. It does not have to be the authorization server. Pointing at a separate issuer is normal and often better, because it keeps token issuance out of your data path.

Step three: the authorization server metadata

The client now takes the issuer URL and fetches its metadata from /.well-known/oauth-authorization-server or the OpenID Connect equivalent. That document is where the actual endpoints live: authorization_endpoint, token_endpoint, registration_endpoint, and the supported PKCE methods.

At this point the client knows where to send a user for consent, where to exchange a code for a token, and whether it can create its own client credentials.

Step four: self-enrollment

This is the step that turns a visitor into a user, and it is the one most servers skip.

Dynamic client registration lets a client POST its own metadata to the registration_endpoint and receive a client_id back. Redirect URIs, client name, grant types. No human account creation. No dashboard.

Skip this and you have reintroduced the human step you were trying to remove. Every new client now needs someone to log into your admin panel and create an entry by hand. That is fine if you have twelve integration partners. It is fatal if you want any agent that discovers your URL to be able to connect.

If open registration worries you, the honest middle ground is software statements or a registration access policy, not switching it off entirely. Refusing all self-registration is a decision to only ever be used by people who already emailed you.

Step five: binding the token to your server

The client then runs a standard authorization code flow with PKCE, and it must include a resource parameter naming your canonical resource identifier. That comes from RFC 8707, and it exists to stop a real attack.

Without audience binding, a token issued for one service can be replayed against another that trusts the same issuer. The second service has no way to know the token was not meant for it. That is the confused deputy problem in its plainest form.

So the rule on your side is short. Validate that the token's audience is your resource identifier. Reject it if it is not. Never accept a token that was issued for something else, and never pass a client's token through to an upstream API as if it were your own. Token passthrough looks convenient and quietly destroys the audit trail that made the token useful.

What this looks like from the agent's side

The user types a URL into their client. The client requests the endpoint, gets a 401, reads the header, fetches two metadata documents, registers itself, opens a browser tab. The user sees a consent screen with a scope list, clicks approve, and the tab closes. The tools appear.

Elapsed time: seconds. Human actions: one click. That is the difference between a server that can be adopted and a server that has to be sold.

Why this matters specifically for filing data

Disclosure data has a natural fit with agent workflows because the questions are compound and the sources are separate. Congressional trades come from the Clerk of the House financial disclosure portal. Institutional holdings and private placements come from EDGAR, and the SEC sets out its access expectations, including the declared User-Agent requirement, on Accessing EDGAR Data.

Those feeds also come with timing rules the agent should state rather than hide. Congressional filings run on a 45-day clock under the STOCK Act, covered in the 45-day rule and why it matters. Institutional 13F holdings arrive on their own lag, explained in 13F deadlines and the 45-day lag. Form D has its own structure, walked through in how to read a Form D filing.

The connection to OAuth is direct. Scoped, audience-bound tokens are what let you attribute a query to a specific consenting user and apply per-user limits without ever seeing a shared secret sitting in a config file. Discovery is what lets that user arrive at all.

Free alternatives, said plainly

You do not need a paid identity vendor for any of this. Keycloak, Ory Hydra and Authentik are open source, self-hostable, and all support the metadata endpoints and dynamic registration described here. Several open-source MCP framework packages will emit the WWW-Authenticate header and the protected resource document for you with a few lines of configuration.

Hosted options like Auth0, WorkOS or Stytch cost money and buy you operational burden removal, not capability you cannot otherwise have. If you already run an identity provider, the work here is likely two new routes and an audience check, not a migration.

How to test your own server in one minute

Run curl -i https://your-server/mcp with no auth. You should see a 401 and a WWW-Authenticate header containing resource_metadata. Then curl that metadata URL and confirm you get JSON with resource and authorization_servers. Then curl the issuer's metadata and confirm registration_endpoint is present.

Three requests. If any of them returns a 404 or an empty body, an agent trying to enroll would stop at exactly that point.

The honest limits

Discovery does not make your server secure. It makes it reachable. You still need audience validation, short token lifetimes, real scope enforcement, and rate limits that survive a client retrying in a loop.

It also does not guarantee adoption. A discoverable server with unclear tool descriptions still confuses models. The plumbing being correct is a floor, not a finish.

And nothing on this page is investment advice. It is infrastructure guidance about connecting agents to public disclosure data, with no claim about what any filing means for any price.

If you want to see what a properly scoped agent connection actually returns before you build one, the Congress Stock Trades report shows the same House disclosure data an enrolled agent would receive, scored and linked back to the original filings, so you can judge the output quality first and wire up the auth chain second.


Want the signal instead of the raw filings? Get a free report preview. Prefer the tool to the write-up? Browse all data feeds or connect the free MCP server.