Permissions and access

Check that each MCP tool's OAuth scope matches its permission

You will map each MCP tool to the scope it requires and confirm the token carries that scope. This keeps tool calls from failing with insufficient-permission errors and keeps access no broader than intended.

Scope mismatch

  • Tool calls return -32005 Insufficient permissions
  • Token lacks the per-tool scope
  • Login succeeds but tool access fails

Scopes matched

  • Each tool maps to the scope the server checks
  • Token carries that scope via scope or a custom claim
  • Unused scopes removed

Why it matters

A scope mismatch fails at call time. On a self-hosted obsidian-mcp-server, every tool call returned -32005 / Insufficient permissions because the JWT lacked the per-tool scope the server checked (obsidian-mcp-server issue #47 (opens external site)). Manifests can drift from what the server actually does too: an auto-generated Octocode manifest included a filesystem permission the server does not need (octocode issue #127 (opens external site)).

Steps

  1. List every tool your MCP server exposes and note the operation each one performs: read, write, delete or execute.
  2. For each tool, write down the exact scope name the server checks. The obsidian-mcp-server case used tool:obsidian_list_notes:read, but that is one example, so confirm the names your own server expects rather than assuming a pattern.
  3. Decode a token from a real tool call and compare its claims with your list. Check the scope claim and any custom claim the server reads.
  4. If your identity provider cannot put per-tool scopes into scope, add a property mapping that emits a separate claim. The recommended route in obsidian-mcp-server v3.1.6 is a custom mcp_tool_scopes claim, which the server reads alongside scp and scope. It can be a string or an array:
json
{"mcp_tool_scopes": "tool:obsidian_list_notes:read tool:obsidian_get_note:read"}
  1. Remove any scope that no tool needs, and keep write scopes separate from read scopes so read-only clients never receive them.
  2. Retest each tool with a token from the client you actually use, since some clients, such as Claude.ai, do not let you choose requested scopes.
  3. If you must, use MCP_AUTH_DISABLE_SCOPE_CHECKS=true temporarily. It is a bypass, not the fix. It skips per-tool scope checks but still validates signature, audience, issuer and expiry, and it logs a startup warning. Remove it once the claim mapping works.

Common mistakes

  • Assuming a scope is present because login succeeded. Login proves identity; check the claims in the token that the tool call uses.
  • Using the scope-check bypass as a permanent fix instead of adding the missing claim.
  • Copying a generated permission manifest without checking each entry against what the server does. The Octocode maintainer found one entry unnecessary.
  • Granting environment-variable read access broadly. It can expose API keys to the sandboxed process, so limit it.

Checklist

0 of 8 done

Sources

  1. How to fix 'exec: ./azmcp: permission denied' when running the azure-mcp Docker image?
  2. obsidian-mcp-server: -32005 Insufficient permissions with Authentik/Keycloak OIDC?
  3. Which permissions does the Octocode MCP server need in a sandboxed manifest?
Was this guide useful?