Jeremiah Lowin Jeremiah Lowin marvin-context-protocol[bot] commited on
Commit
341332a
·
unverified ·
1 Parent(s): d0abf98

Add documentation for get_access_token() dependency function (#1446)

Browse files

Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>

docs/docs.json CHANGED
@@ -162,7 +162,6 @@
162
  "pages": [
163
  "patterns/tool-transformation",
164
  "patterns/decorating-methods",
165
- "patterns/http-requests",
166
  "patterns/testing",
167
  "patterns/cli",
168
  "patterns/contrib"
 
162
  "pages": [
163
  "patterns/tool-transformation",
164
  "patterns/decorating-methods",
 
165
  "patterns/testing",
166
  "patterns/cli",
167
  "patterns/contrib"
docs/patterns/http-requests.mdx DELETED
@@ -1,87 +0,0 @@
1
- ---
2
- title: HTTP Requests
3
- sidebarTitle: HTTP Requests
4
- description: Accessing and using HTTP requests in FastMCP servers
5
- icon: network-wired
6
- ---
7
- import { VersionBadge } from '/snippets/version-badge.mdx'
8
-
9
- <VersionBadge version="2.2.11" />
10
-
11
- ## Overview
12
-
13
- When running FastMCP as a web server, your MCP tools, resources, and prompts might need to access the underlying HTTP request information, such as headers, client IP, or query parameters.
14
-
15
- FastMCP provides a clean way to access HTTP request information through a dependency function.
16
-
17
- ## Accessing HTTP Requests
18
-
19
- The recommended way to access the current HTTP request is through the `get_http_request()` dependency function:
20
-
21
- ```python {2, 3, 11}
22
- from fastmcp import FastMCP
23
- from fastmcp.server.dependencies import get_http_request
24
- from starlette.requests import Request
25
-
26
- mcp = FastMCP(name="HTTP Request Demo")
27
-
28
- @mcp.tool
29
- async def user_agent_info() -> dict:
30
- """Return information about the user agent."""
31
- # Get the HTTP request
32
- request: Request = get_http_request()
33
-
34
- # Access request data
35
- user_agent = request.headers.get("user-agent", "Unknown")
36
- client_ip = request.client.host if request.client else "Unknown"
37
-
38
- return {
39
- "user_agent": user_agent,
40
- "client_ip": client_ip,
41
- "path": request.url.path,
42
- }
43
- ```
44
-
45
- This approach works anywhere within a request's execution flow, not just within your MCP function. It's useful when:
46
-
47
- 1. You need access to HTTP information in helper functions
48
- 2. You're calling nested functions that need HTTP request data
49
- 3. You're working with middleware or other request processing code
50
-
51
- ## Accessing HTTP Headers Only
52
-
53
- If you only need request headers and want to avoid potential errors, you can use the `get_http_headers()` helper:
54
-
55
- ```python {2}
56
- from fastmcp import FastMCP
57
- from fastmcp.server.dependencies import get_http_headers
58
-
59
- mcp = FastMCP(name="Headers Demo")
60
-
61
- @mcp.tool
62
- async def safe_header_info() -> dict:
63
- """Safely get header information without raising errors."""
64
- # Get headers (returns empty dict if no request context)
65
- headers = get_http_headers()
66
-
67
- # Get authorization header
68
- auth_header = headers.get("authorization", "")
69
- is_bearer = auth_header.startswith("Bearer ")
70
-
71
- return {
72
- "user_agent": headers.get("user-agent", "Unknown"),
73
- "content_type": headers.get("content-type", "Unknown"),
74
- "has_auth": bool(auth_header),
75
- "auth_type": "Bearer" if is_bearer else "Other" if auth_header else "None",
76
- "headers_count": len(headers)
77
- }
78
- ```
79
-
80
- By default, `get_http_headers()` excludes problematic headers like `host` and `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
81
-
82
- ## Important Notes
83
-
84
- - HTTP requests are only available when FastMCP is running as part of a web application
85
- - Accessing the HTTP request with `get_http_request()` outside of a web request context will raise a `RuntimeError`
86
- - The `get_http_headers()` function **never raises errors** - it returns an empty dict when no request context is available
87
- - The `get_http_request()` function returns a standard [Starlette Request](https://www.starlette.io/requests/) object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/servers/context.mdx CHANGED
@@ -89,7 +89,7 @@ async def data_analysis_request(dataset: str, ctx: Context) -> str:
89
  ```
90
 
91
 
92
- ### Via Dependency Function
93
 
94
  <VersionBadge version="2.2.11" />
95
 
@@ -285,4 +285,144 @@ async def request_info(ctx: Context) -> dict:
285
 
286
  <Warning>
287
  The MCP request is part of the low-level MCP SDK and intended for advanced use cases. Most users will not need to use it directly.
288
- </Warning>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  ```
90
 
91
 
92
+ ### Via Runtime Dependency Function
93
 
94
  <VersionBadge version="2.2.11" />
95
 
 
285
 
286
  <Warning>
287
  The MCP request is part of the low-level MCP SDK and intended for advanced use cases. Most users will not need to use it directly.
288
+ </Warning>
289
+
290
+ ## Runtime Dependencies
291
+
292
+ ### HTTP Requests
293
+
294
+ <VersionBadge version="2.2.11" />
295
+
296
+ The recommended way to access the current HTTP request is through the `get_http_request()` dependency function:
297
+
298
+ ```python {2, 3, 11}
299
+ from fastmcp import FastMCP
300
+ from fastmcp.server.dependencies import get_http_request
301
+ from starlette.requests import Request
302
+
303
+ mcp = FastMCP(name="HTTP Request Demo")
304
+
305
+ @mcp.tool
306
+ async def user_agent_info() -> dict:
307
+ """Return information about the user agent."""
308
+ # Get the HTTP request
309
+ request: Request = get_http_request()
310
+
311
+ # Access request data
312
+ user_agent = request.headers.get("user-agent", "Unknown")
313
+ client_ip = request.client.host if request.client else "Unknown"
314
+
315
+ return {
316
+ "user_agent": user_agent,
317
+ "client_ip": client_ip,
318
+ "path": request.url.path,
319
+ }
320
+ ```
321
+
322
+ This approach works anywhere within a request's execution flow, not just within your MCP function. It's useful when:
323
+
324
+ 1. You need access to HTTP information in helper functions
325
+ 2. You're calling nested functions that need HTTP request data
326
+ 3. You're working with middleware or other request processing code
327
+
328
+ ### HTTP Headers
329
+ <VersionBadge version="2.2.11" />
330
+
331
+ If you only need request headers and want to avoid potential errors, you can use the `get_http_headers()` helper:
332
+
333
+ ```python {2, 10}
334
+ from fastmcp import FastMCP
335
+ from fastmcp.server.dependencies import get_http_headers
336
+
337
+ mcp = FastMCP(name="Headers Demo")
338
+
339
+ @mcp.tool
340
+ async def safe_header_info() -> dict:
341
+ """Safely get header information without raising errors."""
342
+ # Get headers (returns empty dict if no request context)
343
+ headers = get_http_headers()
344
+
345
+ # Get authorization header
346
+ auth_header = headers.get("authorization", "")
347
+ is_bearer = auth_header.startswith("Bearer ")
348
+
349
+ return {
350
+ "user_agent": headers.get("user-agent", "Unknown"),
351
+ "content_type": headers.get("content-type", "Unknown"),
352
+ "has_auth": bool(auth_header),
353
+ "auth_type": "Bearer" if is_bearer else "Other" if auth_header else "None",
354
+ "headers_count": len(headers)
355
+ }
356
+ ```
357
+
358
+ By default, `get_http_headers()` excludes problematic headers like `host` and `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
359
+
360
+ ### Access Tokens
361
+
362
+ <VersionBadge version="2.11.0" />
363
+
364
+ When using authentication with your FastMCP server, you can access the authenticated user's access token information using the `get_access_token()` dependency function:
365
+
366
+ ```python {2, 10}
367
+ from fastmcp import FastMCP
368
+ from fastmcp.server.dependencies import get_access_token, AccessToken
369
+
370
+ mcp = FastMCP(name="Auth Token Demo")
371
+
372
+ @mcp.tool
373
+ async def get_user_info() -> dict:
374
+ """Get information about the authenticated user."""
375
+ # Get the access token (None if not authenticated)
376
+ token: AccessToken | None = get_access_token()
377
+
378
+ if token is None:
379
+ return {"authenticated": False}
380
+
381
+ return {
382
+ "authenticated": True,
383
+ "client_id": token.client_id,
384
+ "scopes": token.scopes,
385
+ "expires_at": token.expires_at,
386
+ "token_claims": token.claims, # JWT claims or custom token data
387
+ }
388
+ ```
389
+
390
+ This is particularly useful when you need to:
391
+
392
+ 1. **Access user identification** - Get the `client_id` or subject from token claims
393
+ 2. **Check permissions** - Verify scopes or custom claims before performing operations
394
+ 3. **Multi-tenant applications** - Extract tenant information from token claims
395
+ 4. **Audit logging** - Track which user performed which actions
396
+
397
+ #### Working with Token Claims
398
+
399
+ The `claims` field contains all the data from the original token (JWT claims for JWT tokens, or custom data for other token types):
400
+
401
+ ```python {2, 3, 9, 12, 15}
402
+ from fastmcp import FastMCP
403
+ from fastmcp.server.dependencies import get_access_token
404
+
405
+ mcp = FastMCP(name="Multi-tenant Demo")
406
+
407
+ @mcp.tool
408
+ async def get_tenant_data(resource_id: str) -> dict:
409
+ """Get tenant-specific data using token claims."""
410
+ token: AccessToken | None = get_access_token()
411
+
412
+ # Extract tenant ID from token claims
413
+ tenant_id = token.claims.get("tenant_id") if token else None
414
+
415
+ # Extract user ID from standard JWT subject claim
416
+ user_id = token.claims.get("sub") if token else None
417
+
418
+ # Use tenant and user info to authorize and filter data
419
+ if not tenant_id:
420
+ raise ValueError("No tenant information in token")
421
+
422
+ return {
423
+ "resource_id": resource_id,
424
+ "tenant_id": tenant_id,
425
+ "user_id": user_id,
426
+ "data": f"Tenant-specific data for {tenant_id}",
427
+ }
428
+ ```
src/fastmcp/server/auth/__init__.py CHANGED
@@ -1,13 +1,21 @@
1
- from .auth import OAuthProvider, TokenVerifier, RemoteAuthProvider
 
 
 
 
 
 
2
  from .providers.jwt import JWTVerifier, StaticTokenVerifier
3
 
4
 
5
  __all__ = [
 
6
  "OAuthProvider",
7
  "TokenVerifier",
8
  "JWTVerifier",
9
  "StaticTokenVerifier",
10
  "RemoteAuthProvider",
 
11
  ]
12
 
13
 
 
1
+ from .auth import (
2
+ OAuthProvider,
3
+ TokenVerifier,
4
+ RemoteAuthProvider,
5
+ AccessToken,
6
+ AuthProvider,
7
+ )
8
  from .providers.jwt import JWTVerifier, StaticTokenVerifier
9
 
10
 
11
  __all__ = [
12
+ "AuthProvider",
13
  "OAuthProvider",
14
  "TokenVerifier",
15
  "JWTVerifier",
16
  "StaticTokenVerifier",
17
  "RemoteAuthProvider",
18
+ "AccessToken",
19
  ]
20
 
21
 
src/fastmcp/server/auth/providers/jwt.py CHANGED
@@ -15,8 +15,7 @@ from pydantic import AnyHttpUrl, SecretStr
15
  from pydantic_settings import BaseSettings, SettingsConfigDict
16
  from typing_extensions import TypedDict
17
 
18
- from fastmcp.server.auth import TokenVerifier
19
- from fastmcp.server.auth.auth import AccessToken
20
  from fastmcp.server.auth.registry import register_provider
21
  from fastmcp.utilities.logging import get_logger
22
  from fastmcp.utilities.types import NotSet, NotSetT
 
15
  from pydantic_settings import BaseSettings, SettingsConfigDict
16
  from typing_extensions import TypedDict
17
 
18
+ from fastmcp.server.auth import AccessToken, TokenVerifier
 
19
  from fastmcp.server.auth.registry import register_provider
20
  from fastmcp.utilities.logging import get_logger
21
  from fastmcp.utilities.types import NotSet, NotSetT
src/fastmcp/server/auth/registry.py CHANGED
@@ -6,7 +6,7 @@ from collections.abc import Callable
6
  from typing import TYPE_CHECKING, TypeVar
7
 
8
  if TYPE_CHECKING:
9
- from fastmcp.server.auth.auth import AuthProvider
10
 
11
  # Type variable for auth providers
12
  T = TypeVar("T", bound="AuthProvider")
 
6
  from typing import TYPE_CHECKING, TypeVar
7
 
8
  if TYPE_CHECKING:
9
+ from fastmcp.server.auth import AuthProvider
10
 
11
  # Type variable for auth providers
12
  T = TypeVar("T", bound="AuthProvider")
src/fastmcp/server/dependencies.py CHANGED
@@ -7,7 +7,7 @@ from mcp.server.auth.middleware.auth_context import (
7
  )
8
  from starlette.requests import Request
9
 
10
- from fastmcp.server.auth.auth import AccessToken
11
 
12
  if TYPE_CHECKING:
13
  from fastmcp.server.context import Context
 
7
  )
8
  from starlette.requests import Request
9
 
10
+ from fastmcp.server.auth import AccessToken
11
 
12
  if TYPE_CHECKING:
13
  from fastmcp.server.context import Context
src/fastmcp/server/http.py CHANGED
@@ -23,7 +23,7 @@ from starlette.responses import Response
23
  from starlette.routing import BaseRoute, Mount, Route
24
  from starlette.types import Lifespan, Receive, Scope, Send
25
 
26
- from fastmcp.server.auth.auth import AuthProvider
27
  from fastmcp.utilities.logging import get_logger
28
 
29
  if TYPE_CHECKING:
 
23
  from starlette.routing import BaseRoute, Mount, Route
24
  from starlette.types import Lifespan, Receive, Scope, Send
25
 
26
+ from fastmcp.server.auth import AuthProvider
27
  from fastmcp.utilities.logging import get_logger
28
 
29
  if TYPE_CHECKING:
src/fastmcp/server/server.py CHANGED
@@ -49,7 +49,7 @@ from fastmcp.prompts import Prompt, PromptManager
49
  from fastmcp.prompts.prompt import FunctionPrompt
50
  from fastmcp.resources import Resource, ResourceManager
51
  from fastmcp.resources.template import ResourceTemplate
52
- from fastmcp.server.auth.auth import AuthProvider
53
  from fastmcp.server.auth.registry import get_registered_provider
54
  from fastmcp.server.http import (
55
  StarletteWithLifespan,
 
49
  from fastmcp.prompts.prompt import FunctionPrompt
50
  from fastmcp.resources import Resource, ResourceManager
51
  from fastmcp.resources.template import ResourceTemplate
52
+ from fastmcp.server.auth import AuthProvider
53
  from fastmcp.server.auth.registry import get_registered_provider
54
  from fastmcp.server.http import (
55
  StarletteWithLifespan,
tests/server/auth/test_remote_auth_provider.py CHANGED
@@ -3,7 +3,7 @@ import pytest
3
  from pydantic import AnyHttpUrl
4
 
5
  from fastmcp import FastMCP
6
- from fastmcp.server.auth.auth import AccessToken, RemoteAuthProvider, TokenVerifier
7
 
8
 
9
  class SimpleTokenVerifier(TokenVerifier):
 
3
  from pydantic import AnyHttpUrl
4
 
5
  from fastmcp import FastMCP
6
+ from fastmcp.server.auth import AccessToken, RemoteAuthProvider, TokenVerifier
7
 
8
 
9
  class SimpleTokenVerifier(TokenVerifier):
tests/server/auth/test_static_token_verifier.py CHANGED
@@ -3,7 +3,7 @@
3
  import httpx
4
 
5
  from fastmcp.server import FastMCP
6
- from fastmcp.server.auth.auth import AccessToken
7
  from fastmcp.server.auth.providers.jwt import StaticTokenVerifier
8
 
9
 
 
3
  import httpx
4
 
5
  from fastmcp.server import FastMCP
6
+ from fastmcp.server.auth import AccessToken
7
  from fastmcp.server.auth.providers.jwt import StaticTokenVerifier
8
 
9
 
tests/server/http/test_bearer_auth_backend.py CHANGED
@@ -4,7 +4,7 @@ import pytest
4
  from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend
5
  from starlette.requests import HTTPConnection
6
 
7
- from fastmcp.server.auth.auth import AccessToken
8
  from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
9
 
10
 
 
4
  from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend
5
  from starlette.requests import HTTPConnection
6
 
7
+ from fastmcp.server.auth import AccessToken
8
  from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
9
 
10