WebMCP · executeTool · browser interoperability · 13 September 2026
WebMCP executeTool input errors: separate the current draft from runtime drift
If document.modelContext.getTools() succeeds but manual execution fails, inspect the API boundary before rewriting the tool. The current 10 September WebMCP draft requires an object as executeTool input and exposes RegisteredTool.inputSchema as an object. A live implementation report filed on 1 September observed the opposite shape: JSON-string input succeeded and inputSchema arrived as serialized text.
Published 13 September 2026 · sources checked 13 September 2026
Object
Current draft inputObject requirement
RegisteredTool
Pass the object returned by getTools(), not only its name
object
Current draft inputSchema shape on RegisteredTool
DOMString
Current draft executeTool return type
Current normative target
The 10 September draft is unambiguous about the object boundary
The current WebMCP Draft Community Group Report defines executeTool(RegisteredTool tool, optional any inputObject). Its algorithm rejects an inputObject that is not an Object, then serializes that object to JSON internally before dispatching the tool. The draft example likewise calls executeTool(tool, {a: 10}).
For discovery, getTools() currently constructs a RegisteredTool whose inputSchema member is an object. The algorithm parses the internally stored JSON string back into a JavaScript value before returning the tool, and the specification describes that value as a deep copy of the schema supplied at registration.
The return boundary is intentionally different: the current draft says executeTool resolves to the stringified result of the tool execution. Do not use a string return value as evidence that input arguments should also be strings.
Why developers are still seeing the opposite
A current implementation report documents exactly this mismatch
WebMCP issue #278, opened 1 September 2026, reports a tested browser implementation where an object argument caused an input parsing/type error, while JSON.stringify({ message: "hello" }) succeeded. The same report observed tool.inputSchema as a string containing serialized JSON Schema.
The project also has an open pull request titled Clarify executeTool input and schema values. Taken together with the newer 10 September draft text, that is a strong signal to treat this failure as browser/spec-version interoperability drift rather than as proof that every WebMCP client should encode arguments as JSON text.
Fast diagnosis
Read the raw runtime shape before changing your tool
| Observed signal | What it suggests | First action |
|---|---|---|
| executeTool(tool, {…}) throws a parse/type error | Runtime may lag the current draft or expose a transitional implementation | Capture browser/build and retry with the smallest object payload before changing application logic. |
| executeTool(tool, JSON.stringify({…})) works | Observed compatibility behavior, not the current draft contract | Treat it as a gated runtime workaround, not a portable API assumption. |
| typeof tool.inputSchema === 'string' | Runtime is exposing serialized schema text | Parse only for inspection/validation; current draft RegisteredTool.inputSchema is an object. |
| executeTool resolves but typeof result === 'string' | Expected by the current draft | Parse the result only when your tool contract says its payload is JSON. |
| A tool name string is passed instead of the returned tool object | Wrong executeTool target shape | Use the RegisteredTool returned by getTools(). |
Minimal current-draft probe
Test the portable shape first and log what the browser actually returns
const tools = await document.modelContext.getTools();
const tool = tools.find((entry) => entry.name === "example_tool");
if (!tool) throw new Error("example_tool not registered");
console.log({
toolName: tool.name,
schemaType: typeof tool.inputSchema,
inputSchema: tool.inputSchema,
});
const result = await document.modelContext.executeTool(
tool,
{ message: "hello" },
);
console.log({ resultType: typeof result, result });This follows the current draft: a RegisteredTool from getTools(), an object input, and a string result. If that exact probe fails while a JSON-string input succeeds, record the browser/version and isolate the workaround. That evidence is more useful than changing the tool schema until the failure disappears.
Compatibility pattern
Gate transitional behavior; do not let it become your permanent contract
- 1. Feature-detect the current surface. Prefer
document.modelContext; do not assume an old tutorial targets the current draft. - 2. Await registration and discovery. Keep a clear boundary between registering a tool and retrieving the
RegisteredToolthat an in-page caller executes. - 3. Inspect the schema value. If a runtime returns a string, parse it only where needed for diagnostics or validation. Do not double-stringify a value merely because one implementation did so.
- 4. Try the current draft shape first. Pass an object to
executeTool. If a known runtime requires JSON text, scope that fallback to the detected implementation and keep telemetry around it. - 5. Keep consequences server-authorized. A compatibility branch must not weaken authentication, spending limits, idempotency or confirmation rules for state-changing tools.
- 6. Remove the fallback when the runtime converges. WebMCP is a draft. Compatibility code should have an owner, a target environment and an expiry condition.
Do not confuse three different JSON boundaries
Registration schema, execution input and execution result have different shapes
| Boundary | Current 10 Sep draft | Debugging rule |
|---|---|---|
| registerTool inputSchema | JavaScript object describing JSON Schema | Keep it serializable and valid; the browser stores a serialized form internally. |
| RegisteredTool.inputSchema | Object / deep copy from getTools() | If the runtime returns a string, record it as version drift and parse defensively. |
| executeTool inputObject | Object; non-Objects are rejected by the current algorithm | Object first. JSON-string fallback only for a verified affected runtime. |
| executeTool result | Stringified tool result | Parse only when the tool contract says the returned text contains JSON. |
Continue from diagnosis to agent-ready delivery
Primary sources
- WebMCP — Draft Community Group Report, 10 September 2026 →
- WebMCP issue #278 — executeTool argument encoding and returned schema shape →
- WebMCP PR #279 — clarify executeTool input and schema values →
WebMCP remains a Draft Community Group Report rather than a W3C Standard. Browser behavior and the specification can change; verify the current draft and your target runtime before treating this page as a frozen API contract.