Skip to content

Commit ee277dc

Browse files
authored
Merge branch 'main' into fix/prevent-session-hang
2 parents ee177aa + 3d7b311 commit ee277dc

87 files changed

Lines changed: 3473 additions & 2229 deletions

File tree

Some content is hidden

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

.github/workflows/claude-code-review.yml

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

.github/workflows/claude.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
jobs:
1515
claude:
1616
if: |
17-
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && !startsWith(github.event.comment.body, '@claude review')) ||
1818
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
1919
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
2020
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))

AGENTS.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Development Guidelines
2+
3+
## Branching Model
4+
5+
<!-- TODO: drop this section once v2 ships and main becomes the stable line -->
6+
7+
- `main` is currently the V2 rework. Breaking changes are expected here — when removing or
8+
replacing an API, delete it outright and document the change in
9+
`docs/migration.md`. Do not add `@deprecated` shims or backward-compat layers
10+
on `main`.
11+
- `v1.x` is the release branch for the current stable line. Backport PRs target
12+
this branch and use a `[v1.x]` title prefix.
13+
- `README.md` is frozen at v1 (a pre-commit hook rejects edits). Edit
14+
`README.v2.md` instead.
15+
16+
## Package Management
17+
18+
- ONLY use uv, NEVER pip
19+
- Installation: `uv add <package>`
20+
- Running tools: `uv run --frozen <tool>`. Always pass `--frozen` so uv doesn't
21+
rewrite `uv.lock` as a side effect.
22+
- Cross-version testing: `uv run --frozen --python 3.10 pytest ...` to run
23+
against a specific interpreter (CI covers 3.10–3.14).
24+
- Upgrading: `uv lock --upgrade-package <package>`
25+
- FORBIDDEN: `uv pip install`, `@latest` syntax
26+
- Don't raise dependency floors for CVEs alone. The `>=` constraint already
27+
lets users upgrade. Only raise a floor when the SDK needs functionality from
28+
the newer version, and don't add SDK code to work around a dependency's
29+
vulnerability. See Kludex/uvicorn#2643 and python-sdk #1552 for reasoning.
30+
31+
## Code Quality
32+
33+
- Type hints required for all code
34+
- Public APIs must have docstrings. When a public API raises exceptions a
35+
caller would reasonably catch, document them in a `Raises:` section. Don't
36+
list exceptions from argument validation or programmer error.
37+
- `src/mcp/__init__.py` defines the public API surface via `__all__`. Adding a
38+
symbol there is a deliberate API decision, not a convenience re-export.
39+
- IMPORTANT: All imports go at the top of the file — inline imports hide
40+
dependencies and obscure circular-import bugs. Only exception: when a
41+
top-level import genuinely can't work (lazy-loading optional deps, or
42+
tests that re-import a module).
43+
44+
## Testing
45+
46+
- Framework: `uv run --frozen pytest`
47+
- Async testing: use anyio, not asyncio
48+
- Do not use `Test` prefixed classes — write plain top-level `test_*` functions.
49+
Legacy files still contain `Test*` classes; do NOT follow that pattern for new
50+
tests even when adding to such a file.
51+
- IMPORTANT: Tests should be fast and deterministic. Prefer in-memory async execution;
52+
reach for threads only when necessary, and subprocesses only as a last resort.
53+
- For end-to-end behavior, an in-memory `Client(server)` is usually the
54+
cleanest approach (see `tests/client/test_client.py` for the canonical
55+
pattern). For narrower changes, testing the function directly is fine. Use
56+
judgment.
57+
- Test files mirror the source tree: `src/mcp/client/stdio.py`
58+
`tests/client/test_stdio.py`. Add tests to the existing file for that module.
59+
- Avoid `anyio.sleep()` with a fixed duration to wait for async operations. Instead:
60+
- Use `anyio.Event` — set it in the callback/handler, `await event.wait()` in the test
61+
- For stream messages, use `await stream.receive()` instead of `sleep()` + `receive_nowait()`
62+
- Exception: `sleep()` is appropriate when testing time-based features (e.g., timeouts)
63+
- Wrap indefinite waits (`event.wait()`, `stream.receive()`) in `anyio.fail_after(5)` to prevent hangs
64+
- Pytest is configured with `filterwarnings = ["error"]`, so warnings fail
65+
tests. Don't silence warnings from your own code; fix the underlying cause.
66+
Scoped `ignore::` entries for upstream libraries are acceptable in
67+
`pyproject.toml` with a comment explaining why.
68+
69+
### Coverage
70+
71+
CI requires 100% (`fail_under = 100`, `branch = true`).
72+
73+
- Full check: `./scripts/test` (~23s). Runs coverage + `strict-no-cover` on the
74+
default Python. Not identical to CI: CI runs 3.10–3.14 × {ubuntu, windows}
75+
× {locked, lowest-direct}, and some branch-coverage quirks only surface on
76+
specific matrix entries.
77+
- Targeted check while iterating (~4s, deterministic):
78+
79+
```bash
80+
uv run --frozen coverage erase
81+
uv run --frozen coverage run -m pytest tests/path/test_foo.py
82+
uv run --frozen coverage combine
83+
uv run --frozen coverage report --include='src/mcp/path/foo.py' --fail-under=0
84+
# UV_FROZEN=1 propagates --frozen to the uv subprocess strict-no-cover spawns
85+
UV_FROZEN=1 uv run --frozen strict-no-cover
86+
```
87+
88+
Partial runs can't hit 100% (coverage tracks `tests/` too), so `--fail-under=0`
89+
and `--include` scope the report. `strict-no-cover` has no false positives on
90+
partial runs — if your new test executes a line marked `# pragma: no cover`,
91+
even a single-file run catches it.
92+
93+
Avoid adding new `# pragma: no cover`, `# type: ignore`, or `# noqa` comments.
94+
In tests, use `assert isinstance(x, T)` to narrow types instead of
95+
`# type: ignore`. In library code (`src/`), a `# pragma: no cover` needs very
96+
good reasoning — it usually means a test is missing. Audit before pushing:
97+
98+
```bash
99+
git diff origin/main... | grep -E '^\+.*(pragma|type: ignore|noqa)'
100+
```
101+
102+
What the existing pragmas mean:
103+
104+
- `# pragma: no cover` — line is never executed. CI's `strict-no-cover` (skipped
105+
on Windows runners) fails if it IS executed. When your test starts covering
106+
such a line, remove the pragma.
107+
- `# pragma: lax no cover` — excluded from coverage but not checked by
108+
`strict-no-cover`. Use for lines covered on some platforms/versions but not
109+
others.
110+
- `# pragma: no branch` — excludes branch arcs only. coverage.py misreports the
111+
`->exit` arc for nested `async with` on Python 3.11+ (worse on 3.14/Windows).
112+
113+
## Breaking Changes
114+
115+
When making breaking changes, document them in `docs/migration.md`. Include:
116+
117+
- What changed
118+
- Why it changed
119+
- How to migrate existing code
120+
121+
Search for related sections in the migration guide and group related changes together
122+
rather than adding new standalone sections.
123+
124+
## Formatting & Type Checking
125+
126+
- Format: `uv run --frozen ruff format .`
127+
- Lint: `uv run --frozen ruff check . --fix`
128+
- Type check: `uv run --frozen pyright`
129+
- Pre-commit runs all of the above plus markdownlint, a `uv.lock` consistency
130+
check, and README checks — see `.pre-commit-config.yaml`
131+
132+
## Exception Handling
133+
134+
- **Always use `logger.exception()` instead of `logger.error()` when catching exceptions**
135+
- Don't include the exception in the message: `logger.exception("Failed")` not `logger.exception(f"Failed: {e}")`
136+
- **Catch specific exceptions** where possible:
137+
- File ops: `except (OSError, PermissionError):`
138+
- JSON: `except json.JSONDecodeError:`
139+
- Network: `except (ConnectionError, TimeoutError):`
140+
- **FORBIDDEN** `except Exception:` - unless in top-level handlers

CLAUDE.md

Lines changed: 1 addition & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1 @@
1-
# Development Guidelines
2-
3-
This document contains critical information about working with this codebase. Follow these guidelines precisely.
4-
5-
## Core Development Rules
6-
7-
1. Package Management
8-
- ONLY use uv, NEVER pip
9-
- Installation: `uv add <package>`
10-
- Running tools: `uv run <tool>`
11-
- Upgrading: `uv lock --upgrade-package <package>`
12-
- FORBIDDEN: `uv pip install`, `@latest` syntax
13-
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: 120 chars maximum
20-
- FORBIDDEN: imports inside functions. THEY SHOULD BE AT THE TOP OF THE FILE.
21-
22-
3. Testing Requirements
23-
- Framework: `uv run --frozen pytest`
24-
- Async testing: use anyio, not asyncio
25-
- Do not use `Test` prefixed classes, use functions
26-
- Coverage: test edge cases and errors
27-
- New features require tests
28-
- Bug fixes require regression tests
29-
- IMPORTANT: The `tests/client/test_client.py` is the most well designed test file. Follow its patterns.
30-
- IMPORTANT: Be minimal, and focus on E2E tests: Use the `mcp.client.Client` whenever possible.
31-
- Coverage: CI requires 100% (`fail_under = 100`, `branch = true`).
32-
- Full check: `./scripts/test` (~20s, matches CI exactly)
33-
- Targeted check while iterating:
34-
35-
```bash
36-
uv run --frozen coverage erase
37-
uv run --frozen coverage run -m pytest tests/path/test_foo.py
38-
uv run --frozen coverage combine
39-
uv run --frozen coverage report --include='src/mcp/path/foo.py' --fail-under=0
40-
```
41-
42-
Partial runs can't hit 100% (coverage tracks `tests/` too), so `--fail-under=0`
43-
and `--include` scope the report to what you actually changed.
44-
- Avoid `anyio.sleep()` with a fixed duration to wait for async operations. Instead:
45-
- Use `anyio.Event` — set it in the callback/handler, `await event.wait()` in the test
46-
- For stream messages, use `await stream.receive()` instead of `sleep()` + `receive_nowait()`
47-
- Exception: `sleep()` is appropriate when testing time-based features (e.g., timeouts)
48-
- Wrap indefinite waits (`event.wait()`, `stream.receive()`) in `anyio.fail_after(5)` to prevent hangs
49-
50-
Test files mirror the source tree: `src/mcp/client/streamable_http.py` → `tests/client/test_streamable_http.py`
51-
Add tests to the existing file for that module.
52-
53-
- For commits fixing bugs or adding features based on user reports add:
54-
55-
```bash
56-
git commit --trailer "Reported-by:<name>"
57-
```
58-
59-
Where `<name>` is the name of the user.
60-
61-
- For commits related to a Github issue, add
62-
63-
```bash
64-
git commit --trailer "Github-Issue:#<number>"
65-
```
66-
67-
- NEVER ever mention a `co-authored-by` or similar aspects. In particular, never
68-
mention the tool used to create the commit message or PR.
69-
70-
## Pull Requests
71-
72-
- Create a detailed message of what changed. Focus on the high level description of
73-
the problem it tries to solve, and how it is solved. Don't go into the specifics of the
74-
code unless it adds clarity.
75-
76-
- NEVER ever mention a `co-authored-by` or similar aspects. In particular, never
77-
mention the tool used to create the commit message or PR.
78-
79-
## Breaking Changes
80-
81-
When making breaking changes, document them in `docs/migration.md`. Include:
82-
83-
- What changed
84-
- Why it changed
85-
- How to migrate existing code
86-
87-
Search for related sections in the migration guide and group related changes together
88-
rather than adding new standalone sections.
89-
90-
## Python Tools
91-
92-
## Code Formatting
93-
94-
1. Ruff
95-
- Format: `uv run --frozen ruff format .`
96-
- Check: `uv run --frozen ruff check .`
97-
- Fix: `uv run --frozen ruff check . --fix`
98-
- Critical issues:
99-
- Line length (88 chars)
100-
- Import sorting (I001)
101-
- Unused imports
102-
- Line wrapping:
103-
- Strings: use parentheses
104-
- Function calls: multi-line with proper indent
105-
- Imports: try to use a single line
106-
107-
2. Type Checking
108-
- Tool: `uv run --frozen pyright`
109-
- Requirements:
110-
- Type narrowing for strings
111-
- Version warnings can be ignored if checks pass
112-
113-
3. Pre-commit
114-
- Config: `.pre-commit-config.yaml`
115-
- Runs: on git commit
116-
- Tools: Prettier (YAML/JSON), Ruff (Python)
117-
- Ruff updates:
118-
- Check PyPI versions
119-
- Update config rev
120-
- Commit config first
121-
122-
## Error Resolution
123-
124-
1. CI Failures
125-
- Fix order:
126-
1. Formatting
127-
2. Type errors
128-
3. Linting
129-
- Type errors:
130-
- Get full line context
131-
- Check Optional types
132-
- Add type narrowing
133-
- Verify function signatures
134-
135-
2. Common Issues
136-
- Line length:
137-
- Break strings with parentheses
138-
- Multi-line function calls
139-
- Split imports
140-
- Types:
141-
- Add None checks
142-
- Narrow string types
143-
- Match existing patterns
144-
145-
3. Best Practices
146-
- Check git status before commits
147-
- Run formatters before type checks
148-
- Keep changes minimal
149-
- Follow existing patterns
150-
- Document public APIs
151-
- Test thoroughly
152-
153-
## Exception Handling
154-
155-
- **Always use `logger.exception()` instead of `logger.error()` when catching exceptions**
156-
- Don't include the exception in the message: `logger.exception("Failed")` not `logger.exception(f"Failed: {e}")`
157-
- **Catch specific exceptions** where possible:
158-
- File ops: `except (OSError, PermissionError):`
159-
- JSON: `except json.JSONDecodeError:`
160-
- Network: `except (ConnectionError, TimeoutError):`
161-
- **FORBIDDEN** `except Exception:` - unless in top-level handlers
1+
@AGENTS.md

README.v2.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -681,11 +681,11 @@ The Context object provides the following capabilities:
681681
- `ctx.mcp_server` - Access to the MCPServer server instance (see [MCPServer Properties](#mcpserver-properties))
682682
- `ctx.session` - Access to the underlying session for advanced communication (see [Session Properties and Methods](#session-properties-and-methods))
683683
- `ctx.request_context` - Access to request-specific data and lifespan resources (see [Request Context Properties](#request-context-properties))
684-
- `await ctx.debug(message)` - Send debug log message
685-
- `await ctx.info(message)` - Send info log message
686-
- `await ctx.warning(message)` - Send warning log message
687-
- `await ctx.error(message)` - Send error log message
688-
- `await ctx.log(level, message, logger_name=None)` - Send log with custom level
684+
- `await ctx.debug(data)` - Send debug log message
685+
- `await ctx.info(data)` - Send info log message
686+
- `await ctx.warning(data)` - Send warning log message
687+
- `await ctx.error(data)` - Send error log message
688+
- `await ctx.log(level, data, logger_name=None)` - Send log with custom level
689689
- `await ctx.report_progress(progress, total=None, message=None)` - Report operation progress
690690
- `await ctx.read_resource(uri)` - Read a resource by URI
691691
- `await ctx.elicit(message, schema)` - Request additional information from user with validation

docs/api.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)