Merge pull request #272 from modelcontextprotocol/uv-qr-server

feat(qr-server): migrate to uv with PEP 723 inline dependencies
This commit is contained in:
Anton Pidkuiko 2026-01-15 14:29:33 +00:00 • committed by GitHub
commit be8d725974
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 62 additions and 30 deletions

View file

@ -73,6 +73,8 @@ jobs:
with: with:
node-version: "20" node-version: "20"
- uses: astral-sh/setup-uv@v5
- run: npm ci - run: npm ci
- name: Install Playwright browsers - name: Install Playwright browsers

View file

@ -67,8 +67,8 @@ Or edit your `package.json` manually:
| [**Scenario Modeler**](examples/scenario-modeler-server) | [**Budget Allocator**](examples/budget-allocator-server) | [**Customer Segmentation**](examples/customer-segmentation-server) | | [**Scenario Modeler**](examples/scenario-modeler-server) | [**Budget Allocator**](examples/budget-allocator-server) | [**Customer Segmentation**](examples/customer-segmentation-server) |
| [![System Monitor](examples/system-monitor-server/grid-cell.png "Real-time OS metrics")](examples/system-monitor-server) | [![Transcript](examples/transcript-server/grid-cell.png "Live speech transcription")](examples/transcript-server) | [![Video Resource](examples/video-resource-server/grid-cell.png "Binary video via MCP resources")](examples/video-resource-server) | | [![System Monitor](examples/system-monitor-server/grid-cell.png "Real-time OS metrics")](examples/system-monitor-server) | [![Transcript](examples/transcript-server/grid-cell.png "Live speech transcription")](examples/transcript-server) | [![Video Resource](examples/video-resource-server/grid-cell.png "Binary video via MCP resources")](examples/video-resource-server) |
| [**System Monitor**](examples/system-monitor-server) | [**Transcript**](examples/transcript-server) | [**Video Resource**](examples/video-resource-server) | | [**System Monitor**](examples/system-monitor-server) | [**Transcript**](examples/transcript-server) | [**Video Resource**](examples/video-resource-server) |
| [![PDF Server](examples/pdf-server/grid-cell.png "Interactive PDF viewer with chunked loading")](examples/pdf-server) | | | | [![PDF Server](examples/pdf-server/grid-cell.png "Interactive PDF viewer with chunked loading")](examples/pdf-server) | [![QR Code](examples/qr-server/grid-cell.png "QR code generator")](examples/qr-server) | |
| [**PDF Server**](examples/pdf-server) | | | | [**PDF Server**](examples/pdf-server) | [**QR Code (Python)**](examples/qr-server) | |
### Starter Templates ### Starter Templates

View file

@ -11,19 +11,20 @@ A minimal Python MCP server that generates customizable QR codes with an interac
- Interactive widget that displays in MCP-UI enabled clients - Interactive widget that displays in MCP-UI enabled clients
- Supports both HTTP (for web clients) and stdio (for Claude Desktop) - Supports both HTTP (for web clients) and stdio (for Claude Desktop)
## Prerequisites
This server uses [uv](https://docs.astral.sh/uv/) for dependency management. Install it first:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
## Quick Start ## Quick Start
```bash ```bash
# Create virtual environment # Run server (HTTP mode) - uv handles dependencies automatically
python3 -m venv .venv uv run server.py
source .venv/bin/activate # → QR Code Server listening on http://localhost:3108/mcp
# Install dependencies
pip install -r requirements.txt
# Run server (HTTP mode)
python server.py
# → QR Server listening on http://localhost:3108/mcp
``` ```
## Usage ## Usage
@ -31,7 +32,7 @@ python server.py
### HTTP Mode (for basic-host / web clients) ### HTTP Mode (for basic-host / web clients)
```bash ```bash
python server.py uv run server.py
``` ```
Connect from basic-host: Connect from basic-host:
@ -43,7 +44,7 @@ SERVERS='["http://localhost:3108/mcp"]' bun serve.ts
### Stdio Mode (for Claude Desktop) ### Stdio Mode (for Claude Desktop)
```bash ```bash
python server.py --stdio uv run server.py --stdio
``` ```
Add to Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`): Add to Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
@ -52,8 +53,8 @@ Add to Claude Desktop config (`~/Library/Application Support/Claude/claude_deskt
{ {
"mcpServers": { "mcpServers": {
"qr": { "qr": {
"command": "/path/to/qr-server/.venv/bin/python", "command": "uv",
"args": ["/path/to/qr-server/server.py", "--stdio"] "args": ["run", "/path/to/qr-server/server.py", "--stdio"]
} }
} }
} }
@ -135,9 +136,8 @@ Generate a QR code with optional customization.
``` ```
qr-server/ qr-server/
├── server.py # MCP server (FastMCP + uvicorn) ├── server.py # MCP server (FastMCP + uvicorn, deps inline via PEP 723)
├── widget.html # Interactive UI widget ├── widget.html # Interactive UI widget
├── requirements.txt
└── README.md └── README.md
``` ```
@ -153,10 +153,12 @@ The widget uses MCP Apps SDK protocol:
## Dependencies ## Dependencies
- `mcp[cli]` - MCP Python SDK with FastMCP Dependencies are declared inline in `server.py` using [PEP 723](https://peps.python.org/pep-0723/) and managed by [uv](https://docs.astral.sh/uv/):
- `mcp` - MCP Python SDK with FastMCP
- `qrcode[pil]` - QR code generation with Pillow - `qrcode[pil]` - QR code generation with Pillow
- `uvicorn` - ASGI server (included with mcp) - `uvicorn` - ASGI server
- `starlette` - CORS middleware (included with mcp) - `starlette` - CORS middleware
## License ## License

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -0,0 +1,10 @@
{
"name": "@modelcontextprotocol/server-qr",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "uv run server.py",
"dev": "uv run server.py",
"build": "echo 'No build step needed for Python server'"
}
}

View file

@ -1,2 +0,0 @@
mcp[cli]>=1.23.3
qrcode[pil]>=7.4

16
examples/qr-server/server.py Normal file → Executable file
View file

@ -1,3 +1,13 @@
#!/usr/bin/env uv run
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "mcp>=1.9.0",
# "qrcode[pil]>=8.0",
# "uvicorn>=0.34.0",
# "starlette>=0.46.0",
# ]
# ///
""" """
QR Code MCP Server - Generates QR codes from text QR Code MCP Server - Generates QR codes from text
""" """
@ -17,12 +27,12 @@ WIDGET_URI = "ui://qr-server/widget.html"
HOST = os.environ.get("HOST", "0.0.0.0") # 0.0.0.0 for Docker compatibility HOST = os.environ.get("HOST", "0.0.0.0") # 0.0.0.0 for Docker compatibility
PORT = int(os.environ.get("PORT", "3108")) PORT = int(os.environ.get("PORT", "3108"))
mcp = FastMCP("QR Server", port=PORT, stateless_http=True) mcp = FastMCP("QR Code Server", port=PORT, stateless_http=True)
@mcp.tool(meta={"ui/resourceUri": WIDGET_URI}) @mcp.tool(meta={"ui/resourceUri": WIDGET_URI})
def generate_qr( def generate_qr(
text: str, text: str = "https://modelcontextprotocol.io",
box_size: int = 10, box_size: int = 10,
border: int = 4, border: int = 4,
error_correction: str = "M", error_correction: str = "M",
@ -122,5 +132,5 @@ if __name__ == "__main__":
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
) )
print(f"QR Server listening on http://{HOST}:{PORT}/mcp") print(f"QR Code Server listening on http://{HOST}:{PORT}/mcp")
uvicorn.run(app, host=HOST, port=PORT) uvicorn.run(app, host=HOST, port=PORT)

View file

@ -27,7 +27,7 @@
<body> <body>
<div id="qr"></div> <div id="qr"></div>
<script type="module"> <script type="module">
import { App } from "https://unpkg.com/@modelcontextprotocol/ext-apps@0.3.1/app-with-deps"; import { App } from "https://unpkg.com/@modelcontextprotocol/ext-apps@0.4.0/app-with-deps";
const app = new App({ name: "QR Widget", version: "1.0.0" }); const app = new App({ name: "QR Widget", version: "1.0.0" });

8
package-lock.json generated
View file

@ -587,6 +587,10 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"examples/qr-server": {
"name": "@modelcontextprotocol/server-qr",
"version": "1.0.0"
},
"examples/scenario-modeler-server": { "examples/scenario-modeler-server": {
"name": "@modelcontextprotocol/server-scenario-modeler", "name": "@modelcontextprotocol/server-scenario-modeler",
"version": "0.4.0", "version": "0.4.0",
@ -2428,6 +2432,10 @@
"resolved": "examples/pdf-server", "resolved": "examples/pdf-server",
"link": true "link": true
}, },
"node_modules/@modelcontextprotocol/server-qr": {
"resolved": "examples/qr-server",
"link": true
},
"node_modules/@modelcontextprotocol/server-scenario-modeler": { "node_modules/@modelcontextprotocol/server-scenario-modeler": {
"resolved": "examples/scenario-modeler-server", "resolved": "examples/scenario-modeler-server",
"link": true "link": true

View file

@ -54,8 +54,8 @@
"test:e2e": "playwright test", "test:e2e": "playwright test",
"test:e2e:update": "playwright test --update-snapshots", "test:e2e:update": "playwright test --update-snapshots",
"test:e2e:ui": "playwright test --ui", "test:e2e:ui": "playwright test --ui",
"test:e2e:docker": "docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test'", "test:e2e:docker": "docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'apt-get update -qq && apt-get install -qq -y python3-venv curl > /dev/null && curl -LsSf https://astral.sh/uv/install.sh | sh && export PATH=\"$HOME/.local/bin:$PATH\" && npm i -g bun && npm ci && npx playwright test'",
"test:e2e:docker:update": "npm run build:all && docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test --update-snapshots'", "test:e2e:docker:update": "npm run build:all && docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'apt-get update -qq && apt-get install -qq -y python3-venv curl > /dev/null && curl -LsSf https://astral.sh/uv/install.sh | sh && export PATH=\"$HOME/.local/bin:$PATH\" && npm i -g bun && npm ci && npx playwright test --update-snapshots'",
"preexamples:build": "npm run build", "preexamples:build": "npm run build",
"examples:build": "bun examples/run-all.ts build", "examples:build": "bun examples/run-all.ts build",
"examples:start": "NODE_ENV=development npm run build && bun examples/run-all.ts start", "examples:start": "NODE_ENV=development npm run build && bun examples/run-all.ts start",
@ -64,7 +64,7 @@
"prepare": "node scripts/setup-bun.mjs && npm run build && husky", "prepare": "node scripts/setup-bun.mjs && npm run build && husky",
"docs": "typedoc", "docs": "typedoc",
"docs:watch": "typedoc --watch", "docs:watch": "typedoc --watch",
"generate:screenshots": "npm run build:all && docker run --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test tests/e2e/generate-grid-screenshots.spec.ts'", "generate:screenshots": "npm run build:all && docker run --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'apt-get update -qq && apt-get install -qq -y python3-venv curl > /dev/null && curl -LsSf https://astral.sh/uv/install.sh | sh && export PATH=\"$HOME/.local/bin:$PATH\" && npm i -g bun && npm ci && npx playwright test tests/e2e/generate-grid-screenshots.spec.ts'",
"prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check", "prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check",
"prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write", "prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write",
"check:versions": "node scripts/check-versions.mjs" "check:versions": "node scripts/check-versions.mjs"

View file

@ -32,7 +32,8 @@ export default defineConfig({
url: "http://localhost:8080", url: "http://localhost:8080",
// Always start fresh servers to avoid stale state issues // Always start fresh servers to avoid stale state issues
reuseExistingServer: false, reuseExistingServer: false,
timeout: 120000, // 3 minutes to allow uv to download Python dependencies on first run
timeout: 180000,
}, },
// Snapshot configuration // Snapshot configuration
expect: { expect: {

View file

@ -49,6 +49,7 @@ const SERVERS = [
{ key: "customer-segmentation", name: "Customer Segmentation Server" }, { key: "customer-segmentation", name: "Customer Segmentation Server" },
{ key: "map-server", name: "Map Server" }, { key: "map-server", name: "Map Server" },
{ key: "pdf-server", name: "PDF Server" }, { key: "pdf-server", name: "PDF Server" },
{ key: "qr-server", name: "QR Code Server" },
{ key: "scenario-modeler", name: "SaaS Scenario Modeler" }, { key: "scenario-modeler", name: "SaaS Scenario Modeler" },
{ key: "shadertoy", name: "ShaderToy Server" }, { key: "shadertoy", name: "ShaderToy Server" },
{ key: "sheet-music", name: "Sheet Music Server" }, { key: "sheet-music", name: "Sheet Music Server" },

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB