Protect a FastMCP App
Use the FastMCP adapter when a FastMCP app should reject invalid, expired, insufficient, or revoked Caracal mandates. Python uses caracalai-fastmcp; TypeScript uses @caracalai/fastmcp.
Prerequisites
Section titled “Prerequisites”- A mandate-aware MCP resource, stable audience, tool scopes, issuer, and zone ID.
- A shared production revocation store.
- A FastMCP hook that can reject before tool code runs; verification after dispatch is too late.
Install
Section titled “Install”pip install "caracalai-fastmcp[fastmcp]" caracalai-revocationnpm install @caracalai/fastmcp @caracalai/verify @caracalai/revocationUse caracalai-revocation-redis (Python) or the Redis store in @caracalai/revocation (TypeScript) for shared production revocation. The in-memory store is only for local development and tests.
Create an authenticator (Python)
Section titled “Create an authenticator (Python)”from caracalai_fastmcp import CaracalAuthfrom caracalai_revocation import InMemoryRevocationStore
auth = CaracalAuth( issuer="https://sts.pipernet.example", audience="resource://pipernet", expected_zone_id="0195f2a9-1b22-7c3d-9e4f-5a6b7c8d9e0f", required_scopes=["mcp:tool:call"], required_targets=["resource://pipernet"], revocations=InMemoryRevocationStore(), require_session=True,)
async def startup(): await auth.warmup()Verify before running a tool (Python)
Section titled “Verify before running a tool (Python)”from caracalai_fastmcp import CaracalAuthError
async def handle_tool_call(token: str, payload: dict): try: claims = await auth(token) except CaracalAuthError as err: return {"error": err.code, "error_description": err.description, "error_hint": err.hint}
return { "subject": claims.sub, "result": await run_tool(payload), }Wire this check into the FastMCP auth or request hook used by your server. The important boundary is that the mandate is verified before the tool handler performs work.
Verify before running a tool (TypeScript)
Section titled “Verify before running a tool (TypeScript)”import { extractBearer, verifyFastMcpToken, FastMcpAuthError } from '@caracalai/fastmcp'import { createMandateVerifier } from '@caracalai/verify'import { InMemoryRevocationStore } from '@caracalai/revocation'
const verifier = createMandateVerifier({ issuer: 'https://sts.pipernet.example', audience: 'resource://pipernet', zoneId: '0195f2a9-1b22-7c3d-9e4f-5a6b7c8d9e0f', revocations: new InMemoryRevocationStore(),})
async function handleToolCall(authorization: string, payload: unknown) { const token = extractBearer(authorization) if (!token) return { error: 'missing_token' }
try { const context = await verifyFastMcpToken(token, verifier, { requiredScopes: ['mcp:tool:call'], requiredTargets: ['resource://pipernet'], requireSession: true, }) return { subject: context.sub, result: await runTool(payload) } } catch (err) { if (err instanceof FastMcpAuthError) return { error: err.code } throw err }}The boundary is the same in both languages: verify the mandate before the tool handler performs work.
Validate
Section titled “Validate”| Test | Expected result |
|---|---|
| Missing bearer token | missing_token or framework 401. |
| Wrong audience | invalid_token. |
| Missing scope | insufficient_scope. |
| Revoked session | session_revoked. |
Mandate without a Session with require_session=True / requireSession: true | session_required. |
Expected result: the tool handler runs only for a mandate matching issuer, audience, zone, target, scope, Session requirements, and current revocation state.
For exact call signatures, use FastMCP Adapter reference.
Next Step
Section titled “Next Step”Use Protect an MCP Server only when the framework hook cannot use this dedicated adapter.

