acp-sdk 0.0.6__py3-none-any.whl → 1.0.0rc1__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.
Files changed (69) hide show
  1. acp_sdk/client/__init__.py +1 -0
  2. acp_sdk/client/client.py +135 -0
  3. acp_sdk/models.py +219 -0
  4. acp_sdk/server/__init__.py +2 -0
  5. acp_sdk/server/agent.py +32 -0
  6. acp_sdk/server/bundle.py +133 -0
  7. acp_sdk/server/context.py +6 -0
  8. acp_sdk/server/server.py +137 -0
  9. acp_sdk/server/telemetry.py +45 -0
  10. acp_sdk/server/utils.py +12 -0
  11. acp_sdk-1.0.0rc1.dist-info/METADATA +53 -0
  12. acp_sdk-1.0.0rc1.dist-info/RECORD +15 -0
  13. acp/__init__.py +0 -138
  14. acp/cli/__init__.py +0 -6
  15. acp/cli/claude.py +0 -139
  16. acp/cli/cli.py +0 -471
  17. acp/client/__main__.py +0 -79
  18. acp/client/session.py +0 -372
  19. acp/client/sse.py +0 -145
  20. acp/client/stdio.py +0 -153
  21. acp/server/__init__.py +0 -3
  22. acp/server/__main__.py +0 -50
  23. acp/server/highlevel/__init__.py +0 -9
  24. acp/server/highlevel/agents/__init__.py +0 -5
  25. acp/server/highlevel/agents/agent_manager.py +0 -110
  26. acp/server/highlevel/agents/base.py +0 -20
  27. acp/server/highlevel/agents/templates.py +0 -21
  28. acp/server/highlevel/context.py +0 -185
  29. acp/server/highlevel/exceptions.py +0 -25
  30. acp/server/highlevel/prompts/__init__.py +0 -4
  31. acp/server/highlevel/prompts/base.py +0 -167
  32. acp/server/highlevel/prompts/manager.py +0 -50
  33. acp/server/highlevel/prompts/prompt_manager.py +0 -33
  34. acp/server/highlevel/resources/__init__.py +0 -23
  35. acp/server/highlevel/resources/base.py +0 -48
  36. acp/server/highlevel/resources/resource_manager.py +0 -94
  37. acp/server/highlevel/resources/templates.py +0 -80
  38. acp/server/highlevel/resources/types.py +0 -185
  39. acp/server/highlevel/server.py +0 -705
  40. acp/server/highlevel/tools/__init__.py +0 -4
  41. acp/server/highlevel/tools/base.py +0 -83
  42. acp/server/highlevel/tools/tool_manager.py +0 -53
  43. acp/server/highlevel/utilities/__init__.py +0 -1
  44. acp/server/highlevel/utilities/func_metadata.py +0 -210
  45. acp/server/highlevel/utilities/logging.py +0 -43
  46. acp/server/highlevel/utilities/types.py +0 -54
  47. acp/server/lowlevel/__init__.py +0 -3
  48. acp/server/lowlevel/helper_types.py +0 -9
  49. acp/server/lowlevel/server.py +0 -643
  50. acp/server/models.py +0 -17
  51. acp/server/session.py +0 -315
  52. acp/server/sse.py +0 -175
  53. acp/server/stdio.py +0 -83
  54. acp/server/websocket.py +0 -61
  55. acp/shared/__init__.py +0 -0
  56. acp/shared/context.py +0 -14
  57. acp/shared/exceptions.py +0 -14
  58. acp/shared/memory.py +0 -87
  59. acp/shared/progress.py +0 -40
  60. acp/shared/session.py +0 -413
  61. acp/shared/version.py +0 -3
  62. acp/types.py +0 -1258
  63. acp_sdk-0.0.6.dist-info/METADATA +0 -46
  64. acp_sdk-0.0.6.dist-info/RECORD +0 -57
  65. acp_sdk-0.0.6.dist-info/entry_points.txt +0 -2
  66. acp_sdk-0.0.6.dist-info/licenses/LICENSE +0 -22
  67. {acp/client → acp_sdk}/__init__.py +0 -0
  68. {acp → acp_sdk}/py.typed +0 -0
  69. {acp_sdk-0.0.6.dist-info → acp_sdk-1.0.0rc1.dist-info}/WHEEL +0 -0
acp/server/session.py DELETED
@@ -1,315 +0,0 @@
1
- """
2
- ServerSession Module
3
-
4
- This module provides the ServerSession class, which manages communication between the
5
- server and client in the MCP (Model Context Protocol) framework. It is most commonly
6
- used in MCP servers to interact with the client.
7
-
8
- Common usage pattern:
9
- ```
10
- server = Server(name)
11
-
12
- @server.call_tool()
13
- async def handle_tool_call(ctx: RequestContext, arguments: dict[str, Any]) -> Any:
14
- # Check client capabilities before proceeding
15
- if ctx.session.check_client_capability(
16
- types.ClientCapabilities(experimental={"advanced_tools": dict()})
17
- ):
18
- # Perform advanced tool operations
19
- result = await perform_advanced_tool_operation(arguments)
20
- else:
21
- # Fall back to basic tool operations
22
- result = await perform_basic_tool_operation(arguments)
23
-
24
- return result
25
-
26
- @server.list_prompts()
27
- async def handle_list_prompts(ctx: RequestContext) -> list[types.Prompt]:
28
- # Access session for any necessary checks or operations
29
- if ctx.session.client_params:
30
- # Customize prompts based on client initialization parameters
31
- return generate_custom_prompts(ctx.session.client_params)
32
- else:
33
- return default_prompts
34
- ```
35
-
36
- The ServerSession class is typically used internally by the Server class and should not
37
- be instantiated directly by users of the MCP framework.
38
- """
39
-
40
- from enum import Enum
41
- from typing import Any
42
-
43
- import anyio
44
- import anyio.lowlevel
45
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
46
- from pydantic import AnyUrl
47
-
48
- import acp.types as types
49
- from acp.server.models import InitializationOptions
50
- from acp.shared.session import (
51
- BaseSession,
52
- RequestResponder,
53
- )
54
-
55
-
56
- class InitializationState(Enum):
57
- NotInitialized = 1
58
- Initializing = 2
59
- Initialized = 3
60
-
61
-
62
- class ServerSession(
63
- BaseSession[
64
- types.ServerRequest,
65
- types.ServerNotification,
66
- types.ServerResult,
67
- types.ClientRequest,
68
- types.ClientNotification,
69
- ]
70
- ):
71
- _initialized: InitializationState = InitializationState.NotInitialized
72
- _client_params: types.InitializeRequestParams | None = None
73
-
74
- def __init__(
75
- self,
76
- read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception],
77
- write_stream: MemoryObjectSendStream[types.JSONRPCMessage],
78
- init_options: InitializationOptions,
79
- ) -> None:
80
- super().__init__(
81
- read_stream, write_stream, types.ClientRequest, types.ClientNotification
82
- )
83
- self._initialization_state = InitializationState.NotInitialized
84
- self._init_options = init_options
85
-
86
- @property
87
- def client_params(self) -> types.InitializeRequestParams | None:
88
- return self._client_params
89
-
90
- def check_client_capability(self, capability: types.ClientCapabilities) -> bool:
91
- """Check if the client supports a specific capability."""
92
- if self._client_params is None:
93
- return False
94
-
95
- # Get client capabilities from initialization params
96
- client_caps = self._client_params.capabilities
97
-
98
- # Check each specified capability in the passed in capability object
99
- if capability.roots is not None:
100
- if client_caps.roots is None:
101
- return False
102
- if capability.roots.listChanged and not client_caps.roots.listChanged:
103
- return False
104
-
105
- if capability.sampling is not None:
106
- if client_caps.sampling is None:
107
- return False
108
-
109
- if capability.experimental is not None:
110
- if client_caps.experimental is None:
111
- return False
112
- # Check each experimental capability
113
- for exp_key, exp_value in capability.experimental.items():
114
- if (
115
- exp_key not in client_caps.experimental
116
- or client_caps.experimental[exp_key] != exp_value
117
- ):
118
- return False
119
-
120
- return True
121
-
122
- async def _received_request(
123
- self, responder: RequestResponder[types.ClientRequest, types.ServerResult]
124
- ):
125
- match responder.request.root:
126
- case types.InitializeRequest(params=params):
127
- self._initialization_state = InitializationState.Initializing
128
- self._client_params = params
129
- async with responder:
130
- await responder.respond(
131
- types.ServerResult(
132
- types.InitializeResult(
133
- protocolVersion=types.LATEST_PROTOCOL_VERSION,
134
- capabilities=self._init_options.capabilities,
135
- serverInfo=types.Implementation(
136
- name=self._init_options.server_name,
137
- version=self._init_options.server_version,
138
- ),
139
- instructions=self._init_options.instructions,
140
- )
141
- )
142
- )
143
- case _:
144
- if self._initialization_state != InitializationState.Initialized:
145
- raise RuntimeError(
146
- "Received request before initialization was complete"
147
- )
148
-
149
- async def _received_notification(
150
- self, notification: types.ClientNotification
151
- ) -> None:
152
- # Need this to avoid ASYNC910
153
- await anyio.lowlevel.checkpoint()
154
- match notification.root:
155
- case types.InitializedNotification():
156
- self._initialization_state = InitializationState.Initialized
157
- case _:
158
- if self._initialization_state != InitializationState.Initialized:
159
- raise RuntimeError(
160
- "Received notification before initialization was complete"
161
- )
162
-
163
- async def send_log_message(
164
- self, level: types.LoggingLevel, data: Any, logger: str | None = None
165
- ) -> None:
166
- """Send a log message notification."""
167
- await self.send_notification(
168
- types.ServerNotification(
169
- types.LoggingMessageNotification(
170
- method="notifications/message",
171
- params=types.LoggingMessageNotificationParams(
172
- level=level,
173
- data=data,
174
- logger=logger,
175
- ),
176
- )
177
- )
178
- )
179
-
180
- async def send_resource_updated(self, uri: AnyUrl) -> None:
181
- """Send a resource updated notification."""
182
- await self.send_notification(
183
- types.ServerNotification(
184
- types.ResourceUpdatedNotification(
185
- method="notifications/resources/updated",
186
- params=types.ResourceUpdatedNotificationParams(uri=uri),
187
- )
188
- )
189
- )
190
-
191
- async def create_message(
192
- self,
193
- messages: list[types.SamplingMessage],
194
- *,
195
- max_tokens: int,
196
- system_prompt: str | None = None,
197
- include_context: types.IncludeContext | None = None,
198
- temperature: float | None = None,
199
- stop_sequences: list[str] | None = None,
200
- metadata: dict[str, Any] | None = None,
201
- model_preferences: types.ModelPreferences | None = None,
202
- ) -> types.CreateMessageResult:
203
- """Send a sampling/create_message request."""
204
- return await self.send_request(
205
- types.ServerRequest(
206
- types.CreateMessageRequest(
207
- method="sampling/createMessage",
208
- params=types.CreateMessageRequestParams(
209
- messages=messages,
210
- systemPrompt=system_prompt,
211
- includeContext=include_context,
212
- temperature=temperature,
213
- maxTokens=max_tokens,
214
- stopSequences=stop_sequences,
215
- metadata=metadata,
216
- modelPreferences=model_preferences,
217
- ),
218
- )
219
- ),
220
- types.CreateMessageResult,
221
- )
222
-
223
- async def list_roots(self) -> types.ListRootsResult:
224
- """Send a roots/list request."""
225
- return await self.send_request(
226
- types.ServerRequest(
227
- types.ListRootsRequest(
228
- method="roots/list",
229
- )
230
- ),
231
- types.ListRootsResult,
232
- )
233
-
234
- async def send_ping(self) -> types.EmptyResult:
235
- """Send a ping request."""
236
- return await self.send_request(
237
- types.ServerRequest(
238
- types.PingRequest(
239
- method="ping",
240
- )
241
- ),
242
- types.EmptyResult,
243
- )
244
-
245
- async def send_progress_notification(
246
- self, progress_token: str | int, progress: float, total: float | None = None
247
- ) -> None:
248
- """Send a progress notification."""
249
- await self.send_notification(
250
- types.ServerNotification(
251
- types.ProgressNotification(
252
- method="notifications/progress",
253
- params=types.ProgressNotificationParams(
254
- progressToken=progress_token,
255
- progress=progress,
256
- total=total,
257
- ),
258
- )
259
- )
260
- )
261
-
262
- async def send_resource_list_changed(self) -> None:
263
- """Send a resource list changed notification."""
264
- await self.send_notification(
265
- types.ServerNotification(
266
- types.ResourceListChangedNotification(
267
- method="notifications/resources/list_changed",
268
- )
269
- )
270
- )
271
-
272
- async def send_tool_list_changed(self) -> None:
273
- """Send a tool list changed notification."""
274
- await self.send_notification(
275
- types.ServerNotification(
276
- types.ToolListChangedNotification(
277
- method="notifications/tools/list_changed",
278
- )
279
- )
280
- )
281
-
282
- async def send_prompt_list_changed(self) -> None:
283
- """Send a prompt list changed notification."""
284
- await self.send_notification(
285
- types.ServerNotification(
286
- types.PromptListChangedNotification(
287
- method="notifications/prompts/list_changed",
288
- )
289
- )
290
- )
291
-
292
- async def send_agents_list_changed(self) -> None:
293
- """Send an agent list changed notification."""
294
- await self.send_notification(
295
- types.ServerNotification(
296
- types.AgentListChangedNotification(
297
- method="notifications/agents/list_changed",
298
- )
299
- )
300
- )
301
-
302
- async def send_agent_run_progress(
303
- self, progress_token: str | int, delta: dict[str, Any]
304
- ) -> None:
305
- """Send an agent run progress notification."""
306
- await self.send_notification(
307
- types.ServerNotification(
308
- types.AgentRunProgressNotification(
309
- method="notifications/agents/run/progress",
310
- params=types.AgentRunProgressNotificationParams(
311
- progressToken=progress_token, delta=delta
312
- ),
313
- )
314
- )
315
- )
acp/server/sse.py DELETED
@@ -1,175 +0,0 @@
1
- """
2
- SSE Server Transport Module
3
-
4
- This module implements a Server-Sent Events (SSE) transport layer for MCP servers.
5
-
6
- Example usage:
7
- ```
8
- # Create an SSE transport at an endpoint
9
- sse = SseServerTransport("/messages/")
10
-
11
- # Create Starlette routes for SSE and message handling
12
- routes = [
13
- Route("/sse", endpoint=handle_sse),
14
- Mount("/messages/", app=sse.handle_post_message),
15
- ]
16
-
17
- # Define handler functions
18
- async def handle_sse(request):
19
- async with sse.connect_sse(
20
- request.scope, request.receive, request._send
21
- ) as streams:
22
- await app.run(
23
- streams[0], streams[1], app.create_initialization_options()
24
- )
25
-
26
- # Create and run Starlette app
27
- starlette_app = Starlette(routes=routes)
28
- uvicorn.run(starlette_app, host="0.0.0.0", port=port)
29
- ```
30
-
31
- See SseServerTransport class documentation for more details.
32
- """
33
-
34
- import logging
35
- from contextlib import asynccontextmanager
36
- from typing import Any
37
- from urllib.parse import quote
38
- from uuid import UUID, uuid4
39
-
40
- import anyio
41
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
42
- from pydantic import ValidationError
43
- from sse_starlette import EventSourceResponse
44
- from starlette.requests import Request
45
- from starlette.responses import Response
46
- from starlette.types import Receive, Scope, Send
47
-
48
- import acp.types as types
49
-
50
- logger = logging.getLogger(__name__)
51
-
52
-
53
- class SseServerTransport:
54
- """
55
- SSE server transport for MCP. This class provides _two_ ASGI applications,
56
- suitable to be used with a framework like Starlette and a server like Hypercorn:
57
-
58
- 1. connect_sse() is an ASGI application which receives incoming GET requests,
59
- and sets up a new SSE stream to send server messages to the client.
60
- 2. handle_post_message() is an ASGI application which receives incoming POST
61
- requests, which should contain client messages that link to a
62
- previously-established SSE session.
63
- """
64
-
65
- _endpoint: str
66
- _read_stream_writers: dict[
67
- UUID, MemoryObjectSendStream[types.JSONRPCMessage | Exception]
68
- ]
69
-
70
- def __init__(self, endpoint: str) -> None:
71
- """
72
- Creates a new SSE server transport, which will direct the client to POST
73
- messages to the relative or absolute URL given.
74
- """
75
-
76
- super().__init__()
77
- self._endpoint = endpoint
78
- self._read_stream_writers = {}
79
- logger.debug(f"SseServerTransport initialized with endpoint: {endpoint}")
80
-
81
- @asynccontextmanager
82
- async def connect_sse(self, scope: Scope, receive: Receive, send: Send):
83
- if scope["type"] != "http":
84
- logger.error("connect_sse received non-HTTP request")
85
- raise ValueError("connect_sse can only handle HTTP requests")
86
-
87
- logger.debug("Setting up SSE connection")
88
- read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
89
- read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
90
-
91
- write_stream: MemoryObjectSendStream[types.JSONRPCMessage]
92
- write_stream_reader: MemoryObjectReceiveStream[types.JSONRPCMessage]
93
-
94
- read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
95
- write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
96
-
97
- session_id = uuid4()
98
- session_uri = f"{quote(self._endpoint)}?session_id={session_id.hex}"
99
- self._read_stream_writers[session_id] = read_stream_writer
100
- logger.debug(f"Created new session with ID: {session_id}")
101
-
102
- sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[
103
- dict[str, Any]
104
- ](0)
105
-
106
- async def sse_writer():
107
- logger.debug("Starting SSE writer")
108
- async with sse_stream_writer, write_stream_reader:
109
- await sse_stream_writer.send({"event": "endpoint", "data": session_uri})
110
- logger.debug(f"Sent endpoint event: {session_uri}")
111
-
112
- async for message in write_stream_reader:
113
- logger.debug(f"Sending message via SSE: {message}")
114
- await sse_stream_writer.send(
115
- {
116
- "event": "message",
117
- "data": message.model_dump_json(
118
- by_alias=True, exclude_none=True
119
- ),
120
- }
121
- )
122
-
123
- async with anyio.create_task_group() as tg:
124
- response = EventSourceResponse(
125
- content=sse_stream_reader, data_sender_callable=sse_writer
126
- )
127
- logger.debug("Starting SSE response task")
128
- tg.start_soon(response, scope, receive, send)
129
-
130
- logger.debug("Yielding read and write streams")
131
- yield (read_stream, write_stream)
132
-
133
- async def handle_post_message(
134
- self, scope: Scope, receive: Receive, send: Send
135
- ) -> None:
136
- logger.debug("Handling POST message")
137
- request = Request(scope, receive)
138
-
139
- session_id_param = request.query_params.get("session_id")
140
- if session_id_param is None:
141
- logger.warning("Received request without session_id")
142
- response = Response("session_id is required", status_code=400)
143
- return await response(scope, receive, send)
144
-
145
- try:
146
- session_id = UUID(hex=session_id_param)
147
- logger.debug(f"Parsed session ID: {session_id}")
148
- except ValueError:
149
- logger.warning(f"Received invalid session ID: {session_id_param}")
150
- response = Response("Invalid session ID", status_code=400)
151
- return await response(scope, receive, send)
152
-
153
- writer = self._read_stream_writers.get(session_id)
154
- if not writer:
155
- logger.warning(f"Could not find session for ID: {session_id}")
156
- response = Response("Could not find session", status_code=404)
157
- return await response(scope, receive, send)
158
-
159
- json = await request.json()
160
- logger.debug(f"Received JSON: {json}")
161
-
162
- try:
163
- message = types.JSONRPCMessage.model_validate(json)
164
- logger.debug(f"Validated client message: {message}")
165
- except ValidationError as err:
166
- logger.error(f"Failed to parse message: {err}")
167
- response = Response("Could not parse message", status_code=400)
168
- await response(scope, receive, send)
169
- await writer.send(err)
170
- return
171
-
172
- logger.debug(f"Sending message to writer: {message}")
173
- response = Response("Accepted", status_code=202)
174
- await response(scope, receive, send)
175
- await writer.send(message)
acp/server/stdio.py DELETED
@@ -1,83 +0,0 @@
1
- """
2
- Stdio Server Transport Module
3
-
4
- This module provides functionality for creating an stdio-based transport layer
5
- that can be used to communicate with an MCP client through standard input/output
6
- streams.
7
-
8
- Example usage:
9
- ```
10
- async def run_server():
11
- async with stdio_server() as (read_stream, write_stream):
12
- # read_stream contains incoming JSONRPCMessages from stdin
13
- # write_stream allows sending JSONRPCMessages to stdout
14
- server = await create_my_server()
15
- await server.run(read_stream, write_stream, init_options)
16
-
17
- anyio.run(run_server)
18
- ```
19
- """
20
-
21
- import sys
22
- from contextlib import asynccontextmanager
23
-
24
- import anyio
25
- import anyio.lowlevel
26
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
27
-
28
- import acp.types as types
29
-
30
-
31
- @asynccontextmanager
32
- async def stdio_server(
33
- stdin: anyio.AsyncFile[str] | None = None,
34
- stdout: anyio.AsyncFile[str] | None = None,
35
- ):
36
- """
37
- Server transport for stdio: this communicates with an MCP client by reading
38
- from the current process' stdin and writing to stdout.
39
- """
40
- # Purposely not using context managers for these, as we don't want to close
41
- # standard process handles.
42
- if not stdin:
43
- stdin = anyio.wrap_file(sys.stdin)
44
- if not stdout:
45
- stdout = anyio.wrap_file(sys.stdout)
46
-
47
- read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
48
- read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
49
-
50
- write_stream: MemoryObjectSendStream[types.JSONRPCMessage]
51
- write_stream_reader: MemoryObjectReceiveStream[types.JSONRPCMessage]
52
-
53
- read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
54
- write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
55
-
56
- async def stdin_reader():
57
- try:
58
- async with read_stream_writer:
59
- async for line in stdin:
60
- try:
61
- message = types.JSONRPCMessage.model_validate_json(line)
62
- except Exception as exc:
63
- await read_stream_writer.send(exc)
64
- continue
65
-
66
- await read_stream_writer.send(message)
67
- except anyio.ClosedResourceError:
68
- await anyio.lowlevel.checkpoint()
69
-
70
- async def stdout_writer():
71
- try:
72
- async with write_stream_reader:
73
- async for message in write_stream_reader:
74
- json = message.model_dump_json(by_alias=True, exclude_none=True)
75
- await stdout.write(json + "\n")
76
- await stdout.flush()
77
- except anyio.ClosedResourceError:
78
- await anyio.lowlevel.checkpoint()
79
-
80
- async with anyio.create_task_group() as tg:
81
- tg.start_soon(stdin_reader)
82
- tg.start_soon(stdout_writer)
83
- yield read_stream, write_stream
acp/server/websocket.py DELETED
@@ -1,61 +0,0 @@
1
- import logging
2
- from contextlib import asynccontextmanager
3
-
4
- import anyio
5
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
6
- from starlette.types import Receive, Scope, Send
7
- from starlette.websockets import WebSocket
8
-
9
- import acp.types as types
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- @asynccontextmanager
15
- async def websocket_server(scope: Scope, receive: Receive, send: Send):
16
- """
17
- WebSocket server transport for MCP. This is an ASGI application, suitable to be
18
- used with a framework like Starlette and a server like Hypercorn.
19
- """
20
-
21
- websocket = WebSocket(scope, receive, send)
22
- await websocket.accept(subprotocol="mcp")
23
-
24
- read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
25
- read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
26
-
27
- write_stream: MemoryObjectSendStream[types.JSONRPCMessage]
28
- write_stream_reader: MemoryObjectReceiveStream[types.JSONRPCMessage]
29
-
30
- read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
31
- write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
32
-
33
- async def ws_reader():
34
- try:
35
- async with read_stream_writer:
36
- async for message in websocket.iter_json():
37
- try:
38
- client_message = types.JSONRPCMessage.model_validate(message)
39
- except Exception as exc:
40
- await read_stream_writer.send(exc)
41
- continue
42
-
43
- await read_stream_writer.send(client_message)
44
- except anyio.ClosedResourceError:
45
- await websocket.close()
46
-
47
- async def ws_writer():
48
- try:
49
- async with write_stream_reader:
50
- async for message in write_stream_reader:
51
- obj = message.model_dump(
52
- by_alias=True, mode="json", exclude_none=True
53
- )
54
- await websocket.send_json(obj)
55
- except anyio.ClosedResourceError:
56
- await websocket.close()
57
-
58
- async with anyio.create_task_group() as tg:
59
- tg.start_soon(ws_reader)
60
- tg.start_soon(ws_writer)
61
- yield (read_stream, write_stream)
acp/shared/__init__.py DELETED
File without changes
acp/shared/context.py DELETED
@@ -1,14 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Generic, TypeVar
3
-
4
- from acp.shared.session import BaseSession
5
- from acp.types import RequestId, RequestParams
6
-
7
- SessionT = TypeVar("SessionT", bound=BaseSession)
8
-
9
-
10
- @dataclass
11
- class RequestContext(Generic[SessionT]):
12
- request_id: RequestId
13
- meta: RequestParams.Meta | None
14
- session: SessionT
acp/shared/exceptions.py DELETED
@@ -1,14 +0,0 @@
1
- from acp.types import ErrorData
2
-
3
-
4
- class McpError(Exception):
5
- """
6
- Exception type raised when an error arrives over an MCP connection.
7
- """
8
-
9
- error: ErrorData
10
-
11
- def __init__(self, error: ErrorData):
12
- """Initialize McpError."""
13
- super().__init__(error.message)
14
- self.error = error