Add MCP client quickstart guides and sample app - #4400
Conversation
📝 WalkthroughWalkthroughAdds MCP server and client quickstarts, restructures MCP documentation navigation, activates MCP developer shortcuts, and introduces an OAuth-authenticated calculator server/client sample with scope-protected tools. ChangesMCP quickstarts and calculator sample
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant MCPServer
participant ThunderID
participant Browser
Client->>MCPServer: Request protected-resource metadata
MCPServer-->>Client: Return OAuth metadata and scopes
Client->>ThunderID: Register client and authorize
ThunderID->>Browser: Present sign-in
Browser-->>ThunderID: Return authorization code
ThunderID-->>Client: Return access token
Client->>MCPServer: Call scoped calculator tool
MCPServer-->>Client: Return permitted result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. 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
🧹 Nitpick comments (5)
docs/content/getting-started/connect-your-mcp/connect-a-client.mdx (1)
141-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse "select" instead of "choose" for UI selection.
-2. Choose the `calculator` server and authenticate. +2. Select the `calculator` server and authenticate.As per path instructions: flag "'choose'/'pick' instead of 'select' for UI dropdowns."
🤖 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/connect-a-client.mdx` around lines 141 - 142, Update the UI instruction in the numbered steps to use “select” instead of “choose” when referring to the calculator server.Source: Path instructions
samples/apps/mcp-calculator-sample/README.md (1)
13-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a language to the fenced project-structure block.
markdownlint flags this fence for missing a language identifier (MD040).
As per static analysis: markdownlint-cli2 flags "Fenced code blocks should have a language specified" at this line.📝 Proposed fix
-``` +```text mcp-calculator-sample/🤖 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 `@samples/apps/mcp-calculator-sample/README.md` around lines 13 - 25, Add the text language identifier to the fenced project-structure block in the README, changing the opening fence to a text-designated fence while preserving the directory listing unchanged.Source: Linters/SAST tools
docs/content/getting-started/connect-your-mcp/python.mdx (2)
89-99: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConcept bleed: RBAC/scope mechanics explained inline.
This paragraph explains, in three sentences, how role membership determines which scopes land in a token, an implementation concept, mid-quickstart. Per the doc-quality guideline, quickstarts should link to a concepts page rather than defining this beyond one sentence.
Consider trimming to one sentence with a pointer to a permissions/roles concept page (if one exists), keeping only the actionable instruction here.
As per path instructions: "Flag any page that introduces a concept that belongs on a separate concepts page, mid-way through a quickstart or guide. Quickstarts and guides should link to concepts pages, not define terms inline beyond one sentence."
🤖 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 89 - 99, Trim the introductory paragraph in the role-creation section to one actionable sentence explaining that the user must hold the calculator permissions for their token to include the required scopes, and link to the existing permissions/roles concepts page for further scope and RBAC details. Keep the six role and assignment steps unchanged.Source: Path instructions
312-336: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate "What's Next" card grid across 3 quickstarts. All three pages repeat the same raw inline-style JSX card-grid markup verbatim; a shared MDX component (e.g.
<NextStepsGrid items={[...]} />) would remove the duplication and make future style/link updates single-source.
docs/content/getting-started/connect-your-mcp/python.mdx#L312-L336: extract this card grid into a shared component, passing the 3 card links as data.docs/content/getting-started/connect-your-mcp/build-a-client.mdx#L151-L166: replace with the same shared component, passing its 2 card links.docs/content/getting-started/connect-your-mcp/connect-a-client.mdx#L160-L175: replace with the same shared component, passing its 2 card links.🤖 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 312 - 336, Extract the repeated inline-style “What’s Next” card grid into a shared NextStepsGrid MDX component that accepts card data, preserving each card’s title, description, link, and ProductName usage. Replace the grids in docs/content/getting-started/connect-your-mcp/python.mdx lines 312-336, build-a-client.mdx lines 151-166, and connect-a-client.mdx lines 160-175 with the shared component, passing three cards for python.mdx and the existing two cards for each sibling page.docs/src/components/McpQuickstartFlow.tsx (1)
41-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse an Oxygen UI container.
Line 41 can use
Boxinstead of a rawdiv. Should this use an Oxygen UI component instead of raw HTML?Proposed fix
+import {Box} from '`@wso2/oxygen-ui`'; + - <div className="uc-agent-wayfinder-arch uc-mcp-qs-arch"> + <Box className="uc-agent-wayfinder-arch uc-mcp-qs-arch"> <svg> ... </svg> - </div> + </Box>🤖 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/McpQuickstartFlow.tsx` at line 41, Replace the raw div in McpQuickstartFlow with the Oxygen UI Box component while preserving the existing className and rendered structure.Source: Path instructions
🤖 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/src/components/McpQuickstartFlow.tsx`:
- Around line 42-154: Replace the hand-built SVG inside McpQuickstartFlow with a
Mermaid-based architecture flow diagram, preserving the existing MCP OAuth
participants, interactions, labels, and productName interpolation. Remove the
raw SVG groups, shapes, markers, and text, and use the repository’s established
Mermaid rendering approach if available.
In `@samples/apps/mcp-calculator-sample/client/client.py`:
- Around line 3-23: Replace DiskStore with FileTreeStore in
samples/apps/mcp-calculator-sample/client/client.py, remove the
py-key-value-aio[disk] extra, and configure key and collection sanitization.
Update docs/content/getting-started/connect-your-mcp/build-a-client.mdx lines
67-117 to use FileTreeStore with the same sanitization settings and remove the
[disk] extra from its dependencies and bullets.
- Line 3: Replace the OAuth cache’s DiskStore usage with FastMCP’s recommended
FileTreeStore, updating the OAuth construction and related imports in the client
sample. Remove the py-key-value-aio[disk] dependency from the dependencies
declaration and retain only the dependency needed for FileTreeStore.
---
Nitpick comments:
In `@docs/content/getting-started/connect-your-mcp/connect-a-client.mdx`:
- Around line 141-142: Update the UI instruction in the numbered steps to use
“select” instead of “choose” when referring to the calculator server.
In `@docs/content/getting-started/connect-your-mcp/python.mdx`:
- Around line 89-99: Trim the introductory paragraph in the role-creation
section to one actionable sentence explaining that the user must hold the
calculator permissions for their token to include the required scopes, and link
to the existing permissions/roles concepts page for further scope and RBAC
details. Keep the six role and assignment steps unchanged.
- Around line 312-336: Extract the repeated inline-style “What’s Next” card grid
into a shared NextStepsGrid MDX component that accepts card data, preserving
each card’s title, description, link, and ProductName usage. Replace the grids
in docs/content/getting-started/connect-your-mcp/python.mdx lines 312-336,
build-a-client.mdx lines 151-166, and connect-a-client.mdx lines 160-175 with
the shared component, passing three cards for python.mdx and the existing two
cards for each sibling page.
In `@docs/src/components/McpQuickstartFlow.tsx`:
- Line 41: Replace the raw div in McpQuickstartFlow with the Oxygen UI Box
component while preserving the existing className and rendered structure.
In `@samples/apps/mcp-calculator-sample/README.md`:
- Around line 13-25: Add the text language identifier to the fenced
project-structure block in the README, changing the opening fence to a
text-designated fence while preserving the directory listing unchanged.
🪄 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: 0c12d23f-e779-4beb-99cd-75171de95873
📒 Files selected for processing (16)
docs/content/getting-started/connect-your-mcp/_category_.jsondocs/content/getting-started/connect-your-mcp/build-a-client.mdxdocs/content/getting-started/connect-your-mcp/connect-a-client.mdxdocs/content/getting-started/connect-your-mcp/python.mdxdocs/sidebars.tsdocs/src/components/DeveloperShortcut.tsxdocs/src/components/McpQuickstartFlow.tsxdocs/src/components/WayfinderDiagrams.cssdocs/src/theme/DocSidebarItem/Category/index.tsxdocs/src/theme/DocSidebarItem/Link/index.tsxdocs/src/theme/MDXComponents.tsxsamples/apps/mcp-calculator-sample/.gitignoresamples/apps/mcp-calculator-sample/README.mdsamples/apps/mcp-calculator-sample/client/client.pysamples/apps/mcp-calculator-sample/server/.env.examplesamples/apps/mcp-calculator-sample/server/server.py
| <svg | ||
| className="uc-agent-wayfinder-arch__svg" | ||
| viewBox="0 0 820 330" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| role="img" | ||
| aria-label={`MCP OAuth flow: an MCP client discovers ${productName} as the authorization server through RFC 9728 metadata, obtains a scoped JWT via Authorization Code with PKCE, then calls MCP tools with the token. The MCP server validates the JWT offline against ${productName}'s JWKS endpoint.`} | ||
| > | ||
| <defs> | ||
| <marker | ||
| id="mcp-qs-arrow" | ||
| viewBox="0 0 10 10" | ||
| refX="9" | ||
| refY="5" | ||
| markerWidth="6" | ||
| markerHeight="6" | ||
| orient="auto-start-reverse" | ||
| > | ||
| <path d="M0,0 L10,5 L0,10 z" fill="currentColor" /> | ||
| </marker> | ||
| </defs> | ||
|
|
||
| {/* User at top */} | ||
| <g className="uc-agent-wayfinder-arch__consumers"> | ||
| <text x="170" y="12" textAnchor="middle" className="uc-agent-wayfinder-arch__group-label"> | ||
| User | ||
| </text> | ||
| <g transform="translate(156,20)"> | ||
| <g transform="scale(0.5)"> | ||
| <PersonIcon className="uc-agent-wayfinder-arch__icon" /> | ||
| </g> | ||
| </g> | ||
| </g> | ||
|
|
||
| {/* User → MCP Client */} | ||
| <g className="uc-agent-wayfinder-arch__edges"> | ||
| <line x1="170" y1="52" x2="170" y2="76" markerEnd="url(#mcp-qs-arrow)" /> | ||
| </g> | ||
|
|
||
| {/* MCP Client */} | ||
| <g className="uc-agent-wayfinder-arch__app" transform="translate(20,76)"> | ||
| <rect width="300" height="88" rx="10" /> | ||
| <text x="150" y="30" textAnchor="middle" className="uc-agent-wayfinder-arch__app-title"> | ||
| MCP Client | ||
| </text> | ||
| <text x="150" y="50" textAnchor="middle" className="uc-agent-wayfinder-arch__sub"> | ||
| e.g., Claude Desktop, MCP Inspector | ||
| </text> | ||
| <line x1="24" y1="62" x2="276" y2="62" className="uc-agent-wayfinder-arch__divider" /> | ||
| <text x="150" y="79" textAnchor="middle" className="uc-agent-wayfinder-arch__detail"> | ||
| Discovers auth, signs in, calls tools | ||
| </text> | ||
| </g> | ||
|
|
||
| {/* ThunderID — tall box on the right */} | ||
| <g className="uc-agent-wayfinder-arch__idp" transform="translate(560,76)"> | ||
| <rect width="220" height="240" rx="10" /> | ||
| <text x="110" y="42" textAnchor="middle" className="uc-agent-wayfinder-arch__idp-title"> | ||
| {productName} | ||
| </text> | ||
| <text x="110" y="64" textAnchor="middle" className="uc-agent-wayfinder-arch__sub"> | ||
| Authorization Server | ||
| </text> | ||
| <line x1="26" y1="78" x2="194" y2="78" className="uc-agent-wayfinder-arch__divider" /> | ||
| <text x="110" y="104" textAnchor="middle" className="uc-agent-wayfinder-arch__detail"> | ||
| Signs users in and | ||
| </text> | ||
| <text x="110" y="122" textAnchor="middle" className="uc-agent-wayfinder-arch__detail"> | ||
| issues scoped JWTs | ||
| </text> | ||
| </g> | ||
|
|
||
| {/* Your MCP Server — bottom left */} | ||
| <g className="uc-agent-wayfinder-arch__svc" transform="translate(20,228)"> | ||
| <rect width="300" height="88" rx="10" /> | ||
| <text x="150" y="30" textAnchor="middle" className="uc-agent-wayfinder-arch__app-title"> | ||
| Your MCP Server | ||
| </text> | ||
| <text x="150" y="50" textAnchor="middle" className="uc-agent-wayfinder-arch__sub"> | ||
| OAuth 2.0 Resource Server | ||
| </text> | ||
| <line x1="24" y1="62" x2="276" y2="62" className="uc-agent-wayfinder-arch__divider" /> | ||
| <text x="150" y="79" textAnchor="middle" className="uc-agent-wayfinder-arch__detail"> | ||
| Publishes RFC 9728 metadata, validates JWTs | ||
| </text> | ||
| </g> | ||
|
|
||
| {/* Edges */} | ||
| <g className="uc-agent-wayfinder-arch__edges"> | ||
| {/* MCP Client → ThunderID: Sign in */} | ||
| <line x1="320" y1="110" x2="560" y2="110" markerEnd="url(#mcp-qs-arrow)" /> | ||
| <text x="440" y="102" textAnchor="middle" className="uc-agent-wayfinder-arch__edge-label"> | ||
| Sign in (Auth Code + PKCE) | ||
| </text> | ||
|
|
||
| {/* ThunderID → MCP Client: Token */} | ||
| <line x1="560" y1="136" x2="320" y2="136" markerEnd="url(#mcp-qs-arrow)" /> | ||
| <text x="440" y="152" textAnchor="middle" className="uc-agent-wayfinder-arch__edge-label"> | ||
| Scoped access token (JWT) | ||
| </text> | ||
|
|
||
| {/* MCP Client → Your MCP Server: Call tools */} | ||
| <line x1="170" y1="164" x2="170" y2="228" markerEnd="url(#mcp-qs-arrow)" /> | ||
| <text x="184" y="200" className="uc-agent-wayfinder-arch__edge-label"> | ||
| Call tools with token | ||
| </text> | ||
|
|
||
| {/* Your MCP Server → ThunderID: Validate */} | ||
| <line x1="320" y1="272" x2="560" y2="272" markerEnd="url(#mcp-qs-arrow)" /> | ||
| <text x="440" y="264" textAnchor="middle" className="uc-agent-wayfinder-arch__edge-label"> | ||
| Validate JWT via JWKS | ||
| </text> | ||
| </g> | ||
| </svg> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Replace the hand-built architecture SVG with Mermaid.
This is an architecture-flow diagram assembled from raw SVG elements. Should this diagram use Mermaid instead of raw SVG?
🤖 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/McpQuickstartFlow.tsx` around lines 42 - 154, Replace the
hand-built SVG inside McpQuickstartFlow with a Mermaid-based architecture flow
diagram, preserving the existing MCP OAuth participants, interactions, labels,
and productName interpolation. Remove the raw SVG groups, shapes, markers, and
text, and use the repository’s established Mermaid rendering approach if
available.
Source: Path instructions
| @@ -0,0 +1,39 @@ | |||
| # /// script | |||
| # requires-python = ">=3.11" | |||
| # dependencies = ["fastmcp>=3.4,<4", "py-key-value-aio[disk]"] | |||
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
FastMCP FileTreeStore constructor signature py-key-value-aio filetree
💡 Result:
The FileTreeStore constructor is provided by the py-key-value-aio library, which powers storage backends for FastMCP [1]. The constructor signature is as follows [2]: init( *, data_directory, metadata_directory=None, default_collection=None, serialization_adapter=None, key_sanitization_strategy=None, collection_sanitization_strategy=None, auto_create=True,) Parameters: - data_directory (Path | str): The required base directory where the store will save data [2]. - metadata_directory (Path | str | None): Optional directory for metadata; defaults to None [2]. - default_collection (str | None): An optional default collection; defaults to None [2]. - serialization_adapter (SerializationAdapter | None): Optional adapter for data serialization; defaults to None [2]. - key_sanitization_strategy (SanitizationStrategy | None): Optional strategy to sanitize keys to prevent filesystem errors (highly recommended for production) [3][2]. - collection_sanitization_strategy (SanitizationStrategy | None): Optional strategy to sanitize collection names [3][2]. - auto_create (bool): Whether to automatically create the data directory; defaults to True [2]. When using FileTreeStore within FastMCP, it is common practice to provide explicit sanitization strategies (such as FileTreeV1KeySanitizationStrategy and FileTreeV1CollectionSanitizationStrategy) to ensure that keys containing special characters—often found in URLs or OAuth client IDs—do not cause FileNotFoundError crashes on the filesystem [3][4][5].
Citations:
- 1: https://github.com/jlowin/fastmcp/blob/main/docs/v2/servers/storage-backends.mdx
- 2: https://strawgate.com/py-key-value/api/stores/
- 3: https://fastmcp.wiki/en/servers/storage-backends
- 4: https://github.com/jlowin/fastmcp/blob/a52ab0e9/docs/servers/storage-backends.mdx
- 5: OAuth proxy: localhost redirect URI port matching + FileTreeStore URL-based client_id path errors PrefectHQ/fastmcp#3574
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repository file =="
wc -l samples/apps/mcp-calculator-sample/client/client.py || true
sed -n '1,80p' samples/apps/mcp-calculator-sample/client/client.py || true
echo "== nearby references to py-key-value-aio and DiskStore/FileTreeStore =="
rg -n "py-key-value-aio|DiskStore|FileTreeStore|token_storage|diskcache|CVE|pickle|storage" samples/apps/mcp-calculator-sample -S || true
echo "== package pin/version declarations mentioning py-key-value-aio or fastmcp =="
rg -n "py-key-value-aio|fastmcp" pyproject.toml poetry.lock uv.lock requirements*.txt samples -S || true
echo "== git status/stat =="
git status --short || true
git diff --stat || true
git diff HEAD samples/apps/mcp-calculator-sample/client/client.py | sed -n '1,180p' || trueRepository: thunder-id/thunderid
Length of output: 2029
🌐 Web query:
CVE-2025-69872 diskcache pickle RCE FastMCP FileTreeStore DiskStore
💡 Result:
CVE-2025-69872 is a security vulnerability in the diskcache library (up to version 5.6.3) involving unsafe deserialization of untrusted data [1][2]. Because diskcache uses Python's pickle module by default to serialize and store data, an attacker with write access to the cache directory can inject a malicious pickle payload [3][4]. When the application performs a standard read operation (e.g., cache.get()), the pickle.load() function triggers, potentially leading to arbitrary code execution (RCE) within the context of the application process [4]. Impact on FastMCP: FastMCP previously utilized diskcache via the py-key-value-aio[disk] dependency for its default OAuth client storage (DiskStore) [5][6]. Due to this vulnerability, FastMCP has taken the following actions [5][7]: 1. Default Storage Change: The default OAuth client storage backend has been migrated from DiskStore to FileTreeStore [5][7]. FileTreeStore uses JSON serialization instead of pickle, eliminating the RCE risk associated with diskcache [5]. 2. Dependency Removal: FastMCP has removed diskcache as a default dependency to resolve security scanner alerts and blockages in security-conscious deployment pipelines [5][6][8]. 3. User Impact: - Users relying on default storage will experience an automatic re-registration of OAuth clients upon their first connection after upgrading [7][9]. - If a project explicitly requires DiskStore, it can still be used by manually installing pip install 'py-key-value-aio[disk]', though this re-introduces the vulnerable diskcache package into the environment [7][10][9]. - When migrating to FileTreeStore, developers are required to configure key and collection sanitization strategies to prevent filesystem errors caused by special characters in keys (e.g., URL-based OAuth client IDs) [7][9].
Citations:
- 1: https://nvd.nist.gov/vuln/detail/CVE-2025-69872
- 2: https://security.snyk.io/vuln/SNYK-PYTHON-DISKCACHE-15268422
- 3: Vulnerability CVE-2025-69872 grantjenks/python-diskcache#357
- 4: https://github.com/EthanKim88/ethan-cve-disclosures/blob/main/CVE-2025-69872-DiskCache-Pickle-Deserialization.md
- 5: Drop diskcache dependency (CVE-2025-69872) PrefectHQ/fastmcp#3185
- 6: Consider making diskcache optional or using safer serialization (CVE-2025-69872) PrefectHQ/fastmcp#3166
- 7: https://gofastmcp.com/getting-started/upgrading/from-fastmcp-2
- 8: Make py-key-value-aio[disk] extra optional — diskcache has unpatched CVE PrefectHQ/fastmcp#3181
- 9: https://github.com/PrefectHQ/fastmcp/blob/main/docs/getting-started/upgrading/from-fastmcp-2.mdx
- 10: https://gofastmcp.com/v3/getting-started/upgrading/from-fastmcp-2
Move this OAuth cache off DiskStore.
This sample installs py-key-value-aio[disk] and passes DiskStore directly to OAuth(...), which pulls in diskcache and re-enables the pickle-deserialization RCE path identified as CVE-2025-69872. FastMCP’s current guidance is to use FileTreeStore instead, which does not require py-key-value-aio[disk].
Also applies to lines 12 and 19-23.
🤖 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 `@samples/apps/mcp-calculator-sample/client/client.py` at line 3, Replace the
OAuth cache’s DiskStore usage with FastMCP’s recommended FileTreeStore, updating
the OAuth construction and related imports in the client sample. Remove the
py-key-value-aio[disk] dependency from the dependencies declaration and retain
only the dependency needed for FileTreeStore.
| # dependencies = ["fastmcp>=3.4,<4", "py-key-value-aio[disk]"] | ||
| # /// | ||
| """OAuth-authenticated client for the Calculator MCP server.""" | ||
|
|
||
| import asyncio | ||
| from pathlib import Path | ||
|
|
||
| from fastmcp import Client | ||
| from fastmcp.client.auth import OAuth | ||
| from key_value.aio.stores.disk import DiskStore | ||
|
|
||
| MCP_URL = "http://localhost:8000/mcp" | ||
| THUNDERID_CA_CERT = "../server/thunderid.cert" | ||
| TOKEN_CACHE_DIR = Path(__file__).parent / ".mcp-oauth-cache" | ||
| CALLBACK_PORT = 52360 | ||
|
|
||
| oauth = OAuth( | ||
| mcp_url=MCP_URL, | ||
| token_storage=DiskStore(directory=TOKEN_CACHE_DIR), | ||
| callback_port=CALLBACK_PORT, | ||
| ) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^samples/apps/mcp-calculator-sample/client/client\.py$|^docs/content/getting-started/connect-your-mcp/build-a-client\.mdx$|pyproject\.toml|requirements|lock)'
echo
echo "== sample client =="
cat -n samples/apps/mcp-calculator-sample/client/client.py
echo
echo "== docs excerpt =="
sed -n '55,135p' docs/content/getting-started/connect-your-mcp/build-a-client.mdx
echo
echo "== related references =="
rg -n "DiskStore|FileTreeStore|py-key-value-aio|CVE-2025-69872|2025-69872|ThunderID|thunderid" samples/docs content docs -S || trueRepository: thunder-id/thunderid
Length of output: 2029
🌐 Web query:
FastMCP OAuth DiskStore CVE-2025-69872 py-key-value-aio disk
💡 Result:
CVE-2025-69872 is a security vulnerability in the python-diskcache library (versions up to 5.6.3) [1][2][3]. The vulnerability stems from the library's default use of Python's pickle serialization [1][2][4]. If an attacker gains write access to the cache directory, they can manipulate the serialized data to achieve arbitrary code execution when the victim application reads from the cache [1][3]. This issue significantly impacted the FastMCP ecosystem because FastMCP previously depended on py-key-value-aio[disk], which in turn pulled in the vulnerable diskcache package [5][6][7]. Key points regarding the impact and resolution: - Vulnerability Details: CVE-2025-69872 is rated with a high CVSS 3.1 severity (base score 9.8/Critical) [1][3]. It remains an issue for users relying on the affected versions of diskcache [6][7]. - FastMCP Resolution: To address this, FastMCP has moved away from DiskStore as the default OAuth client storage mechanism [8]. In newer versions, the default storage has been changed to FileTreeStore [8]. - Migration Guidance: Users upgrading from earlier versions of FastMCP are advised to transition from DiskStore to FileTreeStore [8]. When using FileTreeStore, it is critical to configure key and collection sanitization strategies to prevent filesystem errors [8]. Users who still require DiskStore can continue to use it by explicitly adding the py-key-value-aio[disk] dependency, though this re-introduces the vulnerable diskcache dependency into their environment [8]. - Security Best Practice: FastMCP documentation consistently emphasizes that for production environments, OAuth token storage must be encrypted using mechanisms such as FernetEncryptionWrapper [9][10][11]. Plaintext storage is considered insecure [10][12].
Citations:
- 1: https://nvd.nist.gov/vuln/detail/CVE-2025-69872
- 2: https://osv.dev/vulnerability/CVE-2025-69872
- 3: https://ubuntu.com/security/CVE-2025-69872
- 4: Vulnerability CVE-2025-69872 grantjenks/python-diskcache#357
- 5: Make py-key-value-aio[disk] extra optional — diskcache has unpatched CVE PrefectHQ/fastmcp#3181
- 6: Consider making diskcache optional or using safer serialization (CVE-2025-69872) PrefectHQ/fastmcp#3166
- 7: chore: Check for diskcache vulnerability fix (GHSA-w8v5-vhqr-4h9v) prebid/salesagent#1036
- 8: https://gofastmcp.com/getting-started/upgrading/from-fastmcp-2
- 9: https://fastmcp.wiki/en/v2/clients/auth/oauth
- 10: https://gofastmcp.com/v2/servers/storage-backends
- 11: https://gofastmcp.com/clients/auth/oauth
- 12: https://fastmcp.wiki/en/v2/servers/storage-backends
Replace DiskStore with the safer default OAuth storage in the sample and docs. The sample still explicitly uses DiskStore via py-key-value-aio[disk], which reintroduces diskcache’s known pickle RCE risk (CVE-2025-69872). Switch both samples/apps/mcp-calculator-sample/client/client.py and the build-a-client.mdx code/bullets to FileTreeStore with key and collection sanitization, and remove the [disk] extra.
📍 Affects 2 files
samples/apps/mcp-calculator-sample/client/client.py#L3-L23(this comment)docs/content/getting-started/connect-your-mcp/build-a-client.mdx#L67-L117
🤖 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 `@samples/apps/mcp-calculator-sample/client/client.py` around lines 3 - 23,
Replace DiskStore with FileTreeStore in
samples/apps/mcp-calculator-sample/client/client.py, remove the
py-key-value-aio[disk] extra, and configure key and collection sanitization.
Update docs/content/getting-started/connect-your-mcp/build-a-client.mdx lines
67-117 to use FileTreeStore with the same sanitization settings and remove the
[disk] extra from its dependencies and bullets.
Add two new docs pages and a runnable sample: - connect-a-client.mdx: connect MCP Inspector and Claude Code to the Calculator server - build-a-client.mdx: build a Python FastMCP OAuth client from scratch - samples/apps/mcp-calculator-sample/: server + client code matching the guides - python.mdx: add sample code link and What's Next card, fix sign-in wording Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b608ceb to
7b1c1da
Compare
# Conflicts: # docs/sidebars.ts # docs/src/components/DeveloperShortcut.tsx
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
docs/content/getting-started/connect-your-mcp/server/python.mdx (1)
312-343: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftExtract the "What's Next" card grid into a shared MDX component.
All four new quickstart pages hand-roll the same raw
<div>/<a>card-grid markup with inlinestyle={{...}}props (grid layout, borders, padding, link colors), differing only in title/description/href text. This is significant copy-pasted markup that will drift as pages are added or restyled.
docs/content/getting-started/connect-your-mcp/server/python.mdx#L312-L343: replace this grid with a shared<NextSteps>/<NextStepCard title=... description=... href=...>component (or similar) instead of inline-styled<div>/<a>.docs/content/getting-started/connect-your-mcp/client/build-a-client/python.mdx#L151-L173: use the same shared component here.docs/content/getting-started/connect-your-mcp/client/connect/mcp-inspector.mdx#L83-L105: use the same shared component here.docs/content/getting-started/connect-your-mcp/client/connect/claude-code.mdx#L136-L151: use the same shared component here.🤖 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/server/python.mdx` around lines 312 - 343, Extract the repeated “What’s Next” card-grid markup into a shared NextSteps/NextStepCard MDX component, preserving each card’s existing title, description, href, and ProductName content. Replace the inline-styled grids in docs/content/getting-started/connect-your-mcp/server/python.mdx lines 312-343, docs/content/getting-started/connect-your-mcp/client/build-a-client/python.mdx lines 151-173, docs/content/getting-started/connect-your-mcp/client/connect/mcp-inspector.mdx lines 83-105, and docs/content/getting-started/connect-your-mcp/client/connect/claude-code.mdx lines 136-151 with that shared component, and ensure the shared component owns the common layout and styling.
🤖 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/client/connect/mcp-inspector.mdx`:
- Around line 59-61: Update the “Running this from a clone of the repository?”
note in the MCP Inspector documentation to remove the incorrect claim that the
root package.json pins pnpm and causes EBADDEVENGINES. Replace it with the
verified actual cause and accurate workaround, or remove the note entirely if no
applicable repository-clone issue exists.
- Line 84: Update the link in the MCP Inspector page’s Claude Code anchor from
`./claude-code` to `../claude-code` so it resolves to the sibling page in the
parent connect directory.
In `@docs/content/getting-started/connect-your-mcp/server/python.mdx`:
- Around line 45-50: Trim the “How It Works” section in the Python MCP
quickstart by removing the inline OAuth 2.0, PKCE, JWT, and JWKS flow
explanation, then replace it with a brief pointer to the existing “Securing MCP”
concepts page referenced in the “What’s Next” section. Keep the
<McpOAuthFlowDiagram /> only if it remains useful without the removed
explanation.
---
Nitpick comments:
In `@docs/content/getting-started/connect-your-mcp/server/python.mdx`:
- Around line 312-343: Extract the repeated “What’s Next” card-grid markup into
a shared NextSteps/NextStepCard MDX component, preserving each card’s existing
title, description, href, and ProductName content. Replace the inline-styled
grids in docs/content/getting-started/connect-your-mcp/server/python.mdx lines
312-343,
docs/content/getting-started/connect-your-mcp/client/build-a-client/python.mdx
lines 151-173,
docs/content/getting-started/connect-your-mcp/client/connect/mcp-inspector.mdx
lines 83-105, and
docs/content/getting-started/connect-your-mcp/client/connect/claude-code.mdx
lines 136-151 with that shared component, and ensure the shared component owns
the common layout and styling.
🪄 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: fcec1108-7279-4f52-9d00-f15bdfa77ae8
📒 Files selected for processing (10)
docs/content/getting-started/connect-your-mcp/client/_category_.jsondocs/content/getting-started/connect-your-mcp/client/build-a-client/_category_.jsondocs/content/getting-started/connect-your-mcp/client/build-a-client/python.mdxdocs/content/getting-started/connect-your-mcp/client/connect/_category_.jsondocs/content/getting-started/connect-your-mcp/client/connect/claude-code.mdxdocs/content/getting-started/connect-your-mcp/client/connect/mcp-inspector.mdxdocs/content/getting-started/connect-your-mcp/server/_category_.jsondocs/content/getting-started/connect-your-mcp/server/python.mdxdocs/sidebars.tsdocs/src/components/DeveloperShortcut.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/src/components/DeveloperShortcut.tsx
| :::note Running this from a clone of the repository? | ||
| If you launch this command from inside a clone of the <ProductName /> repository (for example, from `samples/apps/mcp-calculator-sample/server/`), it fails with `npm error code EBADDEVENGINES`, because npm walks up to the repository's root `package.json`, which pins `pnpm` as the package manager. Launch Inspector from a directory outside the repository instead, and point `NODE_EXTRA_CA_CERTS` at the absolute path of `thunderid.cert`. | ||
| ::: |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the repo root package.json pins pnpm as the package manager.
cat package.json | jq '.packageManager, .engines'Repository: thunder-id/thunderid
Length of output: 168
Fix the EBADDEVENGINES troubleshooting claim
The repository root package.json does not declare packageManager or engines, so npm walking upward to it would not produce the claimed npm error code EBADDEVENGINES based on a pinned pnpm. Update this note to describe the actual cause, or remove it if this does not apply.
🤖 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/client/connect/mcp-inspector.mdx`
around lines 59 - 61, Update the “Running this from a clone of the repository?”
note in the MCP Inspector documentation to remove the incorrect claim that the
root package.json pins pnpm and causes EBADDEVENGINES. Replace it with the
verified actual cause and accurate workaround, or remove the note entirely if no
applicable repository-clone issue exists.
| ## What's Next | ||
|
|
||
| <div style={{display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: '1rem', marginTop: '1.5rem'}}> | ||
| <a href="./claude-code" style={{textDecoration: 'none', color: 'inherit'}}> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Broken sibling link: ./claude-code should be ../claude-code.
claude-code.mdx is a sibling page in the same connect/ directory. Using ./claude-code resolves to /client/connect/mcp-inspector/claude-code/, which doesn't exist, instead of /client/connect/claude-code/.
Based on learnings: "when linking to a sibling doc within the same directory, use ../sibling-slug (not ./sibling-slug)... Using ./sibling-slug would resolve incorrectly in this URL structure."
🔗 Proposed fix
- <a href="./claude-code" style={{textDecoration: 'none', color: 'inherit'}}>
+ <a href="../claude-code" style={{textDecoration: 'none', color: 'inherit'}}>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a href="./claude-code" style={{textDecoration: 'none', color: 'inherit'}}> | |
| <a href="../claude-code" style={{textDecoration: 'none', color: 'inherit'}}> |
🤖 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/client/connect/mcp-inspector.mdx`
at line 84, Update the link in the MCP Inspector page’s Claude Code anchor from
`./claude-code` to `../claude-code` so it resolves to the sibling page in the
parent connect directory.
Source: Learnings
| ## How It Works | ||
|
|
||
| Your MCP server becomes an OAuth 2.0 resource server. When a client connects without a token, the server responds with a `401` and RFC 9728 metadata that points the client to <ProductName /> for authentication. The client runs an Authorization Code + PKCE flow, gets a JWT scoped to the user's role-based permissions, and presents it on reconnect. The server validates the token offline against <ProductName />'s JWKS and filters visible tools by the token's scopes. | ||
|
|
||
| <McpOAuthFlowDiagram /> | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Trim "How It Works" and link to the concepts page instead of explaining OAuth 2.0/PKCE/JWKS inline.
This paragraph defines the OAuth 2.0 resource-server flow, PKCE, and offline JWKS validation from scratch mid-quickstart. The page already links to a dedicated "Securing MCP" concepts page in What's Next (line 329-335) — the concept belongs there, and this section should point to it rather than re-explain it.
As per path instructions: "Flag any page that introduces a concept that belongs on a separate concepts page, mid-way through a quickstart or guide. Quickstarts and guides should link to concepts pages, not define terms inline beyond one sentence."
🤖 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/server/python.mdx` around lines
45 - 50, Trim the “How It Works” section in the Python MCP quickstart by
removing the inline OAuth 2.0, PKCE, JWT, and JWKS flow explanation, then
replace it with a brief pointer to the existing “Securing MCP” concepts page
referenced in the “What’s Next” section. Keep the <McpOAuthFlowDiagram /> only
if it remains useful without the removed explanation.
Source: Path instructions
Summary
samples/apps/mcp-calculator-sample/): runnable server and client code matching the guides, with PEP 723 inline metadata for zero-installuv run.python.mdx): add sample code link, What's Next card, fix sign-in wording and Inspector UI labels.All doc claims were verified against a live ThunderID + Calculator MCP server stack.
make build_docspasses. Vale is clean.Test plan
make build_docspasses with no new broken-anchor warningsuv run client.pycompletes the OAuth flow and prints tool results🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation