flock-core 0.4.512__py3-none-any.whl → 0.4.514__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  """Factories for default MCP Callbacks."""
2
2
 
3
- from typing import TYPE_CHECKING, Any
3
+ from typing import Any
4
4
 
5
5
  from mcp.shared.context import RequestContext
6
6
  from mcp.types import (
@@ -8,6 +8,12 @@ from mcp.types import (
8
8
  )
9
9
 
10
10
  from flock.core.logging.logging import FlockLogger, get_logger
11
+ from flock.core.mcp.types.callbacks import (
12
+ default_list_roots_callback,
13
+ default_logging_callback,
14
+ default_message_handler,
15
+ default_sampling_callback,
16
+ )
11
17
  from flock.core.mcp.types.types import (
12
18
  FlockListRootsMCPCallback,
13
19
  FlockLoggingMCPCallback,
@@ -17,18 +23,10 @@ from flock.core.mcp.types.types import (
17
23
  ServerNotification,
18
24
  )
19
25
 
20
- if TYPE_CHECKING:
21
- from flock.core.mcp.types.callbacks import (
22
- default_list_roots_callback,
23
- default_logging_callback,
24
- default_message_handler,
25
- default_sampling_callback,
26
- )
27
-
28
- default_logging_callback_logger = get_logger("core.mcp.callback.logging")
29
- default_sampling_callback_logger = get_logger("core.mcp.callback.sampling")
30
- default_list_roots_callback_logger = get_logger("core.mcp.callback.sampling")
31
- default_message_handler_logger = get_logger("core.mcp.callback.message")
26
+ default_logging_callback_logger = get_logger("mcp.callback.logging")
27
+ default_sampling_callback_logger = get_logger("mcp.callback.sampling")
28
+ default_list_roots_callback_logger = get_logger("mcp.callback.roots")
29
+ default_message_handler_logger = get_logger("mcp.callback.message")
32
30
 
33
31
 
34
32
  def default_flock_mcp_logging_callback_factory(
@@ -88,7 +86,7 @@ def default_flock_mcp_message_handler_callback_factory(
88
86
  ) -> None:
89
87
  await default_message_handler(
90
88
  req=n,
91
- logger_to_use=logger_to_use,
89
+ logger=logger_to_use,
92
90
  associated_client=associated_client,
93
91
  )
94
92
 
@@ -8,26 +8,23 @@ from mcp.shared.context import RequestContext
8
8
  from mcp.shared.session import RequestResponder
9
9
  from mcp.types import (
10
10
  INTERNAL_ERROR,
11
+ CancelledNotification,
11
12
  ClientResult,
12
13
  ErrorData,
13
14
  ListRootsRequest,
14
- ServerNotification as _MCPServerNotification,
15
- ServerRequest,
16
- )
17
-
18
- from flock.core.logging.logging import FlockLogger
19
- from flock.core.mcp.mcp_client import Any
20
- from flock.core.mcp.types.types import (
21
- CancelledNotification,
22
- FlockLoggingMessageNotificationParams,
23
15
  LoggingMessageNotification,
16
+ LoggingMessageNotificationParams,
24
17
  ProgressNotification,
25
18
  ResourceListChangedNotification,
26
19
  ResourceUpdatedNotification,
27
20
  ServerNotification,
21
+ ServerRequest,
28
22
  ToolListChangedNotification,
29
23
  )
30
24
 
25
+ from flock.core.logging.logging import FlockLogger
26
+ from flock.core.mcp.mcp_client import Any
27
+
31
28
 
32
29
  async def handle_incoming_exception(
33
30
  e: Exception,
@@ -129,11 +126,11 @@ async def handle_tool_list_changed_notification(
129
126
  await associated_client.invalidate_tool_cache()
130
127
 
131
128
 
132
- _SERVER_NOTIFICATION_MAP: dict[type[_MCPServerNotification], Callable] = {
129
+ _SERVER_NOTIFICATION_MAP: dict[type[ServerNotification], Callable] = {
133
130
  ResourceListChangedNotification: handle_resource_list_changed_notification,
134
131
  ResourceUpdatedNotification: handle_resource_update_notification,
135
132
  LoggingMessageNotification: lambda n, log, client: handle_logging_message(
136
- params=n,
133
+ params=n.params,
137
134
  logger=log,
138
135
  server_name=client.config.name,
139
136
  ),
@@ -154,7 +151,7 @@ async def handle_incoming_server_notification(
154
151
 
155
152
 
156
153
  async def handle_logging_message(
157
- params: FlockLoggingMessageNotificationParams,
154
+ params: LoggingMessageNotificationParams,
158
155
  logger: FlockLogger,
159
156
  server_name: str,
160
157
  ) -> None:
@@ -1,9 +1,13 @@
1
1
  """Types for Flock's MCP functionality."""
2
2
 
3
+ import importlib
4
+ import inspect
5
+ import os
3
6
  from collections.abc import Awaitable, Callable
4
7
  from contextlib import AbstractAsyncContextManager
5
- from typing import Any
8
+ from typing import Any, Literal
6
9
 
10
+ import httpx
7
11
  from anyio.streams.memory import (
8
12
  MemoryObjectReceiveStream,
9
13
  MemoryObjectSendStream,
@@ -36,6 +40,7 @@ from mcp.types import (
36
40
  from pydantic import AnyUrl, BaseModel, ConfigDict, Field
37
41
 
38
42
  from flock.core.mcp.util.helpers import get_default_env
43
+ from flock.core.serialization.serializable import Serializable
39
44
 
40
45
 
41
46
  class ServerNotification(_MCPServerNotification):
@@ -80,17 +85,40 @@ class MCPRoot(_MCPRoot):
80
85
  """Wrapper for mcp.types.Root."""
81
86
 
82
87
 
83
- class ServerParameters(BaseModel):
88
+ class ServerParameters(BaseModel, Serializable):
84
89
  """Base Type for server parameters."""
85
90
 
86
91
  model_config = ConfigDict(
87
92
  arbitrary_types_allowed=True,
88
93
  )
89
94
 
95
+ transport_type: Literal["stdio", "websockets", "sse", "streamable_http"] = Field(
96
+ ...,
97
+ description="which type of transport these connection params are used for."
98
+ )
99
+
100
+ def to_dict(self, path_type: str = "relative"):
101
+ """Serialize."""
102
+ return self.model_dump(
103
+ exclude_defaults=False,
104
+ exclude_none=True,
105
+ mode="json"
106
+ )
107
+
108
+ @classmethod
109
+ def from_dict(cls, data: dict[str, Any]):
110
+ """Deserialize."""
111
+ return cls(**data)
112
+
90
113
 
91
114
  class StdioServerParameters(_MCPStdioServerParameters, ServerParameters):
92
115
  """Base Type for Stdio Server parameters."""
93
116
 
117
+ transport_type: Literal["stdio"] = Field(
118
+ default="stdio",
119
+ description="Use stdio params."
120
+ )
121
+
94
122
  env: dict[str, str] | None = Field(
95
123
  default_factory=get_default_env,
96
124
  description="Environment for the MCP Server.",
@@ -100,12 +128,121 @@ class StdioServerParameters(_MCPStdioServerParameters, ServerParameters):
100
128
  class WebsocketServerParameters(ServerParameters):
101
129
  """Base Type for Websocket Server params."""
102
130
 
131
+ transport_type: Literal["websockets"] = Field(
132
+ default="websockets",
133
+ description="Use websocket params."
134
+ )
135
+
103
136
  url: str | AnyUrl = Field(..., description="Url the server listens at.")
104
137
 
138
+ class StreamableHttpServerParameters(ServerParameters):
139
+ """Base Type for StreamableHttp params."""
140
+
141
+ transport_type: Literal["streamable_http"] = Field(
142
+ default="streamable_http",
143
+ description="Use streamable http params."
144
+ )
145
+
146
+ url: str | AnyUrl = Field(
147
+ ...,
148
+ description="The url the server listens at."
149
+ )
150
+
151
+ headers: dict[str, Any] | None = Field(
152
+ default=None,
153
+ description="Additional headers to pass to the client."
154
+ )
155
+
156
+ timeout: float | int = Field(
157
+ default=5,
158
+ description="Http Timeout",
159
+ )
160
+
161
+ sse_read_timeout: float | int = Field(
162
+ default=60*5,
163
+ description="How long the client will wait before disconnecting from the server."
164
+ )
165
+
166
+ terminate_on_close: bool = Field(
167
+ default=True,
168
+ description="Terminate connection on close"
169
+ )
170
+
171
+ auth: httpx.Auth | None = Field(
172
+ default=None,
173
+ description="Httpx Auth Scheme"
174
+ )
175
+
176
+ @classmethod
177
+ def from_dict(cls, data: dict[str, Any]):
178
+ """Deserialize the object from a dict."""
179
+ # find and import the concrete implementation for
180
+ # the auth object
181
+ auth_obj: httpx.Auth | None = None
182
+ auth_impl = data.pop("auth", None)
183
+ if auth_impl:
184
+ # find the concrete implementation
185
+ impl = auth_impl.pop("implementation", None)
186
+ params = auth_impl.pop("params", None)
187
+ if impl:
188
+ mod = importlib.import_module(impl["module_path"])
189
+ real_cls = getattr(mod, impl["classname"])
190
+ if params:
191
+ auth_obj = real_cls(**{k: v for k, v in params.items()})
192
+ else:
193
+ # assume that the implementation handles it.
194
+ auth_obj = real_cls()
195
+ else:
196
+ raise ValueError("No concrete implementation for auth provided.")
197
+
198
+ data["auth"] = auth_obj
199
+ return cls(**{k: v for k, v in data.items()})
200
+
201
+ def to_dict(self, path_type = "relative"):
202
+ """Serialize the object."""
203
+ exclude = ["auth"]
204
+
205
+ data = self.model_dump(
206
+ exclude=exclude,
207
+ exclude_defaults=False,
208
+ exclude_none=True,
209
+ )
210
+
211
+ # inject implentation info for auth
212
+ if self.auth is not None:
213
+ file_path = inspect.getsourcefile(type(self.auth))
214
+ if path_type == "relative":
215
+ file_path = os.path.relpath(file_path)
216
+ try:
217
+ # params should be primitive types, keeping with the
218
+ # declarative approach of flock.
219
+ params = {
220
+ k: getattr(self.auth, k)
221
+ for k in getattr(self.auth, "__dict__", {})
222
+ if not k.startswith("_")
223
+ }
224
+ except Exception:
225
+ params = None
226
+
227
+ data["auth"] = {
228
+ "implementation": {
229
+ "class_name": type(self.auth).__name__,
230
+ "module_path": type(self.auth).__module__,
231
+ "file_path": file_path,
232
+ },
233
+ "params": params,
234
+ }
235
+
236
+ return data
105
237
 
106
238
  class SseServerParameters(ServerParameters):
107
239
  """Base Type for SSE Server params."""
108
240
 
241
+ transport_type: Literal["sse"] = Field(
242
+ default="sse",
243
+ description="Use sse server params."
244
+ )
245
+
109
246
  url: str | AnyUrl = Field(..., description="The url the server listens at.")
110
247
 
111
248
  headers: dict[str, Any] | None = Field(
@@ -119,6 +256,72 @@ class SseServerParameters(ServerParameters):
119
256
  description="How long the client will wait before disconnecting from the server.",
120
257
  )
121
258
 
259
+ auth: httpx.Auth | None = Field(
260
+ default=None,
261
+ description="Httpx Auth Scheme."
262
+ )
263
+
264
+ @classmethod
265
+ def from_dict(cls, data: dict[str, Any]):
266
+ """Deserialize the object from a dict."""
267
+ # find and import the concrete implementation for
268
+ # the auth object.
269
+ auth_obj: httpx.Auth | None = None
270
+ auth_impl = data.pop("auth", None) # get the specs for the auth class
271
+ if auth_impl:
272
+ # find the concrete implementation
273
+ impl = auth_impl.pop("implementation", None)
274
+ params = auth_impl.pop("params", None)
275
+ if impl:
276
+ mod = importlib.import_module(impl["module_path"])
277
+ real_cls = getattr(mod, impl["class_name"])
278
+ if params:
279
+ auth_obj = real_cls(**{k: v for k, v in params.items()})
280
+ else:
281
+ # assume that implementation handles it
282
+ auth_obj = real_cls()
283
+ else:
284
+ raise ValueError("No concrete implementation for auth provided.")
285
+
286
+ data["auth"] = auth_obj
287
+ return cls(**{k: v for k, v in data.items()})
288
+
289
+ def to_dict(self, path_type = "relative"):
290
+ """Serialize the object."""
291
+ exclude = ["auth"]
292
+
293
+ data = self.model_dump(
294
+ exclude=exclude,
295
+ exclude_defaults=False,
296
+ exclude_none=True,
297
+ )
298
+
299
+ # inject implentation info for auth
300
+ if self.auth is not None:
301
+ file_path = inspect.getsourcefile(type(self.auth))
302
+ if path_type == "relative":
303
+ file_path = os.path.relpath(file_path)
304
+ try:
305
+ # params should be primitive types, keeping with the
306
+ # declarative approach of flock.
307
+ params = {
308
+ k: getattr(self.auth, k)
309
+ for k in getattr(self.auth, "__dict__", {})
310
+ if not k.startswith("_")
311
+ }
312
+ except Exception:
313
+ params = None
314
+
315
+ data["auth"] = {
316
+ "implementation": {
317
+ "class_name": type(self.auth).__name__,
318
+ "module_path": type(self.auth).__module__,
319
+ "file_path": file_path,
320
+ },
321
+ "params": params,
322
+ }
323
+
324
+ return data
122
325
 
123
326
  MCPCLientInitFunction = Callable[
124
327
  ...,
@@ -9,7 +9,7 @@ from typing import Any, Literal
9
9
  from dspy import Tool
10
10
 
11
11
  from flock.core.logging.logging import get_logger
12
- from flock.core.util.spliter import split_top_level
12
+ from flock.core.util.splitter import split_top_level
13
13
 
14
14
  # Import split_top_level (assuming it's moved or copied appropriately)
15
15
  # Option 1: If moved to a shared util
@@ -1,7 +1,7 @@
1
1
  """Utility functions for resolving input keys to their corresponding values."""
2
2
 
3
3
  from flock.core.context.context import FlockContext
4
- from flock.core.util.spliter import split_top_level
4
+ from flock.core.util.splitter import split_top_level
5
5
 
6
6
 
7
7
  def get_callable_members(obj):
@@ -4,12 +4,13 @@ import copy
4
4
  from contextlib import AbstractAsyncContextManager
5
5
  from typing import Any, Literal
6
6
 
7
+ import httpx
7
8
  from anyio.streams.memory import (
8
9
  MemoryObjectReceiveStream,
9
10
  MemoryObjectSendStream,
10
11
  )
11
12
  from mcp.client.sse import sse_client
12
- from mcp.types import JSONRPCMessage
13
+ from mcp.shared.message import SessionMessage
13
14
  from opentelemetry import trace
14
15
  from pydantic import Field
15
16
 
@@ -63,45 +64,51 @@ class FlockSSEClient(FlockMCPClientBase):
63
64
  additional_params: dict[str, Any] | None = None,
64
65
  ) -> AbstractAsyncContextManager[
65
66
  tuple[
66
- MemoryObjectReceiveStream[JSONRPCMessage | Exception],
67
- MemoryObjectSendStream[JSONRPCMessage],
67
+ MemoryObjectReceiveStream[SessionMessage | Exception],
68
+ MemoryObjectSendStream[SessionMessage],
68
69
  ]
69
70
  ]:
70
71
  """Return an async context manager whose __aenter__ method yields (read_stream, send_stream)."""
71
72
  # avoid modifying the config of the client as a side-effect.
72
73
  param_copy = copy.deepcopy(params)
73
74
 
74
- if self.additional_params:
75
+ if additional_params:
75
76
  override_headers = bool(
76
- self.additional_params.get("override_headers", False)
77
+ additional_params.get("override_headers", False)
77
78
  )
78
- if "headers" in self.additional_params:
79
+ if "headers" in additional_params:
79
80
  if override_headers:
80
- param_copy.headers = self.additional_params.get(
81
+ param_copy.headers = additional_params.get(
81
82
  "headers", params.headers
82
83
  )
83
84
  else:
84
85
  param_copy.headers.update(
85
- self.additional_params.get("headers", {})
86
+ additional_params.get("headers", {})
86
87
  )
87
- if "read_timeout_seconds" in self.additional_params:
88
- param_copy.timeout = self.additional_params.get(
88
+ if "read_timeout_seconds" in additional_params:
89
+ param_copy.timeout = additional_params.get(
89
90
  "read_timeout_seconds", params.timeout
90
91
  )
91
92
 
92
- if "sse_read_timeout" in self.additional_params:
93
- param_copy.sse_read_timeout = self.additional_params.get(
93
+ if "sse_read_timeout" in additional_params:
94
+ param_copy.sse_read_timeout = additional_params.get(
94
95
  "sse_read_timeout",
95
96
  params.sse_read_timeout,
96
97
  )
97
- if "url" in self.additional_params:
98
- param_copy.url = self.additional_params.get(
98
+ if "url" in additional_params:
99
+ param_copy.url = additional_params.get(
99
100
  "url",
100
101
  params.url,
101
102
  )
102
103
 
104
+ if "auth" in additional_params and isinstance(
105
+ additional_params.get("auth"), httpx.Auth
106
+ ):
107
+ param_copy.auth = additional_params.get("auth", param_copy.auth)
108
+
103
109
  return sse_client(
104
110
  url=param_copy.url,
111
+ auth=param_copy.auth,
105
112
  headers=param_copy.headers,
106
113
  timeout=float(param_copy.timeout),
107
114
  sse_read_timeout=float(param_copy.sse_read_timeout),
File without changes
@@ -0,0 +1,169 @@
1
+ """This module provides the Flock Streamable-Http functionality."""
2
+
3
+ import copy
4
+ from collections.abc import Callable
5
+ from contextlib import AbstractAsyncContextManager
6
+ from datetime import timedelta
7
+ from typing import Any, Literal
8
+
9
+ import httpx
10
+ from anyio.streams.memory import (
11
+ MemoryObjectReceiveStream,
12
+ MemoryObjectSendStream,
13
+ )
14
+ from mcp.client.streamable_http import streamablehttp_client
15
+ from mcp.shared.message import SessionMessage
16
+ from opentelemetry import trace
17
+ from pydantic import Field
18
+
19
+ from flock.core.logging.logging import get_logger
20
+ from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
21
+ from flock.core.mcp.mcp_client import FlockMCPClientBase
22
+ from flock.core.mcp.mcp_client_manager import FlockMCPClientManagerBase
23
+ from flock.core.mcp.mcp_config import (
24
+ FlockMCPConfigurationBase,
25
+ FlockMCPConnectionConfigurationBase,
26
+ )
27
+ from flock.core.mcp.types.types import (
28
+ StreamableHttpServerParameters,
29
+ )
30
+
31
+ logger = get_logger("mcp.streamable_http.server")
32
+ tracer = trace.get_tracer(__name__)
33
+
34
+ GetSessionIdCallback = Callable[[], str | None]
35
+
36
+
37
+ class FlockStreamableHttpConnectionConfig(FlockMCPConnectionConfigurationBase):
38
+ """Concrete ConnectionConfig for a StreamableHttpClient."""
39
+
40
+ # Only thing we need to override here is the concrete transport_type
41
+ # and connection parameter fields.
42
+ transport_type: Literal["streamable_http"] = Field(
43
+ default="streamable_http",
44
+ description="Use the streamable_http Transport type.",
45
+ )
46
+
47
+ connection_parameters: StreamableHttpServerParameters = Field(
48
+ ..., description="Streamable HTTP Server Connection Parameters."
49
+ )
50
+
51
+
52
+ class FlockStreamableHttpConfig(FlockMCPConfigurationBase):
53
+ """Configuration for Streamable HTTP Clients."""
54
+
55
+ # The only thing we need to override here is the
56
+ # concrete connection config.
57
+ # The rest is generic enough to handle everything else.
58
+ connection_config: FlockStreamableHttpConnectionConfig = Field(
59
+ ..., description="Concrete StreamableHttp Connection Configuration."
60
+ )
61
+
62
+
63
+ class FlockStreamableHttpClient(FlockMCPClientBase):
64
+ """Client for StreamableHttpServers."""
65
+
66
+ config: FlockStreamableHttpConfig = Field(
67
+ ..., description="Client configuration."
68
+ )
69
+
70
+ async def create_transport(
71
+ self,
72
+ params: StreamableHttpServerParameters,
73
+ additional_params: dict[str, Any] | None = None,
74
+ ) -> AbstractAsyncContextManager[
75
+ tuple[
76
+ MemoryObjectReceiveStream[SessionMessage | Exception],
77
+ MemoryObjectSendStream[SessionMessage],
78
+ GetSessionIdCallback,
79
+ ],
80
+ None,
81
+ ]:
82
+ """Return an async context manager whose __aenter__ method yields (read_stream, send_stream)."""
83
+ param_copy = copy.deepcopy(params)
84
+
85
+ if additional_params:
86
+ override_headers = bool(
87
+ additional_params.get("override_headers", False)
88
+ )
89
+
90
+ if "headers" in additional_params:
91
+ if override_headers:
92
+ param_copy.headers = additional_params.get(
93
+ "headers", params.headers
94
+ )
95
+ else:
96
+ param_copy.headers.update(
97
+ additional_params.get("headers", {})
98
+ )
99
+ if "auth" in additional_params and isinstance(
100
+ additional_params.get("auth"), httpx.Auth
101
+ ):
102
+ param_copy.auth = additional_params.get("auth", param_copy.auth)
103
+
104
+ if "read_timeout_seconds" in additional_params:
105
+ param_copy.timeout = additional_params.get(
106
+ "read_timeout_seconds", params.timeout
107
+ )
108
+
109
+ if "sse_read_timeout" in additional_params:
110
+ param_copy.sse_read_timeout = additional_params.get(
111
+ "sse_read_timeout",
112
+ params.sse_read_timeout,
113
+ )
114
+ if "url" in additional_params:
115
+ param_copy.url = additional_params.get(
116
+ "url",
117
+ params.url,
118
+ )
119
+
120
+ if "terminate_on_close" in additional_params:
121
+ param_copy.terminate_on_close = bool(
122
+ additional_params.get("terminate_on_close", True)
123
+ )
124
+
125
+ timeout_http = timedelta(seconds=param_copy.timeout)
126
+ sse_timeout = timedelta(seconds=param_copy.sse_read_timeout)
127
+
128
+ return streamablehttp_client(
129
+ url=param_copy.url,
130
+ headers=param_copy.headers,
131
+ timeout=timeout_http,
132
+ sse_read_timeout=sse_timeout,
133
+ terminate_on_close=param_copy.terminate_on_close,
134
+ auth=param_copy.auth,
135
+ )
136
+
137
+
138
+ class FlockStreamableHttpClientManager(FlockMCPClientManagerBase):
139
+ """Manager for handling StreamableHttpClients."""
140
+
141
+ client_config: FlockStreamableHttpConfig = Field(
142
+ ..., description="Configuration for clients."
143
+ )
144
+
145
+ async def make_client(
146
+ self, additional_params: dict[str, Any] | None = None
147
+ ) -> FlockStreamableHttpClient:
148
+ """Create a new client instance."""
149
+ new_client = FlockStreamableHttpClient(
150
+ config=self.client_config,
151
+ additional_params=additional_params,
152
+ )
153
+ return new_client
154
+
155
+
156
+ class FlockStreamableHttpServer(FlockMCPServerBase):
157
+ """Class which represents a MCP Server using the streamable Http Transport type."""
158
+
159
+ config: FlockStreamableHttpConfig = Field(
160
+ ..., description="Config for the server."
161
+ )
162
+
163
+ async def initialize(self) -> FlockStreamableHttpClientManager:
164
+ """Called when initializing the server."""
165
+ client_manager = FlockStreamableHttpClientManager(
166
+ client_config=self.config
167
+ )
168
+
169
+ return client_manager
@@ -9,7 +9,7 @@ from anyio.streams.memory import (
9
9
  MemoryObjectSendStream,
10
10
  )
11
11
  from mcp.client.websocket import websocket_client
12
- from mcp.types import JSONRPCMessage
12
+ from mcp.shared.message import SessionMessage
13
13
  from opentelemetry import trace
14
14
  from pydantic import Field
15
15
 
@@ -69,8 +69,8 @@ class FlockWSClient(FlockMCPClientBase):
69
69
  additional_params: dict[str, Any] | None = None,
70
70
  ) -> AbstractAsyncContextManager[
71
71
  tuple[
72
- MemoryObjectReceiveStream[JSONRPCMessage | Exception],
73
- MemoryObjectSendStream[JSONRPCMessage],
72
+ MemoryObjectReceiveStream[SessionMessage | Exception],
73
+ MemoryObjectSendStream[SessionMessage],
74
74
  ]
75
75
  ]:
76
76
  """Return an async context manager whose __aenter__ method yields a read_stream and a send_stream."""