Tool schemas declare additionalProperties: false, but nothing in the dispatch path
enforces it — request decoding reads the keys it knows (req.GetInt / req.GetString
with defaults) and unknown keys simply vanish. The call succeeds with default behavior
and no indication anything was ignored.
Observed shape (agent sessions on my production C# codebase): the agent passed
line_range: [120, 160] to read_file three times — a plausible guess for an option
that doesn't exist (the real window options are offset/limit) — and got the full
file back each time, silently. That's the heaviest tool in the surface paying maximum
token cost for a request that explicitly asked for a 40-line window, and the agent had
no signal to correct itself: no error, no warning, three identical retries. Verified
against current main: line_range appears nowhere in tools_fileops.go; the request
just carries an ignored key.
Why this class is worse than a normal typo hazard for agent callers specifically:
- Agents learn option names from one tool's schema and transfer them to another
(max_bytes is real on the budget layer, limit means different things on
different tools). Cross-tool transfer of a key that doesn't exist on the target is
routine, not exceptional.
- The schema already promises rejection —
additionalProperties: false is in the
published contract. A client that trusts the contract assumes an accepted call meant
every argument was understood.
- The failure is invisible precisely where it's most expensive: oversized responses
the server's own token accounting then attributes to the session.
Possible fix shapes, in order of strictness:
- Enforce the schema at dispatch: unknown key → tool error naming the unknown key and
listing the valid ones. Matches the published contract; strictest for buggy clients
that today "work" by accident.
- Warning rider: accept the call but attach
_ignored_options: [...] to the result,
the same pattern as _truncated_by_budget. Zero breakage; agents get the signal to
self-correct on the next call.
- Either of the above gated by an env toggle during a transition.
Happy to implement whichever shape you prefer — the rider (2) looks like the natural
first step, with (1) as the eventual contract-honoring behavior.
Tool schemas declare
additionalProperties: false, but nothing in the dispatch pathenforces it — request decoding reads the keys it knows (
req.GetInt/req.GetStringwith defaults) and unknown keys simply vanish. The call succeeds with default behavior
and no indication anything was ignored.
Observed shape (agent sessions on my production C# codebase): the agent passed
line_range: [120, 160]toread_filethree times — a plausible guess for an optionthat doesn't exist (the real window options are
offset/limit) — and got the fullfile back each time, silently. That's the heaviest tool in the surface paying maximum
token cost for a request that explicitly asked for a 40-line window, and the agent had
no signal to correct itself: no error, no warning, three identical retries. Verified
against current main:
line_rangeappears nowhere intools_fileops.go; the requestjust carries an ignored key.
Why this class is worse than a normal typo hazard for agent callers specifically:
(
max_bytesis real on the budget layer,limitmeans different things ondifferent tools). Cross-tool transfer of a key that doesn't exist on the target is
routine, not exceptional.
additionalProperties: falseis in thepublished contract. A client that trusts the contract assumes an accepted call meant
every argument was understood.
the server's own token accounting then attributes to the session.
Possible fix shapes, in order of strictness:
listing the valid ones. Matches the published contract; strictest for buggy clients
that today "work" by accident.
_ignored_options: [...]to the result,the same pattern as
_truncated_by_budget. Zero breakage; agents get the signal toself-correct on the next call.
Happy to implement whichever shape you prefer — the rider (2) looks like the natural
first step, with (1) as the eventual contract-honoring behavior.