Skip to content

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

Grant the scopes each tool declares

This check verifies that the token from a sign-in holds every scope that your protected tools declare they need.

Check id tool_scopes (tool scopes). Results:

pass warn skipped

A tool can declare the scopes it needs, for example "securitySchemes": [{"type": "oauth2", "scopes": ["orders:read"]}]. If the token lacks one of them, the server refuses the call, and the user has to sign in again before the tool works.

Every missing scope is one more sign-in for your users in the middle of a task, or a tool that never works when the authorization server will not grant the scope.

  1. Which scopes MCP Tower requests. The first sign-in asks for the scope in your 401 challenge plus every oauth2 scope declared by the tools it saw without a token. With neither, it falls back to scopes_supported in your protected resource metadata. This starts from the same sources as Claude and adds the declared scopes, so one sign-in covers every tool an agent can see.
  2. Which scopes were granted. MCP Tower reads the scope field of the token response. When the authorization server leaves it out, the requested scopes count as granted, as RFC 6749 §5.1 allows.
  3. What each tool needs. For each tool whose sign-in is required or optional, MCP Tower collects the oauth2 scopes from securitySchemes (top level, or in _meta) and from authorization.scopes. Tools declaring no scopes need nothing beyond the token.
  4. A signed-in scan in which every required scope was granted is pass. Any missing scope is warn, and the tool shows “Needs scope” in the tool list. Without a successful signed-in scan the check is skipped.

Tools that appear only after sign-in are a common cause: their scopes were unknown when the first sign-in started. See hidden tools.

StatusMessageMeaningFix
passEvery protected tool’s declared scopes were granted.Every protected tool can be called with the token.
warnSome tools need scopes the token was not granted: <tool>: <scope>; ….Those tools will be refused until the user signs in with the missing scopes.Sign in with the scopes
skippedNeeds a signed-in scan.No token, or the signed-in pass failed; see token accepted.

The next step for the warning is: “Sign in again with scopes <scopes>. If they are still missing, the provider did not grant them: check that the client is allowed to request them.” <scopes> lists every missing scope, separated by commas.

  1. On the server’s page, select Sign in again with these scopes. MCP Tower starts a new sign-in with the scopes of the current token plus the missing ones, and the new token replaces the old one.
  2. Scan again. If the warning is gone, the authorization server grants the scopes, and agents get them too once they ask for them.
  3. To spare your users that second sign-in, make the scopes known before sign-in: list the tools anonymously with their securitySchemes, or put the scopes in your 401 challenge.

A tool declaration that tells agents what to request:

tools/list result (one tool)
{
"name": "get_order",
"description": "Look up one of your orders.",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "string" } }
},
"securitySchemes": [{ "type": "oauth2", "scopes": ["orders:read"] }]
}

The authorization server grants fewer scopes

Section titled “The authorization server grants fewer scopes”

If the warning stays after Sign in again with these scopes, the scopes were requested and the authorization server left them out of the token.

  1. Check that every declared scope exists on the authorization server, spelled exactly as in securitySchemes.
  2. Allow the client to request them. Clients that register themselves with CIMD or DCR often get a default scope set, so allow the scopes for those clients, not only for clients you created.
  3. Check the consent screen: a user who unticks a scope gets a token without it.
  4. Add every scope your tools declare to scopes_supported in the authorization server metadata.

List the tools and their declared scopes without a token:

Terminal window
curl -s https://mcp.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-25' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| jq '.result.tools[] | {name, scopes: [(.securitySchemes // ._meta.securitySchemes // [])[] | select(.type == "oauth2") | .scopes[]]}'

Then compare them with the scope field of the token response from a sign-in that requested them:

Token response
{
"access_token": "<access-token>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<refresh-token>",
"scope": "mcp:tools orders:read inventory:write"
}

The problem is fixed when every scope from the first command appears in scope. Then select Scan again on the server’s page.