Skip to content

Support MCP initialize handshake#848

Open
SYZBZ wants to merge 2 commits into
ramimbo:mainfrom
SYZBZ:syzbz-mcp-initialize-799
Open

Support MCP initialize handshake#848
SYZBZ wants to merge 2 commits into
ramimbo:mainfrom
SYZBZ:syzbz-mcp-initialize-799

Conversation

@SYZBZ
Copy link
Copy Markdown
Contributor

@SYZBZ SYZBZ commented Jun 4, 2026

Summary

  • Add JSON-RPC initialize support on the public MCP endpoint.
  • Return MCP protocol negotiation data with tool server capabilities and serverInfo.
  • Default unsupported, empty, or malformed requested protocol versions to the server-supported MCP version.
  • Add regression coverage so standard MCP clients no longer receive unknown method before tools/list / tools/call.

Bounty #799
Source report: #798 (comment)

Why

The current production MCP endpoint exposes tools/list, but a standard MCP lifecycle initialize request returns JSON-RPC -32601 unknown method. That can break MCP clients that perform the normal initialize handshake before listing or calling tools.

Production repro before this fix:

POST https://mcp.mrwk.online/mcp initialize
-> {"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"unknown method"}}

The official MCP schema describes initialize as returning protocolVersion, server capabilities, and serverInfo: https://modelcontextprotocol.io/specification/2025-06-18/schema#initialize

Review Follow-up

CodeRabbit and jakerated-r asked for protocol-version handling and initialize edge-case coverage. Current head returns the server-supported protocol version for missing, non-string, unsupported, or empty requested versions, with regression tests for those cases.

Test Evidence

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities tests/test_api_mcp.py::test_mcp_initialize_defaults_unsupported_protocol_versions -q -> 5 passed
  • uv run --extra dev python -m pytest -q -> 795 passed, 1 existing Starlette/httpx warning
  • uv run --extra dev ruff format --check . -> 111 files already formatted
  • uv run --extra dev ruff check . -> All checks passed
  • uv run --extra dev mypy app -> Success: no issues found in 42 source files
  • git diff --check origin/main...HEAD -> clean

Scope and Safety

This is a focused MCP handshake compatibility fix only. It does not change tool dispatch, bounty lifecycle, ledger, treasury, wallet, payout, proof, balance, exchange, bridge, off-ramp, cash-out, price behavior, private data, secrets, or production mutation behavior.

Payout

payout address will be provided by the contributor on request.

Summary by CodeRabbit

  • New Features

    • Added MCP protocol initialization: server responds with supported protocol version, reports tool capability (tools.listChanged: false), and includes server info (name and version); falls back to the server protocol when client input is invalid.
  • Tests

    • Added tests for successful initialization and for handling unsupported/invalid protocolVersion inputs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 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: c212fb70-06e5-47a7-8133-8286389330eb

📥 Commits

Reviewing files that changed from the base of the PR and between adf40d8 and fc443eb.

📒 Files selected for processing (1)
  • tests/test_api_mcp.py

📝 Walkthrough

Walkthrough

Adds MCP JSON-RPC initialize handling: server protocol/version constants, an _initialize_response builder that negotiates protocolVersion and reports capabilities/serverInfo, routes initialize requests in handle_mcp_request, and adds tests validating responses and defaulting unsupported protocol inputs.

Changes

MCP Initialize Support

Layer / File(s) Summary
Initialization constants and response builder
app/mcp.py
Introduces MCP_PROTOCOL_VERSION and MCP_SERVER_INFO constants and adds _initialize_response(...) that constructs the JSON-RPC initialize result with negotiated protocolVersion, capabilities (tools.listChanged: False), and serverInfo.
Request routing and initialization tests
app/mcp.py, tests/test_api_mcp.py
Updates handle_mcp_request(...) to intercept method == "initialize" and return _initialize_response(...). Adds test_mcp_initialize_returns_server_capabilities and test_mcp_initialize_defaults_unsupported_protocol_versions to verify JSON-RPC envelope, protocolVersion negotiation/defaulting, capabilities.tools.listChanged, and serverInfo.

Possibly related PRs

  • ramimbo/mergework#329: Centralized handle_mcp_request routing for tools/list and tools/call; this PR extends the same handler to support initialize.
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed Title directly names the key surface change (MCP initialize handshake support) and matches the main code addition in app/mcp.py.
Description check ✅ Passed Description covers summary, motivation, evidence, scope/safety, and test results. All required sections from the template are present and substantive.
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 changes only app/mcp.py and tests/test_api_mcp.py with technical MCP initialize support. No README, docs, or public comment modifications. No investment, price, or off-ramp claims.
Bounty Pr Focus ✅ Passed Changes match stated files: app/mcp.py (initialize support), tests/test_api_mcp.py (regression tests). Includes test evidence, avoids 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.

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: dff5881f-55bf-415f-8923-8bf7f5b86936

📥 Commits

Reviewing files that changed from the base of the PR and between d4d0e48 and 1e42916.

📒 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
@SYZBZ SYZBZ force-pushed the syzbz-mcp-initialize-799 branch from 1e42916 to adf40d8 Compare June 4, 2026 01:27
Copy link
Copy Markdown
Contributor

@jakerated-r jakerated-r left a comment

Choose a reason for hiding this comment

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

Current-head review for 1e4291689771308390baccc8f665dafb5fb82505.

Validation I ran locally on this head:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities -q -> 1 passed
  • 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_malformed_requests_without_500 -q -> 2 passed
  • uv run --extra dev python -m pytest -q -> 791 passed
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev mypy app -> success
  • python3 scripts/docs_smoke.py -> ok
  • git diff --check origin/main...HEAD -> clean
  • git merge-tree --write-tree origin/main HEAD -> clean

Blocker: _initialize_response() currently accepts any string in params.protocolVersion and returns it as the negotiated result.protocolVersion. That lets the server claim compatibility with unsupported or empty protocol versions instead of returning a server-supported version.

The MCP 2025-06-18 lifecycle docs say that if the server supports the requested protocol version, it must respond with the same version; otherwise it must respond with another version it supports. Because this server only declares MCP_PROTOCOL_VERSION = "2025-06-18", echoing arbitrary strings makes the initialize handshake advertise versions the server does not actually support.

I confirmed this on the current head with direct /mcp probes:

  • Supported request protocolVersion: "2025-06-18" -> returns "2025-06-18"
  • Missing params -> returns "2025-06-18"
  • Missing protocolVersion -> returns "2025-06-18"
  • Non-string protocolVersion: 123 -> returns "2025-06-18"
  • Unsupported string protocolVersion: "not-a-version" -> returns "not-a-version"
  • Empty string protocolVersion: "" -> returns ""

That last pair is the compatibility failure: a client can believe it successfully negotiated an invalid or unsupported protocol, then proceed to tools/list / tools/call under a version contract the server never implemented.

Please validate the requested version against the supported protocol set before building the initialize result. A safe repair would be either:

  • return "2025-06-18" whenever the client sends an unsupported protocol version, matching MCP version negotiation, or
  • reject malformed/empty protocol versions with a JSON-RPC invalid-params error if you want stricter request validation.

Please also add regression coverage for unsupported strings and empty strings. The current happy-path test does not catch this negotiation edge.

Copy link
Copy Markdown
Contributor

@jakerated-r jakerated-r 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 current head adf40d89436337d1e3d442d9c8e0389e9bda0bbb after the PR updated while my previous review was posting.

The protocol negotiation blocker I flagged on the earlier snapshot is fixed on this head. _initialize_response() now returns the supported server protocol version unless the client asks for the exact supported MCP_PROTOCOL_VERSION, so unsupported strings no longer get echoed back as negotiated versions.

Current-head probe results:

  • Supported request protocolVersion: "2025-06-18" -> returns "2025-06-18"
  • Missing params -> returns "2025-06-18"
  • Missing protocolVersion -> returns "2025-06-18"
  • Non-string protocolVersion: 123 -> returns "2025-06-18"
  • Unsupported string protocolVersion: "not-a-version" -> returns "2025-06-18"
  • Empty string protocolVersion: "" -> returns "2025-06-18"

Current-head validation:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities -q -> 1 passed
  • 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_malformed_requests_without_500 -q -> 2 passed
  • uv run --extra dev python -m pytest -q -> 794 passed
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev mypy app -> success
  • python3 scripts/docs_smoke.py -> ok
  • git diff --check origin/main...HEAD -> clean
  • git merge-tree --write-tree origin/main HEAD -> clean

The current patch also adds regression coverage for unsupported protocol-version payloads. No remaining blocker from me on this head.

@SYZBZ
Copy link
Copy Markdown
Contributor Author

SYZBZ commented Jun 4, 2026

Addressed the current-head review in fc443eb.

Changes made:

  • added explicit regression coverage for empty-string params.protocolVersion;
  • kept unsupported strings, missing params, and non-string values defaulting to the server-supported MCP_PROTOCOL_VERSION;
  • left the supported 2025-06-18 happy path unchanged.

Fresh verification on current head:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities tests/test_api_mcp.py::test_mcp_initialize_defaults_unsupported_protocol_versions -q -> 5 passed
  • uv run --extra dev python -m pytest -q -> 795 passed, 1 existing Starlette/httpx warning
  • uv run --extra dev ruff format --check . -> 111 files already formatted
  • uv run --extra dev ruff check . -> All checks passed
  • uv run --extra dev mypy app -> Success: no issues found in 42 source files
  • git diff --check origin/main...HEAD -> clean

@jakerated-r
Copy link
Copy Markdown
Contributor

Current-head verification for fc443ebe0f5cad31f27df1ffd4ac5b36795ba92a.

This is the exact head I validated after the PR updated during my earlier reviews. No remaining blocker from me on this head.

What changed since the prior validated head adf40d89436337d1e3d442d9c8e0389e9bda0bbb:

  • tests/test_api_mcp.py only
  • 6 inserted lines
  • Adds the empty-string protocolVersion regression case to test_mcp_initialize_defaults_unsupported_protocol_versions

Direct /mcp initialize probes on fc443ebe0f5cad31f27df1ffd4ac5b36795ba92a:

  • Supported request protocolVersion: "2025-06-18" -> returns "2025-06-18"
  • Missing params -> returns "2025-06-18"
  • Missing protocolVersion -> returns "2025-06-18"
  • Non-string protocolVersion: 123 -> returns "2025-06-18"
  • Unsupported string protocolVersion: "not-a-version" -> returns "2025-06-18"
  • Empty string protocolVersion: "" -> returns "2025-06-18"

Validation on this exact head:

  • uv run --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities tests/test_api_mcp.py::test_mcp_initialize_defaults_unsupported_protocol_versions -q -> 5 passed
  • 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_malformed_requests_without_500 -q -> 2 passed
  • uv run --extra dev python -m pytest -q -> 795 passed
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed
  • uv run --extra dev mypy app -> success
  • python3 scripts/docs_smoke.py -> ok
  • git diff --check origin/main...HEAD -> clean
  • git merge-tree --write-tree origin/main HEAD -> clean (0a2e03b5bccf5a5e954ab4970b461821ded3de8c)

The implementation now returns the supported server protocol for unsupported, malformed, missing, and empty client protocol-version payloads, and the regression tests cover those initialize edge cases.

Copy link
Copy Markdown

@xiefuzheng713-alt xiefuzheng713-alt 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 fc443ebe0f5cad31f27df1ffd4ac5b36795ba92a as a non-author.

Scope inspected:

  • app/mcp.py: verified initialize is handled before tools/list / tools/call, returns the server-supported MCP protocol version, advertises tools.listChanged: false, and includes stable serverInfo without invoking any tool handler, DB path, ledger path, or mutation behavior.
  • tests/test_api_mcp.py: verified coverage for a normal initialize request and missing, non-string, unsupported, and empty protocolVersion inputs defaulting to the server-supported version.
  • Live repro/source check: production POST https://mcp.mrwk.online/mcp with method=initialize still returns JSON-RPC -32601 unknown method, while tools/list succeeds, so this is a focused compatibility fix for the existing public MCP endpoint.

Validation run on this head:

  • uv run --python 3.12 --extra dev python -m pytest tests/test_api_mcp.py::test_mcp_initialize_returns_server_capabilities tests/test_api_mcp.py::test_mcp_initialize_defaults_unsupported_protocol_versions -q -> 5 passed, 1 existing Starlette/httpx warning.
  • uv run --python 3.12 --extra dev python -m pytest tests/test_api_mcp.py -q -> 110 passed, 1 existing Starlette/httpx warning.
  • uv run --python 3.12 --extra dev python -m pytest -q -> 795 passed, 1 existing Starlette/httpx warning.
  • uv run --python 3.12 --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed.
  • uv run --python 3.12 --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted.
  • uv run --python 3.12 --extra dev mypy app -> success.
  • uv run --python 3.12 --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 tree 0a2e03b5bccf5a5e954ab4970b461821ded3de8c.

Currentness and duplicate check: this PR is mergeable at the reviewed head and hosted Quality/readiness/docs/image plus CodeRabbit checks are success. Before posting, I found one current-head human approval and one #654 claim for PR #848, so this is a second current-head non-author review rather than a duplicate pile-on.

No blocker found. The change is narrow MCP handshake compatibility work and does not touch admin-token APIs, bounty lifecycle, treasury/proposal execution, payout, proof creation, ledger mutation, wallet material, exchange, bridge, cash-out, price behavior, private data, or secrets.

Copy link
Copy Markdown
Contributor

@aglichandrap aglichandrap left a comment

Choose a reason for hiding this comment

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

Review evidence (head SHA: a4208100fa):

  • Scope: Implements MCP initialize handshake with protocolVersion, capabilities, and serverInfo.
  • Constants: MCP_PROTOCOL_VERSION = "2025-06-18" and MCP_SERVER_INFO properly defined.
  • Version negotiation: Falls back to server version if client sends mismatched protocolVersion.
  • Files: 2 files, +88/-0 — clean addition.
  • Mergeable: state=clean.

No blockers. Approve.

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.

4 participants