Add MCP quickstart for securing a Python MCP server - #4304
Conversation
📝 WalkthroughWalkthroughChangesThis PR adds a Python quickstart for securing FastMCP servers with OAuth scopes, including JWT verification, scoped tools, MCP Inspector setup, and troubleshooting. It also exposes MCP through documentation navigation, connect selection, developer quickstarts, and Python sidebar icons. MCP quickstart
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant MCPInspector
participant FastMCPServer
participant ThunderID
MCPInspector->>FastMCPServer: Request protected MCP resource
FastMCPServer-->>MCPInspector: Return 401 and resource metadata
MCPInspector->>ThunderID: Authorize with PKCE
ThunderID-->>MCPInspector: Return access token
MCPInspector->>FastMCPServer: Call scoped MCP tools with token
FastMCPServer->>ThunderID: Fetch JWKS for JWT verification
FastMCPServer-->>MCPInspector: Return authorized tool results
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/content/getting-started/connect-your-mcp/python.mdx`:
- Line 101: Update the module docstring in the calculator MCP server sample to
remove the hardcoded “ThunderID” product name, replacing it with a neutral
description or the {{ProductName}} placeholder while preserving the existing
description.
- Around line 121-136: The Python example must close the caller-owned
httpx.AsyncClient created for JWTVerifier. Retain a reference to the client,
then add cleanup in server.py’s FastMCP lifespan/finalizer so it is closed after
all FastMCP lifespan usage completes; preserve the existing no-client behavior
when CA_CERT is unset.
In `@docs/src/components/ConnectTypeSelector.tsx`:
- Around line 24-28: Add tests covering the enabled MCP option in
ConnectTypeSelector.tsx, including its visibility and selection behavior; verify
the Python MCP link metadata in DeveloperShortcut.tsx; and verify the MCP
quickstart list and caption render correctly in DeveloperShortcut.tsx. Cover all
three affected sites: docs/src/components/ConnectTypeSelector.tsx lines 24-28,
docs/src/components/DeveloperShortcut.tsx lines 52-59, and
docs/src/components/DeveloperShortcut.tsx lines 261-307.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b03511e6-6ddc-41f9-a4db-f54ff0ec767d
📒 Files selected for processing (6)
docs/content/getting-started/connect-your-mcp/_category_.jsondocs/content/getting-started/connect-your-mcp/python.mdxdocs/sidebars.tsdocs/src/components/ConnectTypeSelector.tsxdocs/src/components/DeveloperShortcut.tsxdocs/src/theme/DocSidebarItem/Link/index.tsx
| verifier = JWTVerifier( | ||
| jwks_uri=JWKS_URI, | ||
| issuer=ISSUER, | ||
| audience=AUDIENCE, | ||
| http_client=httpx.AsyncClient(verify=CA_CERT) if CA_CERT else None, | ||
| ) | ||
|
|
||
| mcp = FastMCP( | ||
| "Calculator", | ||
| auth=RemoteAuthProvider( | ||
| token_verifier=verifier, | ||
| authorization_servers=[ISSUER], | ||
| base_url="http://localhost:8000", | ||
| scopes_supported=SCOPES, | ||
| ), | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n \
--glob '! .claude/worktrees/**' \
--glob '*.mdx' \
'fastmcp>=3\.4,<4|JWTVerifier\(|http_client=httpx\.AsyncClient' \
docsRepository: thunder-id/thunderid
Length of output: 493
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== target file outline/context =="
wc -l docs/content/getting-started/connect-your-mcp/python.mdx
sed -n '1,190p' docs/content/getting-started/connect-your-mcp/python.mdx | cat -n
echo
echo "== related docs/auth/token verification snippets if present =="
rg -n -C 4 'JWTVerifier|http_client|AClose|aclose|lifespan|shutdown|Close' docs/content docs/src 2>/dev/null || trueRepository: thunder-id/thunderid
Length of output: 22219
🌐 Web query:
FastMCP JWTVerifier http_client caller-supplied client lifecycle document close lifespan
💡 Result:
When you provide a custom http_client to FastMCP's JWTVerifier, you assume full responsibility for managing its lifecycle [1][2][3]. The JWTVerifier itself will not close the client [1][2][3]. To handle cleanup properly, you should use the server's lifespan context manager [1][2][3]. Because FastMCP's lifespan implementation may be executed upon server session start/stop (which can occur per client connection rather than only at total application startup/shutdown) [4][5], managing external resources like an HTTP client via this mechanism requires careful implementation to avoid premature closure or resource leaks [5]. Key points regarding this pattern include: 1. Responsibility: The verifier does not manage the provided httpx.AsyncClient [1][2][3]. 2. Implementation: You must include the client's closure (e.g., await http_client.aclose) within the 'finally' block of the server's lifespan context manager [1][2][3]. 3. SSRF-Safe Incompatibility: The http_client parameter is not supported when ssrf_safe=True, as this mode requires a hardened internal transport that cannot be guaranteed with a user-provided client [1][2][3]. Attempting to use both will raise a ValueError [1][2][3]. 4. Contextual Nuance: Community discussions note that lifespan in FastMCP is often triggered per-session [4][5]. Users should verify if this behavior aligns with their specific resource management needs, as some developers prefer external dependency injection for long-lived resources [5].
Citations:
- 1: https://fastmcp.wiki/en/servers/auth/token-verification
- 2: https://fastmcp.mintlify.app/servers/auth/token-verification
- 3: https://gofastmcp.com/servers/auth/token-verification
- 4: Lifespan's startup and shutdown sections are executed for every tool call PrefectHQ/fastmcp#1115
- 5: Managing connections through lifespan PrefectHQ/fastmcp#1808
Close the caller-owned HTTP client.
httpx.AsyncClient(verify=CA_CERT) is created by the server code and passed to JWTVerifier, but server.py never calls aclose() on it. Add cleanup in the server’s FastMCP lifespan/finalizer, or use an explicit wrapper that ensures the client outlives every fastmcp lifespan use-case before closing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/content/getting-started/connect-your-mcp/python.mdx` around lines 121 -
136, The Python example must close the caller-owned httpx.AsyncClient created
for JWTVerifier. Retain a reference to the client, then add cleanup in
server.py’s FastMCP lifespan/finalizer so it is closed after all FastMCP
lifespan usage completes; preserve the existing no-client behavior when CA_CERT
is unset.
| const OPTIONS = [ | ||
| {Icon: MonitorSmartphone, description: 'Web, mobile & desktop apps', label: 'Application', value: 'app', comingSoon: false}, | ||
| {Icon: Bot, description: 'LLM-powered AI agents', label: 'AI Agent', value: 'agent', comingSoon: true}, | ||
| {Icon: Server, description: 'Model Context Protocol servers', label: 'MCP Server', value: 'mcp', comingSoon: true}, | ||
| {Icon: Server, description: 'Model Context Protocol servers and clients', label: 'MCP', value: 'mcp', comingSoon: false}, | ||
| ] as const; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add coverage for the enabled MCP quickstart flow.
The PR enables a new selectable path but includes no tests. Cover selection, MCP visibility, and the Python quickstart target.
docs/src/components/ConnectTypeSelector.tsx#L24-L28: verify MCP is enabled and selectable.docs/src/components/DeveloperShortcut.tsx#L52-L59: verify the Python MCP link metadata.docs/src/components/DeveloperShortcut.tsx#L261-L307: verify MCP renders its quickstart list and caption.
As per coding guidelines, “Write tests for new features and bug fixes, targeting at least 80% coverage.”
📍 Affects 2 files
docs/src/components/ConnectTypeSelector.tsx#L24-L28(this comment)docs/src/components/DeveloperShortcut.tsx#L52-L59docs/src/components/DeveloperShortcut.tsx#L261-L307
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/src/components/ConnectTypeSelector.tsx` around lines 24 - 28, Add tests
covering the enabled MCP option in ConnectTypeSelector.tsx, including its
visibility and selection behavior; verify the Python MCP link metadata in
DeveloperShortcut.tsx; and verify the MCP quickstart list and caption render
correctly in DeveloperShortcut.tsx. Cover all three affected sites:
docs/src/components/ConnectTypeSelector.tsx lines 24-28,
docs/src/components/DeveloperShortcut.tsx lines 52-59, and
docs/src/components/DeveloperShortcut.tsx lines 261-307.
Source: Coding guidelines
5119a42 to
8afaa5f
Compare
8afaa5f to
98d13f3
Compare
Purpose
Adds the first quickstart for the MCP path on the docs home page: Secure Your MCP Server, a step-by-step guide for protecting a Python (FastMCP) MCP server with ThunderID OAuth, where each tool is guarded by its own scope and tokens are validated offline against the JWKS endpoint.
The "MCP Server" card on the docs landing selector was previously marked coming soon. This PR activates it as MCP (covering servers now, clients later) with a Python quickstart chip, and adds a matching MCP section to the Get Started sidebar.
Approach
docs/content/getting-started/connect-your-mcp/python.mdx, following the established quickstart template (TutorialHero,Stepper,CodeGroup, What's Next cards) used by the application quickstarts.NODE_EXTRA_CA_CERTSfor the self-signed certificate and a scope-removal demonstration of per-tool gating).mcpentry activated inConnectTypeSelectorandDeveloperShortcut(relabeled to "MCP", description covering servers and clients), Python chip added, and apythonicon registered in the sidebar icon map using the existingPythonLogocomponent.Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit