Skip to content

Commit be8d725

Browse files
authored
Merge pull request #272 from modelcontextprotocol/uv-qr-server
feat(qr-server): migrate to uv with PEP 723 inline dependencies
2 parents 0936d52 + 489d28b commit be8d725

13 files changed

Lines changed: 62 additions & 30 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ jobs:
7373
with:
7474
node-version: "20"
7575

76+
- uses: astral-sh/setup-uv@v5
77+
7678
- run: npm ci
7779

7880
- name: Install Playwright browsers

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ Or edit your `package.json` manually:
6767
| [**Scenario Modeler**](examples/scenario-modeler-server) | [**Budget Allocator**](examples/budget-allocator-server) | [**Customer Segmentation**](examples/customer-segmentation-server) |
6868
| [![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) |
6969
| [**System Monitor**](examples/system-monitor-server) | [**Transcript**](examples/transcript-server) | [**Video Resource**](examples/video-resource-server) |
70-
| [![PDF Server](examples/pdf-server/grid-cell.png "Interactive PDF viewer with chunked loading")](examples/pdf-server) | | |
71-
| [**PDF Server**](examples/pdf-server) | | |
70+
| [![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) | |
71+
| [**PDF Server**](examples/pdf-server) | [**QR Code (Python)**](examples/qr-server) | |
7272

7373
### Starter Templates
7474

examples/qr-server/README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,28 @@ A minimal Python MCP server that generates customizable QR codes with an interac
1111
- Interactive widget that displays in MCP-UI enabled clients
1212
- Supports both HTTP (for web clients) and stdio (for Claude Desktop)
1313

14-
## Quick Start
14+
## Prerequisites
15+
16+
This server uses [uv](https://docs.astral.sh/uv/) for dependency management. Install it first:
1517

1618
```bash
17-
# Create virtual environment
18-
python3 -m venv .venv
19-
source .venv/bin/activate
19+
curl -LsSf https://astral.sh/uv/install.sh | sh
20+
```
2021

21-
# Install dependencies
22-
pip install -r requirements.txt
22+
## Quick Start
2323

24-
# Run server (HTTP mode)
25-
python server.py
26-
# → QR Server listening on http://localhost:3108/mcp
24+
```bash
25+
# Run server (HTTP mode) - uv handles dependencies automatically
26+
uv run server.py
27+
# → QR Code Server listening on http://localhost:3108/mcp
2728
```
2829

2930
## Usage
3031

3132
### HTTP Mode (for basic-host / web clients)
3233

3334
```bash
34-
python server.py
35+
uv run server.py
3536
```
3637

3738
Connect from basic-host:
@@ -43,7 +44,7 @@ SERVERS='["http://localhost:3108/mcp"]' bun serve.ts
4344
### Stdio Mode (for Claude Desktop)
4445

4546
```bash
46-
python server.py --stdio
47+
uv run server.py --stdio
4748
```
4849

4950
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
5253
{
5354
"mcpServers": {
5455
"qr": {
55-
"command": "/path/to/qr-server/.venv/bin/python",
56-
"args": ["/path/to/qr-server/server.py", "--stdio"]
56+
"command": "uv",
57+
"args": ["run", "/path/to/qr-server/server.py", "--stdio"]
5758
}
5859
}
5960
}
@@ -135,9 +136,8 @@ Generate a QR code with optional customization.
135136

136137
```
137138
qr-server/
138-
├── server.py # MCP server (FastMCP + uvicorn)
139+
├── server.py # MCP server (FastMCP + uvicorn, deps inline via PEP 723)
139140
├── widget.html # Interactive UI widget
140-
├── requirements.txt
141141
└── README.md
142142
```
143143

@@ -153,10 +153,12 @@ The widget uses MCP Apps SDK protocol:
153153

154154
## Dependencies
155155

156-
- `mcp[cli]` - MCP Python SDK with FastMCP
156+
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/):
157+
158+
- `mcp` - MCP Python SDK with FastMCP
157159
- `qrcode[pil]` - QR code generation with Pillow
158-
- `uvicorn` - ASGI server (included with mcp)
159-
- `starlette` - CORS middleware (included with mcp)
160+
- `uvicorn` - ASGI server
161+
- `starlette` - CORS middleware
160162

161163
## License
162164

examples/qr-server/grid-cell.png

39.3 KB
Loading

examples/qr-server/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "@modelcontextprotocol/server-qr",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"start": "uv run server.py",
7+
"dev": "uv run server.py",
8+
"build": "echo 'No build step needed for Python server'"
9+
}
10+
}

examples/qr-server/requirements.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

examples/qr-server/server.py

100644100755
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
#!/usr/bin/env uv run
2+
# /// script
3+
# requires-python = ">=3.10"
4+
# dependencies = [
5+
# "mcp>=1.9.0",
6+
# "qrcode[pil]>=8.0",
7+
# "uvicorn>=0.34.0",
8+
# "starlette>=0.46.0",
9+
# ]
10+
# ///
111
"""
212
QR Code MCP Server - Generates QR codes from text
313
"""
@@ -17,12 +27,12 @@
1727
HOST = os.environ.get("HOST", "0.0.0.0") # 0.0.0.0 for Docker compatibility
1828
PORT = int(os.environ.get("PORT", "3108"))
1929

20-
mcp = FastMCP("QR Server", port=PORT, stateless_http=True)
30+
mcp = FastMCP("QR Code Server", port=PORT, stateless_http=True)
2131

2232

2333
@mcp.tool(meta={"ui/resourceUri": WIDGET_URI})
2434
def generate_qr(
25-
text: str,
35+
text: str = "https://modelcontextprotocol.io",
2636
box_size: int = 10,
2737
border: int = 4,
2838
error_correction: str = "M",
@@ -122,5 +132,5 @@ async def _read_resource_with_meta(req: types.ReadResourceRequest):
122132
allow_methods=["*"],
123133
allow_headers=["*"],
124134
)
125-
print(f"QR Server listening on http://{HOST}:{PORT}/mcp")
135+
print(f"QR Code Server listening on http://{HOST}:{PORT}/mcp")
126136
uvicorn.run(app, host=HOST, port=PORT)

examples/qr-server/widget.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<body>
2828
<div id="qr"></div>
2929
<script type="module">
30-
import { App } from "https://unpkg.com/@modelcontextprotocol/ext-apps@0.3.1/app-with-deps";
30+
import { App } from "https://unpkg.com/@modelcontextprotocol/ext-apps@0.4.0/app-with-deps";
3131

3232
const app = new App({ name: "QR Widget", version: "1.0.0" });
3333

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
"test:e2e": "playwright test",
5555
"test:e2e:update": "playwright test --update-snapshots",
5656
"test:e2e:ui": "playwright test --ui",
57-
"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'",
58-
"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'",
57+
"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'",
58+
"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'",
5959
"preexamples:build": "npm run build",
6060
"examples:build": "bun examples/run-all.ts build",
6161
"examples:start": "NODE_ENV=development npm run build && bun examples/run-all.ts start",
@@ -64,7 +64,7 @@
6464
"prepare": "node scripts/setup-bun.mjs && npm run build && husky",
6565
"docs": "typedoc",
6666
"docs:watch": "typedoc --watch",
67-
"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'",
67+
"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'",
6868
"prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check",
6969
"prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write",
7070
"check:versions": "node scripts/check-versions.mjs"

0 commit comments

Comments
 (0)