@@ -45,32 +45,43 @@ func registerScenario(name string, handler scenarioHandler) {
4545func init () {
4646 registerScenario ("initialize" , runBasicClient )
4747 registerScenario ("tools_call" , runToolsCallClient )
48+ registerScenario ("request-metadata" , runRequestMetadataClient )
4849 registerScenario ("elicitation-sep1034-client-defaults" , runElicitationDefaultsClient )
4950 registerScenario ("sse-retry" , runSSERetryClient )
51+ registerScenario ("json-schema-ref-no-deref" , runJSONSchemaRefNoDerefClient )
52+ registerScenario ("sep-2322-client-request-state" , runMrtrClient )
53+ registerScenario ("http-standard-headers" , runHTTPStandardHeadersClient )
54+ registerScenario ("http-custom-headers" , runHTTPCustomHeadersClient )
55+ registerScenario ("http-invalid-tool-headers" , runHTTPInvalidToolHeadersClient )
5056
5157 authScenarios := []string {
5258 "auth/2025-03-26-oauth-metadata-backcompat" ,
5359 "auth/2025-03-26-oauth-endpoint-fallback" ,
60+ "auth/authorization-server-migration" ,
5461 "auth/basic-cimd" ,
62+ "auth/iss-normalized" ,
63+ "auth/iss-not-advertised" ,
64+ "auth/iss-supported" ,
65+ "auth/iss-supported-missing" ,
66+ "auth/iss-unexpected" ,
67+ "auth/iss-wrong-issuer" ,
5568 "auth/metadata-default" ,
69+ "auth/metadata-issuer-mismatch" ,
5670 "auth/metadata-var1" ,
5771 "auth/metadata-var2" ,
5872 "auth/metadata-var3" ,
73+ "auth/offline-access-not-supported" ,
74+ "auth/offline-access-scope" ,
5975 "auth/pre-registration" ,
6076 "auth/resource-mismatch" ,
61- "auth/scope-from-www-authenticate" ,
6277 "auth/scope-from-scopes-supported" ,
78+ "auth/scope-from-www-authenticate" ,
6379 "auth/scope-omitted-when-undefined" ,
64- "auth/scope-step-up" ,
6580 "auth/scope-retry-limit" ,
81+ "auth/scope-step-up" ,
6682 "auth/token-endpoint-auth-basic" ,
67- "auth/token-endpoint-auth-post" ,
6883 "auth/token-endpoint-auth-none" ,
69- "auth/iss-supported" ,
70- "auth/iss-not-advertised" ,
71- "auth/iss-supported-missing" ,
72- "auth/iss-wrong-issuer" ,
73- "auth/iss-unexpected" ,
84+ "auth/token-endpoint-auth-post" ,
7485 }
7586 for _ , scenario := range authScenarios {
7687 registerScenario (scenario , runAuthClient )
@@ -293,6 +304,198 @@ func runAuthClient(ctx context.Context, serverURL string, configCtx map[string]a
293304 return nil
294305}
295306
307+ // ============================================================================
308+ // request-metadata scenario (SEP-2575)
309+ // ============================================================================
310+
311+ // runRequestMetadataClient exercises the SEP-2575 wire-level negotiation:
312+ // every request must carry the MCP-Protocol-Version header and the per-request
313+ // _meta envelope, and the client must retry with a supported version when its
314+ // first choice is rejected with -32022. The Go SDK's Connect() drives
315+ // server/discover unconditionally for 2026-07-28, which is exactly that
316+ // mechanism.
317+ func runRequestMetadataClient (ctx context.Context , serverURL string , _ map [string ]any ) error {
318+ session , err := connectToServer (ctx , serverURL )
319+ if err != nil {
320+ return err
321+ }
322+ defer session .Close ()
323+ return nil
324+ }
325+
326+ // ============================================================================
327+ // json-schema-ref-no-deref scenario (SEP-2106)
328+ // ============================================================================
329+
330+ // runJSONSchemaRefNoDerefClient asserts that listTools does not dereference
331+ // network $ref URLs in tool schemas. The Go SDK never fetches external refs;
332+ // a plain connect → listTools is sufficient — if the SDK ever regressed and
333+ // tried to GET the canary URL the conformance referee would record the
334+ // failure.
335+ func runJSONSchemaRefNoDerefClient (ctx context.Context , serverURL string , _ map [string ]any ) error {
336+ session , err := connectToServer (ctx , serverURL )
337+ if err != nil {
338+ return err
339+ }
340+ defer session .Close ()
341+
342+ _ , _ = session .ListTools (ctx , nil )
343+ return nil
344+ }
345+
346+ // ============================================================================
347+ // SEP-2322 multi-round-trip client scenario
348+ // ============================================================================
349+
350+ // runMrtrClient drives the SEP-2322 client-request-state scenario. The
351+ // SDK's built-in client-side MRTR middleware transparently fulfills embedded
352+ // elicitation requests via ElicitationHandler and echoes requestState.
353+ func runMrtrClient (ctx context.Context , serverURL string , _ map [string ]any ) error {
354+ elicitationHandler := func (context.Context , * mcp.ElicitRequest ) (* mcp.ElicitResult , error ) {
355+ return & mcp.ElicitResult {
356+ Action : "accept" ,
357+ Content : map [string ]any {"confirmed" : true },
358+ }, nil
359+ }
360+ session , err := connectToServer (ctx , serverURL ,
361+ withClientOptions (& mcp.ClientOptions {
362+ Capabilities : & mcp.ClientCapabilities {Elicitation : & mcp.ElicitationCapabilities {}},
363+ ElicitationHandler : elicitationHandler ,
364+ }),
365+ )
366+ if err != nil {
367+ return err
368+ }
369+ defer session .Close ()
370+
371+ toolNames := []string {
372+ "test_mrtr_echo_state" ,
373+ "test_mrtr_no_state" ,
374+ "test_mrtr_unrelated" ,
375+ "test_mrtr_no_result_type" ,
376+ }
377+ for _ , name := range toolNames {
378+ if _ , err := session .CallTool (ctx , & mcp.CallToolParams {
379+ Name : name ,
380+ Arguments : map [string ]any {},
381+ }); err != nil {
382+ log .Printf ("CallTool(%q) rejected locally (expected for some cases): %v" , name , err )
383+ }
384+ }
385+ return nil
386+ }
387+
388+ // ============================================================================
389+ // SEP-2243 HTTP header scenarios
390+ // ============================================================================
391+
392+ // runHTTPStandardHeadersClient (SEP-2243 standard headers) drives tools,
393+ // resources and prompts once each so the referee sees Mcp-Method / Mcp-Name
394+ // headers on every verb the SDK supports.
395+ func runHTTPStandardHeadersClient (ctx context.Context , serverURL string , _ map [string ]any ) error {
396+ session , err := connectToServer (ctx , serverURL )
397+ if err != nil {
398+ return err
399+ }
400+ defer session .Close ()
401+
402+ if tools , err := session .ListTools (ctx , nil ); err == nil && len (tools .Tools ) > 0 {
403+ _ , _ = session .CallTool (ctx , & mcp.CallToolParams {
404+ Name : tools .Tools [0 ].Name ,
405+ Arguments : map [string ]any {},
406+ })
407+ }
408+ if res , err := session .ListResources (ctx , nil ); err == nil && len (res .Resources ) > 0 {
409+ _ , _ = session .ReadResource (ctx , & mcp.ReadResourceParams {URI : res .Resources [0 ].URI })
410+ }
411+ if prompts , err := session .ListPrompts (ctx , nil ); err == nil && len (prompts .Prompts ) > 0 {
412+ _ , _ = session .GetPrompt (ctx , & mcp.GetPromptParams {
413+ Name : prompts .Prompts [0 ].Name ,
414+ Arguments : map [string ]string {},
415+ })
416+ }
417+ return nil
418+ }
419+
420+ // runHTTPCustomHeadersClient (SEP-2243 custom headers) lists tools (so the
421+ // SDK caches inputSchema and its x-mcp-header annotations), then makes the
422+ // runner-supplied tool calls so the referee validates the Mcp-Param-*
423+ // headers.
424+ func runHTTPCustomHeadersClient (ctx context.Context , serverURL string , configCtx map [string ]any ) error {
425+ session , err := connectToServer (ctx , serverURL )
426+ if err != nil {
427+ return err
428+ }
429+ defer session .Close ()
430+
431+ if _ , err := session .ListTools (ctx , nil ); err != nil {
432+ return fmt .Errorf ("session.ListTools(): %v" , err )
433+ }
434+ for _ , call := range readToolCallsContext (configCtx ) {
435+ if _ , err := session .CallTool (ctx , & mcp.CallToolParams {
436+ Name : call .Name ,
437+ Arguments : call .Arguments ,
438+ }); err != nil {
439+ log .Printf ("CallTool(%q): %v" , call .Name , err )
440+ }
441+ }
442+ return nil
443+ }
444+
445+ // runHTTPInvalidToolHeadersClient (SEP-2243 invalid-tool filtering) lists
446+ // tools — a correct SDK leaves only the valid ones — then calls each survivor
447+ // so the referee records SUCCESS for every excluded tool never called.
448+ func runHTTPInvalidToolHeadersClient (ctx context.Context , serverURL string , _ map [string ]any ) error {
449+ session , err := connectToServer (ctx , serverURL )
450+ if err != nil {
451+ return err
452+ }
453+ defer session .Close ()
454+
455+ tools , err := session .ListTools (ctx , nil )
456+ if err != nil {
457+ return fmt .Errorf ("session.ListTools(): %v" , err )
458+ }
459+ for _ , tool := range tools .Tools {
460+ _ , err := session .CallTool (ctx , & mcp.CallToolParams {
461+ Name : tool .Name ,
462+ Arguments : map [string ]any {"region" : "us-west1" },
463+ })
464+ if err != nil {
465+ log .Printf ("call %q rejected: %v" , tool .Name , err )
466+ }
467+ }
468+ return nil
469+ }
470+
471+ type toolCall struct {
472+ Name string
473+ Arguments map [string ]any
474+ }
475+
476+ // readToolCallsContext parses the `toolCalls` array from MCP_CONFORMANCE_CONTEXT.
477+ // Format: {"toolCalls": [{"name":"...", "arguments":{...}}, ...]}.
478+ func readToolCallsContext (configCtx map [string ]any ) []toolCall {
479+ raw , ok := configCtx ["toolCalls" ].([]any )
480+ if ! ok {
481+ return nil
482+ }
483+ out := make ([]toolCall , 0 , len (raw ))
484+ for _ , item := range raw {
485+ obj , ok := item .(map [string ]any )
486+ if ! ok {
487+ continue
488+ }
489+ name , _ := obj ["name" ].(string )
490+ args , _ := obj ["arguments" ].(map [string ]any )
491+ if args == nil {
492+ args = map [string ]any {}
493+ }
494+ out = append (out , toolCall {Name : name , Arguments : args })
495+ }
496+ return out
497+ }
498+
296499// ============================================================================
297500// Main entry point
298501// ============================================================================
0 commit comments