Skip to content

fix(http-base): return valid empty results for unhandled list methods - #476

Open
JosephDoUrden wants to merge 1 commit into
modelcontextprotocol:mainfrom
JosephDoUrden:fix/http-scenarios-templates-list
Open

fix(http-base): return valid empty results for unhandled list methods#476
JosephDoUrden wants to merge 1 commit into
modelcontextprotocol:mainfrom
JosephDoUrden:fix/http-scenarios-templates-list

Conversation

@JosephDoUrden

Copy link
Copy Markdown

Fixes #474.

The http scenarios advertise capabilities they don't fully route. http-standard-headers advertises resources but has no route for resources/templates/list, so the request falls through to sendGenericResult, which returned only the stamped draft fields with no resourceTemplates. Both schema versions require that member, so an eager-discovery client schema-rejects the reply and drops the connection before the checks the scenario is actually about get to run. That's the failure cloudflare/agents baselines, and it reproduces with the plain TS SDK client too: listResourceTemplates() throws a zod error on the pre-fix referee and completes on this branch.

The fix is a small map in http-base: when a list-shaped method reaches the generic fallback, return a schema-valid empty result (resourceTemplates: [] and friends) instead of a bare stamp. Six list methods are covered, including roots/list and tasks/list (that one exists only at 2025-11-25, the draft schema has no ListTasksResult, noted in a comment). tools/list can't currently reach the fallback since every scenario routes it, the entry is there for symmetry.

Why the base class and not a route in each scenario: a server advertising resources must serve resources/templates/list, so the invariant belongs where the fallback lives, otherwise every future scenario that advertises a capability re-creates the bug. And http-base is the only place in the repo with a success-shaped fallback at all, the other mock servers return -32601 for unrouted methods, so this can't live in the shared helper either.

Deliberately not covered: completion/complete, prompts/get, resources/read. Those have no meaningful empty default and faking one would mask real scenario-authoring bugs. They still return the bare stamp.

One extra thing the tests pin: the lookup is a ReadonlyMap rather than an object literal, because request.method comes off the wire and an object lookup walks the prototype chain. A client posting method "constructor" would get a function back, and the response serialises to an envelope with neither result nor error. There's a regression test posting prototype names to all three scenario classes.

Tested per CONTRIBUTING: fail and pass both proven (the new tests fail on main), full suite green (532), and an end-to-end run of the http-standard-headers scenario through the runner with @modelcontextprotocol/sdk 1.29 doing eager discovery, pre-fix it dies at listResourceTemplates, post-fix all 12 checks emit real verdicts.

AI disclosure per AI_POLICY.md: this PR was built with AI assistance (investigation and code generation). I verified the defect and the fix against the schema files and a real SDK client myself, and the numbers above are from local runs.

The BaseHttpScenario generic fallback answered any unrouted request
with only the required draft result fields, so a standard list method
a scenario does not route came back without its required list member.
http-standard-headers advertises resources but has no
resources/templates/list route; the reply's missing resourceTemplates
fails schema validation (draft and 2025-11-25 both require it), and
strictly-validating clients drop the connection before the scenario's
header checks run.

Merge schema-valid empty results for the six list-shaped methods
(tools, resources, resource templates, prompts, roots, tasks) into
the fallback, keyed by a ReadonlyMap so method names that collide
with Object.prototype ("constructor", "toString", ...) miss instead
of poisoning the reply with a prototype function. tasks/list has a
typed result only at 2025-11-25; the empty member is harmless on the
draft wire. Non-list results (tools/call, resources/read,
prompts/get, ...) have no meaningful empty default and keep the bare
stamped fallback, so a route a scenario forgot surfaces instead of
being masked.

Tests pin the fixed behaviour on all three http scenario classes,
the prototype-name lookups, and the spec schema's rejection of the
pre-fix payload.

Fixes modelcontextprotocol#474
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

http-standard-headers referee returns an invalid resources/templates/list result

1 participant