When an input fails a union-typed schema, the MCP tool returns an error.message that is the raw stringified ZodError: one entry per union branch, all identical, none naming the expected shape. The same response already carries a correctly flattened error.issues — so the useful message is computed, just not used.
This matters more for MCP than for a human CLI user: an agent reads message first, and 3 kB of repeated text is pure context burn for zero information. The CLI's own hand-written errors are excellent by comparison, which is what makes the contrast jarring:
delete-styles input.deletes is not supported. Expected one of: deletions.
update-design-token-styles input.token is not supported. Expected one of: designTokenId, updates. Did you mean designTokenId?
One turn to fix, no ambiguity. The Zod path gives the opposite.
Environment
webstudio@0.282.1, Node v22.23.1, Linux.
Reproduction
Pass a plain string where update-styles expects a StyleValue object — a very natural first attempt, since CSS values are strings everywhere else in the product:
$ webstudio update-styles '{"updates":[{"instanceId":"<id>","property":"color","value":"red"}],"dryRun":true}'
Result:
error.message — 3068 characters, containing "Invalid input: expected object, received string" 15 times (once per branch of the StyleValue union), with "path": [] on every one.
error.issues — already correct and minimal:
[{"code": "invalid_type", "path": ["value"],
"message": "Invalid input: expected object, received string",
"constraint": "type:object"}]
Nothing in either field says what a StyleValue actually looks like, so the caller still has to go read meta.get_more_tools to learn it is {"type":"keyword","value":"red"}.
Suggestion
- Set
error.message from the already-computed error.issues (dedup identical branch errors, keep the real path) instead of the raw Zod dump.
- For union mismatches, name the expected shape and give one valid example — the tool's
mcpExamples already contains it:
update-styles updates[0].value must be a StyleValue object, received a string.
Example: {"type":"keyword","value":"red"}
That would bring schema errors in line with the quality of the hand-written ones.
When an input fails a union-typed schema, the MCP tool returns an
error.messagethat is the raw stringifiedZodError: one entry per union branch, all identical, none naming the expected shape. The same response already carries a correctly flattenederror.issues— so the useful message is computed, just not used.This matters more for MCP than for a human CLI user: an agent reads
messagefirst, and 3 kB of repeated text is pure context burn for zero information. The CLI's own hand-written errors are excellent by comparison, which is what makes the contrast jarring:One turn to fix, no ambiguity. The Zod path gives the opposite.
Environment
webstudio@0.282.1, Node v22.23.1, Linux.Reproduction
Pass a plain string where
update-stylesexpects aStyleValueobject — a very natural first attempt, since CSS values are strings everywhere else in the product:Result:
error.message— 3068 characters, containing"Invalid input: expected object, received string"15 times (once per branch of theStyleValueunion), with"path": []on every one.error.issues— already correct and minimal:[{"code": "invalid_type", "path": ["value"], "message": "Invalid input: expected object, received string", "constraint": "type:object"}]Nothing in either field says what a
StyleValueactually looks like, so the caller still has to go readmeta.get_more_toolsto learn it is{"type":"keyword","value":"red"}.Suggestion
error.messagefrom the already-computederror.issues(dedup identical branch errors, keep the realpath) instead of the raw Zod dump.mcpExamplesalready contains it:That would bring schema errors in line with the quality of the hand-written ones.