Table of contents
An MCP client that gets a 401 from https://api.example.com/mcp has to find your OAuth metadata somewhere, and RFC 9728 gives it two places to look: /.well-known/oauth-protected-resource/mcp (the path-inserted form) and /.well-known/oauth-protected-resource (the root form). If your server answers only one of them, or answers with the wrong resource value, the client stops before it ever sees your authorization server. In a seven-case lab run on 3 November 2026, two of the seven setups failed discovery, and neither printed an error on the server side.
TL;DR
| Your situation | Do this | Time |
|---|---|---|
MCP endpoint is at /mcp, no metadata served | Serve the file at /.well-known/oauth-protected-resource/mcp and set resource to the full endpoint URL | 10 min |
| You already serve the root file | Change its resource to the endpoint URL, or add the path-inserted file as well | 5 min |
| Clients still miss it | Add resource_metadata="..." to the WWW-Authenticate header on every 401 | 5 min |
| Not sure what you serve | Run npx aiscan-cli yoursite.com (check P4), then the three curl lines below | 2 min |
Short answer: serve the file at the path-inserted URL, put the endpoint's full URL in resource, and point to it from the 401 header. Do all three and client order stops mattering.
Why one file is not always enough
RFC 9728 section 3.1 says that when the resource identifier has a path, the client inserts /.well-known/oauth-protected-resource between the host and the path. For https://api.example.com/mcp that gives /.well-known/oauth-protected-resource/mcp. The root form is the special case for a resource with no path.
The MCP authorization spec tells clients to use the resource_metadata URL from the WWW-Authenticate header when it is present. Without the header they must try the path-inserted URL first and the root URL second. So a server that has only the root file works for clients that fall back, and a server that has only the path-inserted file works for clients that ask first. A client with a different order, or one that skips the fallback, sees a 404.
The second trap is validation. RFC 9728 section 3.3 says the resource value in the file must be identical to the resource identifier the URL was built from, and a mismatch means the client MUST NOT use the data. Copying the root example, where resource is the bare origin, onto an /mcp endpoint fails that check.
The lab
I ran a small Python server with seven configurations and a client that follows the MCP order (header first, then path-inserted, then root) and enforces section 3.3. It is a simulator of the spec, not of any particular product, so a real client may differ. Each server protected /mcp and returned 401.
| Case | What the server serves | Header on 401 | Result |
|---|---|---|---|
| A | Root file, resource = origin | none | Failed: 404 on path form, then root file rejected (resource ≠ endpoint) |
| B | Path-inserted file only | none | OK on the first request |
| C | Root file, resource = endpoint | points to root | OK via header |
| D | Both files | none | OK on the first request |
| E | Path-inserted file, resource = origin | points to it | Failed: file found, then rejected |
| F | Root file, catch-all page returns 200 HTML for unknown paths | none | OK, but only because the client rejects non-JSON |
| G | Path-inserted file only | points to it | OK via header |
Case F is the quiet one. A single-page app that answers every unknown path with 200 text/html makes the path-inserted probe look like it succeeded. My client checks the content type and moves on. A client that does not will try to parse HTML as JSON, which I did not test.
Fix it
1. Serve the path-inserted file
Return JSON with Content-Type: application/json at the path that matches your endpoint. For an endpoint at /mcp:
{
"resource": "https://api.example.com/mcp",
"authorization_servers": ["https://auth.example.com"]
}
Serve it at /.well-known/oauth-protected-resource/mcp. If your endpoint is /v1/mcp, the file goes at /.well-known/oauth-protected-resource/v1/mcp. How you register that route depends on your framework, and a dot-leading folder is often ignored by static hosts, which the .well-known 404 guide covers.
2. Make resource match exactly
Scheme, host, port and path must equal the endpoint URL the client calls, with no trailing-slash difference. A root file may stay, but only if its resource is the same endpoint URL.
3. Point to the file from the 401
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource/mcp"
RFC 9728 section 5.1 defines this parameter. With it, the client does not guess a location at all. The OAuth discovery how-to explains the rest of the chain (authorization server metadata, scopes).
Verify it
AIScan is the quickest route: run npx aiscan-cli yoursite.com and read check P4 (capabilities). Per our OAuth discovery guide, P4 grades the root file only, so a server with just the path-inserted file can pass your MCP clients and still show a P4 gap. Serve both to satisfy both.
If you would rather check by hand, run these three:
curl -sS -o /dev/null -w "%{http_code} %{content_type}\n" https://api.example.com/.well-known/oauth-protected-resource/mcp
curl -sS -o /dev/null -w "%{http_code} %{content_type}\n" https://api.example.com/.well-known/oauth-protected-resource
curl -sSI https://api.example.com/mcp | grep -i www-authenticate
Expected: 200 application/json on the path-inserted URL, 200 application/json or 404 on the root, and a WWW-Authenticate line containing resource_metadata=. Then confirm the file's resource equals the endpoint:
curl -sS https://api.example.com/.well-known/oauth-protected-resource/mcp | jq -e '.resource == "https://api.example.com/mcp"'
true means it matches. A 200 text/html on either well-known URL means your app's catch-all route is answering, so fix the route.
Exclusions and upkeep
- If your MCP server sits at the site root (
https://example.com/), there is no path to insert, and the root file is the right one. - Servers that need no authentication need none of this.
- Recheck after any proxy or router change. Path-based routing rules are the usual way the
.well-knownpath stops reaching your app. - Log the requests that hit these paths, as in logging MCP server requests, to see which client asks where.
For background on how often servers are called at all, see what 60 days of a public MCP server showed, and for the discovery card that sits beside this file, publish an MCP server card.
Next step
Run the scan for check P4, fix what it reports, then browse the rest of the AIScan fix guides and the capabilities check page.
Frequently asked questions
What is the difference between /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/mcp?
The first is the root form, for a resource with no path. The second is the path-inserted form RFC 9728 section 3.1 defines for a resource such as https://api.example.com/mcp. The MCP spec tells clients to try the path-inserted URL first and the root URL second when the 401 carries no resource_metadata.
Do I need both files?
No. Serve the path-inserted file, point to it from the WWW-Authenticate header, and every client that follows the spec finds it. Keep a root file only if its resource value equals the endpoint URL, and note that AIScan check P4 reads the root file.
My client says it cannot find protected resource metadata. What do I check first?
Request /.well-known/oauth-protected-resource/mcp with curl. You want 200 and application/json. A 404 means the route is missing, and 200 text/html means a catch-all page is answering instead of your file.
The metadata file returns 200 but the client rejects it. Why?
Most likely the resource value does not equal the endpoint URL. RFC 9728 section 3.3 says a client must not use metadata when the two differ. Compare scheme, host, port, path and any trailing slash.
Why does a copy of the RFC example fail on my /mcp endpoint?
The example for a path-less resource sets resource to the bare origin. On an endpoint with a path, the identifier includes the path, so the copied value mismatches and the file is discarded.
Does the WWW-Authenticate header replace the well-known file?
No. The header only tells the client where the file is. The file still has to exist at that URL and carry the right resource value.
My MCP server is at the site root. Does path insertion apply?
No. With no path in the resource identifier there is nothing to insert, so /.well-known/oauth-protected-resource is the correct and only location.
The path-inserted URL returns 404 after I moved behind a proxy. What happened?
The proxy or router probably forwards only the MCP route to your app. Add a rule that sends /.well-known/oauth-protected-resource/* to the service that builds the file, then recheck with the three curl commands.

About the author
M Asif Rahman
Entrepreneur, curious about AI
I have been building companies for a long time, and lately most of my curiosity goes to AI. AIScan is a small personal project I built on a flight from Dubai to Narita. I use it to understand how ready the web is for AI agents, and I share what the scans teach me.
Related guides
MCP Server Cards on 808 Sites: 32 Pass, and 52 of 65 Partials Fail E1
Of 808 hosts in our scan corpus, 32 pass check P2, the MCP server card check. Another 65 answer HTTP 200 at the card path and earn only partial credit, and 52 of those 65 also fail our soft404 check.…
OAuth Discovery Metadata on 806 Sites: 133 Pass, and 47 of 50 Soft 200s Fail E1
Of 806 hosts in our scan corpus, 133 pass check P4, the OAuth discovery check. That is 16.5%. The more useful number is 50. fifty hosts answered HTTP 200 at an OAuth metadata path and still did not…
We Run a Public MCP Server: 67 Days of Who Calls It, and How
We run a public MCP server, and for 67 days we have been counting who uses it. Between 26 August and 1 November 2026, AI assistants asked it for scans 375 times, which is 10.1% of the 3,723 scans…
