Publish protected resource metadata
This check verifies that MCP Tower can fetch your server’s protected resource metadata, the JSON document that tells agents which authorization server signs users in.
Check id resource_metadata (resource metadata). Results:
Why it matters
Section titled “Why it matters”The protected resource metadata is the second step of every sign-in, right after the 401 challenge. It names the resource identifier and the authorization server. Without it, an agent knows that your server needs sign-in but not where to send the user, so the connection fails before any sign-in screen appears.
- The MCP authorization specification requires every protected MCP server to publish this
document (RFC 9728) and requires clients to use it to find the authorization server. Clients use
the
resource_metadataURL from the challenge when there is one, and otherwise try the path-suffixed well-known URL, then the root one (MCP: Authorization server discovery). - Claude locates your authorization server only through this document (Claude: Authentication for connectors).
- ChatGPT fetches it from the well-known URL, or from the URL in your
401challenge, to know where to sign in (OpenAI: Authentication).
Other agents follow the MCP specification; MCP Tower has not verified them.
What MCP Tower checks
Section titled “What MCP Tower checks”MCP Tower builds a list of URLs and requests them in order, stopping at the first that works:
- the
resource_metadataURL from your401challenge, if there was one; - the path-suffixed well-known URL: for
https://mcp.example.com/mcpthat ishttps://mcp.example.com/.well-known/oauth-protected-resource/mcp; - the root well-known URL,
https://mcp.example.com/.well-known/oauth-protected-resource. For an endpoint without a path, this is the only well-known URL.
Each request is a GET with Accept: application/json. It follows up to three redirects, gives
up after 10 seconds, and reads at most 64 KB. A URL works when it answers 200 with a JSON body
of at most 64 KB. The result is pass for the first URL that works and fail when none does. On a
failure, every check that needs the document is skipped with “Needs the protected resource
metadata.”
The message lists each URL tried with its outcome in brackets. The outcome is HTTP <status>
whenever the server answered, or the error class when no answer came: dns_error,
connect_error, tls_error, timeout or address_blocked (the address is private or otherwise
not allowed). So HTTP 200 in the list means the server answered 200 but the body was not
valid JSON or was larger than 64 KB, and HTTP 301 or HTTP 302 means a fourth redirect was
needed.
Results
Section titled “Results”| Status | Message | Meaning | Fix |
|---|---|---|---|
| pass | Protected resource metadata found. | One of the URLs served a valid document. | |
| fail | No protected resource metadata was found. Tried: <url> (<outcome>), <url> (<outcome>). | No URL answered 200 with valid JSON of at most 64 KB. | Serve the document or Return JSON, not a page |
The next step for the failure is: “Serve the document at <path-suffixed well-known URL> with
resource and authorization_servers.”
How to fix
Section titled “How to fix”Serve the document
Section titled “Serve the document”- Serve a
GETroute at the path-suffixed URL, such as/.well-known/oauth-protected-resource/mcp. Serve the same document at the root/.well-known/oauth-protected-resourcetoo, for clients that try only that one. - Set
resourceto the exact URL agents connect to, and list your authorization server’s issuer inauthorization_servers. Addscopes_supportedif you have scopes. - Answer
200withContent-Type: application/json, and make sure the route is reachable without a token.
The document agents expect:
{ "resource": "https://mcp.example.com/mcp", "authorization_servers": ["https://auth.example.com"], "scopes_supported": ["mcp:tools"]}location ~ ^/\.well-known/oauth-protected-resource(/mcp)?$ { default_type application/json; return 200 '{"resource":"https://mcp.example.com/mcp","authorization_servers":["https://auth.example.com"],"scopes_supported":["mcp:tools"]}';}const metadata = { resource: "https://mcp.example.com/mcp", authorization_servers: ["https://auth.example.com"], scopes_supported: ["mcp:tools"],};
// Before any authentication middleware, so it needs no token.app.get( [ "/.well-known/oauth-protected-resource/mcp", "/.well-known/oauth-protected-resource", ], (req, res) => res.json(metadata),);METADATA = { "resource": "https://mcp.example.com/mcp", "authorization_servers": ["https://auth.example.com"], "scopes_supported": ["mcp:tools"],}
@app.get("/.well-known/oauth-protected-resource/mcp")@app.get("/.well-known/oauth-protected-resource")async def protected_resource_metadata(): return METADATAReturn JSON, not a page
Section titled “Return JSON, not a page”When the list shows (HTTP 200) for a URL, something answered but not with the document: often a
single-page app’s fallback route that returns HTML for every path, or a document over 64 KB.
- Open the URL and look at the body and the
Content-Type. - Make the route return only the metadata as JSON, ahead of any catch-all route.
- If the URL redirects more than three times, serve the document at the URL itself.
Behind MCP Tower
Section titled “Behind MCP Tower”Agents fetch the metadata from your public hostname, mcp.example.com. MCP Tower forwards every
/.well-known/ request to your origin, origin.example.com, with the same path and query, and
passes your status code through, including 404. It never serves or caches the document itself,
so your origin must serve it. If the resource field names the origin host, MCP Tower rewrites it
to the public hostname; see resource identifier.
Check it yourself
Section titled “Check it yourself”Request the path-suffixed URL as MCP Tower does:
curl -si https://mcp.example.com/.well-known/oauth-protected-resource/mcp \ -H 'Accept: application/json'The problem is fixed when the response is a 200 with a JSON body:
HTTP/1.1 200 OKContent-Type: application/json
{"resource":"https://mcp.example.com/mcp","authorization_servers":["https://auth.example.com"],"scopes_supported":["mcp:tools"]}A 404 here, and at https://mcp.example.com/.well-known/oauth-protected-resource, is the failure
MCP Tower reports. Then select Scan again on the server’s page.
References
Section titled “References”- MCP authorization
2026-07-28: Protected resource metadata discovery requirements - RFC 9728 §2: the metadata fields, and §3: the well-known URL and the response
- Claude: Authentication for connectors, “Serve discovery metadata”
- OpenAI: Authentication, “Host protected resource metadata on your MCP server”