Skip to content

[codex] Structure MCP work-proof selector errors#374

Open
rounaksalim95 wants to merge 1 commit into
ramimbo:mainfrom
rounaksalim95:codex/bounty-315-structured-error-guidance
Open

[codex] Structure MCP work-proof selector errors#374
rounaksalim95 wants to merge 1 commit into
ramimbo:mainfrom
rounaksalim95:codex/bounty-315-structured-error-guidance

Conversation

@rounaksalim95
Copy link
Copy Markdown
Contributor

@rounaksalim95 rounaksalim95 commented May 26, 2026

Summary

  • Return JSON structuredContent for submit_work_proof(format: "json") when a bounty selector is unknown or ambiguous.
  • Preserve existing text-mode behavior and the existing JSON-RPC invalid-argument path for invalid selector types/values.
  • Include machine-readable error codes and matching bounty summaries for ambiguous issue-number lookups so agents can retry with a concrete bounty_id instead of scraping prose.

Why this is distinct

Bounty #315 already has accepted work for the core JSON guidance path and availability fields. This PR targets the remaining selector failure path: JSON callers can now handle missing or ambiguous bounty selectors with structured fields instead of plain text or generic tool errors.

Validation

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_unknown_bounty_selector tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_ambiguous_issue_number tests/test_api_mcp.py::test_mcp_submit_work_proof_reports_unknown_bounty tests/test_api_mcp.py::test_mcp_submit_work_proof_rejects_ambiguous_issue_number -q -> 4 passed.
  • uv run --extra dev python -m pytest tests/test_api_mcp.py -q -> 78 passed.
  • uv run --extra dev python -m pytest -q -> 337 passed.
  • uv run --extra dev ruff check . -> passed.
  • uv run --extra dev ruff format --check . -> 48 files already formatted.
  • uv run --extra dev python -m mypy app -> success.
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check -> clean.

Bounty #315

No private keys, seed material, secrets, deployment credentials, private vulnerability details, payout credentials, or MRWK price claims are included.

Summary by CodeRabbit

  • New Features

    • Work proof submission tool now returns structured JSON error responses with error codes and context information when bounty selection fails or is ambiguous.
  • Documentation

    • Updated work proof submission tool documentation to clarify optional parameters and provide guidance on using JSON format for machine-readable error handling.
  • Tests

    • Added test coverage for structured error responses in work proof submission scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

The PR adds structured JSON error responses to the MCP submit_work_proof tool. When format=json is specified and bounty selection fails (not found) or is ambiguous (multiple matches), the tool now returns a consistent error payload with error codes, context fields, optional matching bounty details, and safety rules instead of plain strings or raised exceptions.

Changes

Structured JSON Error Handling for Bounty Selection

Layer / File(s) Summary
Structured error helper and submit_work_proof integration
app/main.py
Introduces work_proof_selection_error_json helper that builds consistent error payloads with error codes, bounty context, optional matching bounties, and fixed safety/submission metadata. Updates submit_work_proof to return structured JSON errors for bounty_id not found and issue_number not found or ambiguous cases when format=json.
Tests for unknown and ambiguous bounty selection
tests/test_api_mcp.py
Validates structured JSON output for unknown bounty selector (issue_number matches no bounty with bounty_not_found error code) and ambiguous selector (multiple matches with ambiguous_issue_number code and matching_bounties array), asserting error payloads and matching bounty metadata.
Documentation of optional format parameter
docs/agent-guide.md
Documents that bounty_id, issue_number, and format are optional in submit_work_proof and clarifies that format: "json" should be used when structured, machine-readable content is needed for ambiguous bounty selection.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

  • ramimbo/mergework#339: Introduces the structured JSON schema (availability/can_submit/warnings) that this PR builds upon for error responses.
  • ramimbo/mergework#287: Modifies the underlying submit_work_proof bounty selection logic (bounty_id/issue_number not found and ambiguous match detection) that this PR extends with structured error payloads.
  • ramimbo/mergework#317: Also enhances submit_work_proof JSON format handling and structured content responses in the MCP flow.

Suggested labels

mrwk:accepted, mrwk:paid

Suggested reviewers

  • ayskobtw-lil
  • weilixiong
  • TateLyman
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: structuring error responses for MCP work-proof selector failures.
Description check ✅ Passed The description is comprehensive and follows the template structure with summary, evidence of why distinct, and validation details.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown
Contributor

@ayskobtw-lil ayskobtw-lil left a comment

Choose a reason for hiding this comment

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

No blockers found in this focused structured-error guidance change.

Evidence checked:

  • Inspected app/main.py around submit_work_proof and the new work_proof_selection_error_json() helper.
  • Verified format: "json" now returns structuredContent plus matching JSON text for unknown bounty_id / issue_number selectors instead of only plain text.
  • Verified ambiguous issue-number JSON responses include machine-readable error.code, issue_number, can_submit: false, and a matching_bounties summary with actual bounty ids, repos, issue URLs, status, and remaining awards so agents can retry with a concrete bounty_id.
  • Checked that existing text-mode behavior is preserved for unknown bounty selectors, and invalid argument combinations still use the existing JSON-RPC invalid-argument path.
  • Inspected the small docs/agent-guide.md addition and the targeted MCP regressions in tests/test_api_mcp.py.

Validation on PR head 2d54ef5:

  • python -m pytest tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_unknown_bounty_selector tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_ambiguous_issue_number tests/test_api_mcp.py::test_mcp_submit_work_proof_reports_unknown_bounty tests/test_api_mcp.py::test_mcp_submit_work_proof_rejects_ambiguous_issue_number -q -> 4 passed
  • python -m ruff check app/main.py tests/test_api_mcp.py docs/agent-guide.md -> passed
  • python -m mypy app/main.py -> passed
  • git diff --check origin/main...HEAD -> clean
  • Hosted Quality/readiness/docs/image check is green; CodeRabbit was still pending when I reviewed.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/main.py`:
- Around line 1824-1829: The query that builds bounties uses a hard limit of 2
(session.scalars(select(Bounty).where(Bounty.issue_number ==
issue_number).order_by(Bounty.id.desc()).limit(2)).all()), which can hide valid
ambiguous candidates; remove the .limit(2) so the full set of matching rows for
the given issue_number is returned (i.e., call
session.scalars(select(Bounty).where(Bounty.issue_number ==
issue_number).order_by(Bounty.id.desc())).all()) and then build
matching_bounties from that complete result set to ensure all ambiguous matches
are included in JSON responses.

In `@tests/test_api_mcp.py`:
- Around line 1545-1591: Add a companion test case in
test_mcp_submit_work_proof_structures_unknown_bounty_selector that posts the
same JSON-RPC call but with "arguments": {"bounty_id": <missing_id>, "format":
"json"} (e.g., 999) instead of issue_number, using the same
TestClient/create_app setup; extract response =
client.post(...).json()["result"], check response["structuredContent"] and
response["content"][0]["text"] parsed as JSON, and assert the structured dict
mirrors the existing expected payload for a missing bounty (status
"bounty_not_found", availability "unknown_without_bounty", error
{"code":"bounty_not_found",...}, matching_bounties empty, etc.) to lock the
bounty_id-not-found JSON branch. Ensure you reference the existing test function
name when adding the companion assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eb048370-f83c-4834-8b6c-b8845d64a853

📥 Commits

Reviewing files that changed from the base of the PR and between ac99d79 and 2d54ef5.

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

Comment thread app/main.py
Comment on lines 1824 to 1829
bounties = session.scalars(
select(Bounty)
.where(Bounty.issue_number == positive_int_arg("issue_number"))
.where(Bounty.issue_number == issue_number)
.order_by(Bounty.id.desc())
.limit(2)
).all()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return all ambiguous matches for JSON responses.

The query is capped at 2 rows before building matching_bounties, so ambiguous JSON responses can hide valid candidate bounty_ids when more than two matches exist.

💡 Suggested adjustment
-                bounties = session.scalars(
-                    select(Bounty)
-                    .where(Bounty.issue_number == issue_number)
-                    .order_by(Bounty.id.desc())
-                    .limit(2)
-                ).all()
+                base_query = (
+                    select(Bounty)
+                    .where(Bounty.issue_number == issue_number)
+                    .order_by(Bounty.id.desc())
+                )
+                bounties = session.scalars(
+                    base_query if output_format == "json" else base_query.limit(2)
+                ).all()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/main.py` around lines 1824 - 1829, The query that builds bounties uses a
hard limit of 2 (session.scalars(select(Bounty).where(Bounty.issue_number ==
issue_number).order_by(Bounty.id.desc()).limit(2)).all()), which can hide valid
ambiguous candidates; remove the .limit(2) so the full set of matching rows for
the given issue_number is returned (i.e., call
session.scalars(select(Bounty).where(Bounty.issue_number ==
issue_number).order_by(Bounty.id.desc())).all()) and then build
matching_bounties from that complete result set to ensure all ambiguous matches
are included in JSON responses.

Comment thread tests/test_api_mcp.py
Comment on lines +1545 to +1591
def test_mcp_submit_work_proof_structures_unknown_bounty_selector(
sqlite_url: str,
) -> None:
create_schema(sqlite_url)
with session_scope(sqlite_url) as session:
ensure_genesis(session)

client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))

response = client.post(
"/mcp",
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "submit_work_proof",
"arguments": {"issue_number": 999, "format": "json"},
},
},
).json()["result"]

structured = response["structuredContent"]
assert json.loads(response["content"][0]["text"]) == structured
assert structured == {
"bounty_id": None,
"issue_number": 999,
"status": "bounty_not_found",
"availability": "unknown_without_bounty",
"can_submit": False,
"availability_warnings": ["bounty not found"],
"awards_remaining": None,
"reward_mrwk": None,
"repository": None,
"issue_url": None,
"acceptance": None,
"error": {"code": "bounty_not_found", "message": "bounty not found"},
"matching_bounties": [],
"submission_format": (
"Retry with an unambiguous bounty_id before preparing work-proof evidence."
),
"safety_rules": [
"Do not include private keys, seed material, secrets, deployment "
"credentials, private vulnerability details, or price claims."
],
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add JSON coverage for unknown bounty_id selection.

This covers the issue_number not-found JSON branch, but the PR also changed the bounty_id not-found JSON branch and it isn’t asserted yet. Add a companion case for {"bounty_id": <missing>, "format": "json"} to lock that contract.

As per coding guidelines, tests/**/*.py: Add or update tests for changed behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_api_mcp.py` around lines 1545 - 1591, Add a companion test case in
test_mcp_submit_work_proof_structures_unknown_bounty_selector that posts the
same JSON-RPC call but with "arguments": {"bounty_id": <missing_id>, "format":
"json"} (e.g., 999) instead of issue_number, using the same
TestClient/create_app setup; extract response =
client.post(...).json()["result"], check response["structuredContent"] and
response["content"][0]["text"] parsed as JSON, and assert the structured dict
mirrors the existing expected payload for a missing bounty (status
"bounty_not_found", availability "unknown_without_bounty", error
{"code":"bounty_not_found",...}, matching_bounties empty, etc.) to lock the
bounty_id-not-found JSON branch. Ensure you reference the existing test function
name when adding the companion assertions.

Copy link
Copy Markdown
Contributor

@tolga-tom-nook tolga-tom-nook left a comment

Choose a reason for hiding this comment

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

Requesting one focused change before this is safe to merge.

I inspected current head 2d54ef5 for the #315 structured MCP work-proof selector path, specifically app/main.py, tests/test_api_mcp.py, and docs/agent-guide.md.

Evidence checked:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_unknown_bounty_selector tests/test_api_mcp.py::test_mcp_submit_work_proof_structures_ambiguous_issue_number -q -> 2 passed.
  • uv run --extra dev python -m ruff check app/main.py tests/test_api_mcp.py -> passed.

Blocker:

  • The JSON ambiguous-selector branch still builds matching_bounties from a query capped with .limit(2) in app/main.py. The new structured response tells agents to retry with an unambiguous bounty_id, but when more than two bounties share an issue_number, it can omit valid candidate IDs from matching_bounties. That makes the new machine-readable retry guidance incomplete for exactly the ambiguity case it is meant to solve.
  • The added ambiguity test only creates two matches, so it cannot catch the truncation. Please either remove/raise the cap for the structured JSON path or make the response explicitly indicate truncation/next lookup, and add a three-match regression asserting agents can see all intended candidates (or know the list is truncated).

The unknown-selector JSON behavior itself looks consistent; this is limited to the ambiguous issue-number JSON response contract.

@ramimbo ramimbo added the mrwk:needs-info More information needed label May 26, 2026
@ramimbo
Copy link
Copy Markdown
Owner

ramimbo commented May 26, 2026

Maintainer note after #315 and #378 filled: the structured MCP work-proof guidance rounds are closed/filled. This PR is not accepted or paid as-is. If a fresh MCP bounty opens later, rebase against main, address existing review feedback, and reference the new bounty issue.

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

Labels

mrwk:needs-info More information needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants