Skip to content

Add MCP quickstart for securing a Python MCP server - #4304

Merged
sahandilshan merged 1 commit into
thunder-id:mainfrom
sahandilshan:docs/mcp-quickstart
Jul 27, 2026
Merged

Add MCP quickstart for securing a Python MCP server#4304
sahandilshan merged 1 commit into
thunder-id:mainfrom
sahandilshan:docs/mcp-quickstart

Conversation

@sahandilshan

@sahandilshan sahandilshan commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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

  • New page 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.
  • The walkthrough: run ThunderID, register an MCP resource server with four tool permissions, create a role granting them and assign the signing-in user, build a single-file FastMCP server, export the local dev certificate, verify the 401 + RFC 9728 protected-resource metadata with curl, and connect end to end with MCP Inspector (including NODE_EXTRA_CA_CERTS for the self-signed certificate and a scope-removal demonstration of per-tool gating).
  • Console navigation in the guide was verified against the actual Console implementation (resource-server creation wizard, MCP Capabilities panel, role creation wizard, assignment dialog), and the whole flow was validated end to end against a running instance, including negative paths that were folded into the troubleshooting notes (wrong transport type, missing certificate trust, missing role).
  • Selector wiring: mcp entry activated in ConnectTypeSelector and DeveloperShortcut (relabeled to "MCP", description covering servers and clients), Python chip added, and a python icon registered in the sidebar icon map using the existing PythonLogo component.

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • New Features
    • Added MCP as an available connection type in the developer experience.
    • Added a Python quickstart for securing MCP servers with OAuth scopes.
    • Added MCP quickstarts and Python branding to navigation and developer shortcuts.
    • Added guidance for connecting and testing MCP servers with MCP Inspector.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

This 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

Layer / File(s) Summary
Secure Python MCP tutorial
docs/content/getting-started/connect-your-mcp/python.mdx
Documents Console setup, FastMCP OAuth validation, scoped calculator tools, local verification, MCP Inspector authorization, and follow-up resources.
MCP navigation and quickstarts
docs/src/components/ConnectTypeSelector.tsx, docs/src/components/DeveloperShortcut.tsx
Makes MCP selectable and adds Python MCP quickstart links with MCP-specific descriptions.
MCP sidebar presentation
docs/content/getting-started/connect-your-mcp/_category_.json, docs/sidebars.ts, docs/src/theme/DocSidebarItem/Link/index.tsx
Adds the MCP sidebar category and Python technology icon rendering.

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
Loading

Possibly related PRs

Suggested labels: skip-changelog

Suggested reviewers: thiva-k, jayashakthi97, brionmario

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: a new MCP quickstart for securing a Python MCP server.
Description check ✅ Passed The description covers Purpose, Approach, issues, checklist, and security checks, with enough detail for a mostly complete PR template.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the skip-changelog Skip generating changelog for a particular PR label Jul 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 255f904 and ff65d64.

📒 Files selected for processing (6)
  • docs/content/getting-started/connect-your-mcp/_category_.json
  • docs/content/getting-started/connect-your-mcp/python.mdx
  • docs/sidebars.ts
  • docs/src/components/ConnectTypeSelector.tsx
  • docs/src/components/DeveloperShortcut.tsx
  • docs/src/theme/DocSidebarItem/Link/index.tsx

Comment thread docs/content/getting-started/connect-your-mcp/python.mdx Outdated
Comment on lines +121 to +136
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,
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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' \
  docs

Repository: 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 || true

Repository: 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:


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.

Comment on lines 24 to 28
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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-L59
  • docs/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

@sahandilshan
sahandilshan force-pushed the docs/mcp-quickstart branch 2 times, most recently from 5119a42 to 8afaa5f Compare July 27, 2026 04:39
@sahandilshan
sahandilshan force-pushed the docs/mcp-quickstart branch from 8afaa5f to 98d13f3 Compare July 27, 2026 09:36
@sahandilshan
sahandilshan added this pull request to the merge queue Jul 27, 2026
Merged via the queue into thunder-id:main with commit b50c43e Jul 27, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog Skip generating changelog for a particular PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants