Exercise the SEP-2575 stateless probes and SEP-2243 resource/prompt headers in the conformance fixtures - #3442
Conversation
…ares hatchling validates project.readme before building any target, so on a case-sensitive filesystem `uv sync --all-packages` / `uv build --all-packages` failed for the whole workspace with "Readme file does not exist: README.md". The conformance runner's built-in python-sdk config works around exactly this with two targeted syncs. No-Verification-Needed: example file rename only
…eaders in the conformance fixtures Two conformance scenarios were green against this SDK without measuring anything. server-stateless probes tools named test_streaming_elicitation and test_logging_tool for its no-independent-requests-on-stream and no-log-without-logLevel checks. The everything-server had neither, and MCPServer answers an unknown tool with an isError result rather than a JSON-RPC error, so the harness's not-testable guard never fired and both checks passed on "Unknown tool". test_streaming_elicitation is now a tool that genuinely needs elicitation, asked through a Resolve() dependency: on 2026-07-28 the question comes back as an InputRequiredResult instead of an elicitation/create on the response stream (the behaviour the check enforces), on 2025-11-25 it is a mid-call elicitation, and without a declared elicitation capability it is rejected with -32021. test_logging_tool logs once on the request-scoped channel so the per-request logLevel gate is what the check observes. The client's http-standard-headers handler only listed and called a tool, so the Mcp-Method/Mcp-Name checks for resources/list, resources/read, prompts/list and prompts/get were reported SKIPPED. It now reads the first resource and gets the first prompt as well (3 -> 9 checks judged; initialize/notifications/initialized stay SKIPPED because the modern path never sends them). No-Verification-Needed: conformance fixtures only
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/actions/conformance/client.py">
<violation number="1" location=".github/actions/conformance/client.py:319">
P3: The 2025-11-25 conformance leg uses `client_mode() == "legacy"`, so it does send `initialize` and `notifications/initialized`; limit this SKIPPED explanation to modern mode so the fixture documentation matches its actual requests.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| 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 |
There was a problem hiding this comment.
P3: The 2025-11-25 conformance leg uses client_mode() == "legacy", so it does send initialize and notifications/initialized; limit this SKIPPED explanation to modern mode so the fixture documentation matches its actual requests.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/actions/conformance/client.py, line 319:
<comment>The 2025-11-25 conformance leg uses `client_mode() == "legacy"`, so it does send `initialize` and `notifications/initialized`; limit this SKIPPED explanation to modern mode so the fixture documentation matches its actual requests.</comment>
<file context>
@@ -311,11 +311,24 @@ async def run_request_metadata(server_url: str) -> None:
+ 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.
+ """
</file context>
There was a problem hiding this comment.
LGTM — small, self-contained change to conformance fixtures and an example filename, with no src/mcp changes.
What was reviewed: the broadened http-standard-headers handler in .github/actions/conformance/client.py (reuses the existing _stub_required_args helper and guards each round-trip on non-empty lists); the two new everything-server tools, checking that Elicit(message, schema) / Resolve(fn) / ElicitationResult[T].action match the SDK's resolver API and that UserResponse is already defined in the file; the # pyright: ignore[reportDeprecated] on ctx.info, which matches five existing suppressions in the same file for the same deliberately-exercised deprecated channel; and the README.MD→README.md rename, confirmed as a pure R100 rename matching the readme = "README.md" declared in the chatbot's pyproject.
Extended reasoning...
Overview
The PR touches three files and nothing under src/mcp: (1) .github/actions/conformance/client.py extends the http-standard-headers scenario handler to also read the first resource and get the first prompt so the SEP-2243 Mcp-Method/Mcp-Name checks for those methods stop being reported SKIPPED, replacing a hardcoded add_numbers call with the first surfaced tool plus the file's existing _stub_required_args helper; (2) examples/servers/everything-server/mcp_everything_server/server.py adds the two diagnostic tools (test_streaming_elicitation, test_logging_tool) that the conformance server-stateless scenario probes by name, so the SEP-2575 checks measure real behaviour instead of passing vacuously on an unknown-tool isError result; (3) a pure R100 rename of the simple-chatbot README to the casing its pyproject declares.
Security risks
None identified. No authentication, crypto, permission, or transport code is touched. The new tools run only in the example/conformance fixture server; test_streaming_elicitation requests elicitation through the documented Resolve/Elicit API, and test_logging_tool emits a single fixed log line. The client handler only issues standard list/read/get calls against a harness-controlled server. No credentials or secrets appear in the diff.
Level of scrutiny
Low-to-moderate is appropriate: the change is small (49 insertions), touches only CI fixtures and examples, and the exact code paths added are the ones CI's conformance workflow executes, so a regression would surface directly there. I verified the API usage statically against the SDK source: Elicit.__init__(message, schema) and Resolve(fn) in src/mcp/server/mcpserver/resolve.py match the fixture's usage, ElicitationResult members carry .action, UserResponse is defined earlier in the same server file, and Elicit/ElicitationResult/Resolve are all exported from mcp.server.mcpserver. The rename shows 100% similarity and the pyproject indeed declares readme = "README.md".
Other factors
The added # pyright: ignore[reportDeprecated] on ctx.info mirrors five identical suppressions already in the same file for the same deprecated request-scoped channel, and the tool exists precisely to exercise that gate, so it follows the established local pattern rather than introducing a new suppression style. There is no CODEOWNERS file, the bug hunt exited on dry_streak with no findings, and the timeline shows no other reviewer activity or outstanding objections. The one behaviour I could not run locally (conformance results, pyright/ruff) is exactly what the conformance and lint CI legs cover, and the changed code is fixture-only, so a wrong assumption there fails CI rather than shipping to users.
Two conformance scenarios were green against this SDK without measuring anything. This gives the everything-server the two diagnostic tools
server-statelessprobes by name, makes the conformance client'shttp-standard-headershandler round-trip a resource and a prompt as well as a tool, and (separate commit) renamesexamples/clients/simple-chatbot/README.MDto theREADME.mdits pyproject declares so the workspace builds with--all-packageson case-sensitive filesystems.Motivation and Context
server-stateless. Itssep-2575-http-server-no-independent-requests-on-streamandsep-2575-server-no-log-without-loglevelchecks call tools namedtest_streaming_elicitationandtest_logging_tool(stateless.ts). Our fixture had neither.MCPServeranswers an unknown tool with anisError: trueCallToolResult— a result frame — so the harness's not-testable guard (which only fires when every frame is a JSON-RPC error) never tripped, and both checks passed on "Unknown tool". typescript-sdk's fixture carries the pair (everythingServer.ts).Rather than a tool that just returns text (which is byte-identical on the wire to the unknown-tool result),
test_streaming_elicitationis a tool that genuinely needs elicitation, asked through aResolve(...)dependency. On 2026-07-28 the SDK returns the question as anInputRequiredResultinstead of puttingelicitation/createon the response stream — which is the behaviour the check enforces — on 2025-11-25 it is a mid-call elicitation, and with no declared elicitation capability it is rejected with-32021. So the probe now sits on the SDK's version branch rather than beside it.test_logging_toollogs once on the request-scoped channel, so what the second check observes is the per-requestlogLevelgate.http-standard-headers. The scenario reports any oftools/list,tools/call,resources/list,resources/read,prompts/list,prompts/getthe client never sends as SKIPPED rather than failed. Our handler only listed and called a tool, so theMcp-Method/Mcp-Namechecks for resources and prompts were never judged. It now also reads the first resource and gets the first prompt.README case. hatchling validates
project.readmebefore building any target, souv sync --all-packages/uv build --all-packagesfailed on Linux withOSError: Readme file does not exist: README.md. The conformance runner's built-inpython-sdkconfig works around exactly this with two targeted syncs (known-sdks.ts).How Has This Been Tested?
Against conformance
0.2.0-alpha.11locally, throughrun-server.sh/run-client.sh:server-statelessat--spec-version 2026-07-28: 30/30, both SEP-2575 checks SUCCESS on real results. Direct POSTs to the 2026-07-28 entry:test_streaming_elicitationwithelicitationdeclared → 200, singleapplication/jsonresult withresultType: "input_required"and theelicitation/createembedded ininputRequests; with empty capabilities → 400-32021withrequiredCapabilities.elicitation.test_logging_toolemits nonotifications/messagewithout_meta["io.modelcontextprotocol/logLevel"], exactly one withlogLevel: "debug". In-memoryClient(mode="legacy")andClient(mode="auto")both complete the tool via the elicitation callback.http-standard-headersat 2026-07-28: 3 SUCCESS / 8 SKIPPED → 9 SUCCESS / 2 SKIPPED (the remaining two areinitialize/notifications/initialized, which a 2026-07-28 client never sends).tasks-*, expected); client 224/0 · 387/0 · 464/9 (the nine per-check DPoP/WIF, expected). The client totals are +6 each versus main from the header checks moving out of SKIPPED; no baseline changes.uv build --package mcp-simple-chatbotfails before the rename with theOSErrorabove and succeeds after. ruff, pyright, and the markdownlint hook on the renamed file are clean.Breaking Changes
None. Conformance fixtures and an example's file name only.
Types of changes
Checklist
Additional context
test_logging_tooluses the deprecated request-scopedctx.info(SEP-2577) on purpose — there is no non-deprecated channel and the check exists to exercise that gate — with the samereportDeprecatedsuppression the file already carries fortest_tool_with_loggingandtest_reconnection.MCPServerreports an unknown tool as anisErrorresult instead of a JSON-RPC error; any future name-probed harness fixture we lack will be masked the same way. Not changed here.python-sdkrunner entry can go back touv sync --frozen --all-extras --all-packages.v1.xcarries the sameREADME.MD, so thepython-sdk-v1entry's--all-packagesbuild has the same problem there; the rename is its own commit in case a[v1.x]backport is wanted.AI Disclaimer