Skip to content

Hold every MCP tool's arguments to its exact member names #115

Description

@kikashy

Goal

Apply the exact member-name check experimental_test_graphs already uses to the other MCP tools that decode an arguments object.

Why

Every tool advertises additionalProperties: false, which means the exact spelling. encoding/json matches struct field names case-insensitively, so DisallowUnknownFields alone is not enough: a differently-cased member binds to the real field and the call succeeds.

internal/mcp/tools.go has an exactMembers helper for this, added with experimental_test_graphs, but it is used by that tool only. The others decode with DisallowUnknownFields alone.

Demonstrated against a built server, same argument shape, two different answers:

get_pack                  {"PACK_ID":"intake"}   -> isError=false, returns the pack
experimental_test_graphs  {"GRAPH_ID":"x"}       -> isError=true,  "unknown member"

The advertised schema is identical in kind for both. One of them enforces it.

Scope

  • Change internal/mcp/tools.go and internal/mcp/server_test.go only.
  • Call exactMembers before decoding in each tool that takes an arguments object and does not already: get_pack, experimental_evaluate, and experimental_test_packs.
  • Keep each tool's existing error text shape; exactMembers already produces a message naming the offending member and the accepted ones.
  • Do not change any tool's advertised schema, argument semantics, or success behaviour.

Acceptance criteria

  • For each affected tool, a differently-cased member name (PACK_ID, Pack_Id, packId where the schema says pack_id) is refused.
  • Refusals are MCP tool errors inside successful JSON-RPC responses, not top-level protocol errors.
  • Correctly spelled arguments still succeed unchanged, and omitted-optional behaviour is untouched.
  • Table-driven tests using the existing stdio harness, with t.Run per case.
  • env GO111MODULE=on go test ./internal/mcp and go test ./... pass.
  • Every commit includes a DCO sign-off created with git commit -s.

Contributor learning

MCP argument contracts, why a schema keyword needs an enforcing check behind it, and one concrete way Go's JSON decoding is more permissive than a schema.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggoPull requests that update go codegood first issueGood for newcomers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions