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

    • Improved input validation for ledger, bounty, and wallet tools: structured schemas with typed parameters, required fields, defaults, value constraints, and stricter rejection of unknown inputs.
  • Tests

    • Expanded test coverage to validate tool input schemas, required parameters, defaults, enums/ranges, and enforcement of additional-properties restrictions.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

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: fa9cf53f-667b-4b58-9e15-1087f11c56fa

📥 Commits

Reviewing files that changed from the base of the PR and between 5b0a131 and 14caf94.

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

📝 Walkthrough

Walkthrough

The PR replaces minimal MCP tool metadata with full JSON-schema inputSchema objects for nine tools (bounty, wallet/account, ledger, proof), specifying types, required fields, enums, bounds, defaults, and additionalProperties: False. Tests are updated to index tools by name and assert these schema fields.

Changes

MCP Tool Input Schemas

Layer / File(s) Summary
Bounty tool input schemas
app/mcp.py (list_bounties, get_bounty, list_bounty_attempts)
Adds inputSchema for list_bounties (status/sort enums, q, limit bounds, defaults, additionalProperties:false), get_bounty (required id, optional include_awards boolean default), and list_bounty_attempts (required bounty_id, optional include_expired, constrained limit).
Wallet and account tool input schemas
app/mcp.py (get_balance, register_wallet, get_wallet, submit_wallet_transfer)
Adds inputSchema requiring account, public_key_hex (and optional label), address, and full submit_wallet_transfer params (from_address, to_address, amount_mrwk, nonce, memo, signature_hex) with minLength/number bounds, required lists, and additionalProperties: False.
Ledger and proof tool input schemas
app/mcp.py (get_ledger_entry, get_proof)
Adds inputSchema requiring numeric sequence (minimum) and hash (minLength), both disallowing additional properties.
MCP schema validation tests
tests/test_api_mcp.py
Builds tool_by_name from tools/list and asserts inputSchema presence and exact structure: required fields, property types, enums/defaults, numeric/string bounds, and additionalProperties: False for all updated 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 Commit includes 131 unrelated files (migrations, templates, webhooks, docs, configs) beyond the stated MCP schema scope, indicating this is a full repository import rather than a focused bounty PR. Separate the initial repository setup from the MCP schema changes, or clarify this is the canonical baseline commit containing all application code.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concretely names the changed surface: adding MCP input schemas for public tools, with issue reference Refs #406.
Description check ✅ Passed Description includes summary, evidence of the problem and reproduction, validation with test results and tooling checks, and out-of-scope clarification.
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 MCP JSON-schema inputSchema definitions. No investment, price, cash-out, fabricated payout claims, or private security details found in modified files.

✏️ 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 thread tests/test_api_mcp.py Outdated
Comment thread tests/test_api_mcp.py Outdated
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
Contributor

@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.

Copy link
Copy Markdown

@rebel117 rebel117 left a comment

Choose a reason for hiding this comment

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

Reviewed PR #488 at current head for bounty #447.

Evidence checked:

  • Cross-checked every inputSchema entry against the corresponding argument parsing in app/mcp_tools.py call_mcp_tool().
  • list_bounties: status default "open" matches optional_clean_str_arg fallback, limit 1–100 matches list_limit_arg bounds.
  • get_bounty: id required/positive matches positive_int_arg, include_awards boolean default False matches optional_bool_arg.
  • list_bounty_attempts: bounty_id required, include_expired boolean False, limit 25/100 — all match mcp_tools.py.
  • get_balance: account required matches str_arg("account").
  • register_wallet: public_key_hex required, label optional — matches conditional optional_str_arg pattern.
  • get_wallet: address required matches str_arg.
  • submit_wallet_transfer: from_address, to_address, amount_mrwk, nonce, signature_hex required; memo optional — matches str_arg/int_arg calls.
  • get_ledger_entry: sequence required/positive matches positive_int_arg.
  • get_proof: hash required matches str_arg.

Test coverage: the added assertions verify every tool has inputSchema, spot-check required fields, enum values, and bounds. The refactored tool_by_name lookup is cleaner than the previous next() calls.

No blockers from this pass.

Copy link
Copy Markdown

@Baijack-star Baijack-star 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 5b0a131fbe0aa81871623d64af6e283f35493300 as a follow-up to the earlier schema/runtime mismatch reviews.

No blocker found in this current head. The previous required-string mismatch is fixed: the required string fields that flow through non-empty runtime validators now advertise minLength: 1 in tools/list, including get_balance.account, register_wallet.public_key_hex, get_wallet.address, submit_wallet_transfer.from_address, to_address, amount_mrwk, signature_hex, and get_proof.hash. The regression assertions cover those fields, the required arrays, integer lower bounds, limit bounds, enum/default metadata, and additionalProperties: false on the advertised schemas.

I also cross-checked the schema shapes against app/mcp_tools.py: list_bounties keeps optional status/q/limit; get_bounty, list_bounty_attempts, and get_ledger_entry use positive integer selectors; optional booleans/defaults match the handler defaults; and submit_work_proof keeps the existing bounty-id vs issue-number mutual exclusion.

Validation run locally on this head:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ./.venv/bin/python -m pytest tests/test_api_mcp.py::test_mcp_tools_list_and_call tests/test_api_mcp.py::test_mcp_rejects_invalid_string_arguments -q -> 7 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ./.venv/bin/python -m pytest tests/test_api_mcp.py -q -> 76 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ./.venv/bin/python -m pytest -q -> 414 passed
  • ./.venv/bin/python -m ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • ./.venv/bin/python -m ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ./.venv/bin/python -m mypy app/mcp.py app/mcp_tools.py -> success
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ./.venv/bin/python scripts/docs_smoke.py -> docs smoke ok
  • git diff --check origin/main...HEAD -> clean

@tinyopsstudio
Copy link
Copy Markdown

Reviewed current PR #488 head 5b0a131fbe0aa81871623d64af6e283f35493300 for the MCP input schema contract.

No blocker found on this head.

Evidence checked:

  • inspected app/mcp.py, tests/test_api_mcp.py, the current review thread, and the hosted status;
  • confirmed the previously reported required-string schema/runtime mismatch is addressed with minLength: 1 on the required string fields that flow through non-empty runtime validators;
  • confirmed the schema tests now cover required arrays, integer lower bounds, enum/default metadata, additionalProperties: false, and the public tool list shape;
  • ran pytest tests/test_api_mcp.py -q: 76 passed;
  • ran ruff format --check app/mcp.py tests/test_api_mcp.py: 2 files already formatted;
  • ran ruff check app/mcp.py tests/test_api_mcp.py: all checks passed;
  • ran the full test suite: 414 passed.

This looks mergeable from my reviewed slice.

Copy link
Copy Markdown

@eliasx45 eliasx45 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 5b0a131 against refreshed origin/main.

Verdict: request changes.

The schema/runtime contract fix looks good on its own: the required MCP string inputs now advertise minLength: 1, the schema assertions cover the required fields and additionalProperties behavior, and focused MCP tests pass. However, the branch is stale against current main and merge-tree reports content conflicts in both touched files:

  • app/mcp.py
  • tests/test_api_mcp.py

Validation on this head:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ..venv\Scripts\python.exe -m pytest tests\test_api_mcp.py::test_mcp_tools_list_and_call tests\test_api_mcp.py::test_mcp_rejects_invalid_string_arguments 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 -> 9 passed
  • ..venv\Scripts\python.exe -m ruff check app\mcp.py tests\test_api_mcp.py -> passed
  • ..venv\Scripts\python.exe -m ruff format --check app\mcp.py tests\test_api_mcp.py -> 2 files already formatted
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ..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 -> conflicts in app/mcp.py and tests/test_api_mcp.py

Required follow-up: rebase/merge current main, resolve those two conflicts, and rerun the focused MCP schema tests. I do not see a remaining schema-level blocker beyond the stale/conflicting base.

@MINBBBIGcode MINBBBIGcode force-pushed the codex/mcp-input-schemas branch from 5b0a131 to 14caf94 Compare May 29, 2026 00:13
@MINBBBIGcode
Copy link
Copy Markdown
Contributor Author

Resolved the stale-base blocker by rebasing this PR onto current main and resolving the conflicts in app/mcp.py and tests/test_api_mcp.py.

Current head: 14caf94a0a44ba6080f51a34804e70b1aa7c6ba2

Validation run after the rebase:

  • 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_rejects_invalid_string_arguments 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 -> 9 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_api_mcp.py -q -> 81 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest -q -> 445 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 -> already formatted
  • uv run --extra dev python -m mypy app/mcp.py app/mcp_tools.py -> success
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok
  • git diff --check origin/main...HEAD -> clean
  • git merge-tree --write-tree origin/main HEAD -> clean

Copy link
Copy Markdown

@eliasx45 eliasx45 left a comment

Choose a reason for hiding this comment

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

Re-reviewed refreshed head 14caf94a0a44ba6080f51a34804e70b1aa7c6ba2 after the rebase/conflict resolution. Verdict: approve.

The stale-base blocker from my previous review is resolved: GitHub reports the branch cleanly mergeable, and a local synthetic merge against current origin/main succeeded. I also rechecked the schema/runtime contract that this PR is meant to fix: every public MCP tool now advertises an inputSchema, required primitive string args use minLength: 1 where the runtime rejects empty strings, enum/default/range metadata matches the existing argument parsing, and submit_work_proof still keeps the bounty selector constraint plus additionalProperties: false.

Validation on this checkout:

git diff --check origin/main...HEAD
# clean

git merge-tree --write-tree origin/main HEAD
# 2437d55eb40aae31eb4dc4142e19658a00ef5021

python -m py_compile app/mcp.py
# passed

.\.venv\Scripts\python.exe -m pytest -q tests/test_api_mcp.py
# 81 passed, 1 warning in 36.36s

Note: this Windows host did not have uv on PATH, so I created an isolated local venv in the temporary review worktree and installed the repo with the dev extra before running pytest.

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.

7 participants