forked from molecule-ai/molecule-core
Merge pull request #2461 from Molecule-AI/feat/mcp-experimental-channel-capability
feat(mcp): declare experimental.claude/channel capability for push UX
This commit is contained in:
commit
06bebc1b35
@ -149,6 +149,30 @@ async def handle_tool_call(name: str, arguments: dict) -> str:
|
||||
_CHANNEL_NOTIFICATION_METHOD = "notifications/claude/channel"
|
||||
|
||||
|
||||
def _build_initialize_result() -> dict:
|
||||
"""MCP initialize handshake result.
|
||||
|
||||
The ``experimental.claude/channel`` capability declaration is what
|
||||
tells Claude Code's MCP client to route our
|
||||
``notifications/claude/channel`` emissions as conversation
|
||||
interrupts (push UX). Without it the notification arrives over the
|
||||
wire but is silently dropped instead of becoming a ``<channel>``
|
||||
tag in the next agent turn — matching the
|
||||
"Notification arrives but Claude Code doesn't surface it" failure
|
||||
mode anticipated in molecule-core#2444. Mirrors the contract
|
||||
declared by the molecule-mcp-claude-channel bun bridge
|
||||
(server.ts:374).
|
||||
"""
|
||||
return {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {"listChanged": False},
|
||||
"experimental": {"claude/channel": {}},
|
||||
},
|
||||
"serverInfo": {"name": "a2a-delegation", "version": "1.0.0"},
|
||||
}
|
||||
|
||||
|
||||
def _build_channel_notification(msg: dict) -> dict:
|
||||
"""Transform an ``InboxMessage.to_dict()`` into the MCP notification
|
||||
envelope expected by Claude Code's channel-bridge contract.
|
||||
@ -246,11 +270,7 @@ async def main(): # pragma: no cover
|
||||
await write_response({
|
||||
"jsonrpc": "2.0",
|
||||
"id": req_id,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {"tools": {"listChanged": False}},
|
||||
"serverInfo": {"name": "a2a-delegation", "version": "1.0.0"},
|
||||
},
|
||||
"result": _build_initialize_result(),
|
||||
})
|
||||
|
||||
elif method == "notifications/initialized":
|
||||
|
||||
@ -237,3 +237,44 @@ def test_build_channel_notification_handles_missing_fields_gracefully():
|
||||
assert meta["activity_id"] == ""
|
||||
assert meta["peer_id"] == ""
|
||||
assert meta["kind"] == ""
|
||||
|
||||
|
||||
# ============== initialize handshake — capability declaration ==============
|
||||
# Without `experimental.claude/channel`, Claude Code's MCP client drops
|
||||
# our notifications/claude/channel emissions instead of routing them as
|
||||
# inline conversation interrupts. Anticipated as a failure mode in
|
||||
# molecule-core#2444 ("notification arrives but Claude Code doesn't
|
||||
# surface it"). Pin the declaration here so a refactor of
|
||||
# _build_initialize_result can't silently strip the flag.
|
||||
|
||||
|
||||
def test_initialize_declares_experimental_claude_channel_capability():
|
||||
"""Without this capability the push-UX bridge ships, the
|
||||
notifications fire, and nothing happens in the host — silent. This
|
||||
is the contract that flips Claude Code's routing on."""
|
||||
from a2a_mcp_server import _build_initialize_result
|
||||
|
||||
result = _build_initialize_result()
|
||||
experimental = result["capabilities"].get("experimental", {})
|
||||
|
||||
assert "claude/channel" in experimental, (
|
||||
"experimental.claude/channel capability is required for Claude "
|
||||
"Code to surface our notifications/claude/channel emissions as "
|
||||
"conversation interrupts (issue #2444 §2). Removing this would "
|
||||
"regress live push UX while leaving every unit test green."
|
||||
)
|
||||
|
||||
|
||||
def test_initialize_keeps_tools_capability():
|
||||
"""Pin the tools capability too — losing it would break tools/list."""
|
||||
from a2a_mcp_server import _build_initialize_result
|
||||
|
||||
assert "tools" in _build_initialize_result()["capabilities"]
|
||||
|
||||
|
||||
def test_initialize_protocol_version_is_pinned():
|
||||
"""MCP protocol version is part of the handshake contract; bumping
|
||||
it changes what fields the host expects."""
|
||||
from a2a_mcp_server import _build_initialize_result
|
||||
|
||||
assert _build_initialize_result()["protocolVersion"] == "2024-11-05"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user