Repository navigation
Conversation
…fy#703) Route the six MCP tool paths that returned str(ValidationError) through a shared format_validation_error_message() so the message no longer leaks the Pydantic model name, an input_value= echo of the arguments, or an errors.pydantic.dev URL. Behavior and error envelope are unchanged. Signed-off-by: Abhinay Kumar <111532209+abhinyaay@users.noreply.github.com>
…efy#703) Unit tests for format_validation_error_message plus a per-tool-family wiring test (create/update ai_agent, create/update ai_automation, create_automation condition, create_send_task_automation) asserting the surfaced message has no input_value= echo and no errors.pydantic.dev URL. Signed-off-by: Abhinay Kumar <111532209+abhinyaay@users.noreply.github.com>
|
Fix these in this PR. Each comment points at something the reader cannot see. Lines are at Comments and names that point at the plan, the MR, or the release
No other test under |
mocha06
left a comment
There was a problem hiding this comment.
Review against ground truth
The six paths no longer leak the model name, the input_value= echo, or the errors.pydantic.dev URL. The shared helper still keeps Pydantic's Value error, prefix on every validator error, which is the common case for these models (22 validators). One finding, inline.
The scoping decision
- The
value_errorunwrap inportal_element_validation_erroris not portal-specific. It is how Pydantic wraps everyValueErrora validator raises. Moving it into the shared helper is the fix for the finding, and lets the portal formatter delegate too.
Findings
1 finding: packages/mcp/src/pipefy_mcp/tools/validation_helpers.py:157.
4 cleanup items, posted as one note.
Read at
pipefy/ai-toolkit@1071129d(PR head), baseorigin/dev@447df7b0. Probes: the real SDK input models through the new helper, pydantic 2.13.4; a mutation check oncreate_ai_agent(red withstr(exc), green with the helper).- Sibling repositories read-only: none cited in the comments. The hosted deployment and the Copilot consumer were checked for code that parses these messages; neither reads the message text.
| for err in exc.errors(): | ||
| loc = ".".join(str(part) for part in err.get("loc", ())) | ||
| err_type = err.get("type", "") | ||
| msg = err.get("msg", "") |
There was a problem hiding this comment.
When a behavior prompt has a placeholder with no value, the error still starts with Value error, .
Fix: for a value_error row, use ctx["error"] as the message, as portal_element_validation_error does at portal_tool_helpers.py:245-250. Add one validator-error case to the new tests.
User scenario
- A user in Claude Desktop asks the agent to create an AI agent. One behavior prompt contains
{{customer}}and no value for it. - The agent calls
create_ai_agent. - The user reads
behaviors: Value error, Behavior contains {{placeholders}} but no template_params .... They expected the sentence to start atBehavior contains.
How
- The three input models behind the six paths raise
ValueErrorin 22 places. - Pydantic turns each one into a
value_errorrow whosemsgisValue error,plus the original text. - This line copies that
msginto the clause unchanged. - The new tests send only empty lists and an empty string. Those are
too_shortrows and never reach this branch.
Evidence
packages/mcp/src/pipefy_mcp/tools/validation_helpers.py:157@1071129d(the line under review)packages/mcp/src/pipefy_mcp/tools/portal_tool_helpers.py:245-250@1071129d(the existing unwrap; it is generic Pydantic structure, not portal-specific)grep -c "raise ValueError":models/ai_agent.py17,models/ai_automation.py4,models/send_task_automation.py1 (the 22 validators)- Probe, pydantic 2.13.4,
CreateAiAgentInput(..., behaviors=[{"name": "b", "prompt": "hi {{missing}}"}])through the new helper:
behaviors: Value error, Behavior contains {{placeholders}} but no template_params (or placeholders) dict was provided on this behavior.
- Same probe with the unwrap applied:
behaviors: Behavior contains {{placeholders}} but no template_params (or placeholders) dict was provided on this behavior.
- The unwrap changes only
value_errorrows. Nine inputs probed (missing, extra_forbidden, too_short, assertion_error, int_parsing, null, three value_error): output identical for the six non-value_errorcases. grep -rn "Value error" packages/mcp/tests→ no test pins the prefix.test_ai_agent_tools.py:2600,test_ai_automation_tools.py:1619,test_automation_tools.py:1294@1071129d(inputsbehaviors: [],field_ids: [],recipients: ""; each renders astoo_short)
adriannoes
left a comment
There was a problem hiding this comment.
Thank you for the contribution. The six validation paths now share one formatter, and the shorter message still names the field and the problem.
Verdict: merge with notes into dev.
Optional
Your call on both of these.
- Two notes are on their lines in the diff.
What worked well
- One helper renders the six inner validation errors and the argument envelope. The envelope still uses the same clause loop as before.
Review path
Reviewed 1071129d against 447df7b0. CI on that head is green. A local session of this head ran the hygiene tests (24 passed) and the six invalid tool calls. Each returned success: false, named the field and the problem, and created no resource.
| assert "_Probe" not in message | ||
| # ...but the message still names what went wrong. | ||
| assert "missing required argument 'x'" in message | ||
| assert "items" in message |
There was a problem hiding this comment.
Optional. assert "items" in message stays green if the renderer returns only the field name and drops the constraint text.
Assert items: and a stable fragment of the min-length message. Done when: a renderer that drops the msg half fails this test.
Details
- The missing and extra cases in the next test lock the full clause.
- This line is the only lock on the normal
loc: msgbranch. - The tool-family tests check that
input_value=andpydantic.devare absent. They do not check the clause text. - A live call on this head already returned the full min-length sentence. This unit test would still pass if that sentence were dropped.
|
|
||
| @pytest.mark.anyio | ||
| class TestAiAgentValidationMessageHygiene: | ||
| """The inner SDK-model ValidationError must not leak pydantic noise (#703).""" |
There was a problem hiding this comment.
Optional. This docstring cites issue 703. Tracker numbers in source comments go stale. This repo keeps them in the PR and the commits.
Drop (#703) on this line, on the class docstring in test_ai_automation_tools.py, on the condition comment in test_automation_tools.py, and on the send-task test docstring. Done when: those four tests contain no issue number.
| """The inner SDK-model ValidationError must not leak pydantic noise (#703).""" | |
| """The inner SDK-model ValidationError must not leak pydantic noise.""" |
Details
- The same token is on
test_ai_automation_tools.pyline 1606,test_automation_tools.pyline 536 (issue #703), and line 1285. test_validation_helpers.pyand the production modules do not cite the issue.- The PR title, the PR body, and both commit subjects may keep the issue number.
…ipefy#703) Per review on pipefy#740: these test comments and docstrings cite the issue/MR number, which the reader cannot see after merge. Each sentence stands on its own without it, so drop the "(pipefy#703)" / "(issue pipefy#703)" references in the four flagged tests. Signed-off-by: Abhinay Kumar <111532209+abhinyaay@users.noreply.github.com>
|
Thanks for the review. Addressed in the latest commit: dropped the issue-number citations from all four flagged tests — the two |
|
@abhinyaay Verified at Two threads are still open at this head:
|
Summary
Closes #703.
Six MCP tool paths returned
str(ValidationError)directly, leaking the Pydantic model name, aninput_value=echo of the caller's arguments, and anerrors.pydantic.devURL to the agent:create_ai_agent/update_ai_agent(ai_agent_tools.py)create_ai_automation/update_ai_automation(ai_automation_tools.py)conditionparse andcreate_send_task_automation(automation_tools.py)They now route through a shared
format_validation_error_message()invalidation_helpers.py, which rendersexc.errors()asloc: msgclauses (withmissing/extra_forbiddenspecial-cased) and none of that noise. The existing_format_validation_errorsin the validation envelope is refactored to delegate to the same helper, so the clause renderers collapse toward one. Non-breaking: the tools still fail in the same cases with the same error envelope — only the message text gets shorter.Scoping note:
portal_element_validation_error(portal_tool_helpers.py) is left as-is. It carries portal-specific clause handling (avalue_errorinner-cause unwrap and a literal-error message fortype) backed by its own tests, so folding it in would change that behaviour. Happy to do it in a follow-up if you'd prefer a single renderer.Test plan
uv run pytest -m "not integration"— fullpackages/mcpsuite: 2058 passed, 6 skippeduv run ruff check+uv run ruff format --checkon the changed files — cleanAdded
format_validation_error_messageunit tests plus a per-tool-family wiring test (create/update ai_agent, create/update ai_automation, theconditionpath, andcreate_send_task_automation) asserting the surfaced message has noinput_value=and noerrors.pydantic.dev. Each was confirmed to fail before the fix and pass after.Docs / skills
docs/parity.mdupdated when MCP ↔ CLI coverage changedskills/updated in this PR (or a paired PR)Legal / contributions
git commit -s)COMPLIANCE.mdwhen applicable