Compare commits

...
Author SHA1 Message Date
Marcelo Trylesinski
6affe5c0d3
Detect event-loop blocking in tests (#3510)
Some checks failed
Conformance Tests / server-conformance (push) Has been cancelled
Conformance Tests / client-conformance (push) Has been cancelled
Deploy Docs / deploy-docs (push) Has been cancelled
CI / checks (push) Has been cancelled
GitHub Actions Security Analysis / zizmor (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-09-16 12:12:27 +02:00
Marcelo Trylesinski
9972c21aa4
Replace RootModel wrappers with type aliases and TypeAdapter validation (#3470)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-09-07 17:40:21 +02:00
Max
fd66270926
docs: refresh translations, and translate pages in parallel (#3458) 2026-09-07 13:35:53 +01:00
Max
08a3bc8eaf
docs: ask for AI disclosure on comments too (#3459) 2026-09-06 14:44:38 +01:00
Max
7bb486a10f
docs: stop presenting the in-memory client as the way to connect (#3443) 2026-09-05 04:15:58 +01:00
Max
0c9136841f
Add AuthSettings.validate_token_resource to check a bearer token's resource (#3447) 2026-09-05 03:35:55 +01:00
Max
9771e6b59c
Keep following a relative redirect when the endpoint URL carries userinfo (#3450) 2026-09-05 01:24:29 +01:00
Max
a925e55893
Bump the locked versions of eight dev and test dependencies (#3449) 2026-09-05 01:22:35 +01:00
dependabot[bot]
e8b94867ac
Bump pymdown-extensions from 11.0 to 11.0.1 (#3285)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-05 00:15:07 +01:00
Max
c6762e821c
Follow redirects only within the MCP endpoint's origin (#3397) 2026-09-04 23:43:38 +01:00
Max
5fd3abc548
Skip automatic docs previews for fork PRs and drop the setup-uv retry steps (#3445) 2026-09-04 21:56:58 +01:00
Max
a2dab7e2dd
Move the docs-preview workflow scripts out of the YAML into .github/scripts (#3446) 2026-09-04 21:30:04 +01:00
dependabot[bot]
2e0a18199a
Bump the github-actions group with 6 updates (#3424)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-04 20:17:00 +01:00
Max
10dc173637
Exercise the SEP-2575 stateless probes and SEP-2243 resource/prompt headers in the conformance fixtures (#3442) 2026-09-04 17:11:00 +01:00
Max
979208c115
Deprecate constructing the pre-provisioned OAuth clients without an issuer (#3435) 2026-09-04 15:53:21 +01:00
Max
d060b36e1d
Validate the authorization server metadata issuer on every discovery path (#3398) 2026-09-02 16:51:22 +01:00
Max
d2290ca343
Expire idle Streamable HTTP sessions by default and cap concurrent sessions (#3395) 2026-08-28 15:54:49 +01:00
Max
6705402e24
Resolve tool output-schema references within the schema document only (#3394) 2026-08-26 14:15:18 +01:00
Max
e7284ed130
Gate draft PRs too and rewrite the auto-close comment (#3378) 2026-08-26 12:02:43 +01:00
Max
0921d94a74
Point imports of mcp.server.fastmcp at the migration guide (#3388)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-08-25 16:39:57 +01:00
Max
4d6f87e8d2
Build releases with the pinned hatchling and a publish action that accepts Metadata 2.5 (#3380)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-08-24 19:57:07 +01:00
Max
c5d7d0b6d0
docs: refresh translations for recent English changes (#3379) 2026-08-24 19:29:54 +01:00
Max
d8b638304e
Give recursive tool return types an object-rooted output schema (#3376) 2026-08-24 19:06:22 +01:00
Max
56af44727b
Log MCPServer handler exceptions by kind and keep crash details off the wire (#3314)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-08-24 15:51:18 +01:00
Peter Alexander
f1c40b062f
Accept boolean sub-schemas in 2025-11-25 tool schema properties (#3354) 2026-08-24 15:41:59 +01:00
Max
57394b0548
Apply the request body limit to the SSE and OAuth endpoints (#3336) 2026-08-20 16:21:44 +01:00
Max
0cee6249ba
Hand TypedDict tool results to pydantic natively (#3331) 2026-08-20 15:36:21 +01:00
Marcelo Trylesinski
0d92192765
Shorten stdio test comments (#3329) 2026-08-18 10:50:47 +02:00
Max
b2025ab815
Acknowledge notification POSTs with 202 on the 2026-07-28 HTTP entry (#3326) 2026-08-17 21:15:35 +01:00
Max
9057285683
docs: cover the remaining Tier 1 audit items (#3325) 2026-08-17 20:18:30 +01:00
Max
2a1cc94493
Gate external PRs on an assigned, linked issue (#3291)
Signed-off-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-08-17 17:22:04 +01:00
Max
e473cca6a1
Let Client take StdioServerParameters directly (#3321) 2026-08-17 14:31:21 +01:00
Max
fb443cc4b2
MCPServer: content-block returns are unstructured, prompt messages take Image/Audio (#3320) 2026-08-17 14:12:31 +01:00
Max
37b3cb1ef7
Stop framing breaking changes as a workflow in AGENTS.md (#3286) 2026-08-16 18:10:16 +01:00
Max
31b76cbeed
Drop later-revision cache-hint fields on pre-2026 sessions (#3223) 2026-08-16 15:25:22 +01:00
Max
959569ba15
Publish versioning, roadmap, and dependency policies for v2 (#3215) 2026-08-16 15:08:17 +01:00
Max
fc41dd56c8
docs: lead the README client example with a URL, not the server object (#3315) 2026-08-16 14:27:01 +01:00
Max
5285e936a9
Pin text I/O to UTF-8 and fail CI on locale-dependent reads/writes (#3296)
Co-authored-by: ShuQingDollarVoyager <57471784+ShuQingDollarVoyager@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:41:53 +01:00
Max
52ad0a8876
docs: publish translated docs in twelve languages and the tool that maintains them (#3280) 2026-08-14 17:07:34 +01:00
Max
14aa889992
Pin each conformance leg to a spec-revision wire (#3304) 2026-08-14 13:39:06 +01:00
ShuQingDollarVoyager
2378e560fb
Read UTF-8 test fixtures with explicit encoding (#3245)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 11:08:55 +01:00
Max
6e304527a5
Bump conformance harness to 0.2.0-alpha.11 (#3282) 2026-08-11 15:40:21 +01:00
Max
a4f4ccd091
Link the released 2026-07-28 spec and point migrators at /v1/ (#3214) 2026-07-29 15:01:27 +01:00
Max
fe47969fb9
Ask which release line a bug report is on (#3213) 2026-07-29 14:55:15 +01:00
Max
d82ed88eb5
Describe the maintenance line without hardcoding 1.28 (#3212) 2026-07-29 14:54:45 +01:00
Max
b31ddf37ed
Retire wording tied to pre-2.0 milestones (#3211) 2026-07-29 14:54:23 +01:00
Max
6f69a3758e
Present v2 as the stable release across the README, docs, and policies (#3178)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-07-28 14:31:36 +01:00
Max
78e6fbb7e4
Serve v2 docs at the site root, with permanent per-major paths (#3176) 2026-07-28 13:57:21 +01:00
Max
af06330a31
Remove unused StreamableHTTPTransport.get_session_id() (#3205) 2026-07-28 13:55:02 +01:00
Max
68ca87e20b
Document the two-line release process for stable v2 (#3179) 2026-07-28 13:51:31 +01:00
Max
c9c431b71a
Expose the middleware chain on MCPServer and stop sending unrequested change notifications (#3201) 2026-07-28 12:24:23 +01:00
Max
528e366558
Fail fast on server-to-client requests in JSON-response mode instead of hanging (#3195) 2026-07-28 11:04:51 +01:00
Max
27f5cc7a46
Remove unused mcpserver.exceptions.ValidationError (#3199) 2026-07-28 10:25:51 +01:00
Max
89c5e700f2
Gate log notifications on the per-request log-level opt-in at 2026-07-28 (#3198) 2026-07-28 02:20:33 +01:00
Max
b61ce388dd
docs: fix off-by-one hl_lines in apps.md (#3196) 2026-07-28 00:04:57 +01:00
Max
b7c9a916d6
Add mcp.types as a permanent alias for mcp_types (#3190) 2026-07-27 23:47:04 +01:00
Max
923341c98a
Stop answering cancelled requests (#3188) 2026-07-27 23:26:00 +01:00
Max
e8ef138153
docs: fill migration-guide gaps found by automated v1-to-v2 migration runs (#3187) 2026-07-27 23:17:17 +01:00
Max
d3ffe87960
Split the registration request model from the registered-client record (#3181) 2026-07-27 23:11:01 +01:00
Max
b9422f1c9b
Make the per-version wire packages private (mcp_types._v*) (#3191) 2026-07-27 22:16:48 +01:00
Max
45f2a88a9a
Point pre-release install pins at 2.0.0rc1 (#3186)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-07-27 14:23:57 +01:00
Max
333aca7ac8
Repin conformance harness to the published 0.2.0-alpha.10 (#3184) 2026-07-27 14:03:06 +01:00
Max
dcd9c1ee9f
Lengthen the demo signing keys in the identity-assertion examples (#3180) 2026-07-26 11:45:26 +01:00
Jeremiah Lowin
f599cdfcf9
Cache compiled output-schema validators on ClientSession (#3134)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-07-26 11:29:10 +01:00
Max
11934c90ae
Replace FileResource.is_binary with an encoding field (#3171) 2026-07-26 00:58:06 +01:00
Max
814072c94d
Narrow message_handler's parameter to notifications and exceptions (#3168) 2026-07-26 00:24:48 +01:00
Max
47bfa85e83
Remove the unused timeout parameter from OAuthClientProvider (#3165) 2026-07-26 00:22:15 +01:00
Max
3212591946
Stop advertising MCP_* env vars for MCPServer settings; drop pydantic-settings (#3170) 2026-07-25 23:22:27 +01:00
Max
7163d8263f
Remove the deprecated RFC7523OAuthClientProvider (#3169) 2026-07-25 22:50:57 +01:00
Max
1963af52cc
Correct stable v2 target date to 2026-07-28 (#3105) 2026-07-25 20:36:08 +01:00
Max
e90a66b554
Rename scopes= to scope= on the client-credentials OAuth providers (#3166) 2026-07-25 20:23:36 +01:00
Max
5dd062d077
Remove Context.client_id (#3167) 2026-07-25 19:11:51 +01:00
Max
0cb920f126
Make CacheConfig() the Client cache default and None the off switch (#3164) 2026-07-25 17:50:53 +01:00
Max
629ca297d2
Isolate the stdio server's stdin and stdout from handler subprocesses (#3117) 2026-07-25 13:05:51 +01:00
Max
00a70148bc
Serve the 2026-07-28 protocol over stdio: decide the era from the opening request (#3152) 2026-07-24 13:46:37 +01:00
Max
837ef904f8
Align with spec #3002: optional clientInfo, serverInfo in result _meta (#3143) 2026-07-23 12:00:36 +01:00
Max
3a6f2996cd
docs: load media examples from disk instead of inline base64 (#3108) 2026-07-16 20:41:05 +01:00
Max
ebcc4dc3fb
Pin pymdown-extensions back to 11.0 (#3106) 2026-07-16 20:25:33 +01:00
Max
497f7afa61
docs: make API reference rendering independent of page order (#3107) 2026-07-16 18:29:44 +01:00
Andre.Kalberer
e464f72c12
docs: document Windows stdio subprocess stdin handling (#3079) 2026-07-16 11:25:47 +01:00
Marcelo Trylesinski
03aaebd3aa
Add Streamable HTTP request body limits (#3095) 2026-07-16 08:33:32 +02:00
Marcelo Trylesinski
2713b53b12
Replace httpx and httpx-sse with httpx2 (#2972)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-07-14 17:05:08 +01:00
Otis Cui
1216c53693
fix: reject trailing newline in tool-name and URI-template varname validation (#3076)
Python's $ with re.match also matches just before a single trailing newline, so tool-name validation accepted "name\n" and UriTemplate.parse accepted varnames like "foo\n". Switch both checks to re.fullmatch.

Closes #3084
2026-07-10 12:56:54 +00:00
Marcelo Trylesinski
4fc8882c02
docs: replace MkDocs with Zensical (#3073)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-07-10 12:48:46 +01:00
Max
74a242ae7f
ci: pick the docs-preview toolchain from the PR checkout (#3081) 2026-07-09 13:57:14 +01:00
Max
148278e07f
Gate the test matrix and retry setup-uv's flaky manifest fetch (#3080) 2026-07-09 12:32:53 +01:00
Max
9bdc03d54e
Add the client-side subscriptions/listen driver (#3047) 2026-07-07 14:26:09 +01:00
Max
6d2e908f2b
docs: pin mkdocs<2 and silence the mkdocs-material advisory banner in CI (#3072) 2026-07-07 13:45:30 +01:00
Max
867bba6263
Share one event loop per test module to stop Windows socketpair churn (#3070) 2026-07-07 13:19:04 +01:00
Max
d287c9868f
Extend resolver DI to sampling and roots requests (#3049) 2026-07-06 18:25:57 +01:00
Max
53117cb3a9
Make client-side cancellation work over the 2026 transports (#3046) 2026-07-02 19:21:04 +01:00
Max
bf4402725d
docs: restructure the migration guide around topical groups with a navigation layer (#3058) 2026-07-02 18:54:21 +01:00
Max
2359b40285
docs: modernize the site theme (#3057) 2026-07-02 16:17:33 +01:00
Max
e4d95e0d44
docs: add a "What's new in v2" page (#3054) 2026-07-02 15:01:30 +01:00
Max
220d362112
docs: restructure into topical sections and add the four most-asked-for pages (#3044) 2026-07-01 21:06:04 +01:00
Max
080f2a869d
Harden the dual-era stream loop's era-lock and rejection semantics (#3040) 2026-07-01 17:07:12 +01:00
Max
d39c68df23
De-flake conformance CI: solo re-verification, spawn-storm reduction, result artifacts (#3043) 2026-07-01 16:59:06 +01:00
Max
0da9092037
Point pre-release install pins at 2.0.0b1 (#3039)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-07-01 00:14:52 +01:00
Max
e50fb5be19
Serve the 2026-07-28 era over stdio and other stream-pair transports (#3038) 2026-07-01 00:11:56 +01:00
Max
dcf8a6a0b5
Document pydantic.ValidationError in client Raises sections (#3036) 2026-07-01 00:03:17 +01:00
Max
410cc0db31
Add v2 feedback issue template (#3037) 2026-07-01 00:03:11 +01:00
Max
ca10dade2c
Serve subscriptions/listen with a pluggable event bus (SEP-2575) (#3035) 2026-06-30 23:01:04 +01:00
Max
48ef569f7e
Validate Mcp-Param-* headers server-side on the 2026-07-28 HTTP path (SEP-2243) (#3033) 2026-06-30 21:39:32 +01:00
Max
4df609119f
Add a client extension API (#3034) 2026-06-30 21:31:02 +01:00
Max
7322ca56f4
Require integrity protection for MRTR requestState (#3032) 2026-06-30 21:30:32 +01:00
Den Delimarsky
985652491a
Add Cloudflare Pages docs preview with /preview-docs slash command (#3028) 2026-06-30 16:47:21 +01:00
Max
0b200ef03b
Surface skipped conformance scenarios as baselined known failures (#3030) 2026-06-30 13:07:05 +01:00
Max
b15b1d5f07
Add a client-side response cache honoring SEP-2549 caching hints (#3023) 2026-06-30 11:31:06 +01:00
Max
67d7593df1
docs: publish llms.txt and markdown renditions of the docs (#3024) 2026-06-30 11:30:55 +01:00
Max
8d0f928e40
Pass InputRequiredResult through the MCPServer prompt and resource pipelines (#3020) 2026-06-29 16:50:58 +01:00
Max
8f2c97b769
Consult request_state only for the question a resolver is asking (#3019) 2026-06-29 16:44:05 +01:00
Max
533c6a8226
Add cache_hints constructor map for SEP-2549 caching hints (#3015) 2026-06-29 14:11:15 +00:00
Marcelo Trylesinski
c85836a081
Drive resolver elicitation over the 2026-07-28 input_required flow (#2986)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-29 14:39:43 +01:00
Max
24fdd909ac
docs: convert bold cross-references into links, link SEP and RFC mentions (#3017) 2026-06-29 11:07:26 +00:00
Max
f2e63c979a
Promote the v2 README to README.md ahead of the first v2 beta (#3014) 2026-06-29 12:01:54 +01:00
Marcelo Trylesinski
f664db8952
Add resolver dependency injection for MCPServer tools (#2969)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-29 11:51:46 +01:00
Marcelo Trylesinski
4b519782f1
Add a pluggable server extension API with MCP Apps (#3003)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-29 10:58:05 +01:00
Max
fe9723853d
Wire SEP-990 enterprise-managed-authorization conformance fixture (#3007) 2026-06-29 10:42:37 +01:00
Max
e942d00b98
Re-vendor 2026-07-28 schema at spec ead35b59 (SubscriptionsListenResult) (#3006) 2026-06-27 10:15:34 +02:00
Max
3b78f86886
Add docs, tested examples, and a story for SEP-990 identity assertion (#3004) 2026-06-26 21:01:46 +02:00
Max
24717cc8eb
feat: RFC 6570 URI templates with operator-aware security (#2356) 2026-06-26 20:29:17 +02:00
Max
067f90578c
Add SSE response mode to the 2026 streamable-HTTP server entry (#3001) 2026-06-26 19:09:08 +02:00
Marcelo Trylesinski
c0ecb70e24
Support RFC 8693 token exchange for enterprise IdP flows (SEP-990) (#2988)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-26 17:57:10 +02:00
Marcelo Trylesinski
ecdf09d44f
Deprecate Server.__init__ handlers for removed capabilities (#3002) 2026-06-26 17:51:13 +02:00
Max
08b62308d4
Client auto-resolves InputRequiredResult via existing callbacks (SEP-2322) (#2998) 2026-06-26 17:35:23 +02:00
Marcelo Trylesinski
3945bdde11
Remove the dispatch-tier middleware hook (#2997) 2026-06-26 17:08:16 +02:00
Max
3caa445c6c
Pin conformance harness by commit SHA (ed314a73) instead of PR number (#3000) 2026-06-26 17:01:42 +02:00
Marcelo Trylesinski
b31d95a429
Make OpenTelemetry tracing the single default middleware (#2995) 2026-06-26 15:47:37 +02:00
Marcelo Trylesinski
cc596195bb
Switch RFC7523OAuthClientProvider warning to MCPDeprecationWarning (#2996) 2026-06-26 15:27:57 +02:00
Marcelo Trylesinski
5b2713d40c
Mirror x-mcp-header tool arguments into Mcp-Param-* request headers (SEP-2243) (#2990) 2026-06-26 14:36:56 +02:00
Max
3a8da8c0c3
Fix docs/release follow-ups from the mcp-types package split (#2977) 2026-06-26 13:16:09 +02:00
Max
1b92efedc8
Add .claude/skills/test-quality and reference it from AGENTS.md (#2993) 2026-06-26 13:05:05 +02:00
Max
411a6d3980
Rebuild the docs around tested examples; shrink README.v2.md to a pitch (#2978) 2026-06-26 12:49:19 +02:00
Max
4caa41f6d5
Add story-style examples suite (27 stories + harness + CI) (#2957) 2026-06-26 12:02:27 +02:00
Max
9dc8c5f02d
find_invalid_x_mcp_header: never repr a non-string annotation value (#2989)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-06-26 09:54:28 +00:00
Marcelo Trylesinski
f41a5193f3
Preserve empty issuer/resource paths on AuthSettings (#2987) 2026-06-26 11:41:41 +02:00
Marcelo Trylesinski
e9cd1692b0
Remove comment-on-release workflow (#2985) 2026-06-26 09:00:51 +00:00
Max
587340279e
Conformance burn-down: server-side InputRequiredResult, Mcp-Method/Name validation, x-mcp-header filter (14 scenarios → green) (#2974)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-06-26 09:51:59 +02:00
Max
603342f0fe
Set Development Status classifier to Production/Stable (#2975) 2026-06-25 23:47:31 +02:00
Marcelo Trylesinski
0ee7f1b293
Split protocol types into a standalone mcp-types package (#2973) 2026-06-25 19:18:38 +02:00
Max
03681ed55e
Client call_tool: input_responses/request_state retry params; InputRequiredResult via allow_input_required (#2968) 2026-06-25 17:37:00 +02:00
Marcelo Trylesinski
96bf22e57a
Stop flagging snake_case is_error results as tool errors in OTel span (#2971) 2026-06-25 15:24:45 +00:00
Marcelo Trylesinski
1b1abf6ab6
Add GenAI semantic-convention attributes to OpenTelemetryMiddleware (#2970) 2026-06-25 14:43:54 +00:00
Max
f226d00d0a
Client-side 2026-07-28 support: .discover()/.adopt() + Client(mode=); request-metadata green (#2950) 2026-06-25 16:09:23 +02:00
Max
ae13ede143
lowlevel Server: widen on_* return types for InputRequiredResult; add subscriptions/listen slot (#2967) 2026-06-25 14:20:01 +02:00
Max
a527142312
Buffer per-request StreamableHTTP streams to avoid serial-router head-of-line block (#2934) 2026-06-22 16:20:45 +01:00
Max
44ce901ce3
OAuth client: keep refresh_token on non-rotating refresh; restore same-origin issuer binding (#2946) 2026-06-22 15:21:52 +01:00
Marcelo Trylesinski
ad81ca234a
Slim ServerMiddleware to (ctx, call_next) and add OpenTelemetryMiddleware (#2941)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-22 14:46:30 +01:00
Max
5e013d9c54
OAuth client: harden SEP-2352/SEP-2350 edge cases; fix conformance comment (#2936) 2026-06-22 14:45:20 +01:00
Max
2397319a68
Server-side 2026-07-28 stateless support: classifier, driver split, server/discover (#2928) 2026-06-21 19:34:17 +01:00
Marcelo Trylesinski
44724284b3
Bind client credentials to their authorization server (SEP-2352) (#2933) 2026-06-20 18:47:22 +01:00
Marcelo Trylesinski
3169922492
Move scope step-up test to top-level function (#2932) 2026-06-20 18:45:10 +01:00
Marcelo Trylesinski
1331131650
Union previously requested scopes on step-up re-authorization (SEP-2350) (#2931) 2026-06-20 18:45:04 +02:00
Marcelo Trylesinski
4573e4ac33
Deprecate roots, sampling, and logging methods per SEP-2577 (#2926) 2026-06-20 18:25:41 +02:00
Marcelo Trylesinski
5734acf18e
Pass json-schema-ref-no-deref conformance scenario (SEP-2106) (#2924) 2026-06-20 18:23:58 +02:00
Marcelo Trylesinski
cf41441e44
Send application_type during Dynamic Client Registration (SEP-837) (#2930) 2026-06-20 18:19:12 +02:00
Marcelo Trylesinski
60f37e9d7c
Document redirect_uri wire-format change in OAuth migration note (#2929) 2026-06-20 16:14:03 +00:00
Max
f5fe42f5a4
ci(conformance): bump harness to 0.2.0-alpha.5 preview (#2927) 2026-06-20 16:54:50 +01:00
Marcelo Trylesinski
48cf4950dc
Validate the iss authorization-response parameter (RFC 9207 / SEP-2468) (#2921) 2026-06-20 17:54:18 +02:00
Marcelo Trylesinski
b7a5bffed0
Preserve empty URL paths on OAuth metadata models (#2925) 2026-06-20 15:32:03 +00:00
Max
5a3412ddc1
Ignore pre-2026 protocol_version pins at the StreamableHTTP transport (#2923) 2026-06-20 17:29:57 +02:00
冯基魁
fda4c54362
fix: correct MCPServer call_tool result type (#2816)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-06-20 16:56:16 +02:00
Marcelo Trylesinski
f253682393
Return -32602 for resource not found (SEP-2164) (#2920) 2026-06-20 16:55:23 +02:00
Marcelo Trylesinski
0fa03ec69f
Add uv ecosystem to dependabot and drop weekly lockfile workflow (#2919) 2026-06-20 15:13:17 +01:00
Max
84bf9bde05
First end-to-end 2026-07-28 stateless tools/call (experimental entry + ClientSession pin) (#2917) 2026-06-20 14:55:59 +01:00
Max
1cec2d60f4
Relax monolith ElicitRequestURLParams.elicitation_id for 2026-07-28 (#2913) 2026-06-19 16:06:08 +01:00
Max
510832aa45
Re-vendor 2026-07-28 schema and absorb spec #2907 error-code renumber (#2912) 2026-06-19 15:46:15 +01:00
Max
9e6d00318c
ci(conformance): add 2026-07-28 carried-forward leg + bump harness to 0.2.0-alpha.4 (#2911) 2026-06-19 14:48:21 +01:00
Max
364b762ef6
tests/interaction: era-axis machinery for the requirements manifest (#2909) 2026-06-19 12:59:39 +01:00
Max
734746a3d9
Resolve protocol version per request and expose it as ctx.protocol_version (#2886) 2026-06-17 08:46:42 +01:00
Max
c85ccf1c6f
docs: add AI-assisted contribution policy to CONTRIBUTING.md (#2887) 2026-06-16 23:16:07 +01:00
Max
df81c06221
Pass a list to parametrize in test_docs_examples (pytest 9.1.0 compat) (#2890) 2026-06-16 23:15:39 +01:00
Max
47bbab3bc3
Drop stale superset-leniency note from ElicitResult.content docstring (#2884)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-06-16 22:08:29 +01:00
Max
7703df1539
Expand site-absolute spec links in generated docstrings to full URLs (#2885) 2026-06-16 13:55:43 -07:00
Max
65be5a7147
Protocol types for 2026-07-28: superset monolith, committed per-version packages, and wire-method maps (#2849) 2026-06-16 17:40:14 +01:00
dependabot[bot]
2693fb95ae
chore(deps): bump the github-actions group across 1 directory with 9 updates (#2636)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-06-16 09:45:21 +01:00
Max
ac36a395aa
Widen the stdio round-trip test's termination grace and overall timeout (#2880) 2026-06-15 18:23:49 +01:00
Max
ddb7b78ef4
Deflake the issue-1363 tests: wait for lifespan startup instead of sleeping (#2879) 2026-06-15 16:50:57 +01:00
Max
255650dcd1
ci(conformance): run server --suite draft and baseline the 2026-07-28 scenarios (#2878) 2026-06-15 16:45:29 +01:00
Max
a3689ab493
ci(conformance): pin harness to 0.2.0-alpha.3 with expected-failures baseline (#2877) 2026-06-15 15:40:42 +01:00
Max
1012d60004
[v2] ClientSession runs on JSONRPCDispatcher; BaseSession removed (#2838) 2026-06-15 14:46:34 +01:00
Jerome
cf110e3214
Fix 404 links in v1 README to renamed example files (#2822)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-06-12 15:05:20 +01:00
Max
535fc1d0fb
Flush the stdio subprocess's coverage data before the clean-exit line (#2840) 2026-06-12 10:23:37 +01:00
Max
7267818e44
Fix unknown-method error code and add a protocol version registry (#2836) 2026-06-11 16:47:22 +01:00
Max
1e21814ed5
Update the v2 status banner and pin spawned environments to the running SDK version (#2834)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled
2026-06-11 09:41:19 +01:00
Max
e196857bc9
Prepare release machinery for v2 pre-releases (#2831) 2026-06-11 09:03:27 +01:00
Max
6d0c160113
Clarify AGENTS.md policy on compat shims for breaking changes (#2823) 2026-06-09 15:16:07 +00:00
Max
5d826490b6
[v2] Dispatcher/ServerRunner receive-path swap — replaces BaseSession (#2710) 2026-06-09 12:58:47 +01:00
Max
b478bff56d
Remove the unsupported WebSocket transport (#2785) 2026-06-08 12:05:27 +01:00
Max
ac96f88abd
Deflake the session-level timeout test with trio's virtual clock (#2788) 2026-06-05 21:33:48 +01:00
Max
bdc48e98b1
Fix stdio client shutdown bugs and rebuild the stdio test suite (#2773) 2026-06-05 16:15:43 +01:00
Max
19fe9faec8
Run StreamableHTTP transport tests in process instead of over sockets (#2767) 2026-06-03 12:45:00 +01:00
Max
ed39e73c0b
Run SSE and Unicode transport tests in process instead of over sockets (#2765) 2026-06-02 21:46:53 +01:00
Max
b3025f93b4
Run transport security tests in process instead of over sockets (#2764) 2026-06-02 20:30:17 +01:00
Max
ed6adeee7e
docs: require a passing conformance test for new 2026-07-28 spec features (#2761) 2026-06-02 18:34:39 +01:00
yukawithdata
a938126327
Doc: Clarify MCP Client-Server model in What is MCP section (#2459) 2026-06-02 18:32:53 +02:00
Zach Leventer
4f6f0e8bf5
Remove dead commented-out code in register_client (#2500) 2026-06-02 18:30:57 +02:00
Aryan Motgi
60c0420775
docs: correct create_mcp_http_client default timeout docstring (#2683)
Co-authored-by: aryanmotgi <carecompanioninc@gmail.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-06-02 16:28:19 +00:00
Max
c91f406938
Require protocol_version to be a string (#2763) 2026-06-02 17:27:41 +01:00
Max
8cc187fac0
Remove Tasks (SEP-1686) from the SDK (#2714) 2026-06-02 18:27:05 +02:00
S;Co
a5b2ebb660
Clarify CLI subprocess environment comment (#2672)
Co-authored-by: scosemicolon <277933778+scosemicolon@users.noreply.github.com>
2026-06-02 16:02:36 +00:00
Siddhiraj Katkar
453cafb9a9
fix: add 'invalid_target' to AuthorizationErrorCode (RFC 8707) (#2642)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-06-02 16:01:50 +00:00
devteamaegis
2a3d065417
fix: rename .gitattribute to .gitattributes so git actually reads it (#2656)
Co-authored-by: devteamaegis <devteamaegis@users.noreply.github.com>
2026-06-02 15:53:51 +00:00
Max
616476f692
Bind transport sessions to the authenticated principal (#2718) 2026-05-29 17:48:40 +01:00
Max
24725633f1
test: interaction-model end-to-end suite with a requirements manifest (#2691) 2026-05-28 18:48:30 +00:00
Max
3eb579948a
Add subject and claims to AccessToken (#2686) 2026-05-26 13:13:55 +00:00
Marcelo Trylesinski
e8e6484278
ci: add zizmor for GitHub Actions security analysis (#2648) 2026-05-20 17:06:57 +02:00
Max
f4753440da
ci: deploy docs to py.sdk.modelcontextprotocol.io via Pages artifact (v1 at /, v2 at /v2/) (#2634) 2026-05-18 15:28:13 +01:00
Max
161834d4ae
refactor: import SSEError from httpx_sse public API (#2560) 2026-05-08 12:42:44 -04:00
Dayna Blackwell
bf3e0010b8
fix: chain exceptions in get_prompt and read_resource handlers (#2542) 2026-05-08 14:28:36 +01:00
Max
2b0da5631a
build: pin PEP 517 build dependencies (#2547) 2026-05-07 17:32:30 +01:00
Gyeongjun Paik (Kent)
3d7b311de0
fix: align Context logging methods with MCP spec data type (#2366)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-04-14 21:41:51 +00:00
Wils Dawson
437d15aa71
SEP-2207: Refresh token guidance (#2039) 2026-04-14 11:48:07 +01:00
Max
5cbd259c3b
fix: catch PydanticUserError when generating output schema (pydantic 2.13 compat) (#2434) 2026-04-13 17:03:40 +01:00
Felix Weinberger
2dfb51a4d1
fix(auth): coerce empty-string optional URL fields to None in OAuthClientMetadata (#2404) 2026-04-13 15:42:35 +01:00
Max
941089e06a
docs: modernize development guidelines and rename to AGENTS.md (#2413) 2026-04-13 12:50:04 +01:00
dependabot[bot]
8f806da611
chore(deps): bump cryptography from 46.0.5 to 46.0.7 in the uv group across 1 directory (#2406)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-12 12:48:13 +00:00
Matt LeMay
c5f12ec1e9
Add resources parameter to MCPServer (#2414)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-04-12 14:42:12 +02:00
Max Isbey
f27d2aac05
docs: fill migration guide gaps surfaced by automated upgrade eval (#2412) 2026-04-09 13:25:16 +01:00
Marcelo Trylesinski
cf4e435db0
Use shared is_async_callable instead of inspect.iscoroutinefunction (#2389) 2026-04-08 13:12:20 +01:00
dependabot[bot]
d5b9155f14
chore(deps): bump requests from 2.32.5 to 2.33.0 in the uv group across 1 directory (#2350)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 17:11:12 -04:00
Marcelo Trylesinski
37891f42a4
Add basic OpenTelemetry tracing for client and server requests (#2381) 2026-03-31 20:33:33 +00:00
Max Isbey
3ce0f76e6e
Don't block the event loop on sync resource and prompt functions (#2380) 2026-03-31 13:43:56 -04:00
Marcelo Trylesinski
e6235d1667
Propagate contextvars.Context through anyio streams without modifying SessionMessage (#2298) 2026-03-31 12:49:38 -04:00
Max Isbey
fb2276b95f
ci: remove claude-code-review workflow (#2359) 2026-03-27 14:02:41 +00:00
Max Isbey
3517a29c82
feat(server): restore dependencies parameter on MCPServer (#2358) 2026-03-27 13:42:15 +00:00
Marcelo Trylesinski
98f8ef295a
Restrict httpx version to <1.0.0 (#2345)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-03-25 22:29:13 +00:00
Felix Weinberger
7ba4fb881d
ci: skip claude.yml when comment is '@claude review' (#2337) 2026-03-24 20:36:36 +00:00
Max Isbey
92c693bb73
fix: cancel in-flight handlers when transport closes in server.run() (#2306) 2026-03-20 13:37:32 +00:00
Max Isbey
883d893097
test: rewrite cli.claude config tests to assert JSON output directly (#2311)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Felix Weinberger <felixweinberger@users.noreply.github.com>
2026-03-19 15:16:34 +00:00
Jonathan Hefner
5388bea53a
docs: generate hierarchical per-module API reference pages (#2103) 2026-03-18 18:15:17 +00:00
Max Isbey
20dd94632e
feat(client): store InitializeResult as initialize_result (#2300) 2026-03-18 17:31:26 +00:00
Max Isbey
67201a9bbd
test: fix WS test port race; narrow to single smoke test covering both transport ends (#2267) 2026-03-18 15:48:30 +00:00
Max Isbey
7826ade12b
test: convert test_integration.py to in-memory transport (fix flaky) (#2277) 2026-03-18 15:25:11 +00:00
Max Isbey
ff50351f9e
ci: run strict-no-cover in scripts/test to catch stale pragmas locally (#2305) 2026-03-17 19:53:39 +00:00
Max Isbey
1a2244f402
fix: handle non-UTF-8 bytes in stdio server stdin (#2302) 2026-03-17 18:40:39 +00:00
Max Isbey
75a80b6f07
refactor: connect-first stream lifecycle for sse and streamable_http (#2292)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-03-16 23:30:20 +00:00
Max Isbey
abfb482246
refactor(examples): migrate all HTTP examples to streamable_http_app() (#2291) 2026-03-16 11:37:01 +00:00
Max Isbey
e1fd62e0f3
fix: close all memory stream ends in client transport cleanup (#2266) 2026-03-13 14:43:54 +00:00
dependabot[bot]
2c73a2a881
chore(deps): bump black from 25.1.0 to 26.3.1 in the uv group across 1 directory (#2290)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 10:39:56 +00:00
Max Isbey
dd52713517
Rewrite TestChildProcessCleanup with socket-based deterministic liveness probe (#2265) 2026-03-12 12:52:32 +00:00
Max Isbey
62eb08e5b2
fix: don't send log notification on transport error (#2257) 2026-03-09 17:47:27 +00:00
Max Isbey
31a38b5078
fix: correct Context type parameters across examples and tests (#2256) 2026-03-09 16:52:56 +00:00
Shivam Aggarwal
51c53f2c18
fix: accept wildcard media types in Accept header per RFC 7231 (#2152)
Co-authored-by: Shivam <shivam@Shivams-MacBook-Air-2.local>
2026-03-09 16:30:02 +00:00
Max Isbey
7ba41dcfae
fix: make local coverage runs reliable (#2236) 2026-03-06 17:24:18 +00:00
Ramesh Reddy Adutla
eaf971cf25
Add warning log when rejecting request with unknown/expired session ID (#2212)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-03-06 16:55:37 +00:00
Max Isbey
92f1b1500d
fix: remove MIME type validation from MCPServer Resource (#2235) 2026-03-06 14:50:58 +00:00
Giulio Leone
b33c811675
perf: use deque for InMemoryTaskMessageQueue FIFO operations (#2165) 2026-03-05 15:44:33 +00:00
Giulio Leone
7c0224828b
fix(oauth): include client_id in token request body for client_secret_post (#2185)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-05 14:57:33 +00:00
Max Isbey
528abfab86
tests: remove lax-no-cover pragmas by moving assertions before cancellation (#2206) 2026-03-04 16:11:34 +00:00
Varun6578
b3149d2f33
fix: clean up SSE session on client disconnect (#2200)
Co-authored-by: Varun Sharma <sharmava@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-03-04 14:45:11 +00:00
Max Isbey
cc22bf5464
refactor: remove request_ctx ContextVar, thread Context explicitly (#2203)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-03-04 13:23:02 +00:00
Max Isbey
62575edabd
ci: sign weekly lockfile commits as github-actions[bot] (#2148) 2026-02-26 15:36:46 +00:00
Jonathan Hefner
0fe16dd5fd
fix: silence mkdocs social plugin warnings in strict mode (#2109) 2026-02-19 23:12:51 +01:00
Jonathan Hefner
cb07adeca3
docs: add code fences to Example: docstring blocks (#2104) 2026-02-19 21:06:11 +01:00
Jonathan Hefner
c0328540c9
docs: fix docstrings across public API surface (#2095) 2026-02-19 06:45:59 +01:00
Den Delimarsky
688c6e3ade
Update SECURITY.md to use GitHub Security Advisories (#2092) 2026-02-18 21:19:25 -08:00
Max Isbey
43d709c976
ci: pin all GitHub Actions to commit SHAs (#2088) 2026-02-18 19:42:00 +00:00
Max Isbey
0e96aecd1d
fix: use exact match for loopback hosts in issuer URL validation (#2089) 2026-02-18 19:40:52 +00:00
Max Isbey
b9431d483f
fix: prevent command injection in example URL opening (#2082) 2026-02-18 15:16:44 +00:00
Max Isbey
e82203bfc4
refactor: remove unused mcp.shared.progress module (#2080) 2026-02-18 13:10:02 +00:00
Akshan Krithick
fc57c2c4c5
test: fix progress notification assertions for related_request_id (#2038)
Co-authored-by: Lee Hubbard <hubbard.zlee@unknowncyber.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-02-18 11:53:03 +00:00
Felix Weinberger
92140e5086
Add idle session timeout to StreamableHTTPSessionManager (#2022) 2026-02-18 10:47:02 +00:00
Felix Weinberger
be5bb7c4f2
fix: normalize trailing slashes before length check in check_resource_allowed (#2074) 2026-02-17 14:34:59 +00:00
Max Isbey
705497a593
fix: allow null id in JSONRPCError per JSON-RPC 2.0 spec (#2056) 2026-02-17 10:30:34 +00:00
BabyChrist666
3b53fb9a00
fix: add HTTP readiness check to wait_for_server and remove dead code in SSE tests (#2073) 2026-02-17 08:34:47 +01:00
Marcelo Trylesinski
2fe56e56de
fix: handle HTTP error status codes in streamable HTTP client (#2047) 2026-02-14 09:49:42 +01:00
Max Isbey
8f669a77e3
fix: explicitly load required pytest plugins in addopts (#2055) 2026-02-13 18:25:10 +00:00
Marcelo Trylesinski
a287a40184
docs: add coverage verification instruction to CLAUDE.md (#2050) 2026-02-13 13:10:00 +00:00
Max Isbey
29a14ab9e5
fix: skip readme-v1-frozen in CI and add diff-based README.md check (#2048) 2026-02-13 10:30:30 +00:00
Max Isbey
1e0b5c0479
fix: revert README.md to v1 documentation (#2045)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-02-12 16:22:07 +00:00
Max Isbey
0a22a9dc33
refactor: replace lowlevel Server decorators with on_* constructor kwargs (#1985) 2026-02-12 15:55:54 +00:00
dependabot[bot]
d6d3ad9a7c
chore(deps-dev): bump pillow from 12.1.0 to 12.1.1 in the uv group across 1 directory (#2036)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-11 14:57:37 +00:00
Aaron Abbott
f049c8e5c6
Fix leaked anyio streams in streamable_http (#1991)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-02-11 14:52:07 +00:00
Marcelo Trylesinski
99edde1125
feat: add /review-pr Claude Code command for PR reviews (#2035) 2026-02-11 15:26:43 +01:00
dependabot[bot]
e39038d879
chore(deps): bump cryptography from 45.0.5 to 46.0.5 in the uv group across 1 directory (#2031)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-11 14:26:16 +01:00
Felix Weinberger
239d682b6f
fix: pass conformance auth scenarios, add RFC 8707 resource validation (#2010) 2026-02-09 11:41:03 +00:00
Marcelo Trylesinski
7c7c13b648
fix: send JSONRPCError instead of bare exceptions in streamable HTTP client (#2005) 2026-02-09 10:38:46 +00:00
Marcelo Trylesinski
dda845aa12
docs: clarify test file structure convention in CLAUDE.md (#2006) 2026-02-07 14:12:54 +01:00
Marcelo Trylesinski
4598ad9361
ci: disable conformance tests (#2007) 2026-02-07 14:12:19 +01:00
dependabot[bot]
1a943ad085
chore(deps): bump the uv group across 1 directory with 2 updates (#1961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-06 09:07:49 +00:00
Max Isbey
d3133ae6ce
chore: update author metadata to LF Projects (#1992) 2026-02-04 11:01:33 +00:00
Marcelo Trylesinski
1a8c14a5b8
refactor: replace mcp.types as types to from mcp import types (#1986) 2026-02-03 16:42:29 +00:00
Marcelo Trylesinski
4fc49c62bd feat: add ClientRequestContext type alias for client-side handlers (#1989) 2026-02-03 17:37:38 +01:00
Marcelo Trylesinski
74f41ff1ae
Add more maintainers to the PyPI metadata (#1976) 2026-02-03 16:19:56 +00:00
Marcelo Trylesinski
b1f7eec3cd
refactor: split RequestContext between server and client (#1987) 2026-02-03 14:35:07 +01:00
Max Isbey
050aeb6456
Remove unnecessary MCPServer alias (#1984) 2026-02-03 08:59:58 +00:00
Guillaume FORTAINE
71e78842cc
feat: add theme field to Icon type for light/dark mode support (#1978) 2026-02-03 09:20:37 +01:00
dependabot[bot]
6f8b791faf
chore(deps): bump the github-actions group with 5 updates (#1981)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-01 17:37:01 +01:00
Marcelo Trylesinski
21822053df
Support different transports in Client (#1972) 2026-01-30 12:11:27 +00:00
Max Isbey
2137c8fbdb
fix: revert README to v1.x documentation on main (#1971)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-01-29 20:03:01 +00:00
Max Isbey
b38716e5e3
ci: skip Claude code review for Dependabot PRs (#1963) 2026-01-27 13:04:36 +00:00
Marcelo Trylesinski
14744c66c5
Add tests for MCPServer.read_resource exception handling (#1957) 2026-01-27 13:49:06 +01:00
Marcelo Trylesinski
6f0e8e7851
refactor: code style improvements and formatting cleanup (#1962)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:48:52 +01:00
Max Isbey
1b5287c727
fix: remove unused requests dependency from simple-chatbot example (#1958) 2026-01-26 14:02:11 +00:00
Marcelo Trylesinski
acba5478a9
refactor: McpError renamed to MCPError and flatten parameters (#1956)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-01-26 14:37:44 +01:00
Marcelo Trylesinski
65c614e48e
Rename FastMCP to MCPServer (#1951) 2026-01-25 14:45:52 +01:00
Max Isbey
4a2d83a0cb
ci: skip claude code review for fork PRs (#1952) 2026-01-24 19:22:24 +00:00
Marcelo Trylesinski
ce75b2d7d2
refactor: improve type docstrings and remove internal type exports (#1950) 2026-01-24 17:17:09 +01:00
Max Isbey
a7ddfdae07
ci: add strict-no-cover to detect unnecessary coverage pragmas (#1897) 2026-01-23 21:00:20 +01:00
Marcelo Trylesinski
3cfdea0e97
refactor: move stdio package to its own module (#1942) 2026-01-23 18:35:05 +01:00
Marcelo Trylesinski
a1d330de9f
Proper handle extra data in MCP objects (#1937) 2026-01-23 15:35:23 +01:00
Max Isbey
5301298225
Add claude GitHub action (#1922) 2026-01-22 18:43:42 +00:00
Marcelo Trylesinski
f0ab53e194
Add meta to Client methods (#1923) 2026-01-22 14:50:39 +01:00
Marcelo Trylesinski
d77292fb06
refactor: drop test classes (#1924) 2026-01-22 12:37:52 +01:00
Marcelo Trylesinski
7b728a243e
refactor: drop unused logger/logging (#1926) 2026-01-22 11:56:41 +01:00
Marcelo Trylesinski
656f887aff
fix: replace http code by jsonrpc (#1920) 2026-01-21 18:37:55 +01:00
Max Isbey
213cf993ca
Add conformance testing CI pipeline (#1915) 2026-01-21 16:04:35 +00:00
Marcelo Trylesinski
3bcdc1763f
refactor: create the types subpackage, and the jsonrpc module (#1918) 2026-01-21 16:49:07 +01:00
Marcelo Trylesinski
ae7793efd6
refactor: avoid double JSON parsing (#1917) 2026-01-21 13:07:05 +01:00
Marcelo Trylesinski
6d463bb353
refactor: replace AnyFunction by proper types (#1916) 2026-01-21 13:06:46 +01:00
Marcelo Trylesinski
bcb07c2b25
refactor: flatten the methods in Client (#1914) 2026-01-20 13:12:07 +01:00
Marcelo Trylesinski
3dc8b72041
refactor: replace pydantic_core.from_json by json.loads (#1912) 2026-01-19 20:04:28 +01:00
Max Isbey
34e66a3812
refactor: move streamable HTTP app creation from FastMCP to lowlevel Server (#1899) 2026-01-19 18:48:04 +01:00
Rin
0f9a41d777
chore: include cli and ws extras in dev environment (#1905)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-01-19 14:53:08 +01:00
Marcelo Trylesinski
1c258ae203
Don't block the event loop on non-async functions (#1909) 2026-01-19 14:47:37 +01:00
Marcelo Trylesinski
5fdd48a0d3
Completely drop RootModel from types module (#1910) 2026-01-19 14:29:15 +01:00
Marcelo Trylesinski
f4672c5084
Drop RootModel from JSONRPCMessage (#1908) 2026-01-19 14:04:15 +01:00
Felix Weinberger
dcc9b4f8e0
refactor: use Client class in tests (#1900) 2026-01-17 09:46:58 +01:00
Felix Weinberger
d41d0c0128
chore: add D212 lint rule to enforce Google-style docstrings (#1892) 2026-01-16 16:10:52 +00:00
Felix Weinberger
df039bf97c
Add ergonomic Client class for testing MCP servers (#1870) 2026-01-16 15:49:26 +00:00
Marcelo Trylesinski
8adb5bdce8
refactor: move transport-specific parameters from FastMCP constructor to run() (#1898)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:16:20 +00:00
Max Isbey
5d80f4efc8
refactor: move inline imports to module level (#1893) 2026-01-16 14:54:08 +00:00
Marcelo Trylesinski
c06362126b
chore: add more opinions to CLAUDE.md (#1887) 2026-01-16 15:52:33 +01:00
Marcelo Trylesinski
e94b386a13
refactor: use snake case instead of camel case in types (#1894) 2026-01-16 15:51:27 +01:00
Max Isbey
7080fcf1e6
refactor: move inline imports to top of routes.py (#1888) 2026-01-16 15:05:47 +01:00
Felix Weinberger
0622babbd9
fix: URL-decode parameters extracted from resource templates (#1864) 2026-01-16 13:47:21 +00:00
Marcelo Trylesinski
edf0950a6a
Revert mount_path parameter from FastMCP (#1881) 2026-01-16 13:23:08 +00:00
Marcelo Trylesinski
2b4c7ebc61
ci: add alls-green action for single required check (#1882) 2026-01-16 12:14:50 +01:00
Max Isbey
2cf7784a48
ci: replace highest resolution with locked in test matrix (#1869)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-01-16 11:09:25 +01:00
Felix Weinberger
9f427f3292
fix: add default values to Literal type fields in content types (#1867) 2026-01-16 10:01:57 +00:00
Felix Weinberger
d5edcb3e92
docs: add breaking changes guidance to CLAUDE.md (#1879) 2026-01-16 10:54:53 +01:00
Marcelo Trylesinski
68cbabb9d7
refactor: introduce MCPModel base class for protocol types (#1880)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-01-16 10:51:36 +01:00
dependabot[bot]
b63776b14f
chore(deps): bump the github-actions group with 7 updates (#1878)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-16 10:00:49 +01:00
Marcelo Trylesinski
2ce41a8347
Drop dependencies parameter from FastMCP (#1877) 2026-01-16 08:59:44 +00:00
Felix Weinberger
cfb2909631
fix: change Resource URI fields from AnyUrl to str (#1863) 2026-01-16 09:58:57 +01:00
Marcelo Trylesinski
f2abefffaf
Add Dependabot configuration for GitHub Actions (#1876) 2026-01-16 09:55:32 +01:00
Max Isbey
c9e98e53a7
ci: pin all GitHub Actions to commit SHAs (#1875) 2026-01-16 08:52:42 +00:00
Max Isbey
fc5cb6ab48
ci: add weekly lockfile update workflow (#1874) 2026-01-16 09:47:06 +01:00
Marcelo Trylesinski
812a46ab97
Remove deprecated cursor parameter and ResourceReference (#1871) 2026-01-15 21:33:21 +01:00
Max Isbey
72a8631f0a
fix(ci): use bash shell for pytest step to ensure consistent error handling (#1868) 2026-01-15 17:32:22 +01:00
Marcelo Trylesinski
024d7597fd
Drop Content and args parameter in ClientSessionGroup.call_tool (#1866) 2026-01-15 16:24:53 +00:00
Max Isbey
c251e728f4
Add v2 development warning to README (#1862) 2026-01-15 15:39:02 +00:00
Marcelo Trylesinski
f2b89ec83e
docs: add migrations page (#1859) 2026-01-15 10:13:18 +00:00
Marcelo Trylesinski
32a4587da9
chore: drop as _annotations (#1858) 2026-01-15 10:07:06 +00:00
Marcelo Trylesinski
8893b022e8
Drop deprecated streamablehttp_client (#1836) 2026-01-15 11:02:24 +01:00
Max Isbey
b26e5b907f
Add missing TasksCallCapability to enable proper MCP task support (#1854)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-15 10:55:38 +01:00
Yann Jouanin
0da9a074d0
Support for Resource and ResourceTemplate metadata (#1840)
Co-authored-by: Jacem Elwaar <jacem@mcpappsbuilders.com>
2026-01-12 14:45:31 +00:00
Max Isbey
6b69f6354a
docs: fix simple-auth README references to non-existent scripts (#1829) 2026-01-08 14:45:31 +00:00
Marcelo Trylesinski
3ffe142e9a
Support Python 3.14 (#1834) 2026-01-07 16:28:23 +00:00
Marcelo Trylesinski
1fd557afdc
Add type checker to examples/client (#1837) 2026-01-07 17:08:18 +01:00
Yann Jouanin
3863f203e9
Server initialize response update to last spec (add title, description) (#1634) 2026-01-06 21:34:08 +00:00
Jay Hemnani
f397783c39
fix: add explicit type annotation for call_tool decorator (#1826)
Co-authored-by: Jay Hemnani <jayhemnani9910@gmail.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-01-06 21:11:36 +01:00
Marcelo Trylesinski
201de920a0
refactor: use __future__.annotations (#1832) 2026-01-06 20:03:13 +01:00
Marcelo Trylesinski
adcc17b968
types: add missing py.typed from examples package (#1833) 2026-01-06 18:54:58 +00:00
Marcelo Trylesinski
6149b63a44
tests: add missing init files (#1831) 2026-01-06 19:52:09 +01:00
Max Isbey
37de50144f
fix: add StatelessModeNotSupported exception and improve tests (#1828) 2026-01-06 11:26:26 +00:00
Max Isbey
bb6cb029f0
fix: raise clear error for server-to-client requests in stateless mode (#1827) 2026-01-05 17:17:54 +00:00
gazzadownunder
d52937b39c
Make refresh_token grant type optional in DCR handler (#1651)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 13:41:45 +00:00
Max Isbey
c7cbfbb302
docs: add guidance on discussing features before opening PRs (#1760) 2026-01-01 10:07:06 +01:00
Maxime
78a9504ec1
fix: return HTTP 404 for unknown session IDs instead of 400 (#1808)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-12-31 14:06:12 +00:00
jnjpng
a9cc822a10
fix: accept HTTP 201 status code in token exchange (#1503)
Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com>
2025-12-19 18:22:00 +00:00
Ankesh Kumar Thakur
a4bf947540
fix: Token endpoint response for invalid_client (#1481)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-12-19 18:19:00 +00:00
Max Isbey
3f6b0597f1
docs: update CONTRIBUTING with v2 branching strategy (#1804) 2025-12-19 18:11:43 +00:00
V
06748eb4c4
fix: Include extra field for context log (#1535) 2025-12-19 17:54:03 +00:00
Yugan
2aa1ad2a69
feat: standardize timeout values to floats in seconds (#1766) 2025-12-19 12:22:56 +00:00
Yugan
4807eb5a80
Add workflow to comment on PRs when released (#1772) 2025-12-19 11:17:13 +00:00
Max Isbey
ef96a31671
ci: add v1.x branch to main-checks workflow (#1802)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-12-18 16:59:30 +00:00
zenlytix
8ac0cab98c
Fix for Url Elicitation issue 1768 (#1780) 2025-12-15 18:58:17 +01:00
Ondrej Mosnáček
65b36de4eb
fix: use correct python command name in test_stdio.py (#1782)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Signed-off-by: Ondrej Mosnáček <omosnacek@gmail.com>
2025-12-12 14:02:38 +00:00
Marcelo Trylesinski
a3a4b8d11a
Add streamable_http_client which accepts httpx.AsyncClient instead of httpx_client_factory (#1177)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-12-10 16:39:00 +00:00
Camila Rondinini
cc8382ce3e
Fix JSON-RPC error response ID matching (#1720)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-12-10 16:15:21 +00:00
Jeremiah Lowin
0dedbd9831
feat: client-side support for SEP-1577 sampling with tools (#1722) 2025-12-09 23:36:28 +00:00
Max Isbey
779271ae38
chore: remove release-comment workflow (#1758)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-12-09 15:45:08 +00:00
Arjun TS
2bf9b10f63
Skip empty SSE data to avoid parsing errors (#1753)
Co-authored-by: ARJUN-TS1 <arjun.ts1@ibm.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-12-09 15:14:23 +00:00
Anton Pidkuiko
8ac11ec604
fix: allow MIME type parameters in resource validation (RFC 2045) (#1755)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-09 14:56:40 +00:00
Felix Weinberger
b7cc25493c
feat: add workflow to comment on PRs when released (#1750) 2025-12-09 12:22:07 +00:00
Max Isbey
8b984d93a3
refactor(auth): remove unused _register_client method (#1748) 2025-12-08 21:50:20 +00:00
Felix Weinberger
89ff338174
fix: skip priming events and close_sse_stream for old protocol versions (#1719)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-12-04 14:44:08 +00:00
Edison
9ed0b93ceb
fix: handle ClosedResourceError in StreamableHTTP message router (#1384)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-12-04 11:36:23 +01:00
Tyler Mailman
72a34002aa
fix: add lifespan context manager to StreamableHTTP mounting examples (#1669)
Co-authored-by: TheMailmans <tyler@example.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-12-03 22:08:21 +00:00
Felix Weinberger
8e02fc17e1
chore: update LATEST_PROTOCOL_VERSION to 2025-11-25 (#1715)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-12-02 18:27:27 +00:00
Paul Carleton
d3a184119e
Merge commit from fork
Some checks failed
Main branch checks / checks (push) Has been cancelled
* Auto-enable DNS rebinding protection for localhost servers

When a FastMCP server is created with host="127.0.0.1" or "localhost"
and no explicit transport_security is provided, automatically enable
DNS rebinding protection. Both 127.0.0.1 and localhost are allowed
as valid hosts/origins since clients may use either to connect.

* Add tests for auto DNS rebinding protection on localhost

Tests verify that:
- Protection auto-enables for host=127.0.0.1
- Protection auto-enables for host=localhost
- Both 127.0.0.1 and localhost are in allowed hosts/origins
- Protection does NOT auto-enable for other hosts (e.g., 0.0.0.0)
- Explicit transport_security settings are not overridden

* Add IPv6 localhost (::1) support for DNS rebinding protection

Extend auto-enable DNS rebinding protection to also cover IPv6
localhost. When host="::1", protection is now auto-enabled with
appropriate allowed hosts ([::1]:*) and origins (http://[::1]:*).

* Fix import ordering in test file
2025-12-02 13:23:55 +00:00
Felix Weinberger
fa851d93a2
feat: backwards-compatible create_message overloads for SEP-1577 (#1713) 2025-12-02 13:17:45 +00:00
Paul Carleton
f82b0c9371
Support client_credentials flow with JWT and Basic auth (#1663)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-12-02 12:53:55 +00:00
Felix Weinberger
281fd4765e
Add SSE polling support (SEP-1699) (#1654) 2025-12-02 11:44:49 +00:00
Camila Rondinini
2cd178a962
Add on_session_created callback option (#1710) 2025-12-01 17:48:33 +00:00
Max Isbey
c92bb2f7ff
SEP-1686: Tasks (#1645) 2025-11-28 18:51:58 +00:00
Felix Weinberger
5983a650cc
Skip empty SSE data to avoid parsing errors (#1670) 2025-11-26 18:09:39 +00:00
Chris Coutinho
02b7889929
Implement SEP-1036: URL mode elicitation for secure out-of-band interactions (#1580)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-11-25 11:00:21 +00:00
Paul Carleton
27279bc157
Update doc string on custom_route (#1660) 2025-11-24 19:20:56 +00:00
Paul Carleton
f22501315e
feat: implement SEP-991 URL-based client ID (CIMD) support (#1652)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-11-24 17:21:03 +00:00
Felix Weinberger
f2517fe14b
fix: url for spec (#1659) 2025-11-24 16:59:48 +00:00
Felix Weinberger
091afb82dc
Implement SEP-986: Tool name validation (#1655) 2025-11-24 16:46:57 +00:00
Paul Carleton
998f0ee4db
[auth][conformance] add conformance auth client (#1640)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-11-24 13:52:04 +00:00
Tapan Chugh
b19fa6f279
SEP-1330: Elicitation Enum Schema Improvements and Standards Compliance (#1246)
Co-authored-by: Tapan Chugh <tapanc@cs.washington.edu>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-11-23 23:32:08 +00:00
Olivier Chafik
71c475588f
Implement SEP-1577 - Sampling With Tools (#1594)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-22 23:58:14 -05:00
Jon Shea
c51936f61f
Add client_secret_basic authentication support (#1334)
Co-authored-by: Paul Carleton <paulc@anthropic.com>
2025-11-20 20:53:37 +00:00
Felix Weinberger
397089a78e
Add tests for JSON Schema 2020-12 field preservation (SEP-1613) (#1649) 2025-11-20 20:51:13 +00:00
adam jones
fcffa14b5b
docs: Update examples to use stateless HTTP with JSON responses (#1499)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-11-20 15:06:37 +00:00
Liang Wu
9c8f763aa8
chore: Lazy import jsonschema library (#1596)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-11-19 15:30:52 +00:00
Andrii Blyzniuk
5489e8b6fb
fix get_client_metadata_scopes on 401 (#1631)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-11-16 17:18:50 +00:00
inaku
a357380cfa
feat: Pass through and expose additional parameters in ClientSessionGroup.call_tool and .connect_to_server (#1576) 2025-11-16 15:57:43 +00:00
Max Isbey
9724ad1ce6
Fix CI highest resolution test to actually test highest versions (#1609)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-11-13 20:25:43 +00:00
Victorien
116c13e2c6
Refactor func_metadata() implementation (#1496) 2025-11-13 20:21:15 +00:00
Max Isbey
91ccdb3d65
Fix OAuth discovery fallback and URL ordering (#1624) 2025-11-13 19:37:24 +00:00
Max Isbey
5e1aa4072b
Add client_id_metadata_document_supported to OAuthMetadata (#1603) 2025-11-13 17:13:55 +00:00
Max Isbey
7d12e83cf4
refactor: extract OAuth helper functions and simplify provider state (#1586) 2025-11-13 13:28:48 +00:00
Felix Weinberger
be730674ad
Add SEP-1034 conformance test support to everything-server (#1604)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-11-11 17:44:23 +00:00
Max Isbey
720d7130e2
Add coverage baseline commit to git-blame-ignore (#1613) 2025-11-11 14:03:51 +00:00
Max Isbey
a864840825
Add end-of-file-fixer pre-commit hook (#1610) 2025-11-11 13:23:08 +00:00
Max Isbey
89e9c43acf
Get baseline 100% clean coverage (#1553) 2025-11-11 14:09:32 +01:00
Felix Weinberger
da4fce2195
Add everything-server for comprehensive MCP conformance testing (#1587) 2025-11-09 13:45:37 +00:00
Camila Rondinini
9eae96a05e
Add get_server_capabilities() to ClientSession (#1588)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-11-06 20:44:40 +00:00
Max Isbey
b7b0f8eebb
Add --frozen flag to uv run commands in Claude config (#1583) 2025-11-06 16:07:15 +00:00
Chris Coutinho
3390e49c01
Implement SEP-985: OAuth Protected Resource Metadata discovery fallback (#1548)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Paul Carleton <paulc@anthropic.com>
2025-11-05 15:51:02 +00:00
inaku
de2289dc93
Fix typo in ClientSessionGroup doc string (#1572) 2025-11-04 13:33:26 +00:00
Cole Murray
3a9f118e34
Update Starlette to 0.49.1 in uv.lock (#1559) 2025-11-03 08:15:08 +00:00
Koichi ITO
202af49857
docs: use article "an" before "MCP" instead of "a" (#1558) 2025-10-31 09:55:12 +01:00
Paul Carleton
6f2cd0cef0
Fix auth client example URL handling for oauth provider (#1549)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-10-30 15:35:44 +00:00
Max Isbey
8cdac3d2f8
Fix pyright error and replace wildcard import with explicit imports (#1532) 2025-10-29 18:32:26 +00:00
Luca Chang
f161149680
Implement RFC 7523 JWT flows (#1247)
Co-authored-by: Yann Jouanin <yann.jouanin@valueandco.com>
2025-10-29 16:48:08 +00:00
Max Isbey
db9e451551
fix: Replace remaining manual server polling with wait_for_server helper (#1529) 2025-10-29 11:27:57 +00:00
Max Isbey
c44e68f766
Fix flaky timeout test in test_88_random_error (#1525) 2025-10-28 14:45:25 -07:00
Max Isbey
3e86edfb2f
fix: Replace arbitrary sleeps with active server readiness checks in tests (#1527)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 21:42:33 +00:00
Max Isbey
f97f7c4a7d
fix: Replace fixed sleep with active server readiness check in SSE tests (#1526) 2025-10-28 19:49:13 +00:00
Max Isbey
7ac40af302
fix: Update spec links to new modelcontextprotocol.io location (#1491) 2025-10-28 18:13:40 +00:00
Yukuan Jia
673423da0d
fix: replace deprecated dev-dependencies in examples/clients (#1518) 2025-10-28 14:09:14 +00:00
adam jones
31ae5f4dc4
Relax Accept header requirement for JSON-only responses (#1500) 2025-10-27 17:46:04 +00:00
Max Isbey
6c26d087df
update uv to 0.9.5 (#1510)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-10-24 01:04:09 +00:00
Yukuan Jia
340af403bd
Replace deprecated dev-dependencies with dependency-groups (#1488)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-10-23 17:22:16 -07:00
mingo007
b7e4ae7542
test: use errno.ENOENT for command not found assertion (#1498) 2025-10-23 16:30:50 -07:00
Christian Clauss
bbc8b4904e
Upgrade GitHub Actions (#1473) 2025-10-23 11:33:45 -07:00
Felix Weinberger
9f9dbad416
fix: use proper dependency resolution in CI (#1507) 2025-10-23 10:25:48 -07:00
Christian Clauss
35a9ccdb42
fix: uv CVE-2025-62518 astral-tokio-tar issue GHSA-j5gw-2vrg-8fgx (#1505) 2025-10-22 15:52:56 +02:00
Brandon Shar
1200ba0082
Allow CallToolResult to be returned directly to support _meta field for OpenAI Apps (#1459)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-10-21 18:52:08 +00:00
Samuel Felipe Chenatti
40acbc596c
Expose RequestParams._meta in ClientSession.call_tool (#1231)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-10-17 17:57:56 +01:00
Luca Chang
c733eec5d9
Make client examples workspaces to reflect package code (#1466) 2025-10-17 17:13:39 +01:00
Mat Leonard
98f82485bd
feat: add tool metadata in FastMCP.tool decorator (#1463)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-10-17 11:18:34 +00:00
daamitt
dcc68ce56b
fix: Set the Server session initialization state immediately after respond… (#1478)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2025-10-15 13:56:43 +01:00
Felix Weinberger
de89457683
fix: send params as empty object for list methods without cursor (#1453) 2025-10-14 11:11:38 +01:00
Fenn Bailey
cd7253c593
feat: add resource annotations support to FastMCP (#1468) 2025-10-14 11:07:37 +01:00
Rocky Haotian Du
eb34ab7c72
fix: Remove unnecessary constructor from ResourceServerSettings (#1424)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-10-14 09:21:15 +02:00
Felix Weinberger
7a933fe5da
Fix workspace configuration error with structured_output_lowlevel.py (#1471)
Co-authored-by: lorenss-m <saeclmusic@gmail.com>
2025-10-13 15:53:46 +01:00
AishwaryaKalloli
b4e50aa9f9
Handles message type Exception in lowlevel/server.py _handle_message function. Mentioned as TODO on line 528. (#786)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-10-13 14:29:03 +01:00
Dogacan Colak
0e29cc4130
[client] Implement MCP OAuth scope selection and step-up authorization (#1324) 2025-10-13 14:18:00 +01:00
Felix Weinberger
61399b386c
fix: update CLAUDE.md to remove auto-addition of reviewers. (#1431)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-10-06 14:54:13 +01:00
Felix Weinberger
7d335efd82
Update README to link to Python SDK documentation (#1430) 2025-10-06 14:50:17 +01:00
Brandon Wu
b8e758b02b
feat: add ability to remove tools (#1322)
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 14:16:50 +01:00
Marcus Shu
df3e428ee8
Improve OAuth protected resource metadata URL construction per RFC 9728 (#1407) 2025-10-06 13:52:44 +01:00
Marcelo Trylesinski
814c9c024a
Add documentation about testing (#1426) 2025-10-03 14:17:31 +00:00
Marcelo Trylesinski
89619a8604
Add documentation structure (#1425) 2025-10-03 13:17:08 +00:00
Peter Alexander
3e798bfc01
Add icons support for ResourceTemplate (#1412)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-10-01 13:23:46 +01:00
ZhangChuanhui
f676f6c0f0
fix: catch and rethrow SSEError during SSE connection establishment (#975)
Co-authored-by: zhangchuanhui <zhangchal@digitalchina.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-09-30 15:52:32 +01:00
Lorenzo
d648a31359
fix: improve misleading warning for progress callback exceptions (#775) 2025-09-30 14:39:52 +01:00
Yukuan Jia
c81ff64e94
fix: fix the system message in simple-chatbot example (#1394) 2025-09-30 12:22:33 +01:00
Felix Weinberger
8dd15ac71c
Delete CODEOWNERS to eliminate notification overload (#1413) 2025-09-30 11:15:07 +01:00
Peter Alexander
80c0d23d48
Update Icon.sizes to use string array format (#1411) 2025-09-29 14:27:32 +01:00
Peter Alexander
60f4b2d10a
Add comprehensive Unicode tests for streamable HTTP transport (#1381) 2025-09-29 14:09:33 +01:00
automaton82
9323efad99
Issue 1379 patch - Fix MCP server OAuth not working with Visual Studio Code and others with extra grant_types (#1380) 2025-09-29 11:13:16 +01:00
Jon Shea
1940040ac3
Accept additional response_types values from OAuth servers (#1323) 2025-09-26 20:29:06 +01:00
pengwa
146d7efb0f
Add error log for client stdio (#924)
Co-authored-by: Your Name <youremail@yourdomain.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-09-26 15:42:23 +01:00
Peter Alexander
71889d7387
Implementation of SEP 973 - Additional metadata + icons support (#1357)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-09-24 20:29:04 +01:00
Tapan Chugh
b85e7bd1a9
feat: Add SDK support for SEP-1034 default values in elicitation schemas (#1337)
Co-authored-by: Tapan Chugh <tapanc@cs.washington.edu>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-09-24 16:33:45 +01:00
Max Isbey
c0f165776d
Use streamable-http consistently in examples (#1389) 2025-09-23 20:19:14 +01:00
Mike Salvatore
7629fe6b97
Remove "unconditionally" from conditional description (#1289) 2025-09-23 17:13:26 +01:00
Max Isbey
4fb975c6dc
feat: add paginated list decorators for prompts, resources, and tools (#1286)
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-23 14:58:06 +01:00
Steve Billings
03e19f1c90
Fastmcp logging progress example (#1270)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-09-23 14:48:38 +01:00
Yann Jouanin
20596e5f41
Add test for ProtectedResourceMetadataParsing (#1236)
Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-09-23 12:48:21 +01:00
Peter Alexander
7e93a9fc19
Return HTTP 403 for invalid Origin headers (#1353) 2025-09-22 16:44:51 +01:00
Peter Alexander
ca34666663
Use --frozen in pre-commit config (#1375)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-09-17 20:00:46 +02:00
owengo
b41b9175e8
fix: allow elicitations accepted without content (#1285)
Co-authored-by: Olivier Schiavo <olivier.schiavo@wengo.com>
2025-09-17 15:34:15 +01:00
pchoudhury22
ca5cb4cb67
fix(fastmcp): propagate mimeType in resource template list (#1186)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-09-17 14:50:41 +01:00
David Soria Parra
c3717e7ad3
Fix context injection for resources and prompts (#1336)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-09-11 14:30:06 +01:00
Christian Clauss
c47c767ff4
Python lint: Ruff rules for pylint and code complexity (#525) 2025-09-05 15:20:14 +01:00
Eleftheria Stein-Kousathana
47d35f0b3c
Allow ping requests before initialization (#1312) 2025-09-01 22:37:36 +01:00
Reid Geyer
346e794d59
docs: Update README to include link to API docs for #1329 (#1330) 2025-09-01 22:13:48 +01:00
Peter Alexander
356dfa692b
Update simple-resource example to use non-deprecated read_resource return type (#1331)
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-01 17:58:01 +01:00
jess
1644b822b3
changes structured temperature to not deadly (#1328) 2025-08-31 23:09:49 +00:00
Sreenath Somarajapuram
07ae8c0d4e
types: Setting default value for method: Literal (#1292) 2025-08-26 16:22:56 +01:00
Justin Wang
9c6fd15a88
SDK Parity: Avoid Parsing Server Response for non-JsonRPCMessage Requests (#1290) 2025-08-26 16:14:47 +01:00
San Nguyen
eaf7cf41d5
fix: error too many values to unpack (expected 2) (#1279)
Signed-off-by: San Nguyen <vinhsannguyen91@gmail.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-08-23 21:36:39 +01:00
Tim Esler
9a8592e7d4
Add PATHEXT to default STDIO env vars in windows (#1256) 2025-08-22 13:47:19 +01:00
keurcien
e750a06a99
fix: avoid uncessary retries in OAuth authenticated requests (#1206)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-08-22 11:45:54 +01:00
xavier
f4b2957a20
Added Audio to FastMCP (#1130) 2025-08-22 11:45:38 +01:00
Jerome
09e3a05e13
feat: Add CORS configuration for browser-based MCP clients (#1059)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-08-21 14:34:45 +01:00
Felix Weinberger
ff02c596a4
docs: clarify streamable_http_path configuration when mounting servers (#1172) 2025-08-21 14:15:37 +01:00
Max Isbey
824ef8a281
chore: uncomment .idea/ in .gitignore (#1287)
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 15:00:28 +01:00
Moustapha Ebnou
d1ac8d68eb
fix: prevent async generator cleanup errors in StreamableHTTP transport (#1271)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
2025-08-14 13:51:59 +01:00
David Soria Parra
0926613714
Update dependencies and fix type issues (#1268)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-08-14 10:40:47 +01:00
Gregory L
a82c69bfd9
Consistent casing for default headers Accept and Content-Type (#1263) 2025-08-12 11:53:26 +02:00
Marcelo Trylesinski
c7671e470c
Add pyright strict mode on the whole project (#1254) 2025-08-11 18:56:37 +01:00
joesavage-silabs
ef4e167c1a
Remove strict validation on response_modes_supported member of OAuthMetadata (#1243)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-08-07 18:39:35 +01:00
Yann Jouanin
e68e513b42
fix: allow to pass list[str] to token_endpoint_auth_signing_alg_values_supported (#1226) 2025-08-06 10:21:24 +02:00
Omer Korner
d4759288d0
Improve README around the Context object (#1203) 2025-08-05 15:06:57 +01:00
Sreenath Somarajapuram
34e3664e69
chore: Remove unused variable notification_options (#1238) 2025-08-05 10:44:51 +01:00
Yann Jouanin
68e25d478b
Improved supported for ProtectedResourceMetadata (#1235)
Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com>
2025-08-04 18:29:01 +00:00
Tapan Chugh
6c61058190
chore: Remove unused prompt_manager.py file (#1229)
Co-authored-by: Tapan Chugh <tapanc@cs.washington.edu>
2025-08-04 14:19:07 +01:00
Marcelo Trylesinski
959d4e39ae
Unpack settings in FastMCP (#1198)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-07-26 10:37:48 +02:00
lukacf
b34e7207ad
server: skip duplicate response on CancelledError (#1153)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-25 10:16:13 +01:00
Luca Chang
35777b9811
fix: perform auth server metadata discovery fallbacks on any 4xx (#1193)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-07-24 11:32:35 +01:00
Inna Harper
6d2e6d442d
update codeowners group (#1191) 2025-07-23 19:10:38 +01:00
Clare Liguori
6a84a2f79f
fix: fix OAuth flow request object handling (#1174)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-07-21 13:36:21 +01:00
leweng
c260e29a92
Fix: Replace threading.Lock with anyio.Lock for Ray deployment compatibility (#1151) 2025-07-21 11:22:49 +01:00
Marcelo Trylesinski
7570ba30cf
Make sure RequestId is not coerced as int (#1178) 2025-07-21 10:57:47 +01:00
Inna Harper
0b1b52ba45
fix flaky test test_88_random_error (#1171) 2025-07-18 16:21:33 +01:00
Inna Harper
ba58ebcad5
Add CODEOWNERS file for sdk (#1169) 2025-07-18 15:58:28 +01:00
Luca Chang
99c4f3c906
Support falling back to OIDC metadata for auth (#1061)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-07-17 20:06:38 +01:00
Inna Harper
41184ba6c8
README - replace code snippets with examples -- auth examples (#1164) 2025-07-17 18:36:20 +01:00
Inna Harper
813da6a579
fix flaky fix-test_streamablehttp_client_resumption test (#1166) 2025-07-17 18:35:40 +01:00
Stanley Law
11162d7790
docs: add pre-commit install guide on CONTRIBUTING.md (#995)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-07-17 13:17:59 +01:00
Dagang Wei
dd792977dd
Docs: Update CallToolResult parsing in README (#812)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-07-17 12:48:19 +01:00
Ian Davenport
c0bc6668c4
Tests(cli): Add coverage for helper functions (#635) 2025-07-17 11:52:01 +01:00
Marcelo Trylesinski
a99711d43f
chore: don't allow users to create issues outside the templates (#1163) 2025-07-17 10:32:02 +01:00
Inna Harper
e975d0550b
README - replace code snippets with examples - streamable http (#1155) 2025-07-16 21:45:40 +01:00
Inna Harper
0b4ce00be2
README - replace code snippets with examples - add lowlevel to snippets (#1150) 2025-07-16 15:26:16 +01:00
Inna Harper
6566c08446
fix markdown error in README in main (#1147) 2025-07-15 13:23:37 +01:00
Pamela Fox
0130f5b12b
Add streamable HTTP starlette example to Python SDK docs (#1111) 2025-07-15 11:39:59 +01:00
yurikunash
eb5146dc8b
Implement RFC9728 - Support WWW-Authenticate header by MCP client (#1071) 2025-07-15 10:23:39 +01:00
Felix Weinberger
bd9885feda
Add regression test for stateless request memory cleanup (#1140) 2025-07-15 09:29:29 +01:00
Marcelo Trylesinski
95b44fb0d7
tests: use inline_snapshot.Is on parametrized test (#945)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
2025-07-14 21:59:37 +01:00
hopeful0
b8cb3675dc
Close server session after handle stateless request (#1116) 2025-07-14 17:26:06 +01:00
Inna Harper
a10fec7fa6
README - replace code snippets with examples - direct execution and display utilities (#1137) 2025-07-14 15:54:36 +01:00
Inna Harper
9bf5160eea
README - replace code snippets with examples (#1136) 2025-07-14 13:57:47 +01:00
bhosmer-ant
49991fd2c7
Fix BaseModel method name conflicts in func_metadata (#1123) 2025-07-14 08:29:52 +01:00
bhosmer-ant
d28a1a69ee
add regression tests for #1113 (#1122) 2025-07-11 11:53:45 +01:00
yurikunash
fd11eca766
Update routing for streamable HTTP to avoid 307 redirect (#1115)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-11 11:49:24 +01:00
Giuseppe Coviello
0a4e8d4e17
Avoiding to parse string arguments containing valid JSON if the argument annotation is str (#1113) 2025-07-10 17:36:45 -04:00
Jeremiah Lowin
81fe63a81d
Ensure failed oauth registration response is read before accessing response.text (#1118)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-10 22:01:17 +01:00
John T
ee54acbfa3
The lifespan_context object here is not a dictionary like the other e… (#1002)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-10 11:55:34 +01:00
Inna Harper
b2d4322ca0
Fix missing line in generated snippets (#1117) 2025-07-10 10:17:05 +01:00
Stanley Law
6d092af6fd
chore: add markdownlint on pre-commit hook and lint md files (#996)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-07-10 09:43:49 +01:00
Megan Dsouza
f55831ee79
Adding description field to the FastMCP get_prompt method (#614)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-09 20:50:38 +01:00
Brian Soby
7b1078b515
Fix: Prevent session manager shutdown on individual session crash (#841)
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Co-authored-by: Inna Harper <inna.hrpr@gmail.com>
2025-07-09 15:43:21 +01:00
Felix Weinberger
3abefeeeaf
fweinberger/align shutdown with spec (#1091)
Co-authored-by: davenpi <davenport.ianc@gmail.com>
2025-07-09 10:14:30 -04:00
Inna Harper
9b4c2ddf93
fix pyright check in CI (#1108) 2025-07-09 11:31:54 +01:00
Inna Harper
c067ef291e
Fix completions (#1107) 2025-07-09 11:15:49 +01:00
Inna Harper
4fee123e72
clean up log.error (#1109) 2025-07-09 11:15:14 +01:00
AishwaryaKalloli
bd84329a08
Doc update + validation in SseServerTransport + existing test fixes: addresses Issue: #827 (#900) 2025-07-09 08:19:06 +01:00
Daniel Avdar
b16c2a8518
#552 (#707)
Signed-off-by: DanielAvdar <66269169+DanielAvdar@users.noreply.github.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
2025-07-08 17:24:36 +01:00
Inna Harper
beacd6e009
remove max message size (#1105) 2025-07-08 15:56:18 +01:00
Felix Weinberger
e0336213e8
Improve child process termination on POSIX & Windows (#1078)
Co-authored-by: jingx8885 <jingxu8885@qq.com>
Co-authored-by: Surya Prakash Susarla <susarla.surya.prakash.1998@gmail.com>
2025-07-08 15:04:06 +01:00
Felix Weinberger
cf72565d9a
Unify process termination on POSIX & Windows (+ tests) (#1044)
Co-authored-by: Cristian Pufu <cristian.pufu@uipath.com>
2025-07-08 14:57:39 +01:00
shaun smith
08d364d005
Update text to make clear Elicitation is not directly associated with… (#1062) 2025-07-07 22:10:21 +01:00
Seongjun Kim
5c9cf290d8
Fix Pydantic field alias consistency in structured output (#1099) 2025-07-07 15:16:23 +01:00
Frédéric Barthelet
906ceea388
fix: Expose completions capability (#865) 2025-07-07 09:26:22 +01:00
Inna Harper
3d67e723e5
Fix async resource functions not being awaited (#1092)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-07-06 10:22:52 +01:00
Marcelo Trylesinski
3fa7bc7b61
tests: add lowest dependency tests (#1067)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-07-06 09:38:56 +02:00
Inna Harper
adf327164a
Embed code snippets for README from executable examples (#1055) 2025-07-04 17:25:54 +01:00
Ke Yu
7bc190bae8
fix: support "form_post" to be one of response modes in authorization server metadata validation (#1046) 2025-07-04 11:13:13 +01:00
Vividh Mahajan
31076ab2d4
docs: fix dev server command in README (#848) (#859) 2025-07-04 09:05:40 +00:00
Marcelo Trylesinski
9301924f44
chore: bump ruff (#1085) 2025-07-04 09:01:17 +01:00
AishwaryaKalloli
6f43d1f170
Flaky test fix (#1050) 2025-06-29 21:53:32 +01:00
Mohamed Amine Zghal
7942184336
Bump Pydantic in the lockfile (#1041) 2025-06-27 17:41:17 +02:00
Nathan Brake
3f0964dd14
Remove setting global logging config (#1048)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-06-27 12:48:45 +02:00
Inna Harper
dced22344d
Remove frozen from build (#1040)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-06-26 14:41:23 +01:00
Inna Harper
0323831b01
unpin jsonschema version (#1037) 2025-06-26 13:28:15 +01:00
Inna Harper
4b659639f7
Update latest protocol version to 2025-06-18 (#1036) 2025-06-26 10:02:57 +01:00
bhosmer-ant
43bb24f62b
feat: Add structured output support for tool functions (#993) 2025-06-26 09:36:17 +01:00
bhosmer-ant
c8bbfc034d
Add schema validation to lowlevel server (#1005) 2025-06-25 09:23:49 -04:00
David Soria Parra
0b11bdb629
ci: add --frozen flag to all uv commands in workflows (#970)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-25 14:00:14 +01:00
dr3s
41f3bc35ce
Make "resource" optional on earlier protocols (#1017)
Co-authored-by: Andres March <>
2025-06-25 10:18:24 +01:00
Felix Weinberger
4d45bb85a8
refactor: rename DummyProcess to FallbackProcess in Windows stdio (#1015) 2025-06-24 15:44:42 +01:00
Inna Harper
674768802a
Fix /.well-known/oauth-authorization-server dropping path (#1014) 2025-06-24 15:43:26 +01:00
Inna Harper
ce007de66d
Remove github from auth examples (#1011) 2025-06-24 15:43:08 +01:00
The AI Language
ecc916516d
Fix Windows subprocess NotImplementedError (STDIO clients) (#596)
Merging, rename for `DummyProcess` -> `FallbackProcess` coming in https://github.com/modelcontextprotocol/python-sdk/pull/1015
2025-06-24 15:05:23 +01:00
Inna Harper
679b22970e
RFC 8707 Resource Indicators Implementation (#991) 2025-06-23 17:10:20 +01:00
Inna Harper
17f9c00c53
MCP server separation into Authorization Server (AS) and Resource Server (RS) roles per spec PR #338 (#982)
Co-authored-by: Paul Carleton <paulc@anthropic.com>
2025-06-23 14:19:03 +01:00
Inna Harper
d0443a1832
Add resource Link (#974) 2025-06-17 20:23:07 +01:00
David Dworken
86bb54cb28
Add support for DNS rebinding protections (#861) 2025-06-17 20:13:00 +01:00
Felix Weinberger
a2f8766a8a
Add title to tools, resources, prompts (#972)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-17 15:53:14 +01:00
Marc Abramowitz
029f434109
Allow longer duration in test_188_concurrency (#969) 2025-06-17 11:55:29 +01:00
Inna Harper
5429ac6153
Update _meta usage guidance in types (#971) 2025-06-17 11:54:59 +01:00
Inna Harper
69e6572d48
Add support for Elicitation (#625)
Co-authored-by: David Soria Parra <davidsp@anthropic.com>
2025-06-17 11:54:11 +01:00
Inna Harper
a3bcabdce2
Include context into completions (#966) 2025-06-17 09:33:25 +01:00
David Dworken
7b420656de
Fix uncaught exception in MCP server (#967) 2025-06-16 17:04:51 -07:00
Felix Weinberger
1eb1bba83c
feat: add _meta to more objects (#963) 2025-06-16 16:43:54 +01:00
ihrpr
f3cd20c920
Rename ResourceReference to ResourceTemplateReference (#947) 2025-06-13 18:25:36 +01:00
Marcelo Trylesinski
bac2705426
ci: add timeout on the test job (#955) 2025-06-13 15:24:28 +01:00
Dipesh Rathore
1bb16fa96e
clean-up: removed unused ci file (#950) 2025-06-13 08:41:18 +00:00
Felix Weinberger
df15e9566d
feat: implement MCP-Protocol-Version header requirement for HTTP transport (#898) 2025-06-12 18:01:53 +01:00
ihrpr
0bcecffc4c
set timeout for sse in httpx_client_factory (#943) 2025-06-12 09:09:13 +01:00
Marcelo Trylesinski
20dc0fbabb
Allow to pass timeout as float (#941)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-06-12 09:31:31 +02:00
Luca Chang
d69b290b65
chore: create union for working with message content (#939) 2025-06-12 09:01:33 +02:00
Marcelo Trylesinski
185fa49fd1
Use pre-commit on CI (#937) 2025-06-11 16:48:10 +02:00
Marcelo Trylesinski
ba149e34f2
Create issue templates (#935) 2025-06-11 14:27:03 +02:00
Marcelo Trylesinski
d831cc49e1
Add commit to git-blame-ignore-revs (#933) 2025-06-11 11:41:33 +01:00
Marcelo Trylesinski
543961968c
Use 120 characters instead of 88 (#856) 2025-06-11 11:45:50 +02:00
Nandha Reddy
f7265f7b91
Use FileUrl on the resources/list call in the simple-resource example (#907) 2025-06-11 10:48:42 +02:00
David Dworken
29c69e6a47
Fix uncaught exception in MCP server (#822) 2025-06-09 17:03:15 -07:00
dr3s
1a9ead07f5
relax validation (#879) 2025-06-09 19:21:01 +01:00
xiaoming wang
2cbc435c6c
normalize string ID to int in server messages for compatibility (#851)
Co-authored-by: 晓明 王 <wxm@v1i.cc>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-06-08 07:15:21 +00:00
Marcelo Trylesinski
b0b44c2fe4
Use total_seconds instead of seconds on timeout (#896) 2025-06-07 13:11:00 -07:00
Luca Chang
7123556a34
feat: support audio content (#725)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-06-07 14:32:11 +00:00
Sam Tombury
2bce10bdb1
Support Cursor OAuth client registration (#895) 2025-06-07 07:24:11 -07:00
Peng Li
8276632caa
fix wrong order of comments (#899)
Signed-off-by: vincent-pli <justdoit.pli@gmail.com>
2025-06-07 07:18:26 -07:00
Samuel Colvin
544176770b
improve server logging (#867)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-06-02 08:03:18 +00:00
ihrpr
05b7156ea8
Support for http request injection propagation in StreamableHttp (#833)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-05-29 15:21:06 +01:00
Lorenzo
7f94bef85e
Client sampling and roots capabilities set to None if not implemented (#802)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-05-29 09:56:34 +01:00
Alvaro Bartolome
d55cb2bf4e
Fix and update OAuthAuthorizationServerProvider snippet in README.md (#830) 2025-05-28 23:02:46 +01:00
Tim Child
f5dd324354
Prevent stdio connection hang for missing server path. (#401)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-05-28 22:57:46 +01:00
ihrpr
70014a2bbb
Support for http request injection propagation to tools (#816) 2025-05-28 15:59:14 +01:00
Nick Cooper
532b1176f9
RFC: Lower log levels used for connections (#821) 2025-05-27 23:11:08 +01:00
Marshall Roch
7901552eba
send errors to pending requests if server closes (#333)
Co-authored-by: ihrpr <inna@anthropic.com>
2025-05-27 22:55:27 +01:00
ihrpr
9dad26620f
Fix auth tests and ruff format (#818) 2025-05-27 17:00:01 +01:00
Pedro Rodrigues
6e418e62f9
Fix building auth metadata paths (#779)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-05-26 15:19:18 +02:00
Marcelo Trylesinski
8a2359ffeb
Pin uv version (#806) 2025-05-26 12:17:23 +01:00
Really Him
294fc6b4d3
[DOC] Fix example usage of get_context (plus a little more) (#801)
Co-authored-by: Really Him <hesereallyhim@proton.me>
2025-05-26 11:22:39 +02:00
Stephan Lensky
f2f4dbdcbd
Fix async callable object tools (#568) 2025-05-23 17:21:53 +01:00
Lekhnath Parajuli
d1876433af
add support for parallel run (#739)
Co-authored-by: Lekhnath Parajuli <lekhnathparajuli4@gmail.com>
2025-05-23 17:13:24 +01:00
Christian Clauss
073471cdf1
Python lint: Ruff rule PLW1510 -- subprocess-run-without-check (#794) 2025-05-23 16:44:23 +01:00
Nick Cooper
10cf0f78a8
Support custom httpx client creation (#752) 2025-05-23 13:55:01 +01:00
Arash Ashtiani
7c8ad510b7
llamallama-3.2-90b-vision-preview is deprecated (#523) 2025-05-23 13:47:52 +01:00
Clare Liguori
4cd4fe006c
Chatbot example: clean up servers in correct order (#353) 2025-05-23 11:30:50 +01:00
Mo
0e2a34189c
Fix ClientSessionGroup cleanup logic (#787) 2025-05-23 08:57:46 +01:00
Nate Barbettini
e80c0150e1
fix: Pass cursor parameter to server (#745)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-05-21 22:27:06 +01:00
ciccolo-anthropic
2ca2de767b
Change default binding from 0.0.0.0 to 127.0.0.1 to align with security recommendations (#738) 2025-05-20 20:23:19 +01:00
ihrpr
43ded92633
add auth client sse (#760) 2025-05-20 15:21:14 +01:00
Vadym Barda
5a9340af71
fastmcp: allow passing Tool directly to FastMCP constructor (#699) 2025-05-19 19:52:02 -07:00
ihrpr
e33cd41c7a
Add OAuth authentication client for HTTPX (#751)
Co-authored-by: Paul Carleton <paulc@anthropic.com>
2025-05-19 20:38:04 +01:00
ihrpr
6353dd192c
Servers to accept older versions of client (#722)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-05-15 18:33:31 +01:00
ihrpr
5d33861cad
Add progress notification callback for client (#721) 2025-05-15 17:45:58 +01:00
gaojingyu
1bdeed33c2
fix: Update @mcp.resource to use function documentation as default descrip… (#489) 2025-05-15 11:14:50 +01:00
ihrpr
c2f8730d6d
Fix example for multiple stateless servers (#720) 2025-05-15 09:57:12 +01:00
ihrpr
13f018264c
Set 2025-03-26 as the latest protocol version (#719) 2025-05-15 09:29:54 +01:00
ihrpr
b3fbee9c48
Include server examples folder in pyright check (#667) 2025-05-15 09:29:36 +01:00
5enxia
0a388b642f
Add example for stateless server with JSON response (#704) 2025-05-15 09:24:13 +01:00
Jerome
a00b20a427
feat: add cursor pagination support to all client list methods (#718) 2025-05-15 09:04:04 +01:00
Daniel Avdar
b8f7b027c3
Update CI configuration to support multiple OS environments (#708)
Signed-off-by: DanielAvdar <66269169+DanielAvdar@users.noreply.github.com>
2025-05-14 13:43:59 +01:00
Akshey D
9e66f7ce89
feat: add message to ProgressNotification (#435)
Co-authored-by: ihrpr <inna.hrpr@gmail.com>
2025-05-13 18:40:15 +01:00
Lion Yang
c5782129bc
Fix hanging on streams when stdio_client exiting (#559) 2025-05-13 18:36:33 +01:00
john0312
37b54b4c03
fix: Add checks and better error messages on server object for mcp run (#297) 2025-05-13 18:27:10 +01:00
Daniel
8b5838675c
docs: clarify LLM API configuration in mcp_simple_chatbot README (#487)
Co-authored-by: Danny <88821366+ddaltn@users.noreply.github.com>
2025-05-13 14:00:01 +01:00
Takayuki SHIMIZUKAWA
173e0ee3b8
fix typo: server has get_context attribute instead of request_context. (#446) 2025-05-13 12:12:26 +01:00
Mo
7b6a903eb9
Create ClientSessionGroup for managing multiple session connections. (#639) 2025-05-13 09:58:24 +01:00
ihrpr
fdb538bc28
Simple Auth Example to use shttp in addition to sse (#695) 2025-05-12 22:08:23 +01:00
Jeremiah Lowin
1cb740745d
Properly infer prefix for SSE messages (#659) 2025-05-12 21:29:17 +01:00
shaun smith
5289d0686a
Minor tweaks to README for Streamable HTTP (#690) 2025-05-12 21:24:16 +01:00
Nate Barbettini
f745d69dcb
fix: 204 is an acceptable response to DELETEing the session (#697) 2025-05-12 21:22:55 +01:00
mopg
76919cfaa4
Fix typo in authentication section of README (#676) 2025-05-12 18:44:02 +01:00
ihrpr
c6fb822c86
Fix streamable http sampling (#693)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-05-12 18:31:35 +01:00
ihrpr
ed25167fa5
Introduce a function to create a standard AsyncClient with options (#655)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-05-08 20:53:21 +01:00
ihrpr
72003d9cc0
StreamableHttp - update docs (#664) 2025-05-08 20:49:55 +01:00
ihrpr
e4e119b324
Streamable HTTP - improve usability, fast mcp and auth (#641) 2025-05-08 20:43:25 +01:00
inceptmyth
280bab36f4
Fix: Use absolute path to uv executable in Claude Desktop config (#440) 2025-05-08 15:57:41 +01:00
ihrpr
a027d75f60
Auth SSE simple server example (#610)
Co-authored-by: Peter Raboud <praboud@anthropic.com>
Co-authored-by: David Soria Parra <davidsp@anthropic.com>
Co-authored-by: Basil Hosmer <basil@anthropic.com>
Co-authored-by: Paul Carleton <paulc@anthropic.com>
Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com>
2025-05-07 17:52:29 +01:00
yabea
a1307abded
Fix the issue of get Authorization header fails during bearer auth (#637)
Co-authored-by: yangben <yangben@zhihu.com>
2025-05-07 17:42:02 +01:00
ihrpr
9d99aee014
Revert "Add message queue for SSE messages POST endpoint (#459)" (#649) 2025-05-07 16:35:20 +01:00
Samad Yar Khan
c8a14c9dba
docs: fix broken link to OAuthServerProvider in Authentication section of README (#651) 2025-05-07 17:17:11 +02:00
tim-watcha
e0d443c95e
Add mount_path support for proper SSE endpoint routing with multiple FastMCP servers (#540)
Co-authored-by: ihrpr <inna.hrpr@gmail.com>
2025-05-07 11:14:25 +01:00
Akash D
3b1b213a96
Add message queue for SSE messages POST endpoint (#459) 2025-05-06 17:10:43 -07:00
ihrpr
58c5e7223c
SSE FastMCP - do not go though auth when it's not needed (#619) 2025-05-02 17:56:02 +01:00
Akash D
83968b5b2f
Handle SSE Disconnects Properly (#612) 2025-05-02 09:32:46 -07:00
ihrpr
5d8eaf77be
Streamable Http - clean up server memory streams (#604) 2025-05-02 14:59:17 +01:00
ihrpr
74f5fcfa0d
StreamableHttp - client refactoring and resumability support (#595) 2025-05-02 14:49:50 +01:00
ihrpr
cf8b66b82f
use metadata from SessionMessage to propagate related_request_id (#591) 2025-05-02 14:35:17 +01:00
ihrpr
da0cf22355
Wrap JSONRPC messages with SessionMessage for metadata support (#590) 2025-05-02 14:29:00 +01:00
ihrpr
3978c6e1b9
StreamableHttp -- resumability support for servers (#587) 2025-05-02 14:10:40 +01:00
ihrpr
9dfc925090
StreamableHttp client transport (#573) 2025-05-02 13:59:02 +01:00
ihrpr
46523afe30
StreamableHttp - GET request standalone SSE (#561) 2025-05-02 13:52:27 +01:00
ihrpr
72b66a58b1
StreamableHttp - stateless server support (#554) 2025-05-02 13:40:21 +01:00
ihrpr
78f0b11a09
StreamableHttp - Server transport with state management (#553) 2025-05-02 11:58:54 +01:00
Peter Raboud
2210c1be18
Add support for serverside oauth (#255)
Some checks failed
Check uv.lock / check-lock (push) Has been cancelled
Main branch checks / checks (push) Has been cancelled
Co-authored-by: David Soria Parra <davidsp@anthropic.com>
Co-authored-by: Basil Hosmer <basil@anthropic.com>
Co-authored-by: ihrpr <inna@anthropic.com>
2025-05-01 19:42:59 +01:00
bhosmer-ant
82bd8bc1d9
Properly clean up response streams in BaseSession (#515) 2025-05-01 14:45:47 +01:00
bhosmer-ant
1a330ac672
Add ToolAnnotations support in FastMCP and lowlevel servers (#482) 2025-04-30 14:52:56 +01:00
Samuel Colvin
017135434e
add pytest-pretty dev dependency (#546) 2025-04-29 21:02:09 +01:00
Guillaume Raille
96e5327110
add a timeout arguments on per-request basis (as per MCP specifications) (#601) 2025-04-29 13:58:48 +01:00
Samuel Colvin
697b6e8e05
replace inefficient use of to_jsonable_python (#545) 2025-04-26 20:41:19 +02:00
ihrpr
b4c7db6a50
Format files with ruff (#562) 2025-04-23 05:06:51 +01:00
Christian Clauss
babb477dff
Python lint: Ruff rules for comprehensions and performance (#512) 2025-04-15 16:58:33 +02:00
Dan Lapid
8c9269c34b
Move uvicorn import to usage (#502) 2025-04-15 16:51:02 +02:00
Mohamed Amine Zghal
70115b99b3
Fix tests for Pydantic 2.11 (#465) 2025-04-11 09:17:36 +02:00
Jerome
c4beb3e8ef
Support custom client info throughout client APIs (#474)
Co-authored-by: Claude <noreply@anthropic.com>
2025-04-10 14:52:01 +01:00
Junpei Kawamoto
da54ea003e
Allow generic parameters to be passed onto Context on FastMCP tools
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-04-10 10:36:46 +01:00
Marcelo Trylesinski
d6e611f83f
Match ruff version on CI and local (#471) 2025-04-10 10:36:08 +01:00
Nyakku Shigure
58b989c0a3
Fix lifespan_context access example in README (#437) 2025-04-09 09:58:17 +01:00
anupsajjan
c2ca8e03e0
Docs : Enhance README to suggest commands for creating a new UV project before adding mcp dependency. (#408) 2025-04-02 14:51:50 +01:00
徐徐如生
321498ab5d
Fix python -m command error (#387)
Co-authored-by: xzx <xuzexin@thinktown.com>
2025-03-31 07:32:15 +00:00
YungYueh ChanLee
a81b25ae8d
Docs: Change README to correct pip installation command for MCP CLI support (#394) 2025-03-31 09:10:19 +02:00
David Soria Parra
2ea14958f0
Update .env.example 2025-03-27 20:33:30 +00:00
David Soria Parra
2d6b3873ec
fix publish-pypi 2025-03-27 18:03:46 +00:00
David Soria Parra
302d8999ba
set site url 2025-03-27 17:59:31 +00:00
David Soria Parra
f007323542
Create publish-docs-manually.yml 2025-03-27 17:55:23 +00:00
David Soria Parra
d788424caa
fix
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-03-27 16:44:44 +00:00
Pol Avec
4e3c196941
Fixes incorrect casting into int for numbers in string format (#384) 2025-03-27 15:14:01 +00:00
evalstate
6d66ab8be0
Fix/base64 handling (Issue #342) (#343)
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
2025-03-27 09:18:02 +00:00
Sarmad Qadri
e5ee279614
Fixes to stdio_client to support Windows more robustly (#372) 2025-03-27 09:14:56 +00:00
Conor Branagan
d3bf9cd325
Fix typo in starlette import in README (#374) 2025-03-27 08:25:29 +01:00
David Soria Parra
9a2bb6a7e7
refactor: Make types.py strictly typechecked. (#336) 2025-03-26 14:21:35 +00:00
David Soria Parra
df2d3a57c2
Fix #355: Fix type error with lifespan context (#368)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-03-26 14:18:41 +00:00
David Soria Parra
dfbe56d2b2
update gitignore 2025-03-25 15:54:39 +00:00
Marcelo Trylesinski
4e11f2890b
Add mkdocs (#367) 2025-03-25 13:06:15 +01:00
Marcelo Trylesinski
9ec6db201f
ci: test multiple python versions (#345) 2025-03-25 11:57:56 +00:00
Bar Nuri
fd2fd34357
default log level info (#366) 2025-03-25 09:50:15 +00:00
David Soria Parra
568cbd1a66
Fix #201: Move incoming message stream from BaseSession to ServerSession (#325)
Co-authored-by: Claude <noreply@anthropic.com>
2025-03-24 14:14:14 +00:00
Alex
9ae4df85fb
Use default environment even when env is provided(#327)
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-03-21 11:43:08 +00:00
elsejj
e2a7338730
enable custom path of sse server (#246)
Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
2025-03-20 17:56:35 +00:00
Nino Risteski
04586f8f86
Update types.py (#243)
excluded prompts/get change
2025-03-20 17:53:36 +00:00
arthrod
e334f7e4b6
Update __init__.py (#335) 2025-03-20 17:20:40 +00:00
Tom Sheffler
c3fd34a1a6
Enhancement: make stderr of server subprocess configurable (#191)
* make stderr of server subprocess configurable

* Update stdio.py

---------

Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com>
2025-03-20 16:28:57 +00:00
Marcelo Trylesinski
ae77772ea8
Add strict mode to pyright (#315)
* Add strict mode to pyright

* Apply UP rule

* fix readme

* More correct

* Leave wrong Context for now

* Add strict mode to pyright

* Apply UP rule

* fix readme

* fix

* ignore
2025-03-20 09:13:08 +00:00
Niraj Fonseka
5a54d82459
fixing the imports for the prompts example (#318) 2025-03-19 14:42:45 +00:00
Samuel Colvin
805fb469f5
add cwd to StdioServerParameters (#292) 2025-03-19 12:03:10 +00:00
ihrpr
08f4e01b8f
add callback for logging message notification (#314) 2025-03-19 09:40:08 +00:00
comfuture
a9aca20205
Option to mount SSE server to existing ASGI server (#312)
* Option to mount SSE server to existing ASGI server

Fixes #311

Add option to mount SSE server to an existing ASGI server.

* Add a new method `sse_app` in `src/mcp/server/fastmcp/server.py` to return an instance of the SSE server app.
* Update the `run_sse_async` method in `src/mcp/server/fastmcp/server.py` to use the new `sse_app` method.
* Update the documentation in `README.md` to include instructions on how to mount the SSE server to an existing ASGI server.
* Fix the example in `README.md` to use `app.mount('/', mcp.sse_app())` instead.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/modelcontextprotocol/python-sdk/issues/311?shareId=XXXX-XXXX-XXXX-XXXX).

* Add `sse_app` method and update `run_sse_async` method in `server.py`

* Add `sse_app` method to return an instance of the SSE server app
* Update `run_sse_async` method to use the new `sse_app` method

Update `README.md` to include instructions for mounting SSE server

* Add section on mounting the SSE server to an existing ASGI server

* fix: Move import statements to the top of the file/

* docs: Update README to reflect changes in mounting SSE server with Starlette

* docs: Formatting of SSE server mounting example in README
2025-03-19 05:26:49 +01:00
Marcelo Trylesinski
6b6f34eaa6
Improve readability of CLI help option (#295) 2025-03-17 10:24:26 +00:00
Marcelo Trylesinski
c8978681f6
Avoid double JSON encoding/decoding (#287) 2025-03-14 15:08:11 +00:00
Samuel Colvin
3775916c5c
lint docs examples (#286)
* lint docs examples

* format
2025-03-14 11:30:57 +00:00
Marcelo Trylesinski
97201cce59
Strict types on the client side (#285) 2025-03-14 10:56:48 +00:00
Marcelo Trylesinski
7196604468
Revert "refactor: reorganize message handling for better type safety and clar…" (#282)
Some checks failed
Main branch checks / checks (push) Has been cancelled
This reverts commit 9d0f2daddb.
2025-03-14 09:50:46 +00:00
john0312
ebb81d3b2b
Update README to clarify installing mcp to project vs running mcp (#277)
uv add is for adding dependency to uv managed python projects, and folks
not familiar with uv may mistaken the command for running mcp. Added
wordings that clarifies this.
2025-03-13 14:35:53 +00:00
David Soria Parra
9d0f2daddb
refactor: reorganize message handling for better type safety and clarity (#239)
Some checks failed
Main branch checks / checks (push) Has been cancelled
* refactor: improve typing with memory stream type aliases

Move memory stream type definitions to models.py and use them throughout
the codebase for better type safety and maintainability.

GitHub-Issue:#201

* refactor: move streams to ParsedMessage

* refactor: update test files to use ParsedMessage

Updates test files to work with the ParsedMessage stream type aliases
and fixes a line length issue in test_201_client_hangs_on_logging.py.

Github-Issue:#201

* refactor: rename ParsedMessage to MessageFrame for clarity

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: move MessageFrame class to types.py for better code organization

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>

* fix pyright

* refactor: update websocket client to use MessageFrame

Modified the websocket client to work with the new MessageFrame type,
preserving raw message text and properly extracting the root JSON-RPC
message when sending.

Github-Issue:#204

* fix: use NoneType instead of None for type parameters in MessageFrame

🤖 Generated with [Claude Code](https://claude.ai/code)

* refactor: rename root to message
2025-03-13 13:44:55 +00:00
Michaelzag
ad7f7a5473
Changed default log level to error (#258) 2025-03-13 13:16:21 +00:00
Marcelo Trylesinski
1669a3af01
Release on GitHub release (#276) 2025-03-13 13:09:18 +00:00
Mariusz Woloszyn
5c9f688d95
Add support for Linux configuration path in get_claude_config_path (#270)
* feat: add support for Linux configuration path in get_claude_config_path

* On Linux use XDG_CONFIG_HOME environment variable and fall back to $HOME/.config

* update Linux config path to include 'Claude' directory

* fix: format

---------

Co-authored-by: David Soria Parra <davidsp@anthropic.com>
2025-03-13 11:46:06 +00:00
Marcelo Trylesinski
94d326dbf1
Close unclosed resources in the whole project (#267)
* Close resources

* Close all resources

* Update pyproject.toml

* Close all resources

* Close all resources

* try now...

* try to ignore this

* try again

* try adding one more..

* try now

* try now

* revert ci changes
2025-03-13 10:59:45 +00:00
Jerome
1691b905e2
Merge pull request #179 from smithery-ai/patch-1
Create Client websocket.py
2025-03-12 22:53:22 +00:00
Henry Mao
fb7d0c8dac
Pyright 2025-03-12 13:56:05 -07:00
Henry Mao
ba184a2667
Ruff 2025-03-12 13:51:35 -07:00
Henry Mao
2bcca39aae
Update lock 2025-03-12 13:51:35 -07:00
Marcelo Trylesinski
e756315dea
Add ServerSessionT type var to Context (#271)
* Add ServerSessionT type var to Context

* Passing locally

* Try now
2025-03-12 15:35:15 +00:00
David Soria Parra
78fc5c12c0
fix: fix ci 2025-03-12 14:22:30 +00:00
Jerome
d51726a9e8
Merge branch 'main' into patch-1 2025-03-12 09:31:56 +00:00
Marcelo Trylesinski
3e0ab1e7ee
Drop AbstractAsyncContextManager for proper type hints (#257) 2025-03-11 13:17:15 +00:00
Marcelo Trylesinski
5cbea24ecb
Use proper generic for Context (#245) 2025-03-11 13:15:07 +00:00
David Soria Parra
06e692ba5c
fix: fix the name of the env variable 2025-03-11 10:26:38 +00:00
Henry Mao
dec28830b9
Remove try except 2025-03-08 10:53:56 +08:00
Henry Mao
ea8a2dbd6d
Reraise exception and make websocket optional 2025-03-08 10:52:10 +08:00
Henry Mao
9d65e5ac03
Remove optional check 2025-03-07 22:02:16 +08:00
Henry Mao
fd826cc7a6
Fix Websocket Client and Add Test 2025-03-07 22:00:56 +08:00
Henry Mao
fc021eea76
Apply suggestions from code review
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2025-03-06 20:12:51 +08:00
Henry Mao
f67c3ee6c0
Merge branch 'modelcontextprotocol:main' into patch-1 2025-03-06 13:24:01 +08:00
David Soria Parra
775f879813
bump version 2025-02-20 21:40:35 +00:00
David Soria Parra
b1942b31c4
Fix #177: Returning multiple tool results (#222)
* feat: allow lowlevel servers to return a list of resources

The resource/read message in MCP allows of multiple resources
to be returned. However, in the SDK we do not allow this. This
change is such that we allow returning multiple resource in
the lowlevel API if needed. However in FastMCP we stick to
one, since a FastMCP resource defines the mime_type in the decorator
and hence a resource cannot dynamically return different mime_typed resources.
It also is just the better default to only return one resource.
However in the lowlevel API we will allow this.

Strictly speaking this is not a BC break since the new return value
is additive, but if people subclassed server, it will break them.

* feat: lower the type requriements for call_tool to Iterable
2025-02-20 21:31:26 +00:00
Jerome
2628e01f4b
Merge pull request #217 from modelcontextprotocol/jerome/fix/request-context-typing
Updated typing on request context for the server to use server session
2025-02-20 11:07:54 +00:00
David Soria Parra
a50cf92d3a
fix: ruff format 2025-02-20 11:04:46 +00:00
David Soria Parra
10a85e452d
fix: mark test as pytest.mark.anyio 2025-02-20 11:04:29 +00:00
Jerome
ff22f48365
Add client handling for sampling, list roots, ping (#218)
Adds sampling and list roots callbacks to the ClientSession, allowing the client to handle requests from the server.

Co-authored-by: TerminalMan <84923604+SecretiveShell@users.noreply.github.com>
Co-authored-by: David Soria Parra <davidsp@anthropic.com>
2025-02-20 10:49:43 +00:00
Randall Nortman
106619967b
Force stdin/stdout encoding to UTF-8
The character encoding of the stdin/stdout streams in Python is platform-
dependent. On Windows it will be something weird, like CP437 or CP1252,
depending on the locale. This change ensures that no matter the platform,
UTF-8 is used.
2025-02-18 11:23:24 +00:00
Justin Spahr-Summers
5480f67cf9
Merge pull request #207 from hmwildermuth/main
Update URL validation to allow file and other nonstandard schemas
2025-02-13 23:05:59 +00:00
Henry Wildermuth
b53e090299 Update URL validation to allow file and other nonstandard schemas 2025-02-13 14:58:58 -08:00
David Soria Parra
faf3a9d7d6
Merge pull request #205 from modelcontextprotocol/issue-192-request-id-test
Add test to verify request ID preservation
2025-02-13 15:10:00 +00:00
David Soria Parra
92ba4f41d0
Merge pull request #206 from modelcontextprotocol/jerome/fix/188
Jerome/fix/188
2025-02-13 15:09:42 +00:00
David Soria Parra
fbf4acc679
fix: method ordering 2025-02-13 14:54:53 +00:00
Jerome
da53a97ed9
Made message handling concurrent 2025-02-13 14:53:08 +00:00
Jerome
9abfe775cc
Added failing test 2025-02-13 14:46:57 +00:00
Jerome
0b5644723b
Added ignore revs (for gitlense) 2025-02-13 14:46:57 +00:00
Jerome
e4503a72c8
Added scratch/ to gitignore 2025-02-13 14:46:57 +00:00
David Soria Parra
7c47d1f25f
Merge pull request #203 from modelcontextprotocol/davidsp/clean-lifespan
feat: add lifespan context manager support
2025-02-13 14:26:28 +00:00
David Soria Parra
4d3e05f6f6
refactor: improve lifespan context typing and documentation
- Add proper generic parameter for lifespan context type
- Update README with TypedDict example for strong typing
- Fix context variable initialization in server
- Improve property return type safety
- Remove redundant documentation
- Ensure compatibility with existing tests
2025-02-13 10:12:59 +00:00
David Soria Parra
41af509bcb
Add test to verify request ID preservation
This test ensures that the server properly preserves and returns the same request ID in responses,
which is a fundamental part of the JSON-RPC protocol. The test initializes the server, sends
requests with custom IDs, and verifies that these IDs are correctly returned.

Github-Issue:#192
2025-02-12 16:32:21 +00:00
David Soria Parra
fddba00723
refactor: improve server context management with AsyncExitStack
Replace nested context managers with AsyncExitStack to ensure proper cleanup
order during server shutdown and make the code more maintainable.
2025-02-11 12:26:32 +00:00
David Soria Parra
e5815bd162
docs: update README with lifespan examples and usage
Add comprehensive documentation for lifespan support:
- Add usage examples for both Server and FastMPC classes
- Document startup/shutdown patterns
- Show context access in tools and handlers
- Clean up spacing in test files
2025-02-11 12:16:51 +00:00
David Soria Parra
e598750cba
test: add tests for server lifespan support
Adds comprehensive tests for lifespan functionality:
- Tests for both low-level Server and FastMCP classes
- Coverage for startup, shutdown, and context access
- Verifies context passing to request handlers
2025-02-11 12:16:47 +00:00
David Soria Parra
d3ea9009b0
feat: add lifespan support to FastMCP server
Adds support for the lifespan API to FastMCP server, enabling:
- Simple setup with FastMCP constructor
- Type-safe context passing to tools and handlers
- Configuration via Settings class
2025-02-11 12:16:44 +00:00
David Soria Parra
2c7bd8343e
feat: add lifespan support to low-level MCP server
Adds a context manager based lifespan API in mcp.server.lowlevel.server to manage server lifecycles in a
type-safe way. This enables servers to:
- Initialize resources on startup and clean them up on shutdown
- Pass context data from startup to request handlers
- Support async startup/shutdown operations
2025-02-11 12:16:40 +00:00
David Soria Parra
f10665db4c
tests: cleanup 2025-02-05 11:02:51 +00:00
David Soria Parra
c58adfe3f5
Merge pull request #167 from modelcontextprotocol/davidsp/88-v2
feat: add request cancellation and cleanup
2025-02-04 20:23:05 +00:00
David Soria Parra
27bfde95a4
fix: fix tests 2025-02-04 20:16:30 +00:00
David Soria Parra
733db0c9cf
fix: enforce context manager usage for RequestResponder 2025-02-04 20:13:26 +00:00
David Soria Parra
08042c3307
Merge pull request #181 from zzstoatzz/update-fastmcp-server-typing
satisfy `pyright` untyped decorators in `mcp.server.fastmcp.server`
2025-02-04 18:25:32 +00:00
David Soria Parra
08cfbe522a
fix: improve error handling and request cancellation for issue #88 2025-02-04 18:20:17 +00:00
zzstoatzz
ca060014bb centralize type 2025-02-04 11:11:38 -06:00
zzstoatzz
00a44692e7 init 2025-02-04 11:11:38 -06:00
David Soria Parra
51e65c8310
Merge pull request #186 from modelcontextprotocol/fix/141-resource-templates
fix: standardize resource response format
2025-02-04 14:25:28 +00:00
David Soria Parra
827e494df4
feat: add request cancellation and in-flight request tracking
This commit adds support for request cancellation and tracking of
in-flight requests in the MCP protocol implementation. The key
architectural changes are:

1. Request Lifecycle Management:
   - Added _in_flight dictionary to BaseSession to track active requests
   - Requests are tracked from receipt until completion/cancellation
   - Added proper cleanup via on_complete callback

2. Cancellation Support:
   - Added CancelledNotification handling in _receive_loop
   - Implemented cancel() method in RequestResponder
   - Uses anyio.CancelScope for robust cancellation
   - Sends error response on cancellation

3. Request Context:
   - Added request_ctx ContextVar for request context
   - Ensures proper cleanup after request handling
   - Maintains request state throughout lifecycle

4. Error Handling:
   - Improved error propagation for cancelled requests
   - Added proper cleanup of cancelled requests
   - Maintains consistency of in-flight tracking

This change enables clients to cancel long-running requests and
servers to properly clean up resources when requests are cancelled.

Github-Issue:#88
2025-02-03 20:50:05 +00:00
David Soria Parra
888bdd3c34
tests for issue 88 2025-02-03 14:03:41 +00:00
David Soria Parra
960b92355e
add uv.lock for simple-chatbot 2025-02-03 13:25:30 +00:00
David Soria Parra
a9ae38237d
check uv lockfile 2025-02-03 12:33:30 +00:00
David Soria Parra
ce06b6291d
chore: update uv.lock 2025-02-03 11:36:13 +00:00
David Soria Parra
0d3e02f6a2
fix: standardize resource response format
Github-Issue:#141
2025-02-03 11:33:32 +00:00
Jerome
444edf6760
Merge pull request #180 from modelcontextprotocol/jeremy/relax-pydantic
Relax pydantic, pydantic-settings, and uvicorn requirements
2025-01-31 07:07:59 +13:00
David Soria Parra
5e19c7cf73
Merge pull request #176 from sheffler/fixFirstProgressReport
progress_token is 0 on first tool-call and the return is taken mistakenly
2025-01-30 16:00:01 +00:00
jeremy
defd52b2d6
Fix comment about pydantic versioning 2025-01-30 10:21:03 -05:00
jeremy
0d48a4494f
relax pydantic, pydantic-settings, and uvicorn 2025-01-29 22:49:43 -05:00
Henry Mao
1395607583
Create Client websocket.py 2025-01-29 21:27:09 +08:00
David Soria Parra
53bfac0cc2
fix: make progress_token check E711 compat 2025-01-29 10:01:56 +00:00
David Soria Parra
44c0004e6c
Merge pull request #170 from modelcontextprotocol/fix/152-resource-mime-type
fix: respect resource mime type in responses
2025-01-28 18:12:39 +00:00
David Soria Parra
2dc5fbfa3d
fix: add test for #176 2025-01-28 11:19:13 +00:00
Thomas Sheffler
76325b7846 progress_token is 0 on first tool-call and the return is taken mistakenly 2025-01-27 19:04:20 -08:00
David Soria Parra
070e8412c0
refactor: standardize resource response format
Introduce ReadResourceContents type to properly handle MIME types in resource responses. Breaking change in FastMCP read_resource() return type.

Github-Issue:#152
2025-01-27 20:36:10 +00:00
David Soria Parra
f90cf6a2a5
docs: update read_resource examples to handle mime type
Update README examples to show proper handling of the new read_resource() return value
that includes mime type information.

Github-Issue:#152
2025-01-27 15:51:45 +00:00
David Soria Parra
8ff4b5e9d3
fix: respect resource mime type in responses
The server was ignoring mime types set on resources, defaulting to text/plain
for strings and application/octet-stream for bytes. Now properly preserves
the specified mime type in both FastMCP and low-level server implementations.

Note that this is breaks backwards compatibility as it changes the return
values of read_resource() on FastMCP. It is BC compatible on lowlevel since
it only extends the callback.

Github-Issue: #152
Reported-by: eiseleMichael
2025-01-27 15:51:44 +00:00
David Soria Parra
f10c2e3f1f
fix: add test to reproduce #152 2025-01-27 15:50:52 +00:00
David Soria Parra
978cfe3f92
more fixes 2025-01-27 15:49:59 +00:00
David Soria Parra
43a30c1d00
fix tests 2025-01-27 14:36:48 +00:00
David Soria Parra
91840fd9c0
Merge pull request #172 from sheffler/fixAsyncLog
async and await for send_log_message in fastmcp
2025-01-27 10:40:10 +00:00
Thomas Sheffler
7a64d8f9af async and await for logging in fastmcp 2025-01-25 07:03:20 -08:00
David Soria Parra
57c3aa2f0d
docs: improve CLAUDE.md structure and clarity 2025-01-24 18:42:52 +00:00
David Soria Parra
d64b0679b8
Merge pull request #149 from cr7258/patch-1
fix: mcp install command with environment variables
2025-01-24 10:38:05 +00:00
David Soria Parra
5f537a0f69
Merge pull request #166 from modelcontextprotocol/davidsp/refactor
refactor: extract request and notification handling
2025-01-24 09:54:21 +00:00
David Soria Parra
ba25ad6921
Merge pull request #165 from modelcontextprotocol/davidsp/types-2024-11-05
feat: update types to 2024-11-05 schema
2025-01-24 09:53:40 +00:00
David Soria Parra
0a93799068
refactor: extract request and notification handling into separate methods 2025-01-24 09:52:20 +00:00
David Soria Parra
bd742272ab
fix: update types to reflext 2024-11-05 schema 2025-01-24 09:52:16 +00:00
David Soria Parra
c14ec2e0d7
fix typing issues 2025-01-23 13:27:25 +00:00
David Soria Parra
059e831b8d
Merge pull request #98 from 3choff/feature/simple-chatbot-example
Add example client to examples/clients folder
2025-01-23 13:27:10 +00:00
David Soria Parra
0cb3b26514
Merge pull request #157 from micpst/async-resources
feat: add async resources for FastMCP
2025-01-22 13:37:41 +00:00
David Soria Parra
73c62c5ccf
Merge pull request #158 from micpst/fix-logger-debug
fix: fastmcp debug logging
2025-01-20 11:56:12 +00:00
Michał Pstrąg
c184b7a8db fix fastmcp logger debug 2025-01-18 19:59:51 +01:00
Michał Pstrąg
f5f19b2361 add support for async resources 2025-01-18 19:37:39 +01:00
Justin Spahr-Summers
4c71c6168f
Merge pull request #151 from modelcontextprotocol/merrill/tests-for-sse
add minimal tests for the SSE transport
2025-01-15 10:47:29 +00:00
Nick Merrill
d01d49ea6e
add timeout test 2025-01-14 12:10:41 -05:00
Nick Merrill
e798d20cbb
ruff 2025-01-14 11:56:44 -05:00
Nick Merrill
aa7869a62f
add type hints 2025-01-14 11:54:09 -05:00
Nick Merrill
3fa26a5a97
remove unused imports 2025-01-14 11:49:04 -05:00
Nick Merrill
07e721f63f
formatting 2025-01-14 11:47:41 -05:00
Nick Merrill
5097bb7ef8
revert unintended changes 2025-01-14 11:47:00 -05:00
Nick Merrill
7b35ab844a
run server in separate process 2025-01-14 11:44:03 -05:00
Nick Merrill
8d90a3afa3
attempt at server in process 2025-01-14 11:15:05 -05:00
Nick Merrill
7ab1fc71aa
attempt to get server to shut down 2025-01-14 11:01:43 -05:00
Nick Merrill
8f81a85abe
all tests passing with custom port, but not passing all together 2025-01-14 10:45:55 -05:00
Nick Merrill
e79a56435a
passing SSE client test 2025-01-14 10:34:32 -05:00
Nick Merrill
66ccd1c515
test_sse_connection is passing 2025-01-14 10:18:27 -05:00
Nick Merrill
a0e2f7fab7
WIP 2025-01-14 09:38:06 -05:00
Nick Merrill
3f9f7c8311
WIP 2025-01-14 09:27:42 -05:00
Nick Merrill
b0a6aafaf6
WIP 2025-01-14 09:24:44 -05:00
Nick Merrill
f164291483
trying to test SSE 2025-01-14 09:22:05 -05:00
Justin Spahr-Summers
62a0af6f71
Merge pull request #150 from salman1993/sm/add-instructions
feat: add instructions field to InitializeResult
2025-01-13 18:06:53 +00:00
Salman Mohammed
bc6746e89b Add instructions field to ServerSession and FastMCP 2025-01-13 13:00:11 -05:00
Salman Mohammed
135191403a Add instructions field to InitializeResult 2025-01-13 11:58:43 -05:00
Se7en
53fe3417bb
fix: mcp install command with environment variables 2025-01-13 20:12:32 +08:00
David Soria Parra
400dcda2eb
update lock 2025-01-06 20:04:39 +00:00
David Soria Parra
8d17fd327b
Merge pull request #137 from modelcontextprotocol/v1.2.x
fix: #129 resource template handling in FastMCP server
2025-01-06 20:03:43 +00:00
David Soria Parra
4770bcd0b1
fix: #129 resource template handling in FastMCP server 2025-01-06 20:02:03 +00:00
David Soria Parra
b66c6759c3
feat: add example and test for parameter descriptions in FastMCP tools 2025-01-06 12:30:44 +00:00
David Soria Parra
a77aaa79a4
refactor: use built-in typing.Annotated instead of typing_extensions 2025-01-06 10:47:44 +00:00
David Soria Parra
83efb1077a
chore: update uv.lock 2025-01-06 10:47:04 +00:00
David Soria Parra
0c00fdc783
Merge branch 'v1.2.x' 2025-01-06 10:37:46 +00:00
David Soria Parra
6a806549c8
chore: add .DS_Store to gitignore 2025-01-06 10:37:12 +00:00
David Soria Parra
e8dd39f839
fix: #128 fix package in the generated mcp run config
During `mcp install` we are generating a config
that relies on `mcp run`. However `mcp` is only available
as a CLI command if we use the `cli` feature.

We need to specify that we want to install mcp
with the cli feature.

Fixes #128
2025-01-06 10:37:01 +00:00
David Soria Parra
2efa525a66
docs: Add branch selection guidance to CONTRIBUTING.md 2025-01-03 21:27:21 +00:00
David Soria Parra
b8fd814f1c
Bump version 2025-01-03 21:25:02 +00:00
David Soria Parra
af940aeea7
ci: Run main checks on tags
Some checks failed
Main branch checks / checks (push) Has been cancelled
2025-01-03 16:15:00 +00:00
David Soria Parra
435cdda43c
release: Prepare v1.2.0 2025-01-03 16:10:46 +00:00
David Soria Parra
1e52765052
Merge branch 'main' into v1.2.x 2025-01-03 16:10:14 +00:00
David Soria Parra
58bc32e559
merge: Resolve conflicts from v1.1.x merge 2025-01-03 16:06:51 +00:00
David Soria Parra
e32bf07433
docs: Update CLAUDE.md with ruff and pre-commit info 2025-01-03 15:57:56 +00:00
David Soria Parra
e21e8c157d
style: Fix import sorting and update ruff version 2025-01-03 15:57:14 +00:00
David Soria Parra
329f595f5f
fix: Fix line length in test_server.py 2025-01-03 15:53:19 +00:00
David Soria Parra
d06b393cdb
build: bump version to v1.1.3 2025-01-03 15:45:57 +00:00
David Soria Parra
118ddcc9ac
feat: upgrade dev dependencies 2025-01-03 15:30:49 +00:00
David Soria Parra
1bbd27d4a5
docs: Add learnings about tool usage 2025-01-03 15:28:15 +00:00
David Soria Parra
2d8f08d648
fix: Address formatting and type checking issues 2025-01-03 15:23:58 +00:00
David Soria Parra
79ec8dccdb
test: Add test for unlimited tool listing (issue #100) 2025-01-03 15:20:43 +00:00
David Soria Parra
ed993166e4
example: Add FastMCP example with Unicode characters and emojis 2025-01-03 15:20:43 +00:00
David Soria Parra
43796bfa24
test: Add test for non-ASCII character handling in FastMCP tools 2025-01-03 15:20:43 +00:00
David Soria Parra
d93b99e163
Merge pull request #121 from allenporter/mcp-sse-type-error
Fix SSE server uncaught TypeError due to incorrect routing
2025-01-03 14:38:58 +00:00
David Soria Parra
73c055f136
fix: Update AnyIO stream creation to use type annotation syntax 2025-01-02 16:59:26 +00:00
David Soria Parra
ae73191b3e
fix: Change --env-var short flag from -e to -v to avoid conflict with --with-editable 2025-01-02 16:57:53 +00:00
David Soria Parra
bd5170e19e
Merge pull request #120 from SecretiveShell/fix-encoding-errors
add text encoding params to STDIO client
2025-01-02 14:59:36 +00:00
David Soria Parra
21ac4043d5
Merge pull request #118 from samefarrar/mcp-instead-of-fastmcp-install
Use mcp instead of fastmcp CLI as command in "mcp install"
2025-01-02 09:43:03 +00:00
Allen Porter
b89cabc7df
fix: Add constructor for McpError to allow setting field
Backport of #116 by @allenporter to v1.1.x branch.

Fixes an issue where exception handling code fails with AttributeError
when accessing the error field of McpError.
2025-01-02 09:32:43 +00:00
David Soria Parra
312e9da2dc
Merge pull request #90 from restlessronin/issue-87
feat: add version string parameter to 'create_initialization_options'
2025-01-02 09:15:26 +00:00
restlessronin
3de4dc1f13
feat: add version string parameter to Server constructor 2025-01-02 09:11:07 +00:00
restlessronin
34a257147b
chore: revert doc changes 2025-01-02 09:11:07 +00:00
restlessronin
6f108f7f7c
doc: added comment to README example 2025-01-02 09:11:07 +00:00
restlessronin
937c640f04
feat: add version string parameter to 'create_initialization_options' 2025-01-02 09:11:07 +00:00
Allen Porter
247aec9332 Update paths in examples 2024-12-30 10:09:42 -08:00
Allen Porter
1134421a3a Fix inconsistencies in examples 2024-12-30 10:08:57 -08:00
Allen Porter
0970ef4ae0 Fix SSE server bug with uncaught TypeError 2024-12-30 10:04:19 -08:00
TerminalMan
99727a97ae ruff format 2024-12-29 17:09:18 +00:00
TerminalMan
4f36581a5c add text encoding params to STDIO client 2024-12-29 16:41:30 +00:00
samefarrar
e65404aa05 fix: Install still runs the fastmcp command, which is now unecessary (/not installed). We can just run it with mcp. 2024-12-29 15:01:25 +00:00
samefarrar
6194d06f6c test: Add test that uv command inserted into config can run "with --help" 2024-12-29 14:57:34 +00:00
Edoardo Cilia
eba1ad2772
Merge branch 'main' into feature/simple-chatbot-example 2024-12-23 22:59:36 +00:00
David Soria Parra
552f797ac3
build: bump version to v1.2.0rc1 2024-12-23 16:43:32 +00:00
David Soria Parra
59fff69c38
Merge pull request #106 from modelcontextprotocol/davidsp/fastmcp
feat: Integrate FastMCP
2024-12-23 16:00:53 +00:00
David Soria Parra
2c8ea49b6f
Merge branch 'v1.1.x' 2024-12-21 23:17:30 +00:00
David Soria Parra
e691c511ab
Merge pull request #108 from modelcontextprotocol/davidsp/fix-103
Fix Bug: request_meta is not available in server.request_context #103
2024-12-21 23:17:03 +00:00
David Soria Parra
52e69efa6f
Fix deprecation warning 2024-12-21 23:16:25 +00:00
David Soria Parra
37f6a3229c
fix: use pydantic Field with alias for _meta fields
Pydantic treats fields starting with underscore as private/hidden. To fix this,
we need to use Field with alias='_meta' to properly handle these fields while
keeping the external API unchanged. This fixes #103 where meta fields were not
being properly assigned in request contexts.
2024-12-21 23:16:24 +00:00
David Soria Parra
44e1abb7f1
fix tests 2024-12-21 01:17:59 +00:00
David Soria Parra
680afeeed6
fix pydantic version 2024-12-21 01:04:30 +00:00
David Soria Parra
5d9b544ce7
add uvicorn dependency 2024-12-21 00:54:59 +00:00
David Soria Parra
9d58fed2fd
add precommits 2024-12-21 00:49:02 +00:00
David Soria Parra
d2c3e21173
upgrade to pydantic 2.10.0 to fix type issues 2024-12-21 00:47:50 +00:00
David Soria Parra
0b09fa5944
fix type issues 2024-12-21 00:46:37 +00:00
David Soria Parra
561397c409
remove comment 2024-12-21 00:46:37 +00:00
David Soria Parra
a79f51f55f
style: Fix imports and line length formatting 2024-12-21 00:46:36 +00:00
David Soria Parra
7bbf71e29a
docs: Add back advanced usage sections to README
Add back sections on low-level server implementation, client usage, MCP primitives and server capabilities that were previously removed.
2024-12-21 00:46:36 +00:00
David Soria Parra
ca3121282e
docs: Update README to specify [cli] dependency for mcp CLI 2024-12-21 00:46:36 +00:00
David Soria Parra
45cea71d90
docs: Update README with FastMCP-style structure and examples 2024-12-21 00:46:36 +00:00
David Soria Parra
f2128a7977
docs: Update README with new structure and content 2024-12-21 00:46:35 +00:00
David Soria Parra
bed35f3737
Update example servers to use mcp.server.lowlevel 2024-12-21 00:41:59 +00:00
David Soria Parra
b90da481eb
docs: Update README to recommend mcp.server.lowlevel and deprecate mcp.server 2024-12-21 00:41:59 +00:00
David Soria Parra
aa3b550ec6
docs: Update FastMCP import paths and dependencies 2024-12-21 00:41:59 +00:00
David Soria Parra
dad01945c7
docs: Update README.md to include FastMCP
Update the README.md to include FastMCP as the recommended way to build MCP servers. FastMCP is a high-level, Pythonic interface that makes it easy to build MCP servers with minimal boilerplate. The low-level implementation is still available for more control.

🤖 Generated with Claude CLI.

Co-Authored-By: Claude <noreply@anthropic.com>
2024-12-21 00:41:59 +00:00
David Soria Parra
87cee0ff33
feat: Add CLI package 2024-12-21 00:41:58 +00:00
David Soria Parra
15cd7df87f
refactor: Update FastMCP examples to use new import path
Update all FastMCP examples to use mcp.server.fastmcp instead of fastmcp.
Add tests to verify example servers work correctly.

Changes:
- Update import paths in all example files
- Create test_examples.py to verify functionality
- Fix test assertions to match actual response format
2024-12-21 00:41:39 +00:00
David Soria Parra
fe75f43ee6
fix: Update test to match Pydantic's JSON schema output for model references with defaults
Pydantic now uses allOf to combine model references with default values in JSON schema output.
2024-12-21 00:41:39 +00:00
David Soria Parra
557e90d2e7
Integrate FastMCP
This commit integrates FastMCP, a high-level MCP server implementation originally written by Jeremiah Lowin,
into the official MCP SDK. It also updates dependencies and adds new dev dependencies.
It moves the existing SDK into a .lowlevel .
2024-12-21 00:41:38 +00:00
3choff
80a39f2349 Merge branch 'feature/simple-chatbot-example' of https://github.com/3choff/python-sdk into feature/simple-chatbot-example 2024-12-18 16:35:54 +00:00
3choff
466e1e8eb7 refactor: modernize type hints and improve async context handling
- Update type hints to use Python 3.10 syntax (dict, list, X | None)
- Replace requests with httpx for HTTP client consistency
- Improve async context management using AsyncExitStack
- Simplify server cleanup method
2024-12-18 16:35:49 +00:00
Edoardo Cilia
07db4a3dd1
Merge branch 'main' into feature/simple-chatbot-example 2024-12-18 16:32:52 +00:00
David Soria Parra
edb37c8dc0
Fix deprecation warning 2024-12-12 23:18:52 +00:00
David Soria Parra
e98291e001
Merge branch 'v1.1.x' 2024-12-12 17:04:08 +00:00
David Soria Parra
dd1a069508
Add checks to release branch 2024-12-12 17:01:07 +00:00
David Soria Parra
1d4a0d3589
ruff format 2024-12-12 17:01:00 +00:00
David Soria Parra
93bce513a5
Merge branch 'v1.1.x' 2024-12-12 16:42:39 +00:00
David Soria Parra
ef9b4cf6a2
Remove click dependency in client 2024-12-12 16:41:30 +00:00
David Soria Parra
21f2f82b7d
Merge branch 'v1.1.x' 2024-12-12 15:46:22 +00:00
David Soria Parra
ce25842d35
1.1.2 2024-12-12 15:44:17 +00:00
David Soria Parra
8425c9ce9b
Downgrade sse-starlette 2024-12-12 15:44:00 +00:00
3choff
a0216c3e50 Add example client to examples/clients folder 2024-12-11 16:06:10 +00:00
David Soria Parra
aaf32b5307
Merge branch 'v1.1.x' 2024-12-09 14:26:46 +00:00
David Soria Parra
34f380c905
v1.1.1 2024-12-09 14:23:29 +00:00
David Soria Parra
28ed9cba5a
Change lower bounds and update locks 2024-12-09 14:23:08 +00:00
David Soria Parra
ed03a426c7
Use uv --resolution lowest-direct
We need to ensure that we always test against the lowest bounds.
Libraries should set resolution to `lowest-direct`.
2024-12-09 14:22:59 +00:00
David Soria Parra
6391c9e004
Merge pull request #89 from oskarraszkiewicz/main
Fix simple-prompt example README
2024-12-04 22:17:24 +00:00
Oskar Raszkiewicz
e4c00741be Fix simple-prompt example README 2024-12-04 19:44:00 +01:00
David Soria Parra
44ec763f92
Fix READMEs of examples 2024-12-03 22:35:52 +00:00
David Soria Parra
15ed7cb2dc
bump 2024-12-03 22:21:26 +00:00
David Soria Parra
e4e4954b66
v1.1.0 2024-12-03 22:16:52 +00:00
David Soria Parra
c40009524c
Merge pull request #82 from miguelg719/80
Updated example in README.md for increased outreach
2024-12-02 11:39:31 +00:00
Miguel
840ea48371 Updated example in README.md for increased outreach 2024-11-30 01:11:29 -08:00
David Soria Parra
131902f09f
Merge pull request #77 from modelcontextprotocol/davidsp/68
Add handler for resource templates
2024-11-28 11:08:59 +00:00
David Soria Parra
08075ba5e4
Update issue templates 2024-11-28 10:43:12 +00:00
David Soria Parra
587dbb739d
Add handler for resource templates
We missed to have a handler for resource templates. We add this
now. One caveat is still that the `uriTemplate` in `ResourceTemplate`
is not a pydantic type.
2024-11-27 23:01:11 +00:00
David Soria Parra
ab2f502f4a
Merge pull request #70 from modelcontextprotocol/jspahrsummers-patch-1
Fix experimental capabilities example
2024-11-26 15:08:50 +00:00
Justin Spahr-Summers
c6d3bdce0f
Fix experimental capabilities example
See discussion in https://github.com/modelcontextprotocol/specification/pull/65.
2024-11-26 08:28:03 -06:00
David Soria Parra
e41482821c
Merge pull request #66 from simonw/patch-1
Fix first Python demo so it works without further modifications
2024-11-25 20:10:14 +00:00
Simon Willison
569acf077d
Fix first Python demo so it works
You can't do async with ... outside of an async def function
2024-11-25 08:44:19 -08:00
David Soria Parra
6063b09d04
bump 2024-11-25 14:23:53 +00:00
1677 changed files with 264916 additions and 4917 deletions

View file

@ -0,0 +1,118 @@
Review the pull request: $ARGUMENTS
Follow these steps carefully. Use the `gh` CLI for all GitHub interactions.
## Step 1: Resolve the PR
Parse `$ARGUMENTS` to determine the PR. It can be:
- A full URL like `https://github.com/owner/repo/pull/123`
- A `owner/repo#123` reference
- A bare number like `123` (use the current repo)
- A description — search for it with `gh pr list --search "<description>" --limit 5` and pick the best match
Once resolved, fetch the PR metadata:
```bash
gh pr view <PR> --json number,title,body,author,state,baseRefName,headRefName,url,labels,milestone,additions,deletions,changedFiles,createdAt,updatedAt,mergedAt,reviewDecision,reviews,assignees
```
## Step 2: Gather the diff
Get the full diff of the PR:
```bash
gh pr diff <PR>
```
If the diff is very large (>3000 lines), focus on the most important files first and summarize the rest.
## Step 3: Collect PR discussion context
Fetch all comments and review threads:
```bash
gh api repos/{owner}/{repo}/pulls/{number}/comments --paginate
gh api repos/{owner}/{repo}/issues/{number}/comments --paginate
gh api repos/{owner}/{repo}/pulls/{number}/reviews --paginate
```
Pay attention to:
- Reviewer feedback and requested changes
- Author responses and explanations
- Any unresolved conversations
- Approval or rejection status
## Step 4: Find and read linked issues
Look for issue references in:
- The PR body (patterns like `#123`, `fixes #123`, `closes #123`, `resolves #123`)
- The PR branch name (patterns like `issue-123`, `fix/123`)
- Commit messages
For each linked issue, fetch its content:
```bash
gh issue view <number> --json title,body,comments,labels,state
```
Read through issue comments to understand the original problem, user reports, and any discussed solutions.
## Step 5: Analyze and validate
With all context gathered, analyze the PR critically:
1. **Intent alignment**: Does the code change actually solve the problem described in the PR and/or linked issues?
2. **Completeness**: Are there aspects of the issue or requested feature that the PR doesn't address?
3. **Scope**: Does the PR include changes unrelated to the stated goal? Are there unnecessary modifications?
4. **Correctness**: Based on the diff, are there obvious bugs, edge cases, or logic errors?
5. **Testing**: Does the PR include tests? Are they meaningful and do they cover the important cases?
6. **Breaking changes**: Could this PR break existing functionality or APIs?
7. **Unresolved feedback**: Are there reviewer comments that haven't been addressed?
## Step 6: Produce the review summary
Present the summary in this format:
---
### PR Review: `<title>` (<url>)
**Author:** <author> | **Status:** <state> | **Review decision:** <decision>
**Base:** `<base>` ← `<head>` | **Changed files:** <n> | **+<additions> / -<deletions>**
#### Problem
<1-3 sentences describing what problem this PR is trying to solve, based on the PR description and linked issues>
#### Solution
<1-3 sentences describing the approach taken in the code>
#### Key changes
<Bulleted list of the most important changes, grouped by theme. Include file paths.>
#### Linked issues
<List of linked issues with their title, state, and a one-line summary of the discussion>
#### Discussion highlights
<Summary of important comments from reviewers and the author. Flag any unresolved threads.>
#### Concerns
<List any issues found during validation: bugs, missing tests, scope creep, unaddressed feedback, etc. If none, say "No concerns found.">
#### Verdict
<One of: APPROVE / REQUEST CHANGES / NEEDS DISCUSSION, with a brief justification>
#### Suggested action
<Clear recommendation for the reviewer: what to approve, what to push back on, what to ask about>
---

View file

@ -0,0 +1,150 @@
---
name: test-quality
description: Test quality bar for this repo. Read when writing, reviewing, or designing tests — covers naming, abstraction level, assertions, determinism, and the process for agent-driven test work.
---
# Test & code quality guide
What "best practice for new work" means in this repo. Each rule carries its recorded reasoning
where one exists; a rule with no stated why is a convention — follow it anyway.
## Naming & shape
- **Test names are behaviour sentences** stating the observable outcome, not the feature being
poked: `test_elicit_form_decline_returns_no_content`, never `test_elicit_form_decline`.
- Plain top-level `test_*` functions; no `Test` classes (legacy files have them — don't copy).
- **Docstrings: 1–2 sentences of behaviour, honest about provenance** — spec-mandated,
SDK-defined, or pinning a known gap? Say which: provenance is the triage key when the test
later fails. A pinned-gap assertion breaking usually means a change *fixed* the gap; a
spec-mandated assertion breaking means a regression.
- Define things in dependency order; nothing forward-references. For client↔server tests:
handlers → server construction → client setup → act → assert — the test reads in the order
the conversation happens.
- Inline the server (or equivalent setup) in the test, so the whole observable behaviour fits
on one screen. Lift to a file-level fixture only when several tests in *that file* genuinely
share it; never share across files.
- A big multi-step test is fine when the property is irreducibly multi-step (e.g. resumability).
Split when a failure wouldn't tell you which claim broke — not for shortness. Compensate with
a numbered "Steps:" docstring so a reader sees the choreography before the body.
## Level of abstraction
- **Drive through the highest-level public API that can observe the property.** Hand-built wire
requests are brittle and don't prove the user-facing contract; tests that stay above the
internals keep working when the internals change. Drop to raw HTTP only when the assertion is
about something the high-level API *cannot* observe (status codes, headers, wire framing).
- **Scripting a peer over raw streams is a last resort**, reserved for behaviour the typed API
cannot *produce* (malformed input, an impossible peer response). First ask what it would take
for the public API to express it — often a small helper suffices. Every such test's docstring
states why the public API couldn't do it.
- **In-memory / in-process first.** HTTP-, SSE-, and auth-shaped behaviour can all be driven
through an in-process ASGI transport; threads only when necessary, subprocesses only when the
process boundary is itself the thing under test. In-process isn't just faster — it surfaces
bugs (a real stream leak was found this way) that subprocess indirection masks.
- **Tests read like real user code**: no aliasing shims in conftest, no walls of suppressions,
no private imports unless that is genuinely the documented way to do the thing.
- The assertion must prove the round trip — no side-channel state. What the server saw comes
back through the protocol, or via a closure-captured list asserted after the call. Handlers
assert their dispatch identity first (`assert params.name == "add"`), proving the request
that arrived is the request the test sent.
## Assertions
- **Transformations** (input → output the SDK produced) → full-object `snapshot(...)` equality,
so an added or dropped field fails. Regenerate with `--inline-snapshot=fix` so intentional
changes arrive as a reviewable diff; never hand-edit snapshot literals.
- **Pass-through values** (opaque tokens, `_meta`, cursors) → identity against the same
variable you sent. A snapshot of a pass-through value only "matches" because a human checked
two literals correspond — it proves nothing.
- **Errors** → `pytest.raises` + `.code` against the named constant; snapshot SDK-authored
messages; never `match=` on message text. Third-party text (pydantic, jsonschema) → stable
prefix only — never pin text that changes with a dependency upgrade — with a comment saying so.
## Determinism
- **No sleeps, ever.** A sleep guesses at timing instead of waiting on the condition, so it
either flakes or pads the run — sleeps head the list of the older test code's failure modes.
Coordinate with `anyio.Event` so the wait ends exactly when the condition holds (that
discipline is why 529 e2e tests run in ~10 s). Sole exception: tests *of* time-based
features, with a comment.
- Bound every indefinite wait with `anyio.fail_after(5)`. **5 is the standard** — widening it
needs an articulable reason; "10 to be safe" is covering up a flake, not fixing one, and
unexplained widenings propagate (one agent used 10 and every later one copied it).
- **Never assert wall-clock time, even with huge margins.** `elapsed < 0.9` on a ~0.01 s
operation — a 90× margin — was still rejected in review. `fail_after` bounding a hang is the
only timing primitive allowed.
- **Concurrency tests must prove genuine interleaving.** Without barriers the scheduler is free
to serialize — "a" can start, finish, and return before "b" even begins — so two `start_soon`
calls prove nothing. Gate with events so all parties are mid-flight before any proceeds, emit
interleaved, then assert the demux.
- **Don't over-synchronize either.** When delivery ordering is guaranteed (notifications
emitted during a request you're awaiting, over a single ordered in-memory stream), a plain
collected list asserted after the call is correct; events are for messages not tied to an
awaited operation. Verify the ordering guarantee actually holds for your transport first.
- Async tests use anyio, not asyncio.
## Behaviour philosophy
- **Pin current behaviour; never xfail.** A green suite asserting what actually happens is a
regression bar for any refactor; an xfail proves nothing about it. Where current behaviour
falls short of spec, pin the divergent output and record the gap as data — a tracking issue,
with the docstring naming the known gap (suites with a requirements manifest record it as a
divergence entry). Not hidden, not skipped.
- **Hollow-proof check**: before claiming a test covers a behaviour, re-read the claim and ask
"which assertion proves *this*?" A passing test near a behaviour is not proof of it — a full
review of the e2e suite found two such cases even under this discipline.
## Hygiene
- No new `# pragma`, `# type: ignore`, `# noqa` by default — restructure first; a suppression
usually means a test or a type is missing. The narrow sanctioned escape hatches (and the
audit to run before pushing) are in AGENTS.md. In tests, narrow types with `assert
isinstance`; never `Any`/`object` when a real type exists.
- **Warnings raised during tests are findings, not noise** (the repo runs
`filterwarnings = error`). Fix the cause; if the fix can't land in the same change, scope the
suppression to the one fixture that needs it and track the real fix explicitly.
- Registered-but-never-invoked handler bodies are `raise NotImplementedError`, so they cannot
silently become load-bearing.
- Comments live next to the line they explain, not in docstrings; single backticks for code
refs; match the surrounding comment density (one-liners next to one-liners). No
`from __future__ import annotations` (py310+ repo).
- Test work doesn't change `src/` as a side effect — the one mechanical exception is deleting a
pragma a new test now covers. If a test can't be written without a library change, raise it
as a finding or defer the test; don't quietly edit `src/`.
## Process (for agent-driven work)
- **Small chunks.** ≤10 tests per human-reviewed batch; when fanning out to multiple agents,
≤5 per agent. Review quality scales inversely with batch size: the observed shortcuts
(over-stacked tests, a timing assertion, wrong abstraction level) all surfaced in one
oversized 27-test batch.
- **Design → review → implement, as separate steps.** The design deliverable is not just the
plan — it's the judgement calls (abstraction level, deferrals, contested assertions) stated
explicitly, so the reviewer vetoes them before implementation rather than discovering them
in the diff.
- **High-stakes areas get fresh adversarial reviewers** on both the design and the
implementation — fresh, because whoever designed it (or saw the proposed fix) anchors on the
same layer. SERIOUS findings (wrong assertion, missed MUST/SHOULD, an unrecorded known gap)
re-loop; style doesn't. Reserve the full panel for areas where being wrong is worse than
being slow.
- **Investigations pair a full-context look with a fresh agent given only the problem** —
never the proposed fix — and compare conclusions. The unbiased read regularly catches
anchoring on the wrong layer.
- **When review questions a decision, reconsider genuinely**: re-derive the tradeoff, state the
options, recommend with reasons. Defending the original choice is a valid outcome;
reflexively agreeing with every challenge is as bad as ignoring it.
- **Validate the reference artifact before building on it.** Whatever you treat as ground truth
(a spec import, a baseline, a generated list), check it *first* — discovering it was
incomplete after five batches costs far more than before batch one.
- **Verify as you go**: per-file `uv run --frozen pytest <file> -q` + pyright + ruff while
iterating; full suite, coverage, and `./scripts/test` (when `src/` was touched) at
integration.
- **Every agent writes notes**: what it did, what broke, and — most importantly — what it
couldn't decide, so open questions surface instead of being silently resolved by whoever hit
them.
- **Quality over speed is a stated goal, not a preference** ("rushing something out the door is
not the goal here, explicitly so"). Don't quietly de-scope agreed work mid-stream;
renegotiate scope explicitly.
- **Don't copy patterns from existing test code by default** — much of the repo's older test
code is below the current bar (sleeps, mocks, `Test` classes, raciness). These rules define
the bar for new work.

5
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,5 @@
# Applied 120 line-length rule to all files: https://github.com/modelcontextprotocol/python-sdk/pull/856
543961968c0634e93d919d509cce23a1d6a56c21
# Added 100% code coverage baseline with pragma comments: https://github.com/modelcontextprotocol/python-sdk/pull/1553
89e9c43acf7e23cf766357d776ec1ce63ac2c58e

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Generated
uv.lock linguist-generated=true

68
.github/ISSUE_TEMPLATE/bug.yaml vendored Normal file
View file

@ -0,0 +1,68 @@
name: 🐛 MCP Python SDK Bug
description: Report a bug or unexpected behavior in the MCP Python SDK
labels: ["need confirmation"]
body:
- type: markdown
attributes:
value: Thank you for contributing to the MCP Python SDK! ✊
- type: checkboxes
id: checks
attributes:
label: Initial Checks
description: >
Both the 2.x stable line and the 1.x maintenance line are supported, and only
the newest release of each line receives fixes.
options:
- label: I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
required: true
- label: I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
required: true
- type: dropdown
id: release-line
attributes:
label: Release line
description: Which major version of the SDK are you using?
options:
- 2.x (current stable)
- 1.x (maintenance line, v1.x branch)
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: |
Please explain what you're seeing and what you would expect to see.
Please provide as much detail as possible to make understanding and solving your problem as quick as possible. 🙏
validations:
required: true
- type: textarea
id: example
attributes:
label: Example Code
description: >
If applicable, please add a self-contained,
[minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example)
demonstrating the bug.
placeholder: |
from mcp.server.mcpserver import MCPServer
...
render: Python
- type: textarea
id: version
attributes:
label: Python & MCP Python SDK
description: |
Which version of Python and MCP Python SDK are you using?
render: Text
validations:
required: true

1
.github/ISSUE_TEMPLATE/config.yaml vendored Normal file
View file

@ -0,0 +1 @@
blank_issues_enabled: false

View file

@ -0,0 +1,29 @@
name: 🚀 MCP Python SDK Feature Request
description: "Suggest a new feature for the MCP Python SDK"
labels: ["feature request"]
body:
- type: markdown
attributes:
value: Thank you for contributing to the MCP Python SDK! ✊
- type: textarea
id: description
attributes:
label: Description
description: |
Please give as much detail as possible about the feature you would like to suggest. 🙏
You might like to add:
* A demo of how code might look when using the feature
* Your use case(s) for the feature
* Reference to other projects that have a similar feature
validations:
required: true
- type: textarea
id: references
attributes:
label: References
description: |
Please add any links or references that might help us understand your feature request better. 📚

33
.github/ISSUE_TEMPLATE/question.yaml vendored Normal file
View file

@ -0,0 +1,33 @@
name: ❓ MCP Python SDK Question
description: "Ask a question about the MCP Python SDK"
labels: ["question"]
body:
- type: markdown
attributes:
value: Thank you for reaching out to the MCP Python SDK community! We're here to help! 🤝
- type: textarea
id: question
attributes:
label: Question
description: |
Please provide as much detail as possible about your question. 🙏
You might like to include:
* Code snippets showing what you've tried
* Error messages you're encountering (if any)
* Expected vs actual behavior
* Your use case and what you're trying to achieve
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: |
Please provide any additional context that might help us better understand your question, such as:
* Your MCP Python SDK version
* Your Python version
* Relevant configuration or environment details 📝

59
.github/ISSUE_TEMPLATE/v2-feedback.yaml vendored Normal file
View file

@ -0,0 +1,59 @@
name: v2 feedback
description: Bugs, API friction, or docs gaps in v2 of the SDK
title: "[v2] "
labels: ["v2"]
body:
- type: markdown
attributes:
value: |
Anything in v2 that broke, surprised you, or slowed you down is useful, including API friction and docs gaps.
Docs: https://py.sdk.modelcontextprotocol.io/ · Migration from v1: https://py.sdk.modelcontextprotocol.io/migration/
- type: textarea
id: what
attributes:
label: What happened?
description: What did you do, and what went wrong (or felt wrong)? Paste error output verbatim if there is any.
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect?
validations:
required: false
- type: textarea
id: repro
attributes:
label: Code to reproduce
description: The smallest snippet or repository that shows it. For docs feedback, link the page instead.
render: Python
validations:
required: false
- type: input
id: version
attributes:
label: SDK version
description: The published version (`pip show mcp`) or commit.
validations:
required: false
- type: dropdown
id: area
attributes:
label: Area
options:
- Server
- Client
- Transports
- Auth
- Documentation
- Migration
- Other
validations:
required: false

644
.github/actions/conformance/client.py vendored Normal file
View file

@ -0,0 +1,644 @@
"""MCP unified conformance test client.
This client is designed to work with the @modelcontextprotocol/conformance npm package.
It handles all conformance test scenarios via environment variables and CLI arguments.
Contract:
- MCP_CONFORMANCE_SCENARIO env var -> scenario name
- MCP_CONFORMANCE_CONTEXT env var -> optional JSON (for client-credentials scenarios)
- MCP_CONFORMANCE_PROTOCOL_VERSION env var -> spec version the harness mock
server is speaking (e.g. "2025-11-25", "2026-07-28"). Always set; when
--spec-version is omitted the harness picks per-scenario (LATEST_SPEC_VERSION
for active scenarios, DRAFT_PROTOCOL_VERSION for draft-only ones).
- Server URL as last CLI argument (sys.argv[1])
- Must exit 0 within the harness --timeout (CI passes 60s; the default is 30s)
Scenarios:
initialize - Connect, initialize, list tools, close
tools_call - Connect, call add_numbers(a=5, b=3), close
sse-retry - Connect, call test_reconnection, close
json-schema-ref-no-deref - Connect, list tools (no $ref deref)
json-schema-2020-12-preservation - List tools, echo the focal inputSchema back verbatim
request-metadata - Connect with all callbacks; client stamps _meta
http-standard-headers - Tool, resource and prompt round-trips (Mcp-* headers checked)
http-invalid-tool-headers - List tools, call every surfaced tool (x-mcp-header filter)
http-custom-headers - Replay the harness's toolCalls (x-mcp-header -> Mcp-Param-*)
elicitation-sep1034-client-defaults - Elicitation with default accept callback
sep-2322-client-request-state - Drive the MRTR auto-loop (SEP-2322)
auth/client-credentials-jwt - Client credentials with private_key_jwt
auth/client-credentials-basic - Client credentials with client_secret_basic
auth/enterprise-managed-authorization - SEP-990 ID-JAG (RFC 8693 + RFC 7523 jwt-bearer)
auth/* - Authorization code flow (default for auth scenarios)
"""
import asyncio
import json
import logging
import os
import sys
from collections.abc import Callable, Coroutine
from typing import Any, cast
from urllib.parse import parse_qs, urlparse
import httpx2
import mcp_types as types
from mcp_types.version import MODERN_PROTOCOL_VERSIONS
from pydantic import AnyUrl
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.client.auth.extensions.client_credentials import (
ClientCredentialsOAuthProvider,
PrivateKeyJWTOAuthProvider,
SignedJWTParameters,
)
from mcp.client.auth.extensions.identity_assertion import IdentityAssertionOAuthProvider
from mcp.client.auth.utils import build_protected_resource_metadata_discovery_urls
from mcp.client.client import Client
from mcp.client.context import ClientRequestContext
from mcp.client.streamable_http import streamable_http_client
from mcp.shared.auth import AuthorizationCodeResult, OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
# Set up logging to stderr (stdout is for conformance test output)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger(__name__)
#: Spec version the harness is running this scenario at (e.g. "2025-11-25",
#: "2026-07-28"). The harness always sets this (when --spec-version is omitted
#: it picks per-scenario: LATEST_SPEC_VERSION for active scenarios,
#: DRAFT_PROTOCOL_VERSION for draft-only ones), so None means we were invoked
#: outside the harness.
PROTOCOL_VERSION: str | None = os.environ.get("MCP_CONFORMANCE_PROTOCOL_VERSION")
def client_mode() -> str:
"""Pick the Client(mode=) for the harness leg.
On a modern leg (2026-07-28+) -> 'auto' so Client.discover() runs and the
_meta envelope + MCP-Protocol-Version header are stamped on every request.
On a handshake-era leg -> 'legacy' so the initialize handshake runs exactly
as before (no server/discover probe is sent against a mock that would 400 it).
Outside the harness -> 'auto' (probe + fallback).
"""
if PROTOCOL_VERSION is None or PROTOCOL_VERSION in MODERN_PROTOCOL_VERSIONS:
return "auto"
return "legacy"
# Type for async scenario handler functions
ScenarioHandler = Callable[[str], Coroutine[Any, None, None]]
# Registry of scenario handlers
HANDLERS: dict[str, ScenarioHandler] = {}
def register(name: str) -> Callable[[ScenarioHandler], ScenarioHandler]:
"""Register a scenario handler."""
def decorator(fn: ScenarioHandler) -> ScenarioHandler:
HANDLERS[name] = fn
return fn
return decorator
def get_conformance_context() -> dict[str, Any]:
"""Load conformance test context from MCP_CONFORMANCE_CONTEXT environment variable."""
context_json = os.environ.get("MCP_CONFORMANCE_CONTEXT")
if not context_json:
raise RuntimeError(
"MCP_CONFORMANCE_CONTEXT environment variable not set. "
"Expected JSON with client_id, client_secret, and/or private_key_pem."
)
try:
return json.loads(context_json)
except json.JSONDecodeError as e:
raise RuntimeError(f"Failed to parse MCP_CONFORMANCE_CONTEXT as JSON: {e}") from e
class InMemoryTokenStorage(TokenStorage):
"""Simple in-memory token storage for conformance testing."""
def __init__(self) -> None:
self._tokens: OAuthToken | None = None
self._client_info: OAuthClientInformationFull | None = None
async def get_tokens(self) -> OAuthToken | None:
return self._tokens
async def set_tokens(self, tokens: OAuthToken) -> None:
self._tokens = tokens
async def get_client_info(self) -> OAuthClientInformationFull | None:
return self._client_info
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
self._client_info = client_info
class ConformanceOAuthCallbackHandler:
"""OAuth callback handler that automatically fetches the authorization URL
and extracts the auth code, without requiring user interaction.
"""
def __init__(self) -> None:
self._auth_code: str | None = None
self._state: str | None = None
self._iss: str | None = None
async def handle_redirect(self, authorization_url: str) -> None:
"""Fetch the authorization URL and extract the auth code from the redirect."""
logger.debug(f"Fetching authorization URL: {authorization_url}")
async with httpx2.AsyncClient() as client:
response = await client.get(
authorization_url,
follow_redirects=False,
)
if response.status_code in (301, 302, 303, 307, 308):
location = cast(str, response.headers.get("location"))
if location:
redirect_url = urlparse(location)
query_params: dict[str, list[str]] = parse_qs(redirect_url.query)
if "code" in query_params:
self._auth_code = query_params["code"][0]
state_values = query_params.get("state")
self._state = state_values[0] if state_values else None
iss_values = query_params.get("iss")
self._iss = iss_values[0] if iss_values else None
logger.debug(f"Got auth code from redirect: {self._auth_code[:10]}...")
return
else:
raise RuntimeError(f"No auth code in redirect URL: {location}")
else:
raise RuntimeError(f"No redirect location received from {authorization_url}")
else:
raise RuntimeError(f"Expected redirect response, got {response.status_code} from {authorization_url}")
async def handle_callback(self) -> AuthorizationCodeResult:
"""Return the captured auth code, state, and iss."""
if self._auth_code is None:
raise RuntimeError("No authorization code available - was handle_redirect called?")
result = AuthorizationCodeResult(code=self._auth_code, state=self._state, iss=self._iss)
self._auth_code = None
self._state = None
self._iss = None
return result
# --- Stub callbacks (declare capabilities in _meta without doing real work) ---
async def stub_sampling_callback(
context: ClientRequestContext,
params: types.CreateMessageRequestParams,
) -> types.CreateMessageResult | types.ErrorData:
return types.CreateMessageResult(
role="assistant",
content=types.TextContent(type="text", text=""),
model="conformance-stub",
)
async def stub_list_roots_callback(context: ClientRequestContext) -> types.ListRootsResult | types.ErrorData:
return types.ListRootsResult(roots=[])
async def default_elicitation_callback(
context: ClientRequestContext,
params: types.ElicitRequestParams,
) -> types.ElicitResult | types.ErrorData:
"""Accept elicitation and apply defaults from the schema (SEP-1034)."""
content: dict[str, str | int | float | bool | list[str] | None] = {}
# For form mode, extract defaults from the requested_schema
if isinstance(params, types.ElicitRequestFormParams):
schema = params.requested_schema
logger.debug(f"Elicitation schema: {schema}")
properties = schema.get("properties", {})
for prop_name, prop_schema in properties.items():
if "default" in prop_schema:
content[prop_name] = prop_schema["default"]
logger.debug(f"Applied defaults: {content}")
return types.ElicitResult(action="accept", content=content)
# --- Scenario Handlers ---
@register("initialize")
async def run_initialize(server_url: str) -> None:
"""Connect, initialize, list tools, close."""
async with Client(server_url, mode=client_mode()) as client:
logger.debug("Initialized successfully")
await client.list_tools()
logger.debug("Listed tools successfully")
@register("json-schema-ref-no-deref")
async def run_json_schema_ref_no_deref(server_url: str) -> None:
"""Initialize and list tools; the scenario fails only if the client fetches a network $ref.
The client never walks inputSchema or resolves $refs, so listing is enough (SEP-2106).
Pinned to mode='legacy': the harness reports PROTOCOL_VERSION=2026-07-28 for this
scenario but its mock server only speaks the handshake-era lifecycle and 400s a
modern-stamped tools/list. The check is lifecycle-agnostic so this is harmless.
"""
async with Client(server_url, mode="legacy") as client:
await client.list_tools()
@register("json-schema-2020-12-preservation")
async def run_json_schema_2020_12_preservation(server_url: str) -> None:
"""List tools, then echo the focal tool's inputSchema back verbatim (SEP-1613 / SEP-2106).
The harness diffs what the client round-trips through `json_schema_echo` against its
fixture to detect 2020-12 keywords ($schema, $defs, $anchor, additionalProperties,
allOf/anyOf, if/then/else) being stripped while parsing tools/list. Unlike
json-schema-ref-no-deref, this mock is version-aware, so client_mode() applies.
"""
async with Client(server_url, mode=client_mode()) as client:
listed = await client.list_tools()
focal = next(tool for tool in listed.tools if tool.name == "json_schema_2020_12_tool")
await client.call_tool("json_schema_echo", {"schema": focal.input_schema})
@register("tools_call")
async def run_tools_call(server_url: str) -> None:
"""Connect, list tools, call add_numbers(a=5, b=3), close."""
async with Client(server_url, mode=client_mode()) as client:
await client.list_tools()
result = await client.call_tool("add_numbers", {"a": 5, "b": 3})
logger.debug(f"add_numbers result: {result}")
@register("sse-retry")
async def run_sse_retry(server_url: str) -> None:
"""Connect, list tools, call test_reconnection, close."""
async with Client(server_url, mode=client_mode()) as client:
await client.list_tools()
result = await client.call_tool("test_reconnection", {})
logger.debug(f"test_reconnection result: {result}")
@register("request-metadata")
async def run_request_metadata(server_url: str) -> None:
"""Connect on the modern path with every client capability declared.
The scenario inspects every request's `_meta` envelope (SEP-2575) for
protocolVersion / clientInfo / clientCapabilities, and the matching
MCP-Protocol-Version header. mode='auto' makes the SDK send
server/discover (covering the unsupported-version retry check), then adopt
and stamp the envelope on the follow-up requests.
"""
async with Client(
server_url,
mode=client_mode(),
sampling_callback=stub_sampling_callback,
list_roots_callback=stub_list_roots_callback,
elicitation_callback=default_elicitation_callback,
) as client:
await client.list_tools()
result = await client.call_tool("add_numbers", {"a": 5, "b": 3})
logger.debug(f"add_numbers result: {result}")
@register("http-standard-headers")
async def run_http_standard_headers(server_url: str) -> None:
"""Touch tools, resources and prompts on the modern path so each standard header is checked (SEP-2243).
The scenario inspects Mcp-Method on each request and Mcp-Name on tools/call,
resources/read and prompts/get, and reports methods the client never sent as
SKIPPED rather than failed, so exercise one of each. initialize and
notifications/initialized stay SKIPPED: the modern path discovers via
server/discover and never sends them.
"""
async with Client(server_url, mode=client_mode()) as client:
tools = await client.list_tools()
if tools.tools:
await client.call_tool(tools.tools[0].name, _stub_required_args(tools.tools[0].input_schema))
resources = await client.list_resources()
if resources.resources:
await client.read_resource(resources.resources[0].uri)
prompts = await client.list_prompts()
if prompts.prompts:
await client.get_prompt(prompts.prompts[0].name)
def _stub_required_args(input_schema: dict[str, Any]) -> dict[str, Any]:
"""Minimal arguments satisfying a tool inputSchema's required list."""
by_type: dict[str, Any] = {
"string": "x",
"integer": 0,
"number": 0,
"boolean": False,
"object": {},
"array": [],
"null": None,
}
properties = input_schema.get("properties", {})
return {name: by_type.get(properties.get(name, {}).get("type"), "x") for name in input_schema.get("required", [])}
@register("http-invalid-tool-headers")
async def run_http_invalid_tool_headers(server_url: str) -> None:
"""List tools, then call every tool the SDK surfaces (SEP-2243).
The harness mock advertises one valid tool plus several with malformed
x-mcp-header annotations (empty, non-primitive type, duplicate, invalid
chars). The scenario passes if valid_tool is called and the malformed
ones are not -- so a conforming client filters them out of the list_tools
result and the loop below never sees them. The scenario sets
allowClientError, so a per-call failure is logged and skipped rather
than aborting the whole run.
"""
async with Client(server_url, mode=client_mode()) as client:
listed = await client.list_tools()
logger.debug(f"Surfaced tools: {[t.name for t in listed.tools]}")
for tool in listed.tools:
try:
await client.call_tool(tool.name, _stub_required_args(tool.input_schema))
except Exception:
logger.exception(f"call_tool({tool.name!r}) failed")
@register("http-custom-headers")
async def run_http_custom_headers(server_url: str) -> None:
"""List tools, then replay the harness's `toolCalls` so x-mcp-header args mirror into headers (SEP-2243).
The scenario supplies the exact arguments to send (including the null/edge-case values that
exercise omission and Base64 encoding) via the context `toolCalls`; using them verbatim is
what drives every per-parameter check. `list_tools` first so the SDK caches each tool's
annotations; a tool the SDK dropped (invalid annotations) is skipped. Per-call failures are
logged and skipped rather than aborting the run.
"""
tool_calls: list[dict[str, Any]] = []
if os.environ.get("MCP_CONFORMANCE_CONTEXT"):
tool_calls = get_conformance_context().get("toolCalls", [])
async with Client(server_url, mode=client_mode()) as client:
listed = await client.list_tools()
surfaced = {tool.name for tool in listed.tools}
logger.debug(f"Surfaced tools: {sorted(surfaced)}")
for call in tool_calls:
name = call["name"]
if name not in surfaced:
logger.debug(f"skipping {name!r}: not surfaced by list_tools")
continue
try:
await client.call_tool(name, call.get("arguments") or {})
except Exception:
logger.exception(f"call_tool({name!r}) failed")
@register("elicitation-sep1034-client-defaults")
async def run_elicitation_defaults(server_url: str) -> None:
"""Connect with elicitation callback that applies schema defaults."""
async with Client(server_url, mode=client_mode(), elicitation_callback=default_elicitation_callback) as client:
await client.list_tools()
result = await client.call_tool("test_client_elicitation_defaults", {})
logger.debug(f"test_client_elicitation_defaults result: {result}")
@register("sep-2322-client-request-state")
async def run_mrtr_client(server_url: str) -> None:
"""Drive the SEP-2322 client mock through `Client.call_tool`'s auto-loop.
The mock inspects raw `tools/call` params, so registering an
`elicitation_callback` and letting the driver run is enough to satisfy
all five wire-shape checks: the driver echoes `request_state` byte-exact
and omits it when the server sent none, every retry mints a fresh
JSON-RPC id, the unrelated call between auto-loops carries no MRTR
params, and the no-`resultType` response parses as a terminal
`CallToolResult` so the driver never retries it.
"""
async def confirm(
context: ClientRequestContext, params: types.ElicitRequestParams
) -> types.ElicitResult | types.ErrorData:
return types.ElicitResult(action="accept", content={"confirmed": True})
async with Client(server_url, mode=client_mode(), elicitation_callback=confirm) as client:
await client.list_tools()
await client.call_tool("test_mrtr_echo_state", {})
await client.call_tool("test_mrtr_unrelated", {})
await client.call_tool("test_mrtr_no_state", {})
result = await client.call_tool("test_mrtr_no_result_type", {})
assert isinstance(result, types.CallToolResult)
@register("auth/client-credentials-jwt")
async def run_client_credentials_jwt(server_url: str) -> None:
"""Client credentials flow with private_key_jwt authentication."""
context = get_conformance_context()
client_id = context.get("client_id")
private_key_pem = context.get("private_key_pem")
signing_algorithm = context.get("signing_algorithm", "ES256")
if not client_id:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'client_id'")
if not private_key_pem:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'private_key_pem'")
jwt_params = SignedJWTParameters(
issuer=client_id,
subject=client_id,
signing_algorithm=signing_algorithm,
signing_key=private_key_pem,
)
oauth_auth = PrivateKeyJWTOAuthProvider(
server_url=server_url,
storage=InMemoryTokenStorage(),
client_id=client_id,
assertion_provider=jwt_params.create_assertion_provider(),
)
await _run_auth_session(server_url, oauth_auth)
@register("auth/client-credentials-basic")
async def run_client_credentials_basic(server_url: str) -> None:
"""Client credentials flow with client_secret_basic authentication."""
context = get_conformance_context()
client_id = context.get("client_id")
client_secret = context.get("client_secret")
if not client_id:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'client_id'")
if not client_secret:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'client_secret'")
oauth_auth = ClientCredentialsOAuthProvider(
server_url=server_url,
storage=InMemoryTokenStorage(),
client_id=client_id,
client_secret=client_secret,
token_endpoint_auth_method="client_secret_basic",
)
await _run_auth_session(server_url, oauth_auth)
@register("auth/enterprise-managed-authorization")
async def run_enterprise_managed_authorization(server_url: str) -> None:
"""SEP-990 enterprise-managed authorization: RFC 8693 token-exchange at the
enterprise IdP for an ID-JAG, then RFC 7523 jwt-bearer at the MCP
authorization server."""
context = get_conformance_context()
client_id = context.get("client_id")
client_secret = context.get("client_secret")
idp_client_id = context.get("idp_client_id")
idp_id_token = context.get("idp_id_token")
idp_token_endpoint = context.get("idp_token_endpoint")
if not client_id:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'client_id'")
if not client_secret:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'client_secret'")
if not idp_client_id:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_client_id'")
if not idp_id_token:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_id_token'")
if not idp_token_endpoint:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_token_endpoint'")
# IdentityAssertionOAuthProvider takes the AS issuer as configuration (the
# SEP-990 trust model: the resource server is never asked which AS to use).
# The harness does not put the issuer in context, so for conformance we
# learn it from the harness's PRM document (RFC 9728); production
# deployments would supply it as static configuration instead.
prm_url = build_protected_resource_metadata_discovery_urls(None, server_url)[0]
async with httpx2.AsyncClient(timeout=30.0) as http:
prm = (await http.get(prm_url)).raise_for_status().json()
as_issuer = prm["authorization_servers"][0]
async def fetch_id_jag(audience: str, resource: str) -> str:
"""Leg 1 - RFC 8693 token-exchange at the enterprise IdP."""
async with httpx2.AsyncClient(timeout=30.0) as http:
resp = await http.post(
idp_token_endpoint,
data={
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"requested_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"subject_token": idp_id_token,
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"audience": audience,
"resource": resource,
"client_id": idp_client_id,
},
)
resp.raise_for_status()
return resp.json()["access_token"]
oauth_auth = IdentityAssertionOAuthProvider(
server_url=server_url,
storage=InMemoryTokenStorage(),
client_id=client_id,
client_secret=client_secret,
issuer=as_issuer,
assertion_provider=fetch_id_jag,
token_endpoint_auth_method="client_secret_basic",
)
await _run_auth_session(server_url, oauth_auth)
async def run_auth_code_client(server_url: str) -> None:
"""Authorization code flow (default for auth/* scenarios)."""
callback_handler = ConformanceOAuthCallbackHandler()
storage = InMemoryTokenStorage()
# Check for pre-registered client credentials from context
context_json = os.environ.get("MCP_CONFORMANCE_CONTEXT")
if context_json:
try:
context = json.loads(context_json)
client_id = context.get("client_id")
client_secret = context.get("client_secret")
if client_id:
await storage.set_client_info(
OAuthClientInformationFull(
client_id=client_id,
client_secret=client_secret,
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
token_endpoint_auth_method="client_secret_basic" if client_secret else "none",
)
)
logger.debug(f"Pre-loaded client credentials: client_id={client_id}")
except json.JSONDecodeError:
logger.exception("Failed to parse MCP_CONFORMANCE_CONTEXT")
oauth_auth = OAuthClientProvider(
server_url=server_url,
client_metadata=OAuthClientMetadata(
client_name="conformance-client",
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
grant_types=["authorization_code", "refresh_token"],
response_types=["code"],
),
storage=storage,
redirect_handler=callback_handler.handle_redirect,
callback_handler=callback_handler.handle_callback,
client_metadata_url="https://conformance-test.local/client-metadata.json",
)
await _run_auth_session(server_url, oauth_auth)
async def _run_auth_session(server_url: str, oauth_auth: httpx2.Auth) -> None:
"""Common session logic for all OAuth flows."""
http_client = httpx2.AsyncClient(auth=oauth_auth, timeout=30.0)
transport = streamable_http_client(url=server_url, http_client=http_client)
async with Client(transport, mode=client_mode(), elicitation_callback=default_elicitation_callback) as client:
logger.debug("Initialized successfully")
tools_result = await client.list_tools()
logger.debug(f"Listed tools: {[t.name for t in tools_result.tools]}")
# Call the first available tool (different tests have different tools)
if tools_result.tools:
tool_name = tools_result.tools[0].name
try:
result = await client.call_tool(tool_name, {})
logger.debug(f"Called {tool_name}, result: {result}")
except Exception as e:
logger.debug(f"Tool call result/error: {e}")
logger.debug("Connection closed successfully")
def main() -> None:
"""Main entry point for the conformance client."""
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <server-url>", file=sys.stderr)
sys.exit(1)
server_url = sys.argv[1]
scenario = os.environ.get("MCP_CONFORMANCE_SCENARIO")
logger.debug(f"Conformance protocol version: {PROTOCOL_VERSION!r} -> mode={client_mode()!r}")
if scenario:
logger.debug(f"Running explicit scenario '{scenario}' against {server_url}")
handler = HANDLERS.get(scenario)
if handler:
asyncio.run(handler(server_url))
elif scenario.startswith("auth/"):
asyncio.run(run_auth_code_client(server_url))
else:
print(f"Unknown scenario: {scenario}", file=sys.stderr)
sys.exit(1)
else:
logger.debug(f"Running default auth flow against {server_url}")
asyncio.run(run_auth_code_client(server_url))
if __name__ == "__main__":
main()

View file

@ -0,0 +1,26 @@
# Expected failures at the 2025-11-25 wire, read by the `--suite all
# --spec-version 2025-11-25` server and client legs: every scenario the pinned
# harness marks applicable at 2025-11-25, run over the stateful handshake.
#
# That selection is a superset of the frozen 2025-11-25 requirement set (`npx
# $CONFORMANCE_PKG list --requirements 2025-11-25`), so an entry here for a
# scenario in that set means `conformance tier-check` scores the SDK below 100%
# for 2025-11-25 even though CI is green; link the tracking issue next to any
# such entry.
#
# Kept separate from expected-failures.yml even though the bare `--suite all`
# legs currently run these same scenarios at 2025-11-25 too: the harness's
# default wire is due to move (modelcontextprotocol/conformance#426) and this
# file must keep meaning 2025-11-25. Until then a failure here also needs an
# entry there. The file is passed even while empty: run-client.sh keys its solo
# re-verification off the harness's baseline output.
#
# Baseline established against the harness pinned via CONFORMANCE_PKG in
# .github/workflows/conformance.yml. New conformance releases are adopted by
# deliberately bumping that pin and reconciling all three expected-failures
# files in the same change. Entries MUST be removed as they start passing (the
# runner fails on stale entries).
client: []
server: []

View file

@ -0,0 +1,29 @@
# Expected failures at the 2026-07-28 wire, read by the `--suite all
# --spec-version 2026-07-28` server and client legs: every scenario the pinned
# harness marks applicable at 2026-07-28, run stateless with per-request _meta.
#
# That selection is a superset of the frozen 2026-07-28 requirement set (`npx
# $CONFORMANCE_PKG list --requirements 2026-07-28`), so an entry here for a
# scenario in that set means `conformance tier-check` scores the SDK below 100%
# for 2026-07-28 even though CI is green; link the tracking issue next to any
# such entry.
#
# This baseline is separate from expected-failures.yml because entries are
# keyed by scenario name only: a scenario that passes at the harness default
# wire in the bare `--suite all` legs but fails when forced to 2026-07-28 (or
# vice versa) cannot be expressed in a shared file. Where the bare legs already
# default a scenario to 2026-07-28, a failure here also needs an entry there.
#
# Baseline established against the harness pinned via CONFORMANCE_PKG in
# .github/workflows/conformance.yml. New conformance releases are adopted by
# deliberately bumping that pin and reconciling all three expected-failures
# files in the same change.
#
# Entries are grouped by what unblocks them. As each gap closes the
# corresponding scenarios start passing and MUST be removed from this list
# (the runner fails on stale entries), so the baseline burns down per
# milestone.
client: []
server: []

View file

@ -0,0 +1,55 @@
# Conformance scenarios not yet passing against the Python SDK on main, read by
# the bare `--suite all` server and client legs (every scenario shipped with the
# pinned harness, each at the harness's default wire for it). CI exits 0 if only
# these fail, exits 1 on unexpected failures or stale entries.
#
# The wire-pinned legs read expected-failures.2025-11-25.yml and
# expected-failures.2026-07-28.yml instead; see the header of
# .github/workflows/conformance.yml for the leg layout.
#
# Baseline established against the harness pinned via CONFORMANCE_PKG in
# .github/workflows/conformance.yml. New conformance releases are adopted by
# deliberately bumping that pin and reconciling all three expected-failures
# files in the same change.
#
# Entries are grouped by SEP. As each SEP lands in the SDK the corresponding
# scenarios start passing and MUST be removed from this list (the runner fails
# on stale entries), so the baseline burns down per milestone.
client:
# SEP-1932 (DPoP): the SDK's OAuth client does not implement DPoP proofs.
# The entries are per-check (conformance #406) because both scenarios
# pass their non-DPoP checks (discovery, token acquisition, request
# flow) live.
- auth/dpop:sep-1932-client-token-request-proof
- auth/dpop:sep-1932-client-dpop-auth-scheme
- auth/dpop:sep-1932-client-fresh-proof
- auth/dpop-nonce:sep-1932-client-token-request-proof
- auth/dpop-nonce:sep-1932-client-dpop-auth-scheme
- auth/dpop-nonce:sep-1932-client-fresh-proof
- auth/dpop-nonce:sep-1932-client-as-nonce
- auth/dpop-nonce:sep-1932-client-rs-nonce
# Workload identity federation: the OAuth client does not implement the
# urn:ietf:params:oauth:grant-type:jwt-bearer grant (it answers with
# authorization_code). Per-check for the same reason.
- auth/wif-jwt-bearer:wif-grant-type
server:
# SEP-2663 (io.modelcontextprotocol/tasks): the SDK does not implement the
# tasks extension yet. The bare `--suite all` leg is the only one that
# reaches these extension-tagged scenarios (they never match a
# --spec-version filter), so these entries live here only.
#
# `tasks-status-notifications` is intentionally NOT listed: the harness
# skips it unconditionally (pending its rewrite against subscriptions/
# listen), and a baseline entry for a scenario with no failing checks is
# flagged stale.
- tasks-lifecycle
- tasks-capability-negotiation
- tasks-wire-fields
- tasks-request-state-removal
- tasks-mrtr-input
- tasks-request-headers
- tasks-dispatch-and-envelope
- tasks-required-task-error
- tasks-mrtr-composition

108
.github/actions/conformance/run-client.sh vendored Executable file
View file

@ -0,0 +1,108 @@
#!/bin/bash
# Run a client conformance suite, re-verifying unexpected failures solo.
# Concurrent suite runs on a hosted runner can push scenarios with real-time
# waits past tolerance; solo, a real failure fails again while a contention
# artifact passes. Failures that only reproduce under concurrency are excused.
set -uo pipefail
: "${CONFORMANCE_PKG:?set CONFORMANCE_PKG (pinned in .github/workflows/conformance.yml)}"
# One attempt: a solo failure on the quiet runner disproves the contention
# hypothesis; a second try would be the blind retry this script avoids.
SOLO_ATTEMPTS="${CONFORMANCE_SOLO_ATTEMPTS:-1}"
# Relative args resolve from the repo root; same contract as run-server.sh.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../../.." || exit 1
log="$(mktemp)"
trap 'rm -f "$log"' EXIT
npx --yes "$CONFORMANCE_PKG" client "$@" 2>&1 | tee "$log"
rc=${PIPESTATUS[0]}
if [ "$rc" -eq 0 ]; then
exit 0
fi
plain="$(sed 's/\x1b\[[0-9;]*m//g' "$log")"
# If the harness's summary wording changes, the list comes up empty and the
# original exit code passes through - never a false green.
mapfile -t scenarios < <(
printf '%s\n' "$plain" |
sed -n '/^Unexpected failures (not in baseline):$/,/^$/p' |
sed -n 's/^ ✗ //p'
)
if [ "${#scenarios[@]}" -eq 0 ]; then
exit "$rc"
fi
for scenario in "${scenarios[@]}"; do
if ! [[ "$scenario" =~ ^[A-Za-z0-9/_-]+$ ]]; then
echo "Extracted unexpected-failure name '${scenario}' does not look like a scenario name; passing the suite failure through." >&2
exit "$rc"
fi
done
# A stale baseline entry is a configuration error a solo rerun cannot excuse.
# Here-string, not a pipe: grep -q quitting early would SIGPIPE printf and,
# under pipefail, skip this guard exactly when the pattern is present.
if grep -q '^Stale baseline entries' <<<"$plain"; then
echo "Suite also reported stale baseline entries; not retrying." >&2
exit "$rc"
fi
# Drop the suite-only flags: --scenario replaces --suite, and solo runs are
# judged directly rather than against the baseline.
rerun_args=()
output_dir=""
skip_next=0
expect_output_dir=0
for arg in "$@"; do
if [ "$skip_next" -eq 1 ]; then
if [ "$expect_output_dir" -eq 1 ]; then
output_dir="$arg"
fi
skip_next=0
expect_output_dir=0
continue
fi
case "$arg" in
--output-dir)
skip_next=1
expect_output_dir=1
;;
--suite | --expected-failures) skip_next=1 ;;
--output-dir=*) output_dir="${arg#--output-dir=}" ;;
--suite=* | --expected-failures=*) ;;
*) rerun_args+=("$arg") ;;
esac
done
if [ -n "$output_dir" ]; then
rerun_args+=(--output-dir "${output_dir}-solo")
fi
for scenario in "${scenarios[@]}"; do
passed=0
for attempt in $(seq 1 "$SOLO_ATTEMPTS"); do
echo ""
echo "Re-running '${scenario}' solo (attempt ${attempt}/${SOLO_ATTEMPTS})..."
if npx --yes "$CONFORMANCE_PKG" client --scenario "$scenario" "${rerun_args[@]}"; then
passed=1
break
fi
done
if [ "$passed" -ne 1 ]; then
echo "'${scenario}' still fails when run alone: real failure, not suite contention." >&2
exit 1
fi
done
if [ -n "$output_dir" ]; then
mkdir -p "$output_dir"
printf '%s\n' "${scenarios[@]}" > "$output_dir/FLAKE_RESCUED"
fi
echo "All ${#scenarios[@]} unexpected failure(s) passed when re-run solo; the suite failures were parallel-run contention."
# Surface the rescue on the run summary; the log line above and the
# FLAKE_RESCUED marker in the uploaded results are otherwise easy to miss on a
# green run.
echo "::warning title=Conformance flake rescued::${scenarios[*]} failed under suite concurrency and passed solo${output_dir:+ (see ${output_dir}-solo and FLAKE_RESCUED in the uploaded results)}."
exit 0

51
.github/actions/conformance/run-server.sh vendored Executable file
View file

@ -0,0 +1,51 @@
#!/bin/bash
set -e
PORT="${PORT:-3001}"
SERVER_URL="http://localhost:${PORT}/mcp"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../../.."
# Refuse to start if something is already listening on the port. The readiness
# check below cannot tell our server apart from a stale one, so a leftover
# listener would mean silently running conformance against old code.
if (: > "/dev/tcp/localhost/${PORT}") 2>/dev/null; then
echo "Error: port ${PORT} is already in use." >&2
echo "Stop the stale process first (lsof -ti:${PORT} -sTCP:LISTEN | xargs kill) or set PORT to a free port." >&2
exit 1
fi
echo "Starting mcp-everything-server on port ${PORT}..."
uv run --frozen mcp-everything-server --port "$PORT" &
SERVER_PID=$!
cleanup() {
echo "Stopping server (PID: ${SERVER_PID})..."
kill $SERVER_PID 2>/dev/null || true
wait $SERVER_PID 2>/dev/null || true
}
trap cleanup EXIT
# Wait for server to be ready. --max-time keeps a hung listener from wedging
# the loop, and a dead server process fails fast instead of retrying.
echo "Waiting for server to be ready..."
MAX_RETRIES=30
RETRY_COUNT=0
while ! curl -s --max-time 2 "$SERVER_URL" > /dev/null 2>&1; do
if ! kill -0 $SERVER_PID 2>/dev/null; then
echo "Server process exited unexpectedly" >&2
exit 1
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo "Server failed to start after ${MAX_RETRIES} retries" >&2
exit 1
fi
sleep 0.5
done
echo "Server ready at $SERVER_URL"
npx --yes "${CONFORMANCE_PKG:?set CONFORMANCE_PKG (pinned in .github/workflows/conformance.yml)}" \
server --url "$SERVER_URL" "$@"

22
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,22 @@
version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: monthly
cooldown:
default-days: 14
groups:
python-packages:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: monthly
cooldown:
default-days: 14
groups:
github-actions:
patterns:
- "*"

38
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,38 @@
<!--
Pull requests from outside the maintainer team need to link an open issue that
a maintainer has assigned to you (or one labeled `help wanted`); others are
closed automatically until that's in place. See CONTRIBUTING.md for details.
-->
Fixes #
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were tested? -->
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes that apply. -->
- [ ] I am assigned to the linked issue (or it is labeled `help wanted`, or I'm a maintainer)
- [ ] I have disclosed any AI assistance and can explain the change in my own words
- [ ] I have read the [MCP Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions -->

117
.github/scripts/docs_preview.js vendored Normal file
View file

@ -0,0 +1,117 @@
// Docs preview gate and PR comment. .github/workflows/docs-preview.yml wires
// these up: `authorize` decides whether a run may build and deploy a preview
// (and for which commit), `comment` reports the outcome on the pull request.
// The security model is described in the workflow's header.
'use strict';
const MARKER = '<!-- docs-preview -->';
const BOT_LOGIN = 'github-actions[bot]';
// Sets the job outputs `authorized`, `pr_number`, `head_sha` and
// `slash_attempt` for a pull_request_target or /preview-docs issue_comment run.
async function authorize({ github, context, core }) {
const { owner, repo } = context.repo;
async function permissionFor(username) {
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ owner, repo, username });
return { level: data.permission, role: data.role_name };
}
let authorized = false;
let prNumber = '';
let headSha = '';
let slashAttempt = false;
if (context.eventName === 'pull_request_target') {
const pr = context.payload.pull_request;
prNumber = String(pr.number);
headSha = pr.head.sha;
// No automatic preview for fork PRs: actions/checkout refuses to fetch a
// fork's head in a pull_request_target run. A maintainer can still request
// one with /preview-docs. (head.repo is null once the fork is deleted.)
const headRepo = pr.head.repo;
if (!headRepo || headRepo.id !== context.payload.repository.id) {
core.info(`PR #${prNumber} head is on ${headRepo ? headRepo.full_name : 'a deleted fork'}; fork PRs are previewed via /preview-docs only.`);
} else {
// Gate on the *sender* (whoever caused this run — on synchronize that
// is the pusher), not the PR author, so a non-admin pushing to an
// admin-opened branch does not get an automatic build.
const actor = context.payload.sender.login;
const perm = await permissionFor(actor);
authorized = perm.level === 'admin';
core.info(`pull_request_target by ${actor} (level=${perm.level}, role=${perm.role}) → authorized=${authorized}`);
}
} else {
// issue_comment: the job-level `if:` already guarantees this is a PR
// comment starting with /preview-docs.
slashAttempt = true;
const actor = context.payload.comment.user.login;
prNumber = String(context.payload.issue.number);
const perm = await permissionFor(actor);
authorized = perm.level === 'admin' || perm.role === 'maintain';
if (authorized) {
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: Number(prNumber) });
if (pr.state !== 'open') {
authorized = false;
core.info(`PR #${prNumber} is ${pr.state}; refusing to preview.`);
} else {
headSha = pr.head.sha;
}
}
core.info(`/preview-docs by ${actor} (level=${perm.level}, role=${perm.role}) → authorized=${authorized}`);
}
core.setOutput('authorized', String(authorized));
core.setOutput('pr_number', prNumber);
core.setOutput('head_sha', headSha);
core.setOutput('slash_attempt', String(slashAttempt));
}
// Posts or updates the preview comment on the PR. Reads the outcome of the
// earlier jobs from the step's env: AUTHORIZED, PR_NUMBER, HEAD_SHA,
// DEPLOY_RESULT, DEPLOYMENT_URL, ALIAS_URL, RUN_URL.
async function comment({ github, context }) {
const { owner, repo } = context.repo;
const env = process.env;
const issue_number = Number(env.PR_NUMBER);
async function upsert(body) {
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number, per_page: 100 });
const existing = comments.find((c) => c.user?.login === BOT_LOGIN && c.body?.includes(MARKER));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
}
if (env.AUTHORIZED !== 'true') {
await github.rest.issues.createComment({
owner, repo, issue_number,
body: `@${context.actor} — only repository admins or maintainers can run \`/preview-docs\` (and the PR must be open).`,
});
return;
}
if (env.DEPLOY_RESULT !== 'success') {
await upsert(
`${MARKER}\n### 📚 Documentation preview\n\n` +
`❌ Preview build **failed** for \`${env.HEAD_SHA.slice(0, 7)}\` — [workflow logs](${env.RUN_URL}).`
);
return;
}
const previewUrl = env.ALIAS_URL || env.DEPLOYMENT_URL;
const ts = new Date().toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC');
await upsert(
`${MARKER}\n### 📚 Documentation preview\n\n` +
`| | |\n|---|---|\n` +
`| **Preview** | ${previewUrl} |\n` +
`| **Deployment** | ${env.DEPLOYMENT_URL} |\n` +
`| **Commit** | \`${env.HEAD_SHA.slice(0, 7)}\` |\n` +
`| **Triggered by** | @${context.actor} |\n` +
`| **Updated** | ${ts} |\n`
);
}
module.exports = { authorize, comment };

241
.github/scripts/docs_preview.test.js vendored Normal file
View file

@ -0,0 +1,241 @@
// Scenario tests for docs_preview.js: who gets a preview build (`authorize`)
// and what the pull request shows afterwards (`comment`).
//
// node --test .github/scripts/docs_preview.test.js
//
// No dependencies; the GitHub client is a small fake defined at the bottom.
// CI runs it in the checks job (.github/workflows/shared.yml).
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { authorize, comment } = require('./docs_preview.js');
const REPO = { owner: 'modelcontextprotocol', repo: 'python-sdk' };
const BASE_REPO = { id: 1, full_name: 'modelcontextprotocol/python-sdk' };
const HEAD = 'e4dfda7baa127ab00ebcd1d5324560cbe3cdfe42';
const MARKER = '<!-- docs-preview -->';
// `permission` / `role_name` as the collaborators API reports them.
const PEOPLE = {
admin: { permission: 'admin', role_name: 'admin' },
maintainer: { permission: 'write', role_name: 'maintain' },
writer: { permission: 'write', role_name: 'write' },
outsider: { permission: 'read', role_name: 'read' },
};
// ── authorize ──────────────────────────────────────────────────────────────
// `expect` is the full set of job outputs the step writes.
const authorizeScenarios = [
{
name: 'admin pushes to (or opens) a PR → automatic preview of that head',
event: pushed(7, 'admin'),
expect: { authorized: 'true', pr_number: '7', head_sha: HEAD, slash_attempt: 'false' },
},
{
name: 'someone with write but not admin pushes → no automatic preview',
event: pushed(7, 'writer'),
expect: { authorized: 'false', pr_number: '7', head_sha: HEAD, slash_attempt: 'false' },
},
{
// actions/checkout refuses a fork's head under pull_request_target, so
// the run stops here instead of failing in `build`; /preview-docs still works.
name: 'admin pushes to or reopens a fork PR → no automatic preview',
event: pushed(7, 'admin', { fork: 'someone/python-sdk' }),
expect: { authorized: 'false', pr_number: '7', head_sha: HEAD, slash_attempt: 'false' },
permissionLookups: 0,
},
{
name: 'fork PR whose fork has since been deleted → no automatic preview',
event: pushed(7, 'admin', { fork: null }),
expect: { authorized: 'false', pr_number: '7', head_sha: HEAD, slash_attempt: 'false' },
permissionLookups: 0,
},
{
name: 'maintainer comments /preview-docs on a fork PR → previewed like any other',
pr: { fork: 'someone/python-sdk' },
event: slash(7, 'maintainer'),
expect: { authorized: 'true', pr_number: '7', head_sha: HEAD, slash_attempt: 'true' },
},
{
name: 'maintainer comments /preview-docs on an open PR → preview of its current head',
event: slash(7, 'maintainer'),
expect: { authorized: 'true', pr_number: '7', head_sha: HEAD, slash_attempt: 'true' },
},
{
name: 'admin comments /preview-docs → authorized as well',
event: slash(7, 'admin'),
expect: { authorized: 'true', pr_number: '7', head_sha: HEAD, slash_attempt: 'true' },
},
{
name: 'writer without the maintain role comments /preview-docs → refused, recorded as an attempt',
event: slash(7, 'writer'),
expect: { authorized: 'false', pr_number: '7', head_sha: '', slash_attempt: 'true' },
},
{
name: 'outsider comments /preview-docs → refused, recorded as an attempt',
event: slash(7, 'outsider'),
expect: { authorized: 'false', pr_number: '7', head_sha: '', slash_attempt: 'true' },
},
{
name: '/preview-docs on a closed PR → refused even for a maintainer',
pr: { state: 'closed' },
event: slash(7, 'maintainer'),
expect: { authorized: 'false', pr_number: '7', head_sha: '', slash_attempt: 'true' },
},
];
for (const s of authorizeScenarios) {
test(`authorize: ${s.name}`, async () => {
const world = makeWorld({ pr: { number: 7, ...s.pr } });
assert.deepEqual(await runAuthorize(world, s.event), s.expect);
assert.equal(world.writes.length, 0);
if (s.permissionLookups !== undefined) assert.equal(world.permissionLookups, s.permissionLookups);
});
}
test('authorize: a failing permission lookup fails the step instead of deciding either way', async () => {
const world = makeWorld({ pr: { number: 7 } });
world.failPermissionLookup = true;
await assert.rejects(runAuthorize(world, pushed(7, 'admin')), /boom/);
});
// ── comment ────────────────────────────────────────────────────────────────
const DEPLOYED = {
AUTHORIZED: 'true',
PR_NUMBER: '7',
HEAD_SHA: HEAD,
DEPLOY_RESULT: 'success',
DEPLOYMENT_URL: 'https://1a2b3c.mcp-python-sdk-docs.pages.dev',
ALIAS_URL: 'https://pr-7.mcp-python-sdk-docs.pages.dev',
RUN_URL: 'https://github.com/modelcontextprotocol/python-sdk/actions/runs/1',
};
test('comment: a refused /preview-docs gets a plain reply to the commenter, not a preview comment', async () => {
const world = makeWorld({ pr: { number: 7 } });
await runComment(world, { ...DEPLOYED, AUTHORIZED: 'false', HEAD_SHA: '', DEPLOY_RESULT: 'skipped' }, 'outsider');
assert.equal(world.comments.length, 1);
assert.match(world.comments[0].body, /^@outsider — only repository admins or maintainers can run `\/preview-docs`/);
assert.ok(!world.comments[0].body.includes(MARKER));
});
test('comment: first successful deploy posts one preview comment linking the alias URL and the commit', async () => {
const world = makeWorld({ pr: { number: 7 } });
await runComment(world, DEPLOYED, 'admin');
assert.equal(world.comments.length, 1);
const body = world.comments[0].body;
assert.ok(body.startsWith(`${MARKER}\n### 📚 Documentation preview`));
assert.match(body, /\| \*\*Preview\*\* \| https:\/\/pr-7\.mcp-python-sdk-docs\.pages\.dev \|/);
assert.match(body, /\| \*\*Deployment\*\* \| https:\/\/1a2b3c\.mcp-python-sdk-docs\.pages\.dev \|/);
assert.match(body, /\| \*\*Commit\*\* \| `e4dfda7` \|/);
assert.match(body, /\| \*\*Triggered by\*\* \| @admin \|/);
assert.deepEqual(world.writes, ['comment on #7']);
});
test('comment: a later deploy edits the existing preview comment instead of adding another', async () => {
const world = makeWorld({ pr: { number: 7 }, comments: [{ user: 'someone', body: 'LGTM' }, { user: 'github-actions[bot]', body: `${MARKER}\nold table` }] });
await runComment(world, { ...DEPLOYED, HEAD_SHA: 'f'.repeat(40) }, 'admin');
assert.equal(world.comments.length, 2);
assert.match(world.comments[1].body, /\| \*\*Commit\*\* \| `fffffff` \|/);
assert.deepEqual(world.writes, ['edit comment 101']);
});
test("comment: someone else's comment that happens to contain the marker is left alone", async () => {
const world = makeWorld({ pr: { number: 7 }, comments: [{ user: 'someone', body: `quoting ${MARKER} here` }] });
await runComment(world, DEPLOYED, 'admin');
assert.equal(world.comments.length, 2);
assert.equal(world.comments[0].body, `quoting ${MARKER} here`);
assert.deepEqual(world.writes, ['comment on #7']);
});
test('comment: with no alias URL the preview link falls back to the deployment URL', async () => {
const world = makeWorld({ pr: { number: 7 } });
await runComment(world, { ...DEPLOYED, ALIAS_URL: '' }, 'admin');
assert.match(world.comments[0].body, /\| \*\*Preview\*\* \| https:\/\/1a2b3c\.mcp-python-sdk-docs\.pages\.dev \|/);
});
test('comment: a build or deploy that did not succeed is reported with the short SHA and a link to the run', async () => {
const world = makeWorld({ pr: { number: 7 }, comments: [{ user: 'github-actions[bot]', body: `${MARKER}\nold table` }] });
await runComment(world, { ...DEPLOYED, DEPLOY_RESULT: 'skipped', DEPLOYMENT_URL: '', ALIAS_URL: '' }, 'admin');
assert.equal(world.comments.length, 1);
assert.equal(
world.comments[0].body,
`${MARKER}\n### 📚 Documentation preview\n\n❌ Preview build **failed** for \`e4dfda7\` — [workflow logs](${DEPLOYED.RUN_URL}).`
);
});
// ── Harness ────────────────────────────────────────────────────────────────
// `fork`: full name of the fork the head lives on; null for a deleted fork; omitted for a same-repo branch.
function pushed(number, sender, { fork } = {}) {
const repo = fork === undefined ? BASE_REPO : fork === null ? null : { id: 2, full_name: fork };
return { eventName: 'pull_request_target', actor: sender, payload: { action: 'synchronize', repository: BASE_REPO, pull_request: { number, head: { sha: HEAD, repo } }, sender: { login: sender } } };
}
function slash(number, commenter) {
return { eventName: 'issue_comment', actor: commenter, payload: { action: 'created', issue: { number, pull_request: {} }, comment: { body: '/preview-docs', user: { login: commenter } } } };
}
async function runAuthorize(world, event) {
const outputs = {};
const core = { info: () => {}, setOutput: (k, v) => { outputs[k] = v; } };
await authorize({ github: world.github, context: { repo: REPO, ...event }, core });
return outputs;
}
async function runComment(world, env, actor) {
const saved = {};
for (const [k, v] of Object.entries(env)) { saved[k] = process.env[k]; process.env[k] = v; }
try {
await comment({ github: world.github, context: { repo: REPO, actor }, core: {} });
} finally {
for (const [k, v] of Object.entries(saved)) { if (v === undefined) delete process.env[k]; else process.env[k] = v; }
}
}
// ── A tiny in-memory GitHub ────────────────────────────────────────────────
function makeWorld({ pr, comments = [] }) {
const world = { pr: { state: 'open', ...pr }, comments: [], writes: [], failPermissionLookup: false, permissionLookups: 0, nextCommentId: 100 };
for (const c of comments) world.comments.push({ id: world.nextCommentId++, ...c });
const err = (status, message = 'fake error') => Object.assign(new Error(message), { status });
const write = (what) => world.writes.push(what);
const checkPr = (n) => { if (n !== world.pr.number) throw err(404); };
const rest = {
repos: {
getCollaboratorPermissionLevel: async ({ username }) => {
world.permissionLookups++;
if (world.failPermissionLookup) throw err(500, 'boom');
const person = PEOPLE[username];
if (!person) throw err(404, 'not a user');
return { data: { ...person, user: { login: username } } };
},
},
pulls: {
get: async ({ pull_number }) => {
checkPr(pull_number);
const repo = world.pr.fork ? { id: 2, full_name: world.pr.fork } : BASE_REPO;
return { data: { number: pull_number, state: world.pr.state, head: { sha: HEAD, repo } } };
},
},
issues: {
listComments: async ({ issue_number }) => { checkPr(issue_number); return { data: world.comments.map((c) => ({ id: c.id, body: c.body, user: { login: c.user } })) }; },
createComment: async ({ issue_number, body }) => {
checkPr(issue_number);
write(`comment on #${issue_number}`);
world.comments.push({ id: world.nextCommentId++, user: 'github-actions[bot]', body });
},
updateComment: async ({ comment_id, body }) => {
write(`edit comment ${comment_id}`);
const c = world.comments.find((x) => x.id === comment_id);
if (!c) throw err(404);
c.body = body;
},
},
};
world.github = { rest, paginate: async (fn, args) => (await fn(args)).data };
return world;
}

312
.github/scripts/pr_intake_gate.js vendored Normal file
View file

@ -0,0 +1,312 @@
// PR intake gate. The policy lives in CONTRIBUTING.md ("How pull requests get
// in"); .github/workflows/require-linked-issue.yml wires this up to events.
//
// A pull request from someone without triage rights stays open only if its
// description links (Fixes/Closes/Resolves #N) an open issue in this repo that
// is either assigned to the PR author or labeled `help wanted`. Otherwise the
// gate labels it `missing-issue-link`, leaves one comment, and closes it. It
// re-evaluates — and reopens — the PR when the description is edited or the
// author is assigned to the issue. A triage+ user reopening the PR, removing
// the label, or adding `bypass-issue-check` overrides it, and the override
// sticks.
//
// Everything that writes goes through mutate(); when the workflow passes
// ENFORCE=false (its kill switch) the run only logs what it would have done.
'use strict';
const LABEL = 'missing-issue-link'; // marks PRs the gate has closed
const BYPASS_LABEL = 'bypass-issue-check'; // sticky maintainer override
const OPEN_LABEL = 'help wanted'; // issue label that waives assignment
const MARKER = '<!-- require-linked-issue -->';
const BOT_LOGIN = 'github-actions[bot]';
const MAX_ISSUES = 5;
module.exports = async function run({ github, context, core }) {
const { owner, repo } = context.repo;
const enforce = process.env.ENFORCE === 'true';
const contributingUrl = `https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md#how-pull-requests-get-in`;
// ── Entry points ─────────────────────────────────────────────────────────
if (context.eventName === 'issues') {
// Someone was assigned an issue: re-evaluate their gate-closed PRs that
// reference it (they may pass now).
const issueNumber = context.payload.issue.number;
const assignee = context.payload.assignee.login;
const closed = await github.paginate(github.rest.issues.listForRepo, {
owner, repo, state: 'closed', creator: assignee, labels: LABEL, per_page: 100,
});
const prs = closed.filter((i) => i.pull_request && closingRefs(i.body).includes(issueNumber));
console.log(`#${issueNumber} assigned to ${assignee}: ${prs.length} gate-closed PR(s) reference it`);
// Evaluate each independently so one transient failure doesn't strand
// the rest (this event won't fire again for the same assignment).
const failures = [];
for (const pr of prs) {
try {
await evaluate(pr.number, 'assigned', context.payload.sender?.login, issueNumber);
} catch (e) {
failures.push(`#${pr.number}: ${e.message}`);
}
}
if (failures.length) throw new Error(`Could not re-evaluate ${failures.join('; ')}`);
return;
}
if (context.eventName === 'workflow_dispatch') {
const n = parseInt(process.env.PR_NUMBER_INPUT, 10);
if (!Number.isInteger(n) || n <= 0) throw new Error(`Bad pr_number input: ${process.env.PR_NUMBER_INPUT}`);
await evaluate(n, 'dispatch', context.payload.sender?.login);
return;
}
await evaluate(context.payload.pull_request.number, context.payload.action, context.payload.sender?.login);
// ── The rules ────────────────────────────────────────────────────────────
async function evaluate(prNumber, action, sender, hintIssue = null) {
// Always read the PR live; the event payload can be stale by the time a
// queued run starts.
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
const labels = pr.labels.map((l) => l.name);
// An `unlabeled` run only fires for LABEL (see the workflow `if:`), so the
// event itself proves the label was there a moment ago.
const gated = action === 'unlabeled' || labels.includes(LABEL);
console.log(`PR #${prNumber} by ${pr.user.login} (${pr.state}${pr.draft ? ', draft' : ''}) — ${action} by ${sender ?? '-'}, enforce=${enforce}`);
// 0. Scope: open PRs, plus closed PRs the gate closed itself. Merged PRs
// and PRs someone closed for other reasons are left alone.
if (pr.merged_at) return log('merged — nothing to do');
if (pr.state === 'closed' && !gated) return log('closed by someone else — not ours');
// 1. Exempt authors: bots and anyone with triage or better. Drafts are
// gated like any other PR.
if (pr.user.type === 'Bot') return log('author is a bot — exempt');
if (await isTrusted(pr.user.login)) return pass('author has triage+ on this repo');
// 2. Overrides: a triage+ user reopening the PR or removing the label wants
// it open. Anyone else doing so just triggers a re-check.
if ((action === 'reopened' || action === 'unlabeled') && sender && (await isTrusted(sender))) {
return pass(`${sender} ${action === 'reopened' ? 'reopened it' : 'removed the label'} — override`, { sticky: true });
}
if (labels.includes(BYPASS_LABEL)) return pass(`carries ${BYPASS_LABEL}`);
// 3. The rule: the description links an open issue in this repo that is
// labeled `help wanted` or assigned to the author. Only the first few
// references are fetched; a just-assigned issue is checked first.
const author = pr.user.login.toLowerCase();
const refs = closingRefs(pr.body);
if (hintIssue && refs.includes(hintIssue)) refs.unshift(...refs.splice(refs.indexOf(hintIssue), 1));
const linked = [];
for (const num of refs.slice(0, MAX_ISSUES)) {
const issue = await getIssue(num);
if (!issue) continue; // missing, a PR, closed, or transferred away
linked.push(num);
if (issue.labels.some((l) => l.name.toLowerCase() === OPEN_LABEL)) return pass(`#${num} is labeled "${OPEN_LABEL}"`);
if (issue.assignees.some((a) => a.login.toLowerCase() === author)) return pass(`author is assigned to #${num}`);
}
return fail(linked);
// ── Outcomes ─────────────────────────────────────────────────────────
async function pass(reason, { sticky = false } = {}) {
console.log(`PASS: ${reason}`);
if (sticky) await addLabel(prNumber, BYPASS_LABEL);
if (pr.state === 'closed' && !(await reopen(pr, reason))) return;
if (gated) {
await removeLabel(prNumber, LABEL);
await deleteGateComment(prNumber);
}
}
async function fail(linkedIssues) {
console.log(`FAIL: ${linkedIssues.length ? `not assigned to ${linkedIssues.map((n) => `#${n}`).join(', ')}` : 'no usable issue link'}`);
await addLabel(prNumber, LABEL);
await upsertGateComment(prNumber, closedComment(pr.draft, linkedIssues));
if (pr.state === 'open') {
await mutate(`close PR #${prNumber}`, () => github.rest.pulls.update({ owner, repo, pull_number: prNumber, state: 'closed' }));
}
}
function log(msg) {
console.log(msg);
}
}
// ── Comment text ─────────────────────────────────────────────────────────
function closedComment(draft, linkedIssues) {
const issues = linkedIssues.map((n) => `#${n}`).join(', ');
const opener = draft
? "This PR has been closed automatically. It's still a draft, but we close those early so you don't put in more time only to have it closed the moment you mark it ready.\n\n"
: 'This PR has been closed automatically. ';
const rule = `${opener}This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue`;
const situation = linkedIssues.length
? [
`${rule}, and you aren't currently assigned to ${issues}.`,
'',
`If a maintainer assigns you to ${issues}, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take.`,
]
: [
`${rule}, and this PR doesn't link an open issue yet.`,
'',
"- **If you're already assigned to an issue for this**, add `Fixes #<n>` to the description and the PR will reopen on its own.",
`- **If there's no issue yet**, please [open one](https://github.com/${owner}/${repo}/issues/new/choose) instead: what you ran into, why it matters for your use case, and a minimal reproduction. That context is super important to us and is what we use to decide what to prioritise.`,
"- **If there's an issue but you're not assigned**, add `Fixes #<n>` anyway so they're linked, then engage on the issue itself by confirming the repro or describing the approach you'd take. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. If you are assigned, this PR reopens automatically.",
];
return [
MARKER,
...situation,
'',
"You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way.",
'',
`[CONTRIBUTING.md](${contributingUrl}) has the full reasoning, but in short:`,
'',
"- We're a small team with very little capacity to review community PRs right now.",
'- Many recent PRs are AI-generated with little human review, and reviewing one carefully still costs a maintainer as much time as it ever did. A well-described issue is usually more useful to us than the code.',
'',
`*Maintainers: reopen, remove \`${LABEL}\`, or add \`${BYPASS_LABEL}\` to override.*`,
].join('\n');
}
function cannotReopenComment(pr, reason) {
return [
MARKER,
`This PR now passes the intake check (${reason}), but GitHub won't let it be reopened — usually because the branch was force-pushed or deleted while the PR was closed, or because another open PR uses the same branch.`,
'',
`If you have another open PR from this branch, please continue there. Otherwise, either push the branch back to \`${pr.head.sha.slice(0, 7)}\` and edit this PR's description to retry, or open a new PR that links the same issue (if a maintainer had waved this one through, mention that so they can do the same there).`,
].join('\n');
}
// ── Helpers ──────────────────────────────────────────────────────────────
async function mutate(description, fn) {
if (!enforce) {
console.log(`[dry-run] would ${description}`);
return undefined;
}
return fn();
}
// Triage-or-better on this repo, from the permission endpoint's capability
// flags (role names can be custom; author_association hides private org
// members). Only a nonexistent user 404s; any other error must throw rather
// than be read as "untrusted", or a maintainer's PR could be closed.
async function isTrusted(username) {
try {
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ owner, repo, username });
const p = data.user?.permissions;
if (!p) throw new Error(`permission response for ${username} has no capability flags`);
const trusted = Boolean(p.triage || p.push || p.maintain || p.admin);
console.log(` ${username}: ${trusted ? 'trusted' : 'not trusted'} (role ${data.role_name || '-'})`);
return trusted;
} catch (e) {
if (e.status === 404) return false;
throw new Error(`Permission check failed for ${username} (HTTP ${e.status ?? '?'}): ${e.message}`);
}
}
// Issue numbers referenced with a closing keyword, in the forms GitHub itself
// honors: `Fixes #1`, `closes owner/repo#1`, `Resolved https://github.com/owner/repo/issues/1`.
function closingRefs(body) {
const repoRef = `${owner}/${repo}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const re = new RegExp(
`\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\s*:?\\s*(?:${repoRef}#|#|https?://github\\.com/${repoRef}/issues/)(\\d+)`,
'gi',
);
return [...new Set([...(body || '').matchAll(re)].map((m) => parseInt(m[1], 10)))];
}
// The linked issue, or null if it doesn't exist, is actually a PR, isn't
// open, or has been transferred to another repository.
async function getIssue(num) {
let issue;
try {
({ data: issue } = await github.rest.issues.get({ owner, repo, issue_number: num }));
} catch (e) {
if (e.status === 404 || e.status === 410) return null;
throw new Error(`Cannot fetch issue #${num} (HTTP ${e.status ?? '?'}): ${e.message}`);
}
if (issue.pull_request || issue.state !== 'open') return null;
if (!issue.repository_url?.endsWith(`/${owner}/${repo}`)) return null;
return issue;
}
// Reopen a gate-closed PR. GitHub refuses (422) if the branch was rewritten
// or deleted while closed, or another open PR uses it. Explain that in the
// comment and make sure the control label is (still) on, so the PR stays
// gate-managed and a later edit or override retries the reopen.
async function reopen(pr, reason) {
try {
await mutate(`reopen PR #${pr.number}`, () => github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: 'open' }));
return true;
} catch (e) {
if (e.status !== 422) throw e;
core.warning(`GitHub refused to reopen PR #${pr.number}: ${e.message}`);
await addLabel(pr.number, LABEL);
await upsertGateComment(pr.number, cannotReopenComment(pr, reason));
return false;
}
}
async function addLabel(prNumber, name) {
await mutate(`add "${name}" to PR #${prNumber}`, async () => {
await ensureLabelExists(name);
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: [name] });
});
}
async function removeLabel(prNumber, name) {
await mutate(`remove "${name}" from PR #${prNumber}`, async () => {
try {
await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name });
} catch (e) {
if (e.status !== 404) throw e;
}
});
}
async function ensureLabelExists(name) {
const meta = {
[LABEL]: ['b76e79', 'Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)'],
[BYPASS_LABEL]: ['0e8a16', 'Maintainer override for the linked-issue intake gate'],
}[name];
try {
await github.rest.issues.getLabel({ owner, repo, name });
} catch (e) {
if (e.status !== 404) throw e;
try {
await github.rest.issues.createLabel({ owner, repo, name, color: meta[0], description: meta[1] });
} catch (createErr) {
if (createErr.status !== 422) throw createErr; // created concurrently
}
}
}
// The gate keeps at most one comment per PR: authored by the Actions bot and
// carrying MARKER. It's created or updated on failure and deleted on pass.
async function findGateComment(prNumber) {
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: prNumber, per_page: 100 });
return comments.find((c) => c.user?.login === BOT_LOGIN && c.body?.includes(MARKER));
}
async function upsertGateComment(prNumber, body) {
const existing = await findGateComment(prNumber);
if (!existing) {
await mutate(`comment on PR #${prNumber}`, () => github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body }));
} else if (existing.body !== body) {
await mutate(`update the gate comment on PR #${prNumber}`, () => github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }));
}
}
async function deleteGateComment(prNumber) {
const existing = await findGateComment(prNumber);
if (!existing) return;
await mutate(`delete the gate comment on PR #${prNumber}`, async () => {
try {
await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id });
} catch (e) {
if (e.status !== 404) throw e; // already deleted by a concurrent run
}
});
}
};

Some files were not shown because too many files have changed in this diff Show more