Skip to content

Commit 6e4c8d4

Browse files
Merge remote-tracking branch 'origin/main' into jerome/feature/add-client-sampling
2 parents 8f0f7c5 + 1066199 commit 6e4c8d4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2951
-296
lines changed

.git-blame-ignore-revs

Whitespace-only changes.

.github/workflows/check-lock.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Check uv.lock
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "pyproject.toml"
7+
- "uv.lock"
8+
push:
9+
paths:
10+
- "pyproject.toml"
11+
- "uv.lock"
12+
13+
jobs:
14+
check-lock:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
run: |
21+
curl -LsSf https://astral.sh/uv/install.sh | sh
22+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
23+
24+
- name: Check uv.lock is up to date
25+
run: uv lock --check

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
scratch/
23

34
# Byte-compiled / optimized / DLL files
45
__pycache__/
@@ -162,3 +163,6 @@ cython_debug/
162163
# and can be added to the global gitignore or merged into this file. For a more nuclear
163164
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
164165
#.idea/
166+
167+
# vscode
168+
.vscode/

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,12 @@ repos:
1313
- id: ruff-format
1414
- id: ruff
1515
args: [--fix, --exit-non-zero-on-fix]
16+
17+
- repo: local
18+
hooks:
19+
- id: uv-lock-check
20+
name: Check uv.lock is up to date
21+
entry: uv lock --check
22+
language: system
23+
files: ^(pyproject\.toml|uv\.lock)$
24+
pass_filenames: false

CLAUDE.md

Lines changed: 102 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,114 @@
1-
# Tool Usage Learnings
1+
# Development Guidelines
22

3-
This file is intended to be used by an LLM such as Claude.
3+
This document contains critical information about working with this codebase. Follow these guidelines precisely.
44

5-
## UV Package Manager
5+
## Core Development Rules
66

7-
- Use `uv run` to run Python tools without activating virtual environments
8-
- For formatting: `uv run ruff format .`
9-
- For type checking: `uv run pyright`
10-
- For upgrading packages:
11-
- `uv add --dev package --upgrade-package package` to upgrade a specific package
12-
- Don't use `@latest` syntax - it doesn't work
13-
- Be careful with `uv pip install` as it may downgrade packages
7+
1. Package Management
8+
- ONLY use uv, NEVER pip
9+
- Installation: `uv add package`
10+
- Running tools: `uv run tool`
11+
- Upgrading: `uv add --dev package --upgrade-package package`
12+
- FORBIDDEN: `uv pip install`, `@latest` syntax
1413

15-
## Git and GitHub CLI
14+
2. Code Quality
15+
- Type hints required for all code
16+
- Public APIs must have docstrings
17+
- Functions must be focused and small
18+
- Follow existing patterns exactly
19+
- Line length: 88 chars maximum
1620

17-
- When using gh CLI for PRs, always quote title and body:
21+
3. Testing Requirements
22+
- Framework: `uv run pytest`
23+
- Async testing: use anyio, not asyncio
24+
- Coverage: test edge cases and errors
25+
- New features require tests
26+
- Bug fixes require regression tests
27+
28+
- For commits fixing bugs or adding features based on user reports add:
1829
```bash
19-
gh pr create --title "\"my title\"" --body "\"my body\""
30+
git commit --trailer "Reported-by:<name>"
2031
```
21-
- For git commits, use double quotes and escape inner quotes:
32+
Where `<name>` is the name of the user.
33+
34+
- For commits related to a Github issue, add
2235
```bash
23-
git commit -am "\"fix: my commit message\""
36+
git commit --trailer "Github-Issue:#<number>"
2437
```
38+
- NEVER ever mention a `co-authored-by` or similar aspects. In particular, never
39+
mention the tool used to create the commit message or PR.
40+
41+
## Pull Requests
42+
43+
- Create a detailed message of what changed. Focus on the high level description of
44+
the problem it tries to solve, and how it is solved. Don't go into the specifics of the
45+
code unless it adds clarity.
46+
47+
- Always add `jerome3o-anthropic` and `jspahrsummers` as reviewer.
48+
49+
- NEVER ever mention a `co-authored-by` or similar aspects. In particular, never
50+
mention the tool used to create the commit message or PR.
2551

2652
## Python Tools
2753

28-
### Ruff
29-
- Handles both formatting and linting
30-
- For formatting: `uv run ruff format .`
31-
- For checking: `uv run ruff check .`
32-
- For auto-fixing: `uv run ruff check . --fix`
33-
- Common issues:
34-
- Line length (default 88 chars)
35-
- Import sorting (I001 errors)
36-
- Unused imports
37-
- When line length errors occur:
38-
- For strings, use parentheses and line continuation
39-
- For function calls, use multiple lines with proper indentation
40-
- For imports, split into multiple lines
41-
42-
### Pyright
43-
- Type checker
44-
- Run with: `uv run pyright`
45-
- Version warnings can be ignored if type checking passes
46-
- Common issues:
47-
- Optional types need explicit None checks
48-
- String operations need type narrowing
49-
50-
## Pre-commit Hooks
51-
52-
- Configuration in `.pre-commit-config.yaml`
53-
- Runs automatically on git commit
54-
- Includes:
55-
- Prettier for YAML/JSON formatting
56-
- Ruff for Python formatting and linting
57-
- When updating ruff version:
58-
- Check available versions on PyPI
59-
- Update `rev` in config to match available version
60-
- Add and commit config changes before other changes
61-
62-
## Best Practices
63-
64-
1. Always check git status and diff before committing
65-
2. Run formatters before type checkers
66-
3. When fixing CI:
67-
- Start with formatting issues
68-
- Then fix type errors
69-
- Then address any remaining linting issues
70-
4. For type errors:
71-
- Get full context around error lines
72-
- Consider optional types
73-
- Add type narrowing checks when needed
54+
## Code Formatting
55+
56+
1. Ruff
57+
- Format: `uv run ruff format .`
58+
- Check: `uv run ruff check .`
59+
- Fix: `uv run ruff check . --fix`
60+
- Critical issues:
61+
- Line length (88 chars)
62+
- Import sorting (I001)
63+
- Unused imports
64+
- Line wrapping:
65+
- Strings: use parentheses
66+
- Function calls: multi-line with proper indent
67+
- Imports: split into multiple lines
68+
69+
2. Type Checking
70+
- Tool: `uv run pyright`
71+
- Requirements:
72+
- Explicit None checks for Optional
73+
- Type narrowing for strings
74+
- Version warnings can be ignored if checks pass
75+
76+
3. Pre-commit
77+
- Config: `.pre-commit-config.yaml`
78+
- Runs: on git commit
79+
- Tools: Prettier (YAML/JSON), Ruff (Python)
80+
- Ruff updates:
81+
- Check PyPI versions
82+
- Update config rev
83+
- Commit config first
84+
85+
## Error Resolution
86+
87+
1. CI Failures
88+
- Fix order:
89+
1. Formatting
90+
2. Type errors
91+
3. Linting
92+
- Type errors:
93+
- Get full line context
94+
- Check Optional types
95+
- Add type narrowing
96+
- Verify function signatures
97+
98+
2. Common Issues
99+
- Line length:
100+
- Break strings with parentheses
101+
- Multi-line function calls
102+
- Split imports
103+
- Types:
104+
- Add None checks
105+
- Narrow string types
106+
- Match existing patterns
107+
108+
3. Best Practices
109+
- Check git status before commits
110+
- Run formatters before type checks
111+
- Keep changes minimal
112+
- Follow existing patterns
113+
- Document public APIs
114+
- Test thoroughly

README.md

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,41 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you bui
128128
The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
129129

130130
```python
131+
# Add lifespan support for startup/shutdown with strong typing
132+
from dataclasses import dataclass
133+
from typing import AsyncIterator
131134
from mcp.server.fastmcp import FastMCP
132135

133136
# Create a named server
134137
mcp = FastMCP("My App")
135138

136139
# Specify dependencies for deployment and development
137140
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
141+
142+
@dataclass
143+
class AppContext:
144+
db: Database # Replace with your actual DB type
145+
146+
@asynccontextmanager
147+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
148+
"""Manage application lifecycle with type-safe context"""
149+
try:
150+
# Initialize on startup
151+
await db.connect()
152+
yield AppContext(db=db)
153+
finally:
154+
# Cleanup on shutdown
155+
await db.disconnect()
156+
157+
# Pass lifespan to server
158+
mcp = FastMCP("My App", lifespan=app_lifespan)
159+
160+
# Access type-safe lifespan context in tools
161+
@mcp.tool()
162+
def query_db(ctx: Context) -> str:
163+
"""Tool that uses initialized resources"""
164+
db = ctx.request_context.lifespan_context["db"]
165+
return db.query()
138166
```
139167

140168
### Resources
@@ -218,7 +246,7 @@ async def long_task(files: list[str], ctx: Context) -> str:
218246
for i, file in enumerate(files):
219247
ctx.info(f"Processing {file}")
220248
await ctx.report_progress(i, len(files))
221-
data = await ctx.read_resource(f"file://{file}")
249+
data, mime_type = await ctx.read_resource(f"file://{file}")
222250
return "Processing complete"
223251
```
224252

@@ -249,7 +277,7 @@ mcp install server.py
249277
mcp install server.py --name "My Analytics Server"
250278

251279
# Environment variables
252-
mcp install server.py -e API_KEY=abc123 -e DB_URL=postgres://...
280+
mcp install server.py -v API_KEY=abc123 -v DB_URL=postgres://...
253281
mcp install server.py -f .env
254282
```
255283

@@ -334,7 +362,38 @@ def query_data(sql: str) -> str:
334362

335363
### Low-Level Server
336364

337-
For more control, you can use the low-level server implementation directly. This gives you full access to the protocol and allows you to customize every aspect of your server:
365+
For more control, you can use the low-level server implementation directly. This gives you full access to the protocol and allows you to customize every aspect of your server, including lifecycle management through the lifespan API:
366+
367+
```python
368+
from contextlib import asynccontextmanager
369+
from typing import AsyncIterator
370+
371+
@asynccontextmanager
372+
async def server_lifespan(server: Server) -> AsyncIterator[dict]:
373+
"""Manage server startup and shutdown lifecycle."""
374+
try:
375+
# Initialize resources on startup
376+
await db.connect()
377+
yield {"db": db}
378+
finally:
379+
# Clean up on shutdown
380+
await db.disconnect()
381+
382+
# Pass lifespan to server
383+
server = Server("example-server", lifespan=server_lifespan)
384+
385+
# Access lifespan context in handlers
386+
@server.call_tool()
387+
async def query_db(name: str, arguments: dict) -> list:
388+
ctx = server.request_context
389+
db = ctx.lifespan_context["db"]
390+
return await db.query(arguments["query"])
391+
```
392+
393+
The lifespan API provides:
394+
- A way to initialize resources when the server starts and clean them up when it stops
395+
- Access to initialized resources through the request context in handlers
396+
- Type-safe context passing between lifespan and request handlers
338397

339398
```python
340399
from mcp.server.lowlevel import Server, NotificationOptions
@@ -448,7 +507,7 @@ async def run():
448507
tools = await session.list_tools()
449508

450509
# Read a resource
451-
resource = await session.read_resource("file://some/path")
510+
content, mime_type = await session.read_resource("file://some/path")
452511

453512
# Call a tool
454513
result = await session.call_tool("tool-name", arguments={"arg1": "value"})
@@ -492,4 +551,4 @@ We are passionate about supporting contributors of all levels of experience and
492551

493552
## License
494553

495-
This project is licensed under the MIT License - see the LICENSE file for details.
554+
This project is licensed under the MIT License - see the LICENSE file for details.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10

0 commit comments

Comments
 (0)