Make resource match the URL agents connect to
This check verifies that the resource field of your
protected resource metadata is the URL agents connect to.
Check id resource_matches (resource matches). Results:
Why it matters
Section titled “Why it matters”The resource value is your server’s resource identifier. An
agent compares it with the URL it fetched the metadata for, and a document whose resource
names another URL is not used: RFC 9728 requires the two to be identical, so a mismatch stops the
sign-in before the user sees a login screen. The same value is then sent to the authorization
server as the resource parameter, so tokens are issued for it.
- Claude requires
resourceto equal your MCP server URL exactly as the user enters it, including any path (Claude: Authentication for connectors). - ChatGPT sends this exact value as the
resourceparameter during sign-in (OpenAI: Authentication), so a wrong value gives tokens for the wrong URL. - The MCP authorization specification uses the canonical URI of the MCP server as the resource, and prefers the form without a trailing slash (MCP: Canonical server URI).
Other agents follow the MCP specification; MCP Tower has not verified them.
What MCP Tower checks
Section titled “What MCP Tower checks”- MCP Tower reads
resourcefrom the document the protected resource metadata check found. Without that document the check isskippedwith “Needs the protected resource metadata.” - It compares
resourcewith your MCP endpoint URL and, once your server has a public hostname on MCP Tower, with the public URL as well. The comparison is an exact string comparison that ignores only a trailing/. Scheme, host, port, path and letter case must all be the same, sohttps://mcp.example.com:443/mcp,https://MCP.example.com/mcpandhttps://mcp.example.comdo not matchhttps://mcp.example.com/mcp. - A match with either URL is
pass. Anything else isfail. The message names the public URL when there is one, and the endpoint otherwise, because that is the URL agents use.
Query strings are left out of the message.
Results
Section titled “Results”| Status | Message | Meaning | Fix |
|---|---|---|---|
| pass | The metadata’s resource matches the endpoint. | Agents accept the document and ask for tokens for this URL. | |
| fail | resource is “<resource>”, but agents connect to “<public URL or endpoint>”. Agents stop the flow when these differ. | The value names another host, path or port, or is built differently. | Set resource to the endpoint URL |
| skipped | Needs the protected resource metadata. | The document was not found. | Protected resource metadata |
The next step for the failure is: “Set resource in the protected resource metadata to the exact URL agents connect to.”
How to fix
Section titled “How to fix”Set resource to the endpoint URL
Section titled “Set resource to the endpoint URL”- Take the URL users enter in their agent, such as
https://mcp.example.com/mcp. With MCP Tower in front, that is your public hostname plus your MCP path. - Set
resourceto that exact string. Keep the path, use lowercase for the scheme and host, leave out default ports, and drop any trailing slash. - If your server builds the value from the incoming request, build it from a configured URL
instead, so a proxy or a different
Hostheader cannot change it.
A matching document:
{ "resource": "https://mcp.example.com/mcp", "authorization_servers": ["https://auth.example.com"]}# A fixed value, not one built from $host.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"]}';}// From configuration, not from req.hostname.const RESOURCE = process.env.MCP_RESOURCE ?? "https://mcp.example.com/mcp";
app.get("/.well-known/oauth-protected-resource/mcp", (req, res) => res.json({ resource: RESOURCE, authorization_servers: ["https://auth.example.com"], }),);# From configuration, not from request.base_url.RESOURCE = os.environ.get("MCP_RESOURCE", "https://mcp.example.com/mcp")
@app.get("/.well-known/oauth-protected-resource/mcp")async def protected_resource_metadata(): return { "resource": RESOURCE, "authorization_servers": ["https://auth.example.com"], }Behind MCP Tower
Section titled “Behind MCP Tower”Agents connect to https://mcp.example.com/mcp, and MCP Tower forwards to your origin at
origin.example.com. When your origin’s document names the origin, MCP Tower rewrites the
resource field on the way out: if the value is an absolute URL whose scheme, host and port are
the origin’s, they become https://mcp.example.com and the path is kept. It rewrites only
application/json responses of up to 64 KB, and leaves a value that names any other host as it
is. A value with the wrong path is not fixed, so the path must be right at the origin.
That is why this check accepts both your endpoint URL and your public URL. A resource that names
the origin also gets a warning from public audience, because
tokens will be issued for the public hostname.
Check it yourself
Section titled “Check it yourself”Fetch the document and print its resource:
curl -s https://mcp.example.com/.well-known/oauth-protected-resource/mcp \ -H 'Accept: application/json' | jq -r .resourceThe problem is fixed when it prints the URL agents connect to, exactly:
https://mcp.example.com/mcpThen select Scan again on the server’s page.
References
Section titled “References”- RFC 9728 §3.3: the
resourcevalue must be identical to the resource the metadata was requested for - RFC 8707 §2: the
resourceparameter - MCP authorization
2026-07-28: Canonical server URI - Claude: Authentication for connectors, “Serve discovery metadata”
- OpenAI: Authentication, “Host protected resource metadata on your MCP server”