@@ -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
@@ -98,11 +100,13 @@ Use FastMCP's built-in `require_scopes` decorator to enforce per-tool scope requ
98100``` python
99101from fastmcp.server.auth import require_scopes
100102
103+
101104@mcp.tool (auth = require_scopes(" tools/query" ))
102105def query (sql : str ) -> str :
103106 """ Requires the tools/query scope."""
104107 return f " Ran: { sql} " # replace with your real handler
105108
109+
106110@mcp.tool (auth = require_scopes(" tools/admin" , " tools/delete" ))
107111def delete_all () -> str :
108112 """ Requires BOTH tools/admin AND tools/delete scopes."""
@@ -119,21 +123,22 @@ FastMCP enforces scopes **before** the handler runs by **filtering tools the cal
119123from fastmcp.dependencies import CurrentAccessToken
120124from fastmcp.server.auth import AccessToken
121125
126+
122127@mcp.tool ()
123128async def my_tool (data : str , token : AccessToken = CurrentAccessToken()) -> str :
124129 # Standard JWT claims
125- sub = token.claims.get(" sub" ) # Subject (user ID)
126- jti = token.claims.get(" jti" ) # JWT ID
127- iss = token.claims.get(" iss" ) # Issuer
128- aud = token.claims.get(" aud" ) # Audience
129- exp = token.claims.get(" exp" ) # Expiration (Unix timestamp)
130- nbf = token.claims.get(" nbf" ) # Not before
131- iat = token.claims.get(" iat" ) # Issued at
130+ sub = token.claims.get(" sub" ) # Subject (user ID)
131+ jti = token.claims.get(" jti" ) # JWT ID
132+ iss = token.claims.get(" iss" ) # Issuer
133+ aud = token.claims.get(" aud" ) # Audience
134+ exp = token.claims.get(" exp" ) # Expiration (Unix timestamp)
135+ nbf = token.claims.get(" nbf" ) # Not before
136+ iat = token.claims.get(" iat" ) # Issued at
132137
133138 # OAuth claims
134- client_id = token.client_id # Client ID
135- scopes = token.scopes # List of granted scopes
136- expires_at = token.expires_at # Expiration (Unix timestamp)
139+ client_id = token.client_id # Client ID
140+ scopes = token.scopes # List of granted scopes
141+ expires_at = token.expires_at # Expiration (Unix timestamp)
137142
138143 # Custom claims
139144 tenant = token.claims.get(" tenant_id" )
@@ -149,6 +154,7 @@ The `claims` dict contains the **full JWT payload** including all standard and c
149154``` python
150155from fastmcp.server.dependencies import get_access_token
151156
157+
152158@mcp.tool ()
153159async def my_tool (data : str ) -> str :
154160 token = get_access_token() # Returns None if unauthenticated
@@ -220,10 +226,12 @@ Implement your own revocation logic with an async callable:
220226``` python
221227from authplane import VerifiedClaims
222228
229+
223230async def check_blocklist (claims : VerifiedClaims, raw_token : str ) -> bool :
224231 """ Return True to reject the token (it is revoked)."""
225232 return await redis_client.sismember(" revoked_tokens" , claims.jti)
226233
234+
227235await authplane_auth(
228236 issuer = " https://auth.company.com" ,
229237 base_url = " https://mcp.company.com" ,
@@ -253,8 +261,8 @@ result = await authplane_auth(
253261downstream = await result.client.exchange(
254262 TokenExchangeOptions(
255263 subject_token = inbound_token,
256- scope = " tools/add" , # narrow to the minimum
257- resources = (" https://downstream.example" ,), # RFC 8707 audience binding
264+ scope = " tools/add" , # narrow to the minimum
265+ resources = (" https://downstream.example" ,), # RFC 8707 audience binding
258266 )
259267)
260268
@@ -287,6 +295,7 @@ from authplane import ConsentRequiredError
287295from authplane.oauth import TokenExchangeOptions
288296from mcp.shared.exceptions import UrlElicitationRequiredError
289297
298+
290299@mcp.tool (auth = require_scopes(" tools/call_downstream" ))
291300async def call_downstream (payload : str ) -> str :
292301 try :
@@ -379,6 +388,7 @@ When `fetch_settings` is provided, `dev_mode` is ignored for both metadata and J
379388``` python
380389import asyncio
381390
391+
382392async def main () -> None :
383393 result = await authplane_auth(... )
384394 try :
@@ -387,6 +397,7 @@ async def main() -> None:
387397 finally :
388398 await result.aclose()
389399
400+
390401asyncio.run(main())
391402```
392403
0 commit comments