Skip to content

Refs #406: Add MCP input schemas for public tools#488

Open
MINBBBIGcode wants to merge 1 commit into
ramimbo:mainfrom
MINBBBIGcode:codex/mcp-input-schemas
Open

Refs #406: Add MCP input schemas for public tools#488
MINBBBIGcode wants to merge 1 commit into
ramimbo:mainfrom
MINBBBIGcode:codex/mcp-input-schemas

Conversation

@MINBBBIGcode
Copy link
Copy Markdown
Contributor

@MINBBBIGcode MINBBBIGcode commented May 27, 2026

Summary

  • add inputSchema metadata for the public MCP tools that previously only exposed name/description
  • document required arguments, defaults, enum values, and bounds for list/detail/account/wallet/ledger/proof tools
  • extend the MCP tools/list regression so every advertised tool includes a schema

Refs #406.

Evidence

The public MCP tools/list response currently lists 10 tools, but only submit_work_proof advertises an inputSchema. Public tools such as list_bounty_attempts, get_bounty, get_balance, get_wallet, get_ledger_entry, and get_proof accept arguments, but agents cannot discover the accepted shape from the tool list. A live public smoke check reproduced this with unauthenticated JSON-RPC calls and showed list_bounty_attempts works with bounty_id while repo/issue arguments return invalid tool arguments without schema guidance.

This PR keeps handler behavior unchanged and only exposes the existing argument contracts through MCP metadata.

Validation

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_tools_list_and_call tests/test_api_mcp.py::test_mcp_list_bounty_attempts_reports_active_and_expired tests/test_api_mcp.py::test_mcp_get_proof_returns_public_proof_details -q -> 3 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_api_mcp.py -q -> 76 passed
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok
  • uv run --extra dev python -m mypy app -> success
  • git diff --check -> clean
  • changed-file sensitive string scan -> no matches

Out of scope

  • No MCP handler behavior changes.
  • No wallet private keys, signatures, tokens, price claims, exchange claims, bridge claims, or off-ramp claims.

Summary by CodeRabbit

  • New Features

    • Enhanced input validation schemas for ledger, bounty, and wallet management tools with structured parameter constraints and type enforcement across the MCP tool suite.
  • Tests

    • Updated test coverage for MCP tool schema validation and parameter constraint assertions.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

The PR adds full JSON-schema inputSchema definitions to nine MCP tools that were previously minimal name/description entries. The schemas specify parameter types, constraints (enums, bounds, defaults), required fields, and property restrictions. The test suite is updated to validate these schemas directly using a tool_by_name lookup.

Changes

MCP Tool Input Schemas

Layer / File(s) Summary
Bounty tool input schemas
app/mcp.py (lines 18–92)
list_bounties adds optional filters (status, q, limit) with enum and bound constraints. get_bounty requires id and optionally includes include_awards. list_bounty_attempts requires bounty_id with optional include_expired and limit.
Wallet and account tool input schemas
app/mcp.py (lines 94–164)
get_balance requires account. register_wallet requires public_key_hex and allows optional label. get_wallet requires address. submit_wallet_transfer defines required transfer fields (from_address, to_address, amount_mrwk, nonce) and optional memo and signature_hex. All disallow additional properties.
Ledger and proof tool input schemas
app/mcp.py (lines 166–195)
get_ledger_entry requires sequence with validation bounds. get_proof requires hash. Both disallow additional properties.
MCP schema validation tests
tests/test_api_mcp.py (lines 177–214)
Test builds a tool_by_name lookup and asserts inputSchema presence, required fields, property types, enum values, bounds, and additionalProperties: False across all nine tools.

Possibly related PRs

  • ramimbo/mergework#329: Introduces the centralized MCP_TOOLS catalog that this PR expands with detailed inputSchema definitions.
  • ramimbo/mergework#286: Adds status, q, and limit filter behavior to list_bounties; this PR validates those parameters in the exported schema.
  • ramimbo/mergework#289: Adds the include_awards boolean argument to get_bounty; this PR formalizes it in the schema.
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Bounty Pr Focus ⚠️ Warning Initial commit (134 files) vs stated scope (2 files). Three unaddressed review comments: missing minLength on string fields, incomplete test assertions, no tool name uniqueness check. Add minLength constraints to string schemas; expand test assertions for bounds/constraints; add tool name uniqueness validation.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding MCP input schemas for public tools, which matches the core objective of the changeset in app/mcp.py.
Description check ✅ Passed The description covers all required sections with concrete evidence, validation results, and clear out-of-scope boundaries. All test evidence items are documented with passing results.
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 PR adds only technical MCP schema definitions with no investment, price, cash-out, or fabricated payout claims; docs properly describe MRWK as native.

✏️ 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.

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: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 64ebaba4-9165-4954-a4d9-7d09e2d5b841

📥 Commits

Reviewing files that changed from the base of the PR and between d8532d4 and 156c634.

📒 Files selected for processing (2)
  • app/mcp.py
  • tests/test_api_mcp.py

Comment thread app/mcp.py
Comment on lines +99 to +102
"account": {
"type": "string",
"description": "Ledger account id such as github:alice or mrwk1...",
},
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

Add non-empty constraints for required string arguments in inputSchema.

These fields are required and validated as non-empty by runtime str_arg(...), but the schemas currently allow "". That makes tool discovery metadata accept payloads that tools/call rejects.

Proposed schema tightening
                 "account": {
                     "type": "string",
+                    "minLength": 1,
                     "description": "Ledger account id such as github:alice or mrwk1...",
                 },

                 "public_key_hex": {
                     "type": "string",
+                    "minLength": 1,
                     "description": "Wallet public key in hex form.",
                 },

                 "address": {
                     "type": "string",
+                    "minLength": 1,
                     "description": "Registered mrwk1 wallet address.",
                 },

-                "from_address": {"type": "string", "description": "Sender mrwk1 wallet address."},
-                "to_address": {"type": "string", "description": "Recipient mrwk1 wallet address."},
-                "amount_mrwk": {"type": "string", "description": "Amount to transfer in MRWK."},
+                "from_address": {"type": "string", "minLength": 1, "description": "Sender mrwk1 wallet address."},
+                "to_address": {"type": "string", "minLength": 1, "description": "Recipient mrwk1 wallet address."},
+                "amount_mrwk": {"type": "string", "minLength": 1, "description": "Amount to transfer in MRWK."},

                 "signature_hex": {
                     "type": "string",
+                    "minLength": 1,
                     "description": "Hex signature authorizing the transfer.",
                 },

                 "hash": {
                     "type": "string",
+                    "minLength": 1,
                     "description": "Public proof hash.",
                 },

Also applies to: 114-117, 133-136, 148-150, 156-159, 187-190

Comment thread tests/test_api_mcp.py
Comment on lines +177 to +178
tool_by_name = {tool["name"]: tool for tool in tools["result"]["tools"]}
assert all("inputSchema" in tool for tool in tool_by_name.values())
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

Guard against duplicate tool names before map materialization.

tool_by_name = {tool["name"]: ...} silently overwrites duplicates. Add a uniqueness assertion so tools/list regressions fail loudly.

Suggested assertion
-    tool_by_name = {tool["name"]: tool for tool in tools["result"]["tools"]}
+    tools_list = tools["result"]["tools"]
+    tool_by_name = {tool["name"]: tool for tool in tools_list}
+    assert len(tool_by_name) == len(tools_list)

As per coding guidelines, tests/**/*.py: "Focus on whether tests prove the changed behavior and include negative, replay, boundary, or regression cases where relevant."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tool_by_name = {tool["name"]: tool for tool in tools["result"]["tools"]}
assert all("inputSchema" in tool for tool in tool_by_name.values())
tools_list = tools["result"]["tools"]
tool_by_name = {tool["name"]: tool for tool in tools_list}
assert len(tool_by_name) == len(tools_list)
assert all("inputSchema" in tool for tool in tool_by_name.values())

Comment thread tests/test_api_mcp.py
Comment on lines +195 to +214
bounty_tool = tool_by_name["get_bounty"]
assert "accepted awards" in bounty_tool["description"]
attempt_tool = next(
tool for tool in tools["result"]["tools"] if tool["name"] == "list_bounty_attempts"
)
assert bounty_tool["inputSchema"]["required"] == ["id"]
assert bounty_tool["inputSchema"]["properties"]["include_awards"]["type"] == "boolean"
attempt_tool = tool_by_name["list_bounty_attempts"]
assert "active-attempt reservations" in attempt_tool["description"]
assert attempt_tool["inputSchema"]["required"] == ["bounty_id"]
assert attempt_tool["inputSchema"]["properties"]["include_expired"]["type"] == "boolean"
assert tool_by_name["get_balance"]["inputSchema"]["required"] == ["account"]
assert tool_by_name["register_wallet"]["inputSchema"]["required"] == ["public_key_hex"]
assert tool_by_name["get_wallet"]["inputSchema"]["required"] == ["address"]
assert tool_by_name["submit_wallet_transfer"]["inputSchema"]["required"] == [
"from_address",
"to_address",
"amount_mrwk",
"nonce",
"signature_hex",
]
assert tool_by_name["get_ledger_entry"]["inputSchema"]["required"] == ["sequence"]
assert tool_by_name["get_proof"]["inputSchema"]["required"] == ["hash"]
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

Expand schema regression assertions beyond required fields.

For several newly covered tools, this test only checks required. It does not lock in key constraints introduced by this PR (e.g., additionalProperties: False, integer bounds/defaults), so schema contract drift can pass unnoticed.

As per coding guidelines, tests/**/*.py: "Focus on whether tests prove the changed behavior and include negative, replay, boundary, or regression cases where relevant."

Copy link
Copy Markdown
Contributor

@GHX5T-SOL GHX5T-SOL 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 156c634c4bf2bfa85e5d129f470fb9a21228fb1e for the #447 review bounty.

I am requesting changes for one schema-contract mismatch: several newly advertised required string arguments allow the empty string in tools/list, but the runtime tools/call validators reject empty strings. For example, get_balance.inputSchema.properties.account has no minLength, so a schema-driven MCP client can treat { "account": "" } as valid; the handler then rejects it through str_arg("account") / normalized_account() with invalid tool arguments. The same mismatch applies to the other newly advertised required strings that are parsed with non-empty str_arg(...) or stricter normalizers, such as public_key_hex, address, from_address, to_address, amount_mrwk, signature_hex, and hash.

Local repro:

get_balance_account_minLength None
empty_account_call {'jsonrpc': '2.0', 'id': 2, 'error': {'code': -32602, 'message': 'invalid tool arguments'}}

Suggested fix: add minLength: 1 or a stricter pattern/length where the runtime contract is narrower, and extend the schema regression assertions so this does not drift again.

Validation run locally on this head:

PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_tools_list_and_call tests/test_api_mcp.py::test_mcp_list_bounty_attempts_reports_active_and_expired tests/test_api_mcp.py::test_mcp_get_proof_returns_public_proof_details -q -> 3 passed
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_api_mcp.py -q -> 76 passed
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest -q -> 414 passed
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok
uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted
uv run --extra dev python -m mypy app -> success
git diff --check origin/main...HEAD -> clean
gitleaks detect --no-banner --redact --source . --log-opts origin/main..HEAD --exit-code 1 -> no leaks found

Copy link
Copy Markdown

@adliebe adliebe left a comment

Choose a reason for hiding this comment

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

Requesting changes on current head 156c634c4bf2bfa85e5d129f470fb9a21228fb1e.

The direction is right: PR #488 closes the live MCP discovery gap from #405 by adding inputSchema to the public tools that were previously opaque. I checked app/mcp.py against the runtime dispatcher in app/mcp_tools.py, and the required field names/limits mostly line up with the current handler contract for list_bounties, get_bounty, list_bounty_attempts, get_balance, register_wallet, get_wallet, submit_wallet_transfer, get_ledger_entry, and get_proof.

One schema/runtime mismatch should be fixed before merge: several newly advertised required string fields allow "" at the schema layer even though tools/call rejects them through str_arg(..., allow_empty=False). Concrete examples:

  • get_balance.account is advertised as just {"type": "string"}, but call_mcp_tool() calls str_arg("account").
  • register_wallet.public_key_hex, get_wallet.address, submit_wallet_transfer.from_address, to_address, amount_mrwk, signature_hex, and get_proof.hash have the same shape.
  • Existing test coverage already proves at least one mismatch: tests/test_api_mcp.py::test_mcp_rejects_invalid_string_arguments rejects {"account": ""} for get_balance.

For a discovery schema, that matters because an agent using tools/list would see an empty required string as schema-valid, then get -32602 invalid tool arguments only after making the call. Please add minLength: 1 to the required string fields that flow through str_arg(...), and add a regression assertion so this does not drift again.

Validation I ran locally on this head:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_tools_list_and_call -q -> 1 passed
  • uv run --extra dev python -m pytest tests/test_api_mcp.py -q -> 76 passed
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted
  • git diff --check origin/main...HEAD -> clean

Non-blocking merge-order note: PR #471 separately tightens the submit_work_proof repo / issue_number dependency. If both PRs land, make sure #488 does not leave that schema behind.

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.

3 participants