# MCP Auth Bypasses Show Why Tool Calls Need Runtime Authorization

The recent MCP incidents are a useful wake-up call: passing authentication at the edge does **not** mean every downstream tool invocation is safe. The gap is subtle, and attackers are now very good at finding it.

What makes this moment important is not just any single bug, but the repeated pattern. We keep seeing systems that verify identity once, then implicitly trust later operations that should have had their own runtime checks. That is exactly where modern **MCP authorization** needs to mature.

## What the two headline incidents actually showed

In plain terms, the **fast-mcp-telegram CVE** was a token handling bug. The project used Bearer tokens to select a session file path, but weak **Bearer token validation** let crafted input abuse **path traversal** and reach the protected default Telegram session anyway. So a caller could bypass intended protections and act as that session user. See the incident write-up and advisory in [Cyber Security News](https://cybersecuritynews.com/fast-mcp-telegram-vulnerability/) and the [GitHub advisory GHSA-rxw2-pc8j-vxwm](https://github.com/advisories/GHSA-rxw2-pc8j-vxwm).

The **LiteLLM MCP CVE** story is different but equally instructive. LiteLLM had MCP test routes that, under certain conditions, could be exploited to spawn subprocesses; initially this was considered “authenticated only,” but researchers showed it could be chained with a Starlette host-header bypass to become unauthenticated RCE in affected deployments. The chain is documented by [Horizon3.ai](https://horizon3.ai/attack-research/vulnerabilities/cve-2026-42271-chained-with-cve-2026-48710/) and the LiteLLM advisory [GHSA-v4p8-mg3p-g94g](https://github.com/BerriAI/litellm/security/advisories/GHSA-v4p8-mg3p-g94g).

Different root causes, same lesson: an **MCP auth bypass** at one layer can potentially lead to tool misuse if runtime permissions are not re-evaluated per action.

## MCP authentication vs authorization: the boundary teams still blur

You need this phrase operationalized, not just understood: **MCP authentication vs authorization**.

Authentication answers: “Who is this caller?”

Authorization answers: “Can this caller perform this exact action on this exact resource under this exact context right now?”

That means transport/session auth can succeed while **tool-call authorization** should still deny a dangerous `call_tool`. If those checks are fused, you get brittle **call_tool security**.

| Control question | Wrong assumption | Correct control |
| --- | --- | --- |
| “Caller has token, so we’re good” | Token = blanket trust | Token proves identity, not permission |
| `tools/list` allowed | `call_tool` should also be allowed | Discovery can be broader than execution |
| Test/admin route authenticated | It’s safe for all authenticated users | Test/admin routes require stricter role + network controls |
| Past approval exists | Reuse forever | Recompute decision at runtime with context |

## Separate trust levels: tools/list vs call_tool vs MCP admin/test endpoints

A lot of teams run all MCP routes at one trust level. That’s the design bug.

- `tools/list`: low-risk discovery, often metadata-only.
- `call_tool`: medium-to-high risk execution surface, needs per-tool and per-resource checks.
- **MCP test endpoints** and admin operations: highest risk, often equivalent to control-plane privilege.

If your policies don’t model this separation explicitly, one credential class can drift into unintended powers. In practice, high-risk classes should require tighter scopes, additional claims, network location constraints, and stronger approval semantics than ordinary inference-time calls.

## Define a fail-closed authorization model for MCP tool execution

A usable **fail-closed authorization** model can be simple and strict:

- **Read tools**: allow only if subject, tenant, data-class, and purpose are all valid; deny on missing context.
- **Write tools**: require read prerequisites plus intent constraints and destination validation.
- **Destructive tools** (delete, revoke, transfer): require elevated policy path, stronger justification, optional step-up/consent, short TTL.
- **Admin/test tools**: default deny except tightly scoped admin identity, trusted network segment, and explicit policy grant.

The key is deterministic denial on uncertainty. Timeouts, PDP errors, missing attributes, unknown tool IDs, malformed inputs, and stale session context should all resolve to deny. That is real **fail-closed authorization**, ensuring decisions are made with certainty rather than relying on best-effort assumptions.

## How to operationalize this with an MCP Gateway

This is where an **MCP Gateway** becomes practical: it centralizes policy enforcement right before tool execution, so every `call_tool` gets a runtime decision and not just inherited transport trust. A good gateway should treat authn as one input signal and still perform independent runtime authorization checks with policy + context + resource sensitivity.

With Permit **MCP Gateway**, the design target is explicit: enforce runtime decisions across tool classes, keep high-risk admin/test routes on separate policy rails, and produce decision traces that security teams can review quickly. The important architectural point is not branding; it’s enforcing policy at the execution boundary where attacks actually materialize.

## Checklist: shipping robust runtime MCP authorization

- Classify all MCP routes into discovery, execution, and admin/test risk tiers.
- Enforce separate policies for `tools/list`, `call_tool`, and **MCP test endpoints**.
- Implement per-call runtime **tool-call authorization** with tenant, actor, tool, and resource context.
- Make unknown/missing context a deny result, not a warning.
- Lock test/admin endpoints behind strict role, network, and environment constraints.
- Add explicit controls for command-spawning or stdio-backed integrations.
- Validate path and token inputs defensively to prevent **path traversal**-style breaks.
- Rotate/expire credentials and avoid long-lived broad keys.
- Capture both allow and deny outcomes in structured **agent audit logs**.
- Continuously test for bypass paths and chained exploits.

## Audit schema: what to log for both allow and deny decisions

If you want useful **agent audit logs**, log decisions as first-class security events, not debug strings. For every request (allow or deny), capture: decision ID, timestamp, subject identity, delegated-on-behalf-of identity, tenant, endpoint type (`tools/list`, `call_tool`, admin/test), tool name, action class (read/write/destructive/admin), target resource, policy ID/version, input attributes used, decision result, deny reason code (if denied), auth method, source IP/network zone, request correlation ID, and latency.

Also log policy-evaluation integrity signals: missing attributes, stale cache indicators, fallback behavior, and PDP error states. Without those, incident response cannot distinguish “correct deny” from “system degraded.” This schema is what turns authorization from theory into enforceable operations.

## Frequently asked questions

### Why isn’t strong authentication enough for MCP security?

Strong authentication tells you who is at the door, but not what they should be allowed to do once inside. MCP surfaces include many actions with very different risk levels, and those decisions must be made at runtime. If you skip runtime checks, one bypass can expose many tools. That is the core reason **MCP authorization** must be explicit per call.

### What is the practical difference between transport auth failure and runtime authorization failure?

A transport auth failure means the caller never proved identity to the API boundary. A runtime authorization failure means identity may be valid, but the requested operation is not allowed under current policy and context. Both are important, but they solve different problems. Treating them as one control is how blind spots appear.

### Why are MCP test endpoints so dangerous compared to normal tool calls?

Test endpoints often accept richer configuration and can touch execution internals, so they behave closer to admin functions than user functions. Even when intended for diagnostics, they can become command or connectivity pivots if broadly reachable. That is why **MCP test endpoints** should have stricter controls than standard `call_tool` paths. They are high-impact by design.

### How does fail-closed authorization affect reliability?

Done right, it improves security without breaking normal traffic because expected context is modeled and cached appropriately. The system denies only when critical signals are missing, invalid, or risky, which is exactly when permissive behavior is most dangerous. Operationally, teams pair this with clear deny codes and fast debugging paths. Reliability comes from predictable policy behavior, not permissive defaults.

### What should a deny log include so responders can act quickly?

A useful deny record includes who requested what, against which resource, under which policy version, and exactly why denial occurred. It should also include request correlation IDs, network source, and evaluation context health flags. That gives responders both forensic value and remediation direction in one record. Thin logs force teams to guess under pressure.

### Does an MCP Gateway replace app-level and data-level authorization?

No, it should complement them. The gateway enforces at tool execution boundaries, while app/data layers enforce where business logic and data sensitivity are ultimately resolved. Defense-in-depth means policy coherence across these layers, not choosing only one. The gateway closes the runtime action gap that perimeter auth leaves open.
