Summary
SchemaForm's Edit as JSON switch (#2151, PR #2170) lets the whole arguments object be written as JSON. What the user writes is not always what goes on the wire, because the dispatch still applies the coercion the widget form needs.
InspectorClient.callTool runs every string-valued argument through convertStringToolArgs → convertToolParameters, which converts it to the type the tool's inputSchema declares. That exists for a good reason: the widget form hands everything over as text. But a raw-JSON draft already carries real JSON types, so the conversion is applied to values that never needed it.
Reproduce
- Connect a server with a tool whose schema declares a numeric argument —
test-servers/configs/structured-output-http.json's get_temp, or any add-style tool.
- Tools tab → select the tool → Edit as JSON.
- Enter an argument as a string where the schema says number, e.g.
{"count":"01"}.
- Execute, and read the request in the Protocol or Network tab.
Expected: the wire carries {"count":"01"} — what the editor showed.
Actual: the wire carries {"count":1}.
Two consequences, and the second is the more interesting one:
Why it was not fixed in #2170
Copilot raised it on review round 12 of that PR. The fix is not local to the web client:
- Core.
InspectorClient.callTool (and callToolStream, and the task path) would need an option to skip argument coercion — its options?: { skipOutputValidation?: boolean } bag is the natural home. That is shared code: the CLI and TUI call the same methods, so the change wants its own tests.
- Web. "This came from raw mode" has to travel from
SchemaForm through ToolDetailPanel / AppDetailPanel, their screens, and App.tsx's two callTool call sites plus the open-app path. SchemaForm.onChange currently reports values only, with nothing to say where they came from.
There is also a product question underneath, which is the real reason this deserves its own issue rather than a late round of an already-large PR: should the Tools tab be able to send arguments the schema says are invalid? Raw mode's purpose argues yes. If the answer is no, the fix is the opposite one — detect the coercion and refuse it, the way PR #2170's Edit-and-replay modal already does for the same conversion (reshapedReplayParam) — and the two paths should then agree.
Acceptance
Summary
SchemaForm's Edit as JSON switch (#2151, PR #2170) lets the whole arguments object be written as JSON. What the user writes is not always what goes on the wire, because the dispatch still applies the coercion the widget form needs.InspectorClient.callToolruns every string-valued argument throughconvertStringToolArgs→convertToolParameters, which converts it to the type the tool'sinputSchemadeclares. That exists for a good reason: the widget form hands everything over as text. But a raw-JSON draft already carries real JSON types, so the conversion is applied to values that never needed it.Reproduce
test-servers/configs/structured-output-http.json'sget_temp, or anyadd-style tool.{"count":"01"}.Expected: the wire carries
{"count":"01"}— what the editor showed.Actual: the wire carries
{"count":1}.Two consequences, and the second is the more interesting one:
Why it was not fixed in #2170
Copilot raised it on review round 12 of that PR. The fix is not local to the web client:
InspectorClient.callTool(andcallToolStream, and the task path) would need an option to skip argument coercion — itsoptions?: { skipOutputValidation?: boolean }bag is the natural home. That is shared code: the CLI and TUI call the same methods, so the change wants its own tests.SchemaFormthroughToolDetailPanel/AppDetailPanel, their screens, andApp.tsx's twocallToolcall sites plus the open-app path.SchemaForm.onChangecurrently reports values only, with nothing to say where they came from.There is also a product question underneath, which is the real reason this deserves its own issue rather than a late round of an already-large PR: should the Tools tab be able to send arguments the schema says are invalid? Raw mode's purpose argues yes. If the answer is no, the fix is the opposite one — detect the coercion and refuse it, the way PR #2170's Edit-and-replay modal already does for the same conversion (
reshapedReplayParam) — and the two paths should then agree.Acceptance
tools/callfrom raw mode sends exactly what the editor shows, or says why it will not.