Skip to content

Commit a5ac2e0

Browse files
committed
use mta field for related_request_id
1 parent a0a9c5b commit a5ac2e0

File tree

5 files changed

+51
-14
lines changed

5 files changed

+51
-14
lines changed

src/mcp/server/streamableHttp.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,10 +600,13 @@ async def message_router():
600600
if isinstance(
601601
message.root, JSONRPCNotification | JSONRPCRequest
602602
):
603-
# Extract related_request_id from params if it exists
604-
if (params := getattr(message.root, "params", None)) and (
605-
related_id := params.get("related_request_id")
606-
) is not None:
603+
# Extract related_request_id from meta if it exists
604+
if (
605+
(params := getattr(message.root, "params", None))
606+
and (meta := params.get("_meta"))
607+
and (related_id := meta.get("related_request_id"))
608+
is not None
609+
):
607610
target_request_id = str(related_id)
608611
else:
609612
target_request_id = str(message.root.id)

src/mcp/shared/session.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import Any, Generic, TypeVar
77

88
import anyio
9-
import anyio.lowlevel
109
import httpx
1110
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
1211
from pydantic import BaseModel
@@ -24,6 +23,7 @@
2423
JSONRPCNotification,
2524
JSONRPCRequest,
2625
JSONRPCResponse,
26+
NotificationParams,
2727
RequestParams,
2828
ServerNotification,
2929
ServerRequest,
@@ -276,8 +276,23 @@ async def send_notification(
276276
Emits a notification, which is a one-way message that does not expect
277277
a response.
278278
"""
279+
# Some transport implementations may need to set the related_request_id
280+
# to attribute to the notifications to the request that triggered
281+
# them.
282+
# Update notification meta with related request ID if provided
279283
if related_request_id is not None and notification.root.params is not None:
280-
notification.root.params.related_request_id = related_request_id
284+
# Create meta if it doesn't exist
285+
if notification.root.params.meta is None:
286+
# Create meta dict with related_request_id
287+
meta_dict = {"related_request_id": related_request_id}
288+
289+
else:
290+
# Update existing meta with model_validate to properly handle extra fields
291+
meta_dict = notification.root.params.meta.model_dump(
292+
by_alias=True, mode="json", exclude_none=True
293+
)
294+
meta_dict["related_request_id"] = related_request_id
295+
notification.root.params.meta = NotificationParams.Meta(**meta_dict)
281296
jsonrpc_notification = JSONRPCNotification(
282297
jsonrpc="2.0",
283298
**notification.model_dump(by_alias=True, mode="json", exclude_none=True),

src/mcp/types.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ class Meta(BaseModel):
5858
model_config = ConfigDict(extra="allow")
5959

6060
meta: Meta | None = Field(alias="_meta", default=None)
61-
related_request_id: RequestId | None = None
6261
"""
6362
This parameter name is reserved by MCP to allow clients and servers to attach
6463
additional metadata to their notifications.

tests/client/test_logging_callback.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.shared.session import RequestResponder
1010
from mcp.types import (
1111
LoggingMessageNotificationParams,
12+
NotificationParams,
1213
TextContent,
1314
)
1415

@@ -78,6 +79,11 @@ async def message_handler(
7879
)
7980
assert log_result.isError is False
8081
assert len(logging_collector.log_messages) == 1
81-
assert logging_collector.log_messages[0] == LoggingMessageNotificationParams(
82-
level="info", logger="test_logger", data="Test log message"
83-
)
82+
# Create meta object with related_request_id added dynamically
83+
meta = NotificationParams.Meta()
84+
setattr(meta, "related_request_id", "2")
85+
log = logging_collector.log_messages[0]
86+
assert log.level == "info"
87+
assert log.logger == "test_logger"
88+
assert log.data == "Test log message"
89+
assert log.meta == meta

tests/server/fastmcp/test_server.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -544,14 +544,28 @@ async def logging_tool(msg: str, ctx: Context) -> str:
544544

545545
assert mock_log.call_count == 4
546546
mock_log.assert_any_call(
547-
level="debug", data="Debug message", logger=None
547+
level="debug",
548+
data="Debug message",
549+
logger=None,
550+
related_request_id="1",
548551
)
549-
mock_log.assert_any_call(level="info", data="Info message", logger=None)
550552
mock_log.assert_any_call(
551-
level="warning", data="Warning message", logger=None
553+
level="info",
554+
data="Info message",
555+
logger=None,
556+
related_request_id="1",
552557
)
553558
mock_log.assert_any_call(
554-
level="error", data="Error message", logger=None
559+
level="warning",
560+
data="Warning message",
561+
logger=None,
562+
related_request_id="1",
563+
)
564+
mock_log.assert_any_call(
565+
level="error",
566+
data="Error message",
567+
logger=None,
568+
related_request_id="1",
555569
)
556570

557571
@pytest.mark.anyio

0 commit comments

Comments
 (0)