Require integrity protection for MRTR requestState (#3032)

This commit is contained in:
Max 2026-06-30 21:30:32 +01:00 • committed by GitHub
commit 7322ca56f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@
import mcp_types as types
from mcp import MCPError
from mcp.client import Client, ClientRequestContext
from stories._harness import Target, run_client
@ -27,14 +28,37 @@ async def main(target: Target, *, mode: str = "auto") -> None:
first = await client.session.call_tool("deploy", {"env": "staging"}, allow_input_required=True)
assert isinstance(first, types.InputRequiredResult)
assert first.input_requests is not None and "confirm" in first.input_requests
assert first.request_state == "awaiting-confirm"
# Decline this time so the path diverges from the auto-loop run above.
# The boundary sealed server.py's plaintext "awaiting-confirm"; the wire token is opaque.
token = first.request_state
assert token is not None and token != "awaiting-confirm", token
responses: types.InputResponses = {"confirm": types.ElicitResult(action="decline")}
# Tamper demo: flipping any one character fails verification, and every failure
# maps to one frozen wire error; the real reason appears only in the server log.
i = len(token) // 2
tampered = token[:i] + ("A" if token[i] != "A" else "B") + token[i + 1 :]
try:
await client.session.call_tool(
"deploy",
{"env": "staging"},
input_responses=responses,
request_state=tampered,
allow_input_required=True,
)
except MCPError as e:
assert e.code == types.INVALID_PARAMS
assert e.message == "Invalid or expired requestState"
assert e.data == {"reason": "invalid_request_state"}
else:
raise AssertionError("expected MCPError for a tampered requestState")
# The untampered token still completes the round; decline so this path diverges from the auto run.
second = await client.session.call_tool(
"deploy",
{"env": "staging"},
input_responses=responses,
request_state=first.request_state,
request_state=token,
allow_input_required=True,
)
assert isinstance(second, types.CallToolResult)