fix: monkey-patch mcp cancellation race crash (SDK issue #2416)
Some checks failed
Build and Push Docker Image / build (push) Failing after 28s
Some checks failed
Build and Push Docker Image / build (push) Failing after 28s
Patch RequestResponder.respond() and cancel() at startup to handle the race where a notifications/cancelled arrives between handler return and respond(), which crashes the session with "AssertionError: Request already responded to". Also improve build.sh to handle registry push failures gracefully and auto-restart the container after building.
This commit is contained in:
35
server.py
35
server.py
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional, Any
|
||||
|
||||
@@ -11,6 +12,40 @@ import uvicorn
|
||||
# Import the unified API from the schwab_scraper dependency
|
||||
import schwab_scraper.unified_api as api
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Monkey-patch mcp.shared.session.RequestResponder to work around a
|
||||
# cancellation race in mcp==1.27.0 (github.com/modelcontextprotocol/
|
||||
# python-sdk/issues/2416). A concurrent notifications/cancelled can set
|
||||
# _completed=True between handler return and respond(), crashing the session
|
||||
# with "AssertionError: Request already responded to".
|
||||
# Remove once upstream ships a fix (likely mcp>=1.28).
|
||||
# ---------------------------------------------------------------------------
|
||||
def _patch_request_responder():
|
||||
from mcp.shared.session import RequestResponder
|
||||
|
||||
_orig_respond = RequestResponder.respond
|
||||
|
||||
async def _safe_respond(self, response):
|
||||
if self._completed:
|
||||
logging.debug(
|
||||
"respond() skipped for request %s — already completed (race with cancel)",
|
||||
self.request_id,
|
||||
)
|
||||
return
|
||||
return await _orig_respond(self, response)
|
||||
|
||||
_orig_cancel = RequestResponder.cancel
|
||||
|
||||
async def _safe_cancel(self):
|
||||
if self._completed:
|
||||
return
|
||||
return await _orig_cancel(self)
|
||||
|
||||
RequestResponder.respond = _safe_respond
|
||||
RequestResponder.cancel = _safe_cancel
|
||||
|
||||
_patch_request_responder()
|
||||
|
||||
# Initialize FastMCP
|
||||
mcp = FastMCP("SchwabScraper")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user