Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions .github/actions/conformance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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 - Connect, call a tool (Mcp-* headers checked)
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
Expand Down Expand Up @@ -311,11 +311,24 @@ async def run_request_metadata(server_url: str) -> None:

@register("http-standard-headers")
async def run_http_standard_headers(server_url: str) -> None:
"""Connect on the modern path so Mcp-Method / Mcp-Name / MCP-Protocol-Version are sent (SEP-2243)."""
"""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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

server/discover and never sends them.
"""
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}")
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]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import click
from mcp.server import ServerRequestContext
from mcp.server.mcpserver import Context, MCPServer, RequestStateSecurity
from mcp.server.mcpserver import Context, Elicit, ElicitationResult, MCPServer, RequestStateSecurity, Resolve
from mcp.server.mcpserver.exceptions import ToolError
from mcp.server.mcpserver.prompts.base import Prompt, UserMessage
from mcp.server.streamable_http import EventCallback, EventMessage, EventStore
Expand Down Expand Up @@ -350,6 +350,9 @@ def test_x_mcp_header(
return f"region={region}"


# SEP-2575 server-stateless diagnostics (the conformance scenario probes these tools by name)


@mcp.tool()
async def test_missing_capability(ctx: Context) -> str:
"""Tests that a handler-raised MISSING_REQUIRED_CLIENT_CAPABILITY surfaces as a top-level JSON-RPC error.
Expand All @@ -370,6 +373,33 @@ async def test_missing_capability(ctx: Context) -> str:
return "Client declared sampling capability; proceeding."


def _ask_stream_probe() -> Elicit[UserResponse]:
return Elicit("The stateless streaming probe asks for a word", UserResponse)


@mcp.tool()
async def test_streaming_elicitation(
answer: Annotated[ElicitationResult[UserResponse], Resolve(_ask_stream_probe)],
) -> str:
"""A tool that needs elicitation, asked through a resolver (SEP-2575 / SEP-2322).

On 2026-07-28 the question is returned as an InputRequiredResult rather than sent
on the response stream; on earlier versions it is a mid-call elicitation request.
"""
return f"elicitation {answer.action}"


@mcp.tool()
async def test_logging_tool(ctx: Context) -> str:
"""Logs once on the request-scoped channel (SEP-2575).

On 2026-07-28 the message is delivered only when the request's `_meta` sets
`io.modelcontextprotocol/logLevel`.
"""
await ctx.info("test_logging_tool ran") # pyright: ignore[reportDeprecated]
return "logged through the request-scoped, logLevel-gated channel"


# SEP-2322 InputRequiredResult fixtures (multi-round-trip / ephemeral workflow)

NAME_SCHEMA = {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}
Expand Down
Loading