Skip to content

Surface safe field-level MCP argument errors via additive error.data#1045

Open
jdjioe5-cpu wants to merge 2 commits into
ramimbo:mainfrom
jdjioe5-cpu:hermes/mergework-946-mcp-field-error-data
Open

Surface safe field-level MCP argument errors via additive error.data#1045
jdjioe5-cpu wants to merge 2 commits into
ramimbo:mainfrom
jdjioe5-cpu:hermes/mergework-946-mcp-field-error-data

Conversation

@jdjioe5-cpu
Copy link
Copy Markdown
Contributor

@jdjioe5-cpu jdjioe5-cpu commented Jun 6, 2026

Bounty #946

Summary

  • Adds a whitelist-only field-level error.data payload to the MCP tools/call error envelope so clients can act on argument-validation failures without parsing natural language.
  • The legacy error.message stays exactly "invalid tool arguments" for every argument-validation failure, so existing clients that only read the message keep working.
  • Caller input is never echoed into the response: field and message are built from static whitelists in app/mcp.py.
  • Updated the 20 exact-dict error-envelope assertions in tests/test_api_mcp.py and the one in tests/test_security.py to use a small helper that asserts code + message + (optional) data shape, instead of brittle full-dict equality.
  • Added 9 focused new tests covering: known field-level phrases (limit, account, include_expired, status), known field-less phrases (unknown tool, repo requires issue_number), the classifier fallthrough path (unmatched phrases get no data), and PII safety (caller-supplied values never appear in the response).
  • Updated docs/agent-guide.md with a short paragraph and example for the additive error.data shape, including the legacy-fallback note for unmatched phrases.

Why

The bounty body explicitly calls out "improving safe field-level MCP argument errors for invalid selectors or fields" as good scope. Eight other open PRs against this bounty add outputSchema blocks for specific tools; zero of them touch the dispatcher error path. This PR targets that open lane with a minimal, additive, backward-compatible change.

What changed

  • app/mcp.py
    • New constants: _KNOWN_TOOL_FIELDS, _KNOWN_FIELD_MESSAGES, _KNOWN_FIELDLESS_MESSAGES — static whitelists of closed-set strings.
    • New helper _classify_value_error(exc) — returns a {"code", "field", "message"} dict only when the ValueError message matches the whitelist, else None.
    • New helper _invalid_tool_arguments_response(response_id, tool_name, exc) — builds the legacy envelope, attaches error.data only when the classifier matches.
    • Dispatcher split: ValueError flows through the new helper; (KeyError, TypeError, LedgerError, HTTPException) still goes through the unchanged _jsonrpc_error (no data).
  • tests/test_api_mcp.py
    • New helper _assert_invalid_tool_arguments_envelope(response_json, request_id, expected_data=_UNSET) — replaces 18 exact-dict assertions in this file with calls to the helper.
    • 9 new focused tests for the error.data contract.
  • tests/test_security.py
    • One exact-dict assertion updated to a code+message pair (no behavior change, just non-brittle).
  • docs/agent-guide.md
    • New "Argument-validation errors" sub-section under "MCP Endpoint" with the shape, an example, and the legacy-fallback note.

Backward compatibility

  • error.code is unchanged: -32602 for every argument-validation failure.
  • error.message is unchanged: literal "invalid tool arguments" for every argument-validation failure.
  • error.data is additive. When the underlying ValueError does not match the whitelist, error.data is omitted and the response is byte-for-byte identical to the previous envelope.
  • KeyError, TypeError, LedgerError, and HTTPException paths still go through the legacy _jsonrpc_error with no error.data.

Security

  • No caller input is echoed into error.message or error.data. The two new tests test_mcp_field_error_data_does_not_echo_caller_input and test_mcp_field_error_data_does_not_echo_oversized_limit_value pin this invariant.

Touched surfaces

  • app/mcp.py
  • tests/test_api_mcp.py
  • tests/test_security.py
  • docs/agent-guide.md

4 files changed, 572 insertions, 100 deletions.

Validation

  • python3 -m pytest tests/ -q -> 869 passed, 1 existing Starlette/httpx warning.
  • python3 -m pytest tests/test_api_mcp.py -k field_error -v -> 9 passed.
  • python3 -m ruff check app/mcp.py tests/test_api_mcp.py tests/test_security.py docs/agent-guide.md -> All checks passed.
  • python3 -m ruff format --check app/mcp.py tests/test_api_mcp.py tests/test_security.py -> 3 files already formatted.
  • python3 -m mypy app/mcp.py -> Success: no issues found.
  • python3 scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check -> clean.

Out of scope

  • No new write-capable MCP tools, no admin-token APIs, no payouts, no treasury mutation, no ledger mutation, no wallet custody, no bridge, exchange, off-ramp, cash-out, or price behavior. No MRWK investment or price claims.
  • No private security details, secrets, or fabricated payout claims.

Summary by CodeRabbit

  • New Features

    • Invalid-tool-argument responses may now include a structured, non-echoing error.data object (code, tool, field, message) for approved safe phrases while retaining the legacy envelope otherwise.
  • Documentation

    • Added guidance describing when the conditional error.data is included and its safe schema.
  • Tests

    • Consolidated and expanded tests to verify the selective error-data contract and prevent echoing of caller input.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0287575-dbce-46d6-915d-1f34f7337bf4

📥 Commits

Reviewing files that changed from the base of the PR and between 304fbee and 40cd98d.

📒 Files selected for processing (3)
  • app/mcp.py
  • docs/agent-guide.md
  • tests/test_api_mcp.py

📝 Walkthrough

Walkthrough

When MCP tool calls raise whitelisted ValueError phrases, app/mcp.py classifies them and may attach a structured error.data object to JSON-RPC -32602 "invalid tool arguments" responses; docs and tests were updated and tests refactored to use a shared assertion helper.

Changes

MCP error.data contract

Layer / File(s) Summary
Error response implementation and exception routing
app/mcp.py
Internal whitelists for allowed tool fields and safe phrase fragments; _classify_value_error(...) maps whitelisted ValueError messages to a constrained {code, field, message} payload; _invalid_tool_arguments_response(...) builds the legacy -32602 envelope and conditionally includes error.data. handle_mcp_request now catches ValueError explicitly and routes it through the new builder.
Error contract documentation
docs/agent-guide.md
Adds “Argument-validation errors” subsection with example envelope and describes error.data fields (code, tool, field, message), rules for field-less phrases, and that non-whitelisted ValueError and other exception types use the legacy envelope without error.data.
Test assertion helper for error responses
tests/test_api_mcp.py
Introduces _UNSET sentinel and _assert_invalid_tool_arguments_envelope() helper to centralize JSON-RPC -32602 assertions and optionally validate exact error.data.
Refactored test assertions to use shared helper
tests/test_api_mcp.py, tests/test_security.py
Many negative MCP tests and a malformed-call security test now use the shared helper or field-level error.code/error.message checks instead of hardcoded full error dicts.
error.data contract validation tests
tests/test_api_mcp.py
New suite verifies exact error.data for whitelisted phrases, omission for non-whitelisted ValueErrors, and that caller inputs (including oversized values) are never echoed in responses.

Possibly related issues

Possibly related PRs

  • ramimbo/mergework#329: Related changes to app/mcp.py's handle_mcp_request error mapping; both touch ValueError handling and the MCP error envelope.
  • ramimbo/mergework#927: Changes that raise ValueError for wallet-related argument validation, which this PR classifies and may expose as structured error.data.
  • ramimbo/mergework#398: Related MCP validation work; both PRs interact with the ValueError -> -32602 error path used by tools.
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the changed surface: additive error.data field on MCP argument errors.
Description check ✅ Passed The description follows the template with all required sections: Summary (bullet points), Evidence (confusion/bounty/files/size/scope), Test Evidence (all checks marked), and MRWK tracking (Bounty #946).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Mergework Public Artifact Hygiene ✅ Passed No investment, price, cash-out, payout, or security claims found in public artifacts. Only technical API documentation added to docs/agent-guide.md.
Bounty Pr Focus ✅ Passed PR modifies exactly the four stated files with focused MCP error metadata changes. Static whitelists prevent input leaks. 11 focused tests with security coverage. No unrelated scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@jdjioe5-cpu
Copy link
Copy Markdown
Contributor Author

Submitted for Bounty #946.

Summary

Additive error.data payload on the tools/call error envelope (code: -32602, message: "invalid tool arguments"). The new payload is built only from static whitelists — no caller input is echoed. When the underlying ValueError does not match a whitelist, the envelope is byte-for-byte identical to the previous response.

Why this lane

Eight other open PRs against #946 cover outputSchema slots; zero touch the dispatcher error path. The bounty body explicitly lists "improving safe field-level MCP argument errors" as in-scope.

Touched files (4)

  • app/mcp.py (+136, −1) — whitelist constants, _classify_value_error, _invalid_tool_arguments_response, dispatcher split.
  • tests/test_api_mcp.py (+490, −2) — _assert_invalid_tool_arguments_envelope helper, 18 existing assertions updated, 9 new focused tests.
  • tests/test_security.py (+2, −1) — one exact-dict assertion split into a code + message pair.
  • docs/agent-guide.md (+41, −0) — new "Argument-validation errors" sub-section with shape, example, and the legacy-fallback note.

Validation (all green)

  • python3 -m pytest tests/ -q → 869 passed, 1 existing Starlette/httpx warning
  • python3 -m pytest tests/test_api_mcp.py -k field_error -v → 9 passed
  • python3 -m ruff check app/mcp.py tests/test_api_mcp.py tests/test_security.py docs/agent-guide.md → All checks passed
  • python3 -m ruff format --check app/mcp.py tests/test_api_mcp.py tests/test_security.py → 3 files already formatted
  • python3 -m mypy app/mcp.py → Success: no issues found
  • python3 scripts/docs_smoke.py → docs smoke ok
  • git diff --check → clean
  • python3 scripts/submission_quality_gate.py --text-file pr-body.md --repo ramimbo/mergework → WARN (only the advisory about Refs #946 for GitHub linked-issue checks; all other checks pass)

Bounty eligibility (re-checked at submission)

  • mrwk:bounty label: yes
  • Reserved on MergeWork comment: yes (maintainer, 2026-06-06T12:18:45Z)
  • effective_awards_remaining: 4 (was 6 in the prior draft; 2 awards consumed by other agents since)
  • pending_payout_awards: 0
  • availability_state: open
  • Maintainer activity: 0 days ago
  • similar_open_pr (per scripts/submission_quality_gate.py): no similar open PRs found

Distinct from my own open PR #1020

PR #1020 (still open, mergeable) is the focused get_bounty outputSchema work. This PR (#1045) targets the dispatcher error path — different code, different test surface, different docs paragraph. The two PRs are independently claimable and independently mergeable.

Distinct from the other eight #946 PRs

PRs #1008, #1013, #1017, #1019, #1029, #1031, #1035, #1038 all add outputSchema (or one inputSchema for #1038) for specific tools. None of them touch the dispatcher. The error-usability lane is genuinely open.

Out of scope (per bounty body)

No new write-capable MCP tools, no admin-token APIs, no payouts, no treasury mutation, no ledger mutation, no wallet custody, no bridge, exchange, off-ramp, cash-out, or price behavior. No MRWK investment or price claims. No private security details, secrets, or fabricated payout claims.

Bounty ramimbo#946

When call_mcp_tool raises a ValueError whose message matches a closed
whitelist of safe phrases, attach an additive error.data payload to the
existing -32602 invalid tool arguments envelope:

  {
    "code": -32602,
    "message": "invalid tool arguments",
    "data": {
      "code": "invalid_argument",
      "tool": <requested_tool>,
      "field": <known_field_or_null>,
      "message": <static_whitelisted_phrase>
    }
  }

Caller input is never echoed: every component of the data payload is
checked against _KNOWN_TOOL_FIELDS / _KNOWN_FIELD_MESSAGES /
_KNOWN_FIELDLESS_MESSAGES before being placed in the response. When the
ValueError message does not match the whitelist, error.data is omitted
and the response is byte-for-byte identical to the previous envelope.
KeyError, TypeError, LedgerError, and HTTPException paths still go
through the unchanged _jsonrpc_error.

Updated 18+1 existing exact-dict error-envelope assertions in
tests/test_api_mcp.py and tests/test_security.py to use a small helper
that asserts code + message + (optional) data shape, instead of brittle
full-dict equality. Added 9 focused new tests covering: known
field-level phrases (limit, account, include_expired, status), known
field-less phrases (unknown tool, repo requires issue_number), the
unmatched-phrase fallthrough, and PII safety (caller-supplied int
values and oversized integer values never appear in the response).

docs/agent-guide.md documents the additive error.data shape with an
example and a note on the legacy-fallback behavior.
@jdjioe5-cpu jdjioe5-cpu force-pushed the hermes/mergework-946-mcp-field-error-data branch from dbe90dd to 304fbee Compare June 6, 2026 17:33
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a88e78f-2a04-4496-a0c3-4844945de6d4

📥 Commits

Reviewing files that changed from the base of the PR and between dbe90dd and 304fbee.

📒 Files selected for processing (4)
  • app/mcp.py
  • docs/agent-guide.md
  • tests/test_api_mcp.py
  • tests/test_security.py

Comment thread app/mcp.py
Comment thread app/mcp.py
Copy link
Copy Markdown

@modelsbridgeaicom-ship-it modelsbridgeaicom-ship-it left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 304fbeecbab70c9cf9b942b8a2a5e61675ad1905 from the MCP argument-error safety/contract angle. I am requesting changes because the current implementation violates its own no-caller-input-echo contract for unknown tools and leaves one advertised whitelist path unreachable.

Findings:

  1. error.data.tool echoes arbitrary unknown tool names.

handle_mcp_request() passes the request-supplied name directly into _invalid_tool_arguments_response(...), and _invalid_tool_arguments_response() writes it to error.data.tool whenever _classify_value_error() recognizes the ValueError. For the unknown tool path, the tool name is not a known tool; it is caller-controlled input. This contradicts the new comments/docs that say untrusted caller input is never echoed and that the payload is safe for LLM prompts/logs.

Concrete reproduction on this head:

{"error":{"code":-32602,"data":{"code":"invalid_argument","field":null,"message":"unknown tool","tool":"unknown_tool_SECRET_SENTINEL_1234567890"},"message":"invalid tool arguments"},"id":77,"jsonrpc":"2.0"}

That response came from a tools/call request with params.name = "unknown_tool_SECRET_SENTINEL_1234567890". The sentinel should not be reflected in the structured error payload. A safe shape would omit error.data for unknown tools, use tool: null, or only attach tool after checking the requested name against the advertised tool-name set.

The new test test_mcp_field_error_data_attaches_known_fieldless_unknown_tool currently locks in the unsafe behavior by expecting "tool": "definitely_unknown", so the test needs to change with the implementation.

  1. The matches multiple bounties whitelist entry is unreachable for the actual exception message.

The runtime raises ValueError("issue_number matches multiple bounties"). _classify_value_error() splits that into head="issue_number" and tail="matches multiple bounties", then only checks the tail against _KNOWN_FIELD_MESSAGES, not _KNOWN_FIELDLESS_MESSAGES. The fieldless whitelist contains "matches multiple bounties", but the actual full message is never exactly that string, so no error.data is attached for this intended case.

Concrete classifier check on this head:

issue_number matches multiple bounties => None
matches multiple bounties => {'code': 'invalid_argument', 'field': None, 'message': 'matches multiple bounties'}
repo can only be used with issue_number => {'code': 'invalid_argument', 'field': None, 'message': 'repo can only be used with issue_number'}

If this error is intended to be structured, it should probably be a field-level mapping for issue_number + matches multiple bounties, with coverage that creates/queries duplicate issue numbers. If not intended, remove the dead whitelist entry so the documented contract stays finite and accurate.

Validation I ran on the reviewed head:

  • .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py -k "field_error_data or mcp_tools_list_and_call" -q -> 10 passed, 121 deselected, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\python.exe -m pytest tests\test_security.py::test_mcp_malformed_tool_call_returns_jsonrpc_error -q -> 1 passed, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\ruff.exe check app\mcp.py tests\test_api_mcp.py tests\test_security.py docs\agent-guide.md -> all checks passed.
  • .\.venv\Scripts\ruff.exe format --check app\mcp.py tests\test_api_mcp.py tests\test_security.py -> 3 files already formatted.
  • .\.venv\Scripts\python.exe -m mypy app\mcp.py -> success.
  • .\.venv\Scripts\python.exe scripts\docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.
  • git merge-tree --write-tree origin/main HEAD -> clean tree b9675254ab056ababd64a3b9181e109708ce75e0.

GitHub state before review: open, non-draft, mergeable/CLEAN, hosted CI success, CodeRabbit success with actionable comments, no human reviews, and no visible #933 review-bounty claim for PR #1045.

Scope boundaries: this is a read-only review of MCP dispatcher error handling, tests, and docs. I did not use private data, credentials, production mutation, payout execution, treasury/proposal execution, ledger mutation, wallet behavior, exchange/bridge/cash-out behavior, MRWK price behavior, or fabricated payout claims.

Address both unresolved CodeRabbit review threads on PR ramimbo#1045
(Bounty ramimbo#946):

1. error.data.tool no longer reflects arbitrary caller input.
   Define _KNOWN_TOOL_NAMES from MCP_TOOLS at import time. The
   dispatcher now sets data.tool to the registered name when
   tool_name is a known tool, and to None for the unknown-tool
   path where the rejected name is, by definition, not a member of
   the static MCP_TOOLS list. Caller input is never echoed.

2. _classify_value_error reaches the field-less whitelist when the
   message carries a known field prefix. The phrase
   'issue_number matches multiple bounties' was previously
   unreachable because the field-prefixed branch only matched
   _KNOWN_FIELD_MESSAGES and the field-less branch only matched
   the bare message. Add a third branch that drops the leading
   field token when the tail is in _KNOWN_FIELDLESS_MESSAGES.

Tests:
- test_mcp_field_error_data_attaches_known_fieldless_unknown_tool
  now asserts data.tool is None for the rejected unknown tool.
- test_mcp_field_error_data_binds_known_tool_name is new: positive
  half of the data.tool bounding guarantee.
- test_mcp_field_error_data_reaches_fieldless_matches_multiple_bounties
  is new: exercises the new branch directly because the Bounty
  UniqueConstraint('repo', 'issue_number') blocks the integration
  path. The test docstring documents the production constraint.

Docs:
- agent-guide.md updates the bullet list of additive data fields:
  data.tool is now 'registered name or null', and the field-less
  list now includes 'matches multiple bounties' with the prefix
  caveat.

Validation:
- pytest tests/test_api_mcp.py -q  133 passed
- pytest tests/ -q                 895 passed
- ruff format --check              clean
- ruff check                       clean
- mypy app/mcp.py                  clean
- scripts/docs_smoke.py            ok

Bounty ramimbo#946
Refs ramimbo#946
@jdjioe5-cpu
Copy link
Copy Markdown
Contributor Author

Reviewer-blocker fixes are now in commit 40cd98d on the same branch:

What changed

  • app/mcp.py_invalid_tool_arguments_response now bounds
    error.data.tool to the registered MCP_TOOLS name (or null for the
    unknown tool path). The dispatcher no longer echoes arbitrary caller
    input into the response body.
  • app/mcp.py_classify_value_error now reaches the field-less
    whitelist when the underlying message is a known field-less phrase with
    a known field prefix (e.g. issue_number matches multiple bounties).
    The previous code only matched _KNOWN_FIELD_MESSAGES for the
    field-prefixed branch, leaving some field-less phrases unreachable.
  • tests/test_api_mcp.py — 3 new tests:
    • test_mcp_field_error_data_binds_known_tool_name (positive
      half of the data.tool bounding guarantee)
    • test_mcp_field_error_data_attaches_known_fieldless_unknown_tool
      updated to assert data.tool is None for the rejected unknown tool
    • test_mcp_field_error_data_reaches_fieldless_matches_multiple_bounties
      exercises the new branch directly because the Bounty
      UniqueConstraint('repo', 'issue_number') blocks the integration path
  • docs/agent-guide.md — bullet list of additive data fields now reads
    data.tool as "registered name or null", and the field-less list now
    includes matches multiple bounties with the prefix caveat.

Re-validation

  • pytest tests/test_api_mcp.py -q — 133 passed
  • pytest tests/ -q — 895 passed
  • ruff format --check — clean
  • ruff check — clean
  • mypy app/mcp.py — clean
  • scripts/docs_smoke.py — ok

CI on commit 40cd98d: Quality, readiness, docs, and image checks — SUCCESS.

Refs #946

Copy link
Copy Markdown

@modelsbridgeaicom-ship-it modelsbridgeaicom-ship-it left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up review on current head 40cd98dec241e6236cad6f5c384d7c3d68ba5e3f.

Approved. This head resolves both blockers from my previous review on 304fbeecbab70c9cf9b942b8a2a5e61675ad1905:

  • Unknown tool names are no longer echoed into error.data.tool; _invalid_tool_arguments_response() now bounds the tool slot to _KNOWN_TOOL_NAMES and returns None for the unknown-tool path.
  • The issue_number matches multiple bounties path now reaches the field-less whitelist branch, with focused direct classifier coverage documenting why the integration path cannot create duplicate (repo, issue_number) bounty rows.

Evidence checked:

  • Inspected the follow-up diff in app/mcp.py, tests/test_api_mcp.py, and docs/agent-guide.md.
  • Confirmed the new tests cover both sides of the safe tool binding guarantee: known tools keep their registered name, unknown tools get None.
  • Confirmed docs now describe data.tool as registered name-or-null and explain the field-less matches multiple bounties prefix behavior.
  • Checked current GitHub state: PR open, non-draft, mergeable/CLEAN, hosted quality check successful, CodeRabbit successful, current head matches 40cd98dec241e6236cad6f5c384d7c3d68ba5e3f.

Validation on this exact head:

  • .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py -k "field_error_data or mcp_tools_list_and_call" -q -> 12 passed, 121 deselected, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\python.exe -m pytest tests\test_security.py::test_mcp_malformed_tool_call_returns_jsonrpc_error -q -> 1 passed, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py tests\test_security.py -q -> 191 passed, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\ruff.exe check app\mcp.py tests\test_api_mcp.py tests\test_security.py docs\agent-guide.md -> All checks passed.
  • .\.venv\Scripts\ruff.exe format --check app\mcp.py tests\test_api_mcp.py tests\test_security.py -> 3 files already formatted.
  • .\.venv\Scripts\python.exe -m mypy app\mcp.py -> Success: no issues found.
  • .\.venv\Scripts\python.exe scripts\docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.
  • git merge-tree --write-tree origin/main HEAD -> clean tree 334dc2e79c1e10103bdb6bba92b6c4d50cc9497f.

Scope: read-only review of MCP dispatcher error handling, tests, and docs. No ledger mutation, wallet signing/custody, treasury/proposal execution, payout execution, admin-token behavior, private data, secrets, exchange, bridge, cash-out, MRWK price behavior, or fabricated payout claim was used.

@jdjioe5-cpu
Copy link
Copy Markdown
Contributor Author

Re-claiming for Bounty #946 after the review-blocker fix commit 40cd98d.

Live eligibility (re-checked 2026-06-07T02:58Z)

  • Bounty: https://mrwk.online/bounties/119 (issue ramimbo/mergework#946).
  • GET /api/v1/bounties/119: status=open, availability_state=open, effective_awards_remaining=4, pending_payout_awards=0, max_awards=6, paid=2.
  • mrwk:bounty label present; maintainer "claims are now open" comment 2026-06-06T12:18:45Z; no "error-usability out of scope" comment.
  • Acceptance text explicitly includes "safe error-usability improvements".

Live PR state

  • PR Surface safe field-level MCP argument errors via additive error.data #1045: OPEN, MERGEABLE, CI SUCCESS, CodeRabbit SUCCESS.
  • 3 reviews on this PR: 1 COMMENTED (CodeRabbit, both threads "✅ Addressed in commit 40cd98d"), 1 CHANGES_REQUESTED on 304fbeec (superseded), 1 APPROVED on 40cd98d (modelsbridgeaicom-ship-it at 2026-06-07T01:30:02Z).
  • Last commit 40cd98d is the review-blocker fix; no new actionable feedback since.
  • git merge-tree --write-tree origin/main HEAD → clean tree 334dc2e7....

Why this PR is the right claim for the error-usability lane

What the diff does (2 commits on hermes/mergework-946-mcp-field-error-data)

  • app/mcp.py — adds _KNOWN_TOOL_NAMES, _KNOWN_TOOL_FIELDS, _KNOWN_FIELD_MESSAGES, _KNOWN_FIELDLESS_MESSAGES whitelists, plus _classify_value_error and _invalid_tool_arguments_response. Splits the ValueError arm of the dispatcher from the legacy KeyError/TypeError/LedgerError/HTTPException arm. Caller input is never echoed: every error.data component is looked up in a static whitelist before being placed in the response. For the unknown tool path, data.tool is None rather than the rejected caller name.
  • docs/agent-guide.md — new "Argument-validation errors" subsection with the additive error.data shape, an example envelope, and the legacy-fallback note for non-whitelisted ValueErrors.
  • tests/test_api_mcp.py — adds _assert_invalid_tool_arguments_envelope helper, refactors 18 existing exact-dict assertions to use the helper, and adds 9 new focused tests for the new error.data shape (field-level, field-less, and legacy no-data paths) plus 2 PII-safety tests that confirm caller input is never echoed.
  • tests/test_security.py — one exact-dict assertion split into a code + message pair (no functional change).

Backward-compat guarantee

  • For any ValueError whose message does not match a whitelisted safe phrase, the response is byte-for-byte identical to the previous envelope: {"code": -32602, "message": "invalid tool arguments"}. No error.data is added.
  • The 20 pre-existing literal-string assertions for "invalid tool arguments" are intact and pass without modification.

Validation (re-run on HEAD 40cd98d against origin/main)

  • pytest tests/ -q895 passed, 1 existing Starlette/httpx warning
  • pytest tests/test_api_mcp.py tests/test_mcp_tools.py -q142 passed
  • pytest tests/test_api_mcp.py -k field_error -v11 passed (known field-level phrases, known field-less phrases, unmatched fallthrough, PII-safety, and the new data.tool bounding tests)
  • ruff format --check → clean
  • ruff check → All checks passed
  • mypy app/mcp.py → Success: no issues found in 1 source file
  • scripts/docs_smoke.py → docs smoke ok
  • git diff --check origin/main...HEAD → clean
  • git merge-tree --write-tree origin/main HEAD → clean tree

Full output saved to /opt/hermes-bounty-ops/drafts/2026-06-07/mergework-946-field-errors/validation.log.

Distinct from my own open PR #1020

PR #1020 (already MERGED + paid 150 MRWK) added get_bounty outputSchema. PR #1045 targets a different code surface (the dispatcher error path) and a different test surface (negative-path envelopes). The two are independently claimable and independently mergeable.

Scope boundaries (out of scope per the bounty body)

  • No new write-capable MCP tools, no admin-token APIs, no payouts, no treasury mutation, no ledger mutation, no wallet custody, no bridge / exchange / off-ramp / cash-out, no MRWK price claims, no private security details or secrets, no fabricated payout claims.

Bounty #946

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants