Skip to content

50% off your first year. Join before 2027. Claim →

Accept tokens issued for your public hostname

This check verifies that your resource identifier names the public hostname agents use, not the host your MCP server runs on.

Check id public_audience (public audience). Results:

pass warn skipped

Behind MCP Tower, agents connect to your public hostname, such as https://mcp.example.com/mcp, and MCP Tower forwards each request to your origin, such as https://origin.example.com/mcp. Agents never see the origin.

Agents ask the authorization server for a token for the URL they connect to: they send it as the RFC 8707 resource parameter in the sign-in and token requests. The MCP specification requires clients to send it, and requires servers to accept only tokens issued for themselves as the audience. So once traffic goes through MCP Tower, tokens carry the audience https://mcp.example.com/mcp.

If your authorization server only issues tokens for the origin URL, or your MCP server checks the audience against the origin URL, sign-in fails at the token step or every signed-in request gets a 401. Agents also check that the protected resource metadata’s resource is the URL they connect to, and stop the flow when it differs (Claude: Authentication for connectors).

This is a warning, not a failure: the scan itself talks to your origin and works either way. It tells you what must change before agents use the public hostname.

The check reads the resource field of your protected resource metadata, right after resource identifier does, although it is listed last.

  1. If resource names the same host as your MCP endpoint, the origin, and is not your public hostname URL, the result is warn.
  2. Otherwise, for example when resource is https://mcp.example.com/mcp, the result is pass.
  3. When no protected resource metadata was found, the check is skipped with the other discovery checks; see protected resource metadata.

The public hostname URL is the public MCP URL you set up for the server: https://, its hostname, and its path, which is the path of your origin URL unless you publish a different path.

StatusMessageMeaningFix
passThe resource does not name the origin host.Tokens are requested for a URL other than the origin, normally your public URL.
warnThe resource names the origin host. Once traffic goes through your public hostname, agents ask for tokens for that hostname, and your servers must accept them.Your servers are set up for the origin URL only.Accept the public audience
skippedNeeds the protected resource metadata.No metadata document was found, so there is no resource to read.Protected resource metadata

The next step for the warning is: “Make your authorization server and MCP server accept tokens whose audience is the public hostname URL (see the middleware setup docs).”

  1. Register the public hostname URL, https://mcp.example.com/mcp, as a resource or audience your authorization server issues tokens for. Keep the origin URL too while you move traffic over.
  2. Make your MCP server accept tokens whose audience is the public hostname URL. Check the aud claim, or the resource your token introspection returns, against it.
  3. Set resource in your protected resource metadata to the public hostname URL, or leave the origin URL and let MCP Tower rewrite it (see below). In that case the warning stays, as a reminder of steps 1 and 2.
  4. Protect the origin so agents cannot skip the public hostname; see the origin secret.

The protected resource metadata that passes:

/.well-known/oauth-protected-resource/mcp
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["mcp:tools"]
}

The claims of an access token your MCP server should accept:

access token claims
{
"iss": "https://auth.example.com",
"aud": "https://mcp.example.com/mcp",
"scope": "mcp:tools",
"exp": 1793000000
}

MCP Tower forwards every /.well-known/ request to your origin with the same path and query, and changes only the values that name the origin where the public hostname belongs:

  • the resource_metadata parameter of WWW-Authenticate on any response;
  • the resource field of /.well-known/oauth-protected-resource and its path-suffixed forms, for application/json documents up to 64 KB.

A value is rewritten only when it is an absolute URL whose scheme, host and port equal the origin’s: they become https://mcp.example.com, and the path, query and fragment stay. Values that already name the public hostname, or another host, are left alone.

When you publish a different path, the path is mapped too: a resource equal to your origin URL becomes your public MCP URL exactly, and a resource_metadata URL for the origin’s metadata becomes the metadata URL of the public path. With an origin at https://api.example.com/v2 and a public URL of https://mcp.example.com/, resource becomes https://mcp.example.com/ and resource_metadata becomes https://mcp.example.com/.well-known/oauth-protected-resource.

Nothing else changes. authorization_servers and all authorization server metadata, including issuer, authorization_endpoint and token_endpoint, pass through as they are, because the issuer must match the iss claim in your tokens. So MCP Tower fixes what agents read, but it cannot change which audience your authorization server issues or your MCP server accepts. That part is yours. The getting started guide covers the setup.

Fetch the metadata from your origin and from the public hostname:

Terminal window
curl -s https://origin.example.com/.well-known/oauth-protected-resource/mcp
curl -s https://mcp.example.com/.well-known/oauth-protected-resource/mcp

The check passes when the origin’s document names the public hostname:

{ "resource": "https://mcp.example.com/mcp" }

Then confirm that your MCP server accepts a token issued for that audience:

Terminal window
curl -si https://origin.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Authorization: Bearer <access-token>' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

It answers 200, not 401. Then select Scan again on the server’s page.