Title: MCP Python SDK Protocol Compliance Gap-2: Missing Logging and Propagation of Cancellation Reasons · Issue #1420 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: MCP Python SDK Protocol Compliance Gap-2: Missing Logging and Propagation of Cancellation Reasons · Issue #1420 · modelcontextprotocol/python-sdk
X Title: MCP Python SDK Protocol Compliance Gap-2: Missing Logging and Propagation of Cancellation Reasons · Issue #1420 · modelcontextprotocol/python-sdk
Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue Description Assessment: This is a ...
Open Graph Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this ...
X Description: Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening t...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/1420
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"MCP Python SDK Protocol Compliance Gap-2: Missing Logging and Propagation of Cancellation Reasons","articleBody":"### Initial Checks\n\n- [x] I confirm that I'm using the latest version of MCP Python SDK\n- [x] I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue\n\n### Description\n\n\nAssessment: This is a protocol “SHOULD” compliance gap. Both sides should log cancellation reasons. Currently, the SDK neither logs the reason when receiving a cancellation notification nor ensures the reason is populated when sending one.\n\n### Description\nThe MCP specification states: “Both parties SHOULD log cancellation reasons for debugging.” \nIn the current implementation:\n- The receiver of `CancelledNotification` does not read or log the `reason`.\n- The sender side has no high-level path that ensures the `reason` is populated in `CancelledNotificationParams.reason`.\n\n### Detailed Analysis\n- Reason field exists in the type definition but is unused in logic:\n```1200:1207:src/mcp/types.py\nclass CancelledNotificationParams(NotificationParams):\n \"\"\"Parameters for cancellation notifications.\"\"\"\n requestId: RequestId\n \"\"\"The ID of the request to cancel.\"\"\"\n reason: str | None = None\n \"\"\"An optional string describing the reason for the cancellation.\"\"\"\n model_config = ConfigDict(extra=\"allow\")\n```\n\n- Receiver path handles cancellation but does not read or log `reason`:\n```383:387:src/mcp/shared/session.py\n # Handle cancellation notifications\n if isinstance(notification.root, CancelledNotification):\n cancelled_id = notification.root.params.requestId\n if cancelled_id in self._in_flight:\n await self._in_flight[cancelled_id].cancel()\n```\n\n- Local cancellation of inbound requests sends an error response but does not log a reason:\n```135:148:src/mcp/shared/session.py\n async def cancel(self) -\u003e None:\n \"\"\"Cancel this request and mark it as completed.\"\"\"\n ...\n # Send an error response to indicate cancellation\n await self._session._send_response(\n request_id=self.request_id,\n response=ErrorData(code=0, message=\"Request cancelled\", data=None),\n )\n```\n\nObservations:\n- The receiver doesn’t consume `notification.root.params.reason`.\n- `cancel()` does not accept or log a reason.\n- There is no high-level API that constructs and sends `CancelledNotification` with a reason, so the sender side may omit it by default.\n\n### Potential Impact\n- Reduced debuggability: Missing reason logs hinder tracing why requests were cancelled.\n- Interoperability friction: Peers expecting reason-aware logs see inconsistent behavior.\n- Operational visibility loss: Harder to diagnose user-initiated vs system-initiated cancellations.\n\n### Remediation\n- Receiver-side logging:\n - Read `notification.root.params.reason` in the cancellation notification branch.\n - Log at info/warning level with `requestId` and `reason`.\n\n- Sender-side population:\n - When sending `CancelledNotification`, populate `CancelledNotificationParams.reason` with a meaningful value.\n - Add or document a helper to emit cancellation notifications with a `reason`.\n\n- Optional API improvement:\n - Extend `RequestResponder.cancel(reason: str | None)` or add `mark_cancelled_without_response(reason: str | None)` to capture and log the reason in one place.\n\n### Suggested Implementation\n- Location: `src/mcp/shared/session.py`, in the cancellation notification branch and `RequestResponder`.\n\nReceiver-side change (illustrative):\n- Read and log the reason before cancelling:\n - From:\n - `await self._in_flight[cancelled_id].cancel()`\n - To:\n - Extract `reason = notification.root.params.reason`\n - `logger.info(\"Cancelling request %s due to: %s\", cancelled_id, reason)`\n - Call a silent cancel variant that does not send a response, if aligning with the other recommendation.\n\nSender-side usage:\n- Ensure any code path that emits `CancelledNotification` fills `CancelledNotificationParams(reason=...)`, and logs locally when sending.\n\n### Impact\n- Aligns behavior with the spec’s “Both parties SHOULD log cancellation reasons.”\n- Improves observability and postmortem debugging of cancellations.\n- Sets clear semantics for both receiving and sending sides regarding cancellation reasons.\n\n### Example Code\n\n```Python\n\n```\n\n### Python \u0026 MCP Python SDK\n\n```Text\nlatest\n```","author":{"url":"https://github.com/younaman","@type":"Person","name":"younaman"},"datePublished":"2025-10-02T15:39:40.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/1420/python-sdk/issues/1420"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ea596bf1-0378-9b11-26a7-4b2dba9560d3 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | C81E:14E788:3FCD8B:536E83:6A5B2695 |
| html-safe-nonce | b5d4186f1a6f0c8586fb2e2bad9777c965c2a3ac8b65bd3a965f923a82e3e76e |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDODFFOjE0RTc4ODozRkNEOEI6NTM2RTgzOjZBNUIyNjk1IiwidmlzaXRvcl9pZCI6IjUwODk0NjQ0MDU2MjYzOTgzNTciLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 286ef7f9c102721c970f37ad222722ac6e3616a5be0d4acbc2e5733444a62e06 |
| hovercard-subject-tag | issue:3477998234 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/1420/issue_layout |
| twitter:image | https://opengraph.githubassets.com/94f325f8d75ecd715b7f2ee5137def38be7c164287053b2b5cdbddd5b83dce36/modelcontextprotocol/python-sdk/issues/1420 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/94f325f8d75ecd715b7f2ee5137def38be7c164287053b2b5cdbddd5b83dce36/modelcontextprotocol/python-sdk/issues/1420 |
| og:image:alt | Initial Checks I confirm that I'm using the latest version of MCP Python SDK I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | younaman |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width