Scenarios
Each scenario runs per (spec version × docs condition). Client scenarios: the agent must call an instrumented reference server conformantly. Server scenarios: the agent builds a server that survives our conformance probes. Checkpoints are asserted from runner-side observation only.
client/bad-version-response
-
sent-initializeMUST — Sent an initialize request -
disconnect-on-mismatchSHOULD — After the server negotiated an unsupported protocol version, sent nothing further
client/composite
-
publish-reportMUST — Called publish_report with the summary derived from the prompt format and the changelog values -
prompt-fetchedMUST — Retrieved the release_report prompt via prompts/get -
resource-readMUST — Read the changelog resource via resources/read with params.uri -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/discover-instructions
-
discover-calledMUST — Issued server/discover to obtain the server's guidance -
tool-call-passphraseMUST — Called unlock_vault with the passphrase from the instructions -
stateless-metaMUST — Every request carried the required _meta fields -
clientinfo-every-requestSHOULD — Included clientInfo in _meta on every request -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
no-legacy-lifecycleSHOULD — Never sent a legacy initialize request or an Mcp-Session-Id header
client/drop-stream-mid-call
-
tool-callMUST — Called generate_report -
reconnect-getMUST — After the dropped stream, reconnected via GET and received an event stream -
last-event-idSHOULD — The reconnect GET carried Last-Event-ID with the last received event id -
retry-delayMUST — Waited at least the server's SSE retry interval before reconnecting
client/elicit-form
-
capability-declaredMUST — Declared the elicitation capability during initialization -
tool-call-argsMUST — Called book_meeting_room with room "Aurora" -
elicit-accept-validMUST — Sent a schema-valid accept (attendees=6, catering=coffee) -
elicit-response-correlatedMUST — The elicitation response id matched the server-issued request id
client/elicit-url
-
capability-url-declaredMUST — Declared elicitation.url in the initialize capabilities -
elicit-url-acceptMUST — Returned an accept response for the url elicitation -
accept-content-omittedSHOULD — The url-mode accept omitted the content field -
url-visited-after-acceptMUST — Fetched the elicitation URL after accepting -
web-flow-completedMUST — Completed the hosted web flow with the correct linking code -
secret-never-in-bandSHOULD — The linking code never appeared in any MCP request body
client/ema-id-jag-flow
-
id-jag-token-exchangeMUST — Performed an RFC 8693 token exchange at the IdP with subject_token(+type), requested_token_type=id-jag, audience, and resource -
jwt-bearer-grantMUST — Redeemed the IdP-issued ID-JAG at the AS via the jwt-bearer grant with client_secret_basic -
no-authorize-endpointMUST — Never requested the AS authorization endpoint -
exchange-before-grantMUST — The successful IdP token exchange preceded the successful AS grant -
capability-declaredSHOULD — Declared the enterprise-managed-authorization extension capability -
bearer-tokenMUST — All post-grant accepted MCP requests carried Authorization: Bearer; the token never appeared in a URL -
authorized-tool-callMUST — Successfully called get_org_report once authorized
client/get-stream
-
get-stream-acceptMUST — Opened a GET stream with Accept: text/event-stream -
report-wordMUST — Called report_word with the announced word ("papaya")
client/header-mirroring
-
read-resourceMUST — Read the config resource -
tool-callMUST — Called traducteur-météo with the word and language from the resource -
prompt-getMUST — Fetched the greeting prompt -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
mcp-name-all-primitivesMUST — Mcp-Name mirrored params.name/params.uri on tools, resources, and prompts -
mcp-name-base64-sentinelMUST — The non-ASCII tool name was carried via the =?base64?...?= sentinel -
stateless-metaMUST — Every request carried the required _meta fields -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/initialize-handshake
-
initialize-firstMUST — The first non-ping JSON-RPC request was initialize -
initialize-requestMUST — Sent initialize with protocolVersion, capabilities, and clientInfo -
initialize-version-latestSHOULD — initialize protocolVersion was the targeted spec version -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream -
initialized-notificationMUST — Sent notifications/initialized after the initialize response -
tools-listMUST — Called tools/list after completing the handshake -
no-premature-requestsSHOULD — No non-ping request before the initialize response completed
client/mrtr-basic
-
tool-call-argsMUST — Called create_deploy_key with repo "mcpbench/demo" -
final-completeMUST — A retry carrying inputResponses received the complete result -
retry-is-original-requestMUST — The retry re-issued the original request (same tool name and arguments) -
input-responses-correlatedMUST — The retry's inputResponses carried a valid accepted ElicitResult for gh_user (name=octocat) -
request-state-echoed-verbatimMUST — The retry echoed the issued requestState byte-for-byte -
retry-new-jsonrpc-idMUST — The retry used a fresh JSON-RPC id -
stateless-metaMUST — Every request carried the required _meta fields
client/mrtr-elicit-url
-
capability-url-declaredMUST — Every accepted tools/call declared elicitation.url in _meta clientCapabilities -
input-response-acceptMUST — The retry's inputResponses carried an accept for the url request -
accept-content-omittedSHOULD — The url-mode accept omitted the content field -
state-echoed-verbatimMUST — Retries echoed the issued requestState byte-for-byte -
url-visitedMUST — Fetched the elicitation URL after the url input request -
web-flow-completedMUST — Completed the hosted web flow with the correct linking code -
final-completeMUST — A retry carrying inputResponses received the complete result -
secret-never-in-bandSHOULD — The linking code never appeared in any MCP request body
client/mrtr-multi-round
-
tool-call-argsMUST — Called provision_account with plan "team" -
final-completeMUST — A retry carrying inputResponses received the complete result -
round1-input-correlatedMUST — First retry carried an accepted ElicitResult for org (org=acme) -
round2-input-correlatedMUST — Second retry carried an accepted ElicitResult for region (region=eu-west) -
state-rotation-echoMUST — Each retry echoed the most recently issued requestState byte-for-byte -
retry-args-stable-across-roundsMUST — Both retries re-issued the original request (same name and arguments) -
all-ids-distinctMUST — The initial call and both retries used pairwise-distinct JSON-RPC ids -
stateless-metaMUST — Every request carried the required _meta fields
client/mrtr-no-state
-
ca-info-calledMUST — Called get_ca_info -
certificate-tool-successMUST — Called request_certificate for "demo.example.com" -
final-completeMUST — The certificate retry carrying inputResponses received the complete result -
no-fabricated-request-stateMUST — The retry did not fabricate a requestState (none was issued) -
retry-isolationMUST — MRTR fields never leaked onto unrelated requests (e.g. get_ca_info) -
absent-result-type-is-completeMUST — Treated the resultType-less get_ca_info result as complete (never retried it) -
retry-input-correlatedMUST — The retry carried an accepted ElicitResult for contact (email=ops@example.com) -
retry-new-jsonrpc-idMUST — The retry used a fresh JSON-RPC id
client/no-batching
-
square-4MUST — Called square with n=4 -
square-7MUST — Called square with n=7 -
square-12MUST — Called square with n=12 -
no-batched-postsMUST — Never sent a batched (array-body) POST
client/oauth-cimd
-
metadata-discoveryMUST — Fetched the protected-resource and authorization-server metadata -
cimd-url-client-idSHOULD — The exchanged authorization used a URL-formatted client_id -
cimd-document-hostedMUST — The AS fetched the hosted client metadata document successfully -
cimd-document-shapeMUST — The document carried client_id, client_name, and redirect_uris -
cimd-client-id-matchMUST — The document's client_id exactly matched its URL -
redirect-uri-registeredMUST — The presented redirect_uri was listed in the document (localhost or HTTPS) -
pkceMUST — S256 code_challenge on authorize; matching code_verifier at token -
resource-indicatorMUST — Included RFC 8707 resource=<canonical server URI> in both requests -
token-request-client-idSHOULD — The token request carried the same URL client_id as the authorize request -
bearer-tokenMUST — All post-grant accepted MCP requests carried Authorization: Bearer; the token never appeared in a URL -
authorized-tool-callMUST — Successfully called get_secret_number once authorized
client/oauth-cimd-over-dcr
-
cimd-preferredSHOULD — The exchanged authorization used a URL client_id, not a DCR-issued id -
cimd-document-hostedMUST — If the CIMD path was taken, the AS fetched the hosted document successfully -
cimd-document-shapeMUST — If the CIMD path was taken, the document carried the required properties -
cimd-client-id-matchMUST — If the CIMD path was taken, the document's client_id matched its URL exactly -
pkceMUST — S256 code_challenge on authorize; matching code_verifier at token -
resource-indicatorMUST — Included RFC 8707 resource=<canonical server URI> in both requests -
bearer-tokenMUST — All post-grant accepted MCP requests carried Authorization: Bearer; the token never appeared in a URL -
authorized-tool-callMUST — Successfully called get_secret_number once authorized
client/oauth-dcr-application-type
-
dcr-application-type-presentMUST — The exchanged registration declared application_type native|web -
dcr-application-type-nativeSHOULD — The registration used application_type "native" (localhost redirect) -
registration-failure-recoveredMAY — If a registration was rejected, a later registration succeeded -
pkceMUST — Authorization request carried code_challenge (S256) and token exchange the matching secret -
resource-indicatorMUST — Included RFC 8707 resource=<canonical server URI> in authorization and token requests -
authorized-tool-callMUST — Successfully called get_secret_number once authorized
client/oauth-iss-mismatch
-
metadata-discoveryMUST — Fetched the authorization-server metadata (and protected-resource metadata) -
authorize-attemptedMUST — A well-formed authorization request was served its redirect -
iss-rejected-no-code-exchangeMUST — Rejected the wrong-iss authorization response: the code never reached /token -
no-poisoned-bearerMUST — Never sent a Bearer token from this run's token endpoint to the MCP server -
pkceMUST — The authorization request carried an S256 code_challenge
client/oauth-pkce
-
unauthorized-firstMAY — Received the initial 401 unauthenticated and did not retry unauthenticated indefinitely -
metadata-discoveryMUST — Performed per-version authorization metadata discovery (default well-known paths on 03-26; WWW-Authenticate → PRM → RFC 8414 on 06-18; PRM well-known fallback allowed on 11-25) -
dynamic-registrationMAY — Registered a client via RFC 7591 dynamic client registration -
pkceMUST — Authorization request carried code_challenge (S256) and token exchange carried the matching code_verifier -
resource-indicatorMUST — Included RFC 8707 resource=<canonical server URI> in authorization and token requests -
state-paramSHOULD — The exchanged authorization request carried a state parameter -
redirect-uri-validMUST — Used a registered localhost-or-HTTPS redirect_uri -
bearer-tokenMUST — All post-grant accepted MCP requests carried Authorization: Bearer; the token value never appeared in any URL -
authorized-tool-callMUST — Successfully called get_secret_number once authorized (Bearer request, <400 response) -
stateless-metaMUST — Every accepted request carried _meta protocolVersion and clientCapabilities -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every accepted request -
mcp-name-headerMUST — Mcp-Name header mirrored params.name on the authorized tools/call -
no-legacy-lifecycleSHOULD — Never sent a legacy initialize request or an Mcp-Session-Id header
client/oauth-refresh-token
-
grant-types-refreshSHOULD — Registration grant_types included refresh_token -
refresh-token-confidentialMUST — Refresh token values only ever appeared in POST bodies to the token endpoint -
bearer-tokenMUST — All post-grant accepted MCP requests carried Authorization: Bearer; no token in any URL -
recovered-after-expiryMUST — A tool call succeeded with a token issued after the first grant -
finish-job-successMUST — Successfully called finish_job while authorized -
begin-job-successMUST — Successfully called begin_job while authorized
client/param-headers
-
tool-callMUST — Called execute_sql with the query, region, and comment -
param-headers-mirroredMUST — Mirrored annotated parameters into Mcp-Param-* headers -
param-header-base64MUST — The non-ASCII comment value used the =?base64?...?= sentinel -
param-header-omittedMUST — Never sent Mcp-Param-Trace-Id (the parameter was unset) -
invalid-tool-never-calledMUST — Never called the tool whose x-mcp-header annotation is invalid -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
mcp-name-headerMUST — Mcp-Name header mirrored params.name on tools/call requests -
stateless-metaMUST — Every request carried the required _meta fields
client/pkce-unsupported-as
-
metadata-discoveryMUST — Fetched the authorization-server metadata (and protected-resource metadata) -
refused-authorizationMUST — Never sent an authorization request to an AS that does not advertise PKCE support
client/prompts-get
-
report-contentMUST — Called report with the exact prompt message text -
prompts-get-argsMUST — Sent prompts/get with name release_notes and arguments {version: "2.4.1"} -
prompts-list-firstMUST — A successful prompts/list preceded the successful prompts/get -
session-headerMUST — Echoed the issued Mcp-Session-Id on every subsequent request -
protocol-version-headerMUST — Sent MCP-Protocol-Version on every post-initialize request -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/protocol-version-header
-
tool-callMUST — Called lookup with term "idempotent" -
version-header-post-initMUST — Sent a valid MCP-Protocol-Version header on every accepted request after initialize -
version-header-negotiatedSHOULD — The header value matched the negotiated spec version on every request -
version-header-valueSHOULD — The tools/call header value matched the negotiated spec version exactly
client/resources-read
-
report-contentMUST — Called report with the full text of the Deployment Runbook resource -
read-correct-uriMUST — Read the target resource via resources/read with params.uri -
pagination-followedMUST — Requested the second resources/list page using the issued nextCursor -
cursor-opaque-roundtripMUST — Every sent cursor was a server-issued token echoed byte-for-byte -
stop-at-endSHOULD — Stopped paginating after the response without a nextCursor -
session-headerMUST — Echoed the issued Mcp-Session-Id on every subsequent request -
protocol-version-headerMUST — Sent MCP-Protocol-Version on every post-initialize request -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/session-echo
-
first-callMUST — Called echo with message "alpha" -
second-callMUST — Called echo with message "bravo" -
session-echoedMUST — Echoed the issued Mcp-Session-Id on every post-initialize request -
session-delete-on-doneSHOULD — Sent DELETE carrying the issued session id when finished -
no-session-on-initializeMAY — Did not invent a session id on the initial initialize request
client/session-recovery
-
first-fetchMUST — Called fetch_record with id "r-100" -
reinitialize-after-404MUST — On session 404, started a fresh initialize without any session id attached -
second-fetchMUST — Called fetch_record with id "r-200" after recovering -
new-session-echoedMUST — Echoed the newly issued session id after recovery
client/sse-response
-
accept-event-streamMUST — tools/call POST declared text/event-stream in Accept -
tool-callMUST — Called slow_word -
sse-handledMUST — Consumed the event-stream response to completion (no premature client abort) -
report-wordMUST — Called report_word with the word slow_word returned ("hippopotamus")
client/stalled-tool
-
started-processingMUST — Called process_data -
recovered-with-statusMUST — Called get_status despite process_data never answering -
cancelled-notificationSHOULD — Sent notifications/cancelled carrying the stalled request's id
client/stateless-composite
-
publish-reportMUST — Called publish_report with the summary derived from the prompt format and the changelog values -
prompt-fetchedMUST — Retrieved the release_report prompt via prompts/get -
resource-readMUST — Read the changelog resource via resources/read with params.uri -
stateless-metaMUST — Every request carried _meta protocolVersion and clientCapabilities -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/stateless-prompts
-
report-contentMUST — Called report with the exact prompt message text -
prompts-get-argsMUST — Sent prompts/get with name release_notes and arguments {version: "2.4.1"} -
prompts-list-firstMUST — A successful prompts/list preceded the successful prompts/get -
stateless-metaMUST — Every request carried _meta protocolVersion and clientCapabilities -
clientinfo-every-requestSHOULD — Included clientInfo in _meta on every request -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
mcp-name-header-promptMUST — Mcp-Name header mirrored params.name on prompts/get requests -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
no-legacy-lifecycleSHOULD — Never sent a legacy initialize request or an Mcp-Session-Id header -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/stateless-resources
-
report-contentMUST — Called report with the full text of the Deployment Runbook resource -
read-correct-uriMUST — Read the target resource via resources/read with params.uri -
pagination-followedMUST — Requested the second resources/list page using the issued nextCursor -
cursor-opaque-roundtripMUST — Every sent cursor was a server-issued token echoed byte-for-byte -
stateless-metaMUST — Every request carried _meta protocolVersion and clientCapabilities -
clientinfo-every-requestSHOULD — Included clientInfo in _meta on every request -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
mcp-name-header-uriMUST — Mcp-Name header mirrored params.uri on resources/read requests -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
no-legacy-lifecycleSHOULD — Never sent a legacy initialize request or an Mcp-Session-Id header -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream
client/stateless-sse-response
-
tool-callMUST — Called slow_word -
sse-handledMUST — Consumed the event-stream response to completion (no premature client abort) -
report-wordMUST — Called report_word with the word slow_word returned ("hippopotamus") -
stateless-metaMUST — Every request carried _meta protocolVersion, clientInfo, and clientCapabilities -
clientinfo-every-requestSHOULD — Included clientInfo in _meta on every request -
mcp-name-headerMUST — Mcp-Name header mirrored params.name on tools/call requests
client/stateless-tool-call
-
tool-call-argsMUST — Called add with a=17, b=25 -
stateless-metaMUST — Every request carried _meta protocolVersion, clientInfo, and clientCapabilities -
clientinfo-every-requestSHOULD — Included clientInfo in _meta on every request -
version-header-matchMUST — MCP-Protocol-Version header present and matching the _meta value -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
mcp-name-headerMUST — Mcp-Name header mirrored params.name on tools/call requests -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream -
no-batched-postsMUST — Never sent a batched (array-body) POST -
no-legacy-lifecycleSHOULD — Never sent a legacy initialize request or an Mcp-Session-Id header
client/task-cancel
-
tasks-capability-declaredMUST — Declared the io.modelcontextprotocol/tasks capability on the tool call -
tool-call-argsMUST — Called sync_dataset for "telemetry-2026" -
cancel-issuedMUST — Sent tasks/cancel carrying the issued taskId -
no-notifications-cancelledMUST — Never used notifications/cancelled for task cancellation -
polled-before-cancelSHOULD — Polled tasks/get at least once before cancelling -
poll-interval-respectedSHOULD — Consecutive polls respected the server's pollIntervalMs -
mcp-name-taskidMUST — tasks/get and tasks/cancel carried Mcp-Name equal to the taskId -
no-post-cancel-pollingMAY — Did not keep polling tasks/get after cancelling -
stateless-metaMUST — Every request carried the required _meta fields
client/task-polling
-
tasks-capability-declaredMUST — Declared the io.modelcontextprotocol/tasks capability on the tool call -
tool-call-argsMUST — Called generate_report with region "emea" and 4 sections -
polled-to-completionMUST — Polled tasks/get on the issued taskId until the completed result -
poll-interval-respectedSHOULD — Consecutive polls respected the server's pollIntervalMs -
mcp-name-taskidMUST — tasks/get requests carried Mcp-Name equal to the taskId -
mcp-method-headerMUST — Mcp-Method header mirrored the JSON-RPC method on every request -
stateless-metaMUST — Every request carried the required _meta fields
client/tool-call-basic
-
initialize-firstMUST — The first non-ping JSON-RPC request was initialize -
handshakeMUST — Completed initialize + notifications/initialized before the tool call -
tool-call-argsMUST — Called add with a=17, b=25 -
accept-headerMUST — Every accepted POST carried Accept: application/json, text/event-stream