@@ -37,6 +37,7 @@ import asyncio
3737from fastmcp import FastMCP
3838from authplane_fastmcp import authplane_auth
3939
40+
4041async def main () -> None :
4142 result = await authplane_auth(
4243 issuer = " https://auth.company.com" ,
@@ -55,6 +56,7 @@ async def main() -> None:
5556 finally :
5657 await result.aclose()
5758
59+
5860asyncio.run(main())
5961```
6062
@@ -99,11 +101,13 @@ Use FastMCP's built-in `require_scopes` decorator to enforce per-tool scope requ
99101``` python
100102from fastmcp.server.auth import require_scopes
101103
104+
102105@mcp.tool (auth = require_scopes(" tools/query" ))
103106def query (sql : str ) -> str :
104107 """ Requires the tools/query scope."""
105108 return f " Ran: { sql} " # replace with your real handler
106109
110+
107111@mcp.tool (auth = require_scopes(" tools/admin" , " tools/delete" ))
108112def delete_all () -> str :
109113 """ Requires BOTH tools/admin AND tools/delete scopes."""
@@ -120,21 +124,22 @@ FastMCP enforces scopes **before** the handler runs by **filtering tools the cal
120124from fastmcp.dependencies import CurrentAccessToken
121125from fastmcp.server.auth import AccessToken
122126
127+
123128@mcp.tool ()
124129async def my_tool (data : str , token : AccessToken = CurrentAccessToken()) -> str :
125130 # Standard JWT claims
126- sub = token.claims.get(" sub" ) # Subject (user ID)
127- jti = token.claims.get(" jti" ) # JWT ID
128- iss = token.claims.get(" iss" ) # Issuer
129- aud = token.claims.get(" aud" ) # Audience
130- exp = token.claims.get(" exp" ) # Expiration (Unix timestamp)
131- nbf = token.claims.get(" nbf" ) # Not before
132- iat = token.claims.get(" iat" ) # Issued at
131+ sub = token.claims.get(" sub" ) # Subject (user ID)
132+ jti = token.claims.get(" jti" ) # JWT ID
133+ iss = token.claims.get(" iss" ) # Issuer
134+ aud = token.claims.get(" aud" ) # Audience
135+ exp = token.claims.get(" exp" ) # Expiration (Unix timestamp)
136+ nbf = token.claims.get(" nbf" ) # Not before
137+ iat = token.claims.get(" iat" ) # Issued at
133138
134139 # OAuth claims
135- client_id = token.client_id # Client ID
136- scopes = token.scopes # List of granted scopes
137- expires_at = token.expires_at # Expiration (Unix timestamp)
140+ client_id = token.client_id # Client ID
141+ scopes = token.scopes # List of granted scopes
142+ expires_at = token.expires_at # Expiration (Unix timestamp)
138143
139144 # Custom claims
140145 tenant = token.claims.get(" tenant_id" )
@@ -150,6 +155,7 @@ The `claims` dict contains the **full JWT payload** including all standard and c
150155``` python
151156from fastmcp.server.dependencies import get_access_token
152157
158+
153159@mcp.tool ()
154160async def my_tool (data : str ) -> str :
155161 token = get_access_token() # Returns None if unauthenticated
@@ -247,10 +253,12 @@ Implement your own revocation logic with an async callable:
247253``` python
248254from authplane import VerifiedClaims
249255
256+
250257async def check_blocklist (claims : VerifiedClaims, raw_token : str ) -> bool :
251258 """ Return True to reject the token (it is revoked)."""
252259 return await redis_client.sismember(" revoked_tokens" , claims.jti)
253260
261+
254262await authplane_auth(
255263 issuer = " https://auth.company.com" ,
256264 base_url = " https://mcp.company.com" ,
@@ -280,8 +288,8 @@ result = await authplane_auth(
280288downstream = await result.client.exchange(
281289 TokenExchangeOptions(
282290 subject_token = inbound_token,
283- scope = " tools/add" , # narrow to the minimum
284- resources = (" https://downstream.example" ,), # RFC 8707 audience binding
291+ scope = " tools/add" , # narrow to the minimum
292+ resources = (" https://downstream.example" ,), # RFC 8707 audience binding
285293 )
286294)
287295
@@ -314,6 +322,7 @@ from authplane import ConsentRequiredError
314322from authplane.oauth import TokenExchangeOptions
315323from mcp.shared.exceptions import UrlElicitationRequiredError
316324
325+
317326@mcp.tool (auth = require_scopes(" tools/call_downstream" ))
318327async def call_downstream (payload : str ) -> str :
319328 try :
@@ -406,6 +415,7 @@ When `fetch_settings` is provided, `dev_mode` is ignored for both metadata and J
406415``` python
407416import asyncio
408417
418+
409419async def main () -> None :
410420 result = await authplane_auth(... )
411421 try :
@@ -414,6 +424,7 @@ async def main() -> None:
414424 finally :
415425 await result.aclose()
416426
427+
417428asyncio.run(main())
418429```
419430
0 commit comments