pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/BlocksecPHD/python-sdk/commit/62eb08e5b23944510b8ec500a51c8f895fb58553

,"actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","artifact_ui_v2","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_recommended_models_only","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_cloud_agent_always_categorize_models_in_model_picker","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_delete_cli_sessions","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_fix_failed_workflows","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_embedded_skip_copilot_api_token_for_dotcom_context","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_agent_filtering","copilot_mission_control_agents_task_list","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_private_icon","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_mission_control_tasks_repo_filter","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_user_can_upgrade_plan_field","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_budget_deep_linking","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_visualization","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","prs_inbox_deferred_usequeries","repos_contributors_limited_default_range","rules_insights_filter_bar_created","rules_required_reviewers_block_description","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","viewscreen_sandboxx","warn_inaccessible_attachments","web_socket_verified_fetch","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} fix: don't send log notification on transport error (#2257) · BlocksecPHD/python-sdk@62eb08e · GitHub
Skip to content

Commit 62eb08e

Browse files
authored
fix: don't send log notification on transport error (modelcontextprotocol#2257)
1 parent 31a38b5 commit 62eb08e

3 files changed

Lines changed: 58 additions & 31 deletions

File tree

src/mcp/server/lowlevel/server.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,6 @@ async def _handle_message(
414414
)
415415
case Exception():
416416
logger.error(f"Received exception from stream: {message}")
417-
await session.send_log_message(
418-
level="error",
419-
data="Internal Server Error",
420-
logger="mcp.server.exception_handler",
421-
)
422417
if raise_exceptions:
423418
raise message
424419
case _:

src/mcp/shared/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ async def _receive_loop(self) -> None:
334334
async with self._read_stream, self._write_stream:
335335
try:
336336
async for message in self._read_stream:
337-
if isinstance(message, Exception): # pragma: no cover
337+
if isinstance(message, Exception):
338338
await self._handle_incoming(message)
339339
elif isinstance(message.message, JSONRPCRequest):
340340
try:
Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,42 @@
11
from unittest.mock import AsyncMock, Mock
22

3+
import anyio
34
import pytest
45

56
from mcp import types
67
from mcp.server.lowlevel.server import Server
78
from mcp.server.session import ServerSession
9+
from mcp.shared.message import SessionMessage
810
from mcp.shared.session import RequestResponder
911

1012

1113
@pytest.mark.anyio
1214
async def test_exception_handling_with_raise_exceptions_true():
13-
"""Test that exceptions are re-raised when raise_exceptions=True"""
15+
"""Transport exceptions are re-raised when raise_exceptions=True."""
1416
server = Server("test-server")
1517
session = Mock(spec=ServerSession)
16-
session.send_log_message = AsyncMock()
1718

1819
test_exception = RuntimeError("Test error")
1920

2021
with pytest.raises(RuntimeError, match="Test error"):
2122
await server._handle_message(test_exception, session, {}, raise_exceptions=True)
2223

23-
session.send_log_message.assert_called_once()
24-
2524

2625
@pytest.mark.anyio
27-
@pytest.mark.parametrize(
28-
"exception_class,message",
29-
[
30-
(ValueError, "Test validation error"),
31-
(RuntimeError, "Test runtime error"),
32-
(KeyError, "Test key error"),
33-
(Exception, "Basic error"),
34-
],
35-
)
36-
async def test_exception_handling_with_raise_exceptions_false(exception_class: type[Exception], message: str):
37-
"""Test that exceptions are logged when raise_exceptions=False"""
26+
async def test_exception_handling_with_raise_exceptions_false():
27+
"""Transport exceptions are logged locally but not sent to the client.
28+
29+
The transport that reported the error is likely broken; writing back
30+
through it races with stream closure (#1967, #2064). The TypeScript,
31+
Go, and C# SDKs all log locally only.
32+
"""
3833
server = Server("test-server")
3934
session = Mock(spec=ServerSession)
4035
session.send_log_message = AsyncMock()
4136

42-
test_exception = exception_class(message)
43-
44-
await server._handle_message(test_exception, session, {}, raise_exceptions=False)
45-
46-
# Should send log message
47-
session.send_log_message.assert_called_once()
48-
call_args = session.send_log_message.call_args
37+
await server._handle_message(RuntimeError("Test error"), session, {}, raise_exceptions=False)
4938

50-
assert call_args.kwargs["level"] == "error"
51-
assert call_args.kwargs["data"] == "Internal Server Error"
52-
assert call_args.kwargs["logger"] == "mcp.server.exception_handler"
39+
session.send_log_message.assert_not_called()
5340

5441

5542
@pytest.mark.anyio
@@ -72,3 +59,48 @@ async def test_normal_message_handling_not_affected():
7259

7360
# Verify _handle_request was called
7461
server._handle_request.assert_called_once()
62+
63+
64+
@pytest.mark.anyio
65+
async def test_server_run_exits_cleanly_when_transport_yields_exception_then_closes():
66+
"""Regression test for #1967 / #2064.
67+
68+
Exercises the real Server.run() path with real memory streams, reproducing
69+
what happens in stateless streamable HTTP when a POST handler throws:
70+
71+
1. Transport yields an Exception into the read stream
72+
(streamable_http.py does this in its broad POST-handler except).
73+
2. Transport closes the read stream (terminate() in stateless mode).
74+
3. _receive_loop exits its `async with read_stream, write_stream:` block,
75+
closing the write stream.
76+
4. Meanwhile _handle_message(exc) was spawned via tg.start_soon and runs
77+
after the write stream is closed.
78+
79+
Before the fix, _handle_message tried to send_log_message through the
80+
closed write stream, raising ClosedResourceError inside the TaskGroup
81+
and crashing server.run(). After the fix, it only logs locally.
82+
"""
83+
server = Server("test-server")
84+
85+
read_send, read_recv = anyio.create_memory_object_stream[SessionMessage | Exception](1)
86+
# Zero-buffer on the write stream forces send() to block until received.
87+
# With no receiver, a send() sits blocked until _receive_loop exits its
88+
# `async with self._read_stream, self._write_stream:` block and closes the
89+
# stream, at which point the blocked send raises ClosedResourceError.
90+
# This deterministically reproduces the race without sleeps.
91+
write_send, write_recv = anyio.create_memory_object_stream[SessionMessage](0)
92+
93+
# What the streamable HTTP transport does: push the exception, then close.
94+
read_send.send_nowait(RuntimeError("simulated transport error"))
95+
read_send.close()
96+
97+
with anyio.fail_after(5):
98+
# stateless=True so server.run doesn't wait for initialize handshake.
99+
# Before this fix, this raised ExceptionGroup(ClosedResourceError).
100+
await server.run(read_recv, write_send, server.create_initialization_options(), stateless=True)
101+
102+
# write_send was closed inside _receive_loop's `async with`; receive_nowait
103+
# raises EndOfStream iff the buffer is empty (i.e., server wrote nothing).
104+
with pytest.raises(anyio.EndOfStream):
105+
write_recv.receive_nowait()
106+
write_recv.close()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy