{"slug": "mcp-authorization-governing-tool-calls-beyond-oauth", "title": "MCP authorization: governing tool calls beyond OAuth", "summary": "WorkOS is offering early access to Airlock, an authorization layer that evaluates an MCP agent's declared intent and policy before a tools/call request executes, addressing cases where a valid OAuth scope like \"mail:send\" still permits confidential data such as a $3,200 LLM-token spending report to be emailed. The guide recommends enforcing policy on the MCP server or gateway, freezing the validated call so the recipient and body that passed evaluation are the ones executed, and keeping provider credentials server-side because the MCP specification forbids token passthrough. Testing for data leakage is done by replacing the provider adapter with a recording stub.", "body_md": "# MCP authorization: governing tool calls beyond OAuth\n\nSecure MCP tool calls beyond OAuth scopes. Learn where to enforce tool permissions, check request content, and test for data leakage with Airlock.\n\nAn MCP server can validate an agent's access token correctly and still send confidential data to the wrong place. A permission such as \"send email\" does not, by itself, decide whether the message should contain a spending report. This guide shows how to enforce MCP tool permissions on the actual request before it executes.\n\n[WorkOS Airlock](https://workos.com/airlock) provides that authorization layer for governed agent calls, evaluating declared intent and policy before allowing an action, denying it, or requesting human approval. [Request early access](https://workos.com/airlock) to use it. The example below explains the design for a protected HTTP MCP server; the sample tool and pseudocode are illustrative.\n\n## What MCP OAuth scopes authorize\n\nSuppose your server defines a `send_email` tool and a `mail:send` OAuth scope. Neither name is built into MCP. The agent submits this [tools/call request](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#calling-tools):\n\n```\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"send_email\",\n    \"arguments\": {\n      \"to\":\n        \"manager@example.test\",\n      \"subject\": \"Planning\",\n      \"body\": \"Review is Friday.\"\n    }\n  }\n}\n```\n\nThe server validates the token and checks the required scope. The [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) requires the token to be intended for that server; invalid or expired tokens receive a 401 response.\n\nNow keep the token, recipient, and subject unchanged, but replace the body with \"We spent $3,200 on LLM tokens.\" The same scope still covers sending email. If your organization prohibits emailing financial data, the server needs to inspect the proposed content to enforce that rule.\n\nYou can define narrower scopes. But whatever granularity you choose, the application must implement the decision those scopes represent. Reading the scope string cannot establish that a newly generated email contains no confidential information.\n\n## Enforce MCP tool permissions before execution\n\nPut the policy check on the MCP server or gateway that controls the provider call. Apply it to every governed `tools/call` request, including calls submitted directly without a preceding `tools/list` request. This simplified pseudocode shows the initial request path in enforcement mode, before any approval has been granted:\n\n```\nactor = authorize_mcp(request)\ncall = validate_and_freeze(request)\ncontext = load_context(actor)\n\ndecision = evaluate_policy(\n    actor, context, call\n)\n\nif decision != \"allow\":\n    return stop_without_sending(\n        decision\n    )\n\nreturn send_via_provider(\n    actor, call\n)\n```\n\nFreezing the call means the recipient and body used for execution are the ones that passed evaluation. An edit creates a new request to check. A denial, pending approval, or failed evaluation stops this send; an approved action resumes through a separate path that validates the approval.\n\nThe context should include administrator-defined policy and the agent's declared intent. Treat that declaration as a claim about its task, not proof that the user authorized every detail. If policy requires a particular recipient or account, check trusted application records or explicit restrictions. The agent cannot grant itself an exception by describing one in its intent.\n\nKeep provider credentials on the server. MCP [forbids token passthrough](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#token-audience-binding-and-validation): the client's MCP access token must not double as the credential forwarded to Gmail.\n\n## Test for data leakage and failed authorization\n\nReplace the provider adapter with a recording stub. The permitted planning update should produce one call. The financial-data message should produce zero. A forced policy timeout should also produce zero, while an expired token should fail before policy evaluation. Assert the arguments received by the stub, so a correct verdict cannot hide a changed payload.\n\nRepeat the allowed and denied cases with a test mailbox. An allow means the send may be attempted; provider errors and delivery remain separate outcomes. For more cases, use the [agent policy-testing guide](https://workos.com/blog/test-ai-agent-authorization-policies).\n\nAirlock's [Agent Night demo](https://workos.com/blog/agent-night-recap-airlock-intent-based-access-control) shows a financial-data email being blocked on a working Gmail connection:\n\n[Request Airlock early access](https://workos.com/airlock) to add policy enforcement to your agents' tool calls.", "url": "https://wpnews.pro/news/mcp-authorization-governing-tool-calls-beyond-oauth", "canonical_source": "https://workos.com/blog/mcp-authorization-airlock", "published_at": "2026-09-15 15:04:00+00:00", "updated_at": "2026-09-23 02:24:55.976462+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-safety", "ai-policy", "developer-tools"], "entities": ["WorkOS", "Airlock", "Model Context Protocol", "Gmail"], "alternates": {"html": "https://wpnews.pro/news/mcp-authorization-governing-tool-calls-beyond-oauth", "markdown": "https://wpnews.pro/news/mcp-authorization-governing-tool-calls-beyond-oauth.md", "text": "https://wpnews.pro/news/mcp-authorization-governing-tool-calls-beyond-oauth.txt", "jsonld": "https://wpnews.pro/news/mcp-authorization-governing-tool-calls-beyond-oauth.jsonld"}}