Skip to content

Drop progress notifications to keep MCP transport stable#15

Open
felixding wants to merge 1 commit into
agentic-box:mainfrom
felixding:fix-progress-token
Open

Drop progress notifications to keep MCP transport stable#15
felixding wants to merge 1 commit into
agentic-box:mainfrom
felixding:fix-progress-token

Conversation

@felixding
Copy link
Copy Markdown

@felixding felixding commented Apr 26, 2026

Summary

Council deliberation drops the MCP stdio transport mid-run when invoked from spec-strict clients (e.g. Claude Code).

Two compounding causes:

1. Hardcoded progress token (the original bug).
Council.notify sent notifications/progress with progress_token=\"owlex-council\". Per the MCP spec, progressToken must echo the token the client attached to the originating request via _meta.progressToken. An unknown token is treated as a protocol violation:

Connection error: Received a progress notification for an unknown token:
{\"method\":\"notifications/progress\",\"params\":{\"progress\":20,\"total\":100,
 \"message\":\"Council Round 1: querying ...\",
 \"progressToken\":\"owlex-council\"}}
Closing transport (stdio transport error: Error)

2. Even with the right token, async + progress don't mix here.
council_ask returns a task_id synchronously and the deliberation runs in the background. Any progress notification the engine fires arrives AFTER the originating tools/call response — by then the client has completed that request id and deregistered its callback. Echoing back _meta.progressToken (which Claude Code auto-derives from the request id) hits the same crash a few seconds later.

Fix

Drop the progress notification entirely. Keep send_log_message, which has no token requirement and provides equivalent CLI visibility.

Test plan

  • Updated tests/test_council.py::TestCouncilNotify with two tests:
    • test_progress_notifications_are_not_sent — asserts the helper does not call send_progress_notification.
    • test_log_messages_still_flow — asserts send_log_message is still called with the right args.
  • Existing council tests still pass (uv run --with pytest --with pytest-asyncio pytest tests/test_council.py → 10 passed).
  • Verified end-to-end against Claude Code: with the patched build, council_ask no longer drops the connection during round 1.

🤖 Generated with Claude Code

`Council.notify` was sending notifications/progress with a hardcoded
`progress_token="owlex-council"`. Per MCP spec progressToken must
echo the token the client attached to the originating request via
`_meta.progressToken`. Spec-compliant clients (e.g. Claude Code)
treat an unknown token as a protocol violation and drop the stdio
transport, killing the active deliberation mid-run.

Echoing the client's own request_id back in the token doesn't help
either: `council_ask` returns a task_id synchronously and the
deliberation runs in the background, so any progress notification
arrives AFTER the originating tools/call response. The client has
already completed and deregistered its callback for that request id,
so the token is "unknown" by the time the notification lands.

Drop the progress notification entirely and rely on
`send_log_message` for CLI visibility — log messages aren't tied
to a request and don't have this issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@felixding felixding force-pushed the fix-progress-token branch from bfff3ff to fae8156 Compare April 26, 2026 09:50
@felixding felixding changed the title Fix progress notifications dropping the MCP connection Drop progress notifications to keep MCP transport stable Apr 26, 2026
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.

1 participant