Configuration & Environment
An MCP HTTP transport exposes the `tools/list` endpoint without an authentication check, letting anyone enumerate the server's tool surface. Sibling rules covering the same auth-missing-on-transport family: MCP-209 (manifest declaration), MCP-268 (local HTTP).
Tool enumeration is the recon step of an MCP attack. Knowing what tools exist tells an attacker which prompt-injection payloads to send, which destructive operations to target, and which credentials might be reachable. Many MCP servers gate tool *invocation* but leave *listing* open under the assumption that knowing tool names is harmless — but it's exactly the information an attacker needs to plan.
MCP's HTTP transport is becoming common for hosted servers. Unlike stdio (which has implicit transport-level auth via the parent process), HTTP needs explicit authentication on every request. `tools/list` is the most-called method by clients — leaving it unauthenticated is the most common mistake.
from fastapi import FastAPI |
from mcp.server.fastmcp import FastMCP |
mcp = FastMCP("my-mcp") |
app = FastAPI() |
@app.post("/mcp") |
async def handle_mcp(request: dict): |
# No auth — anyone can call tools/list and enumerate the surface. |
return await mcp.handle(request) |
from fastapi import FastAPI, Header, HTTPException |
from mcp.server.fastmcp import FastMCP |
mcp = FastMCP("my-mcp") |
app = FastAPI() |
@app.post("/mcp") |
async def handle_mcp(request: dict, authorization: str = Header(None)): |
if not authorization or not authorization.startswith("Bearer "): |
raise HTTPException(status_code=401) |
return await mcp.handle(request) |
MCPSafe flags HTTP route handlers that parse `tools/list` requests without an `Authorization` header check, JWT decode, API key validation, or equivalent gating. Routes wrapped in middleware that checks bearer tokens are exempted.
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now