Support MCP lifecycle basics#563
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe MCP handler adds protocol negotiation via ChangesMCP Protocol Methods
Possibly related PRs
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e376b027-eb8e-40eb-b0a6-210d31646308
📒 Files selected for processing (2)
app/mcp.pytests/test_api_mcp.py
| protocol_version = ( | ||
| requested_version if isinstance(requested_version, str) else DEFAULT_PROTOCOL_VERSION | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider validating protocol version.
The code accepts any string as protocolVersion without validation. If the MCP spec defines version-specific behavior, accepting unsupported versions could lead to protocol violations or unexpected client behavior. Consider either validating against supported versions or documenting that the server accepts any version string.
eliasx45
left a comment
There was a problem hiding this comment.
I checked this locally against 593c3deab3f6ee488d3155ba987a1b22ee8619dd. The new lifecycle branches mostly behave as described, but I found one protocol-negotiation issue that should be fixed before merge.
Finding: initialize echoes any string-valued params.protocolVersion back to the client. Per the MCP lifecycle spec, if the server supports the requested protocol version it must return that same version; otherwise it must return another protocol version it supports. Link: https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#version-negotiation
In this PR, app/mcp.py:120-123 treats every string as supported:
requested_version if isinstance(requested_version, str) else DEFAULT_PROTOCOL_VERSION
That means unsupported or malformed strings are advertised as negotiated successfully. Local probe against the PR branch:
2025-06-18 200 2025-06-18
1900-01-01 200 1900-01-01
not-a-version 200 not-a-version
This can break generic MCP clients because subsequent requests are supposed to respect the negotiated protocol version, but the server just agreed to arbitrary versions. Suggested fix: keep an explicit supported-version set/list. Return the requested version only if it is in that set; otherwise return DEFAULT_PROTOCOL_VERSION (or a JSON-RPC unsupported-version error with supported/requested data). Please also add a regression test for an unsupported string protocol version.
Other verification I ran:
.\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py -q-> 84 passed.\.venv\Scripts\python.exe -m ruff check app\mcp.py tests\test_api_mcp.py-> all checks passed.\.venv\Scripts\python.exe -m ruff format --check app\mcp.py tests\test_api_mcp.py-> 2 files already formatted.\.venv\Scripts\python.exe -m mypy app\mcp.py-> success.\.venv\Scripts\python.exe scripts\docs_smoke.py-> docs smoke okgit diff --check origin/main...HEAD-> clean
|
Maintainer cleanup note: holding this for changes. The review found an MCP initialize protocol-version negotiation issue: arbitrary string |
Summary
Refs #406.
This adds a focused compatibility fix for the public MCP JSON-RPC endpoint:
/mcpnow responds to the standardinitializerequest with protocol version, tool capability, and server info before clients calltools/listortools/call. It also accepts the follow-upnotifications/initializedlifecycle notification with an empty202response and supports the optionalpinghealth check.Evidence
Live public preflight on 2026-05-28 showed:
That is awkward for generic MCP clients because the MCP lifecycle starts with an
initializeexchange followed by an initialized notification before normal operation, and the protocol also defines a lightweight ping utility. The project already advertises an MCP host and supportstools/list/tools/call; this PR keeps that behavior and adds the missing lifecycle/basic utility responses.Scope
initializebranch inapp/mcp.py.protocolVersionwhen supplied, otherwise a default;capabilities: {"tools": {}};serverInfofor MergeWork MCP.notifications/initializedas a no-body202lifecycle notification.ping.tests/test_api_mcp.py.Validation
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py::test_mcp_initialize_advertises_tool_capability tests\test_api_mcp.py::test_mcp_initialized_notification_returns_accepted tests\test_api_mcp.py::test_mcp_ping_returns_empty_result tests\test_api_mcp.py::test_mcp_tools_list_and_call tests\test_api_mcp.py::test_mcp_rejects_malformed_requests_without_500 -q-> 5 passedPYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py -q-> 79 passedPYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .\.venv\Scripts\python.exe scripts\docs_smoke.py-> docs smoke ok.\.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 formattedPYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .\.venv\Scripts\python.exe -m mypy app\mcp.py-> successgit diff --check-> cleanSafety
No private data, cookies, admin tokens, wallet material, signatures, OAuth state, deployment secrets, price/off-ramp claims, liquidity claims, exchange claims, bridge promises, or fabricated payout claims are included.
Summary by CodeRabbit
New Features
Tests