Skip to content

Commit 653f82d

Browse files
committed
feat: complete missing v1 protocol methods
1 parent accb008 commit 653f82d

11 files changed

Lines changed: 947 additions & 8 deletions

File tree

‎docs/quickstart.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,47 @@ are available as `TerminalAuthMethod`, replacing the incorrect `EnvVarAuthMethod
187187
name. Accepted elicitation content validates scalar values and string lists;
188188
nested objects are not valid form values.
189189

190+
## Additional v1 protocol methods
191+
192+
The SDK exposes all agent and client methods in the bundled `schema-v1.23.0`
193+
method tables. `session/delete` and `logout` are stable and do not require
194+
`use_unstable_protocol=True`. The other methods in the table below are unstable;
195+
enable that flag on the receiving connection (or `run_agent`) to route them.
196+
Check the peer's advertised capabilities before calling these methods.
197+
198+
| Wire method | Python method | Receiver |
199+
| --- | --- | --- |
200+
| `session/delete` | `delete_session(session_id=...)` | Agent |
201+
| `providers/list`, `providers/set`, `providers/disable` | `list_providers`, `set_provider`, `disable_provider` | Agent |
202+
| `logout` | `logout()` | Agent |
203+
| `mcp/connect`, `mcp/disconnect` | `connect_mcp`, `disconnect_mcp` | Client |
204+
| `mcp/message` (request) | `mcp_message` | Either peer |
205+
| `mcp/message` (notification) | `notify_mcp` | Either peer |
206+
| `nes/start`, `nes/suggest`, `nes/close` | `start_nes`, `suggest_nes`, `close_nes` | Agent |
207+
| `nes/accept`, `nes/reject` | `accept_nes`, `reject_nes` | Agent |
208+
| `document/didOpen`, `document/didChange`, `document/didClose`, `document/didSave`, `document/didFocus` | `did_open`, `did_change`, `did_close`, `did_save`, `did_focus` | Agent |
209+
210+
For example, an agent that advertises session deletion implements:
211+
212+
```python
213+
from acp import DeleteSessionResponse
214+
215+
async def delete_session(self, session_id: str, **kwargs) -> DeleteSessionResponse:
216+
await self.session_store.delete(session_id)
217+
return DeleteSessionResponse()
218+
```
219+
220+
A client then calls `await connection.delete_session(session_id=session_id)`.
221+
Deletion removes stored session data; `close_session` only closes the active
222+
session. The SDK dispatches these calls to your implementation; it does not
223+
provide session storage or provider management itself. Missing request handlers
224+
return the JSON-RPC method-not-found error.
225+
226+
MCP requests return the inner JSON result unchanged, including `null`. Use
227+
`notify_mcp` for one-way MCP messages. Both APIs accept `connection_id`, `method`,
228+
and optional `params`. These methods share the same connections and routers
229+
across stdio, HTTP, and WebSocket transports.
230+
190231
## Optional — Talk to the Gemini CLI
191232

192233
_Have the Gemini CLI installed? Run the bridge to exercise permission flows._

‎scripts/gen_signature.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,8 @@ def _format_annotation(self, annotation: t.Any) -> ast.expr:
134134
for argument in rest:
135135
formatted = ast.BinOp(left=formatted, op=ast.BitOr(), right=self._format_annotation(argument))
136136
return formatted
137-
if origin is t.Literal and annotation in self._literals.values():
138-
name = next(name for name, value in self._literals.items() if value is annotation)
139-
self._add_schema_import(name)
140-
return ast.Name(id=name)
137+
if origin is t.Literal:
138+
return self._format_literal(annotation)
141139
elif (
142140
inspect.isclass(annotation)
143141
and issubclass(annotation, BaseModel)
@@ -166,6 +164,19 @@ def _format_annotation(self, annotation: t.Any) -> ast.expr:
166164
self._add_typing_import("Any")
167165
return ast.Name(id="Any")
168166

167+
def _format_literal(self, annotation: t.Any) -> ast.expr:
168+
if annotation in self._literals.values():
169+
name = next(name for name, value in self._literals.items() if value is annotation)
170+
self._add_schema_import(name)
171+
return ast.Name(id=name)
172+
self._add_typing_import("Literal")
173+
values = [ast.Constant(value=value) for value in t.get_args(annotation)]
174+
return ast.Subscript(
175+
value=ast.Name(id="Literal"),
176+
slice=values[0] if len(values) == 1 else ast.Tuple(elts=values, ctx=ast.Load()),
177+
ctx=ast.Load(),
178+
)
179+
169180

170181
def gen_signature(source_dir: Path) -> None:
171182
global schema

‎src/acp/__init__.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414
)
1515
from .schema import (
1616
AcceptElicitationResponse,
17+
AcceptNesNotification,
1718
AuthenticateRequest,
1819
AuthenticateResponse,
1920
CancelElicitationResponse,
2021
CancelNotification,
22+
CloseNesRequest,
23+
CloseNesResponse,
2124
CompleteElicitationNotification,
25+
ConnectMcpRequest,
26+
ConnectMcpResponse,
2227
CreateElicitationRequest,
2328
CreateElicitationResponse,
2429
CreateFormElicitationRequest,
@@ -31,6 +36,17 @@
3136
CreateUrlRequestElicitationRequest,
3237
CreateUrlSessionElicitationRequest,
3338
DeclineElicitationResponse,
39+
DeleteSessionRequest,
40+
DeleteSessionResponse,
41+
DidChangeDocumentNotification,
42+
DidCloseDocumentNotification,
43+
DidFocusDocumentNotification,
44+
DidOpenDocumentNotification,
45+
DidSaveDocumentNotification,
46+
DisableProviderRequest,
47+
DisableProviderResponse,
48+
DisconnectMcpRequest,
49+
DisconnectMcpResponse,
3450
ElicitationBooleanPropertySchema,
3551
ElicitationCapabilities,
3652
ElicitationFormCapabilities,
@@ -50,24 +66,37 @@
5066
InitializeResponse,
5167
KillTerminalRequest,
5268
KillTerminalResponse,
69+
ListProvidersRequest,
70+
ListProvidersResponse,
5371
LoadSessionRequest,
5472
LoadSessionResponse,
73+
LogoutRequest,
74+
LogoutResponse,
75+
MessageMcpNotification,
76+
MessageMcpRequest,
5577
NewSessionRequest,
5678
NewSessionResponse,
5779
OtherElicitationResponse,
5880
PromptRequest,
5981
PromptResponse,
6082
ReadTextFileRequest,
6183
ReadTextFileResponse,
84+
RejectNesNotification,
6285
ReleaseTerminalRequest,
6386
ReleaseTerminalResponse,
6487
RequestPermissionRequest,
6588
RequestPermissionResponse,
6689
SessionNotification,
90+
SetProviderRequest,
91+
SetProviderResponse,
6792
SetSessionConfigOptionResponse,
6893
SetSessionConfigOptionSelectRequest,
6994
SetSessionModeRequest,
7095
SetSessionModeResponse,
96+
StartNesRequest,
97+
StartNesResponse,
98+
SuggestNesRequest,
99+
SuggestNesResponse,
71100
TerminalOutputRequest,
72101
TerminalOutputResponse,
73102
WaitForTerminalExitRequest,
@@ -97,6 +126,35 @@
97126
"AGENT_METHODS",
98127
"CLIENT_METHODS",
99128
# types
129+
"AcceptNesNotification",
130+
"CloseNesRequest",
131+
"CloseNesResponse",
132+
"ConnectMcpRequest",
133+
"ConnectMcpResponse",
134+
"DeleteSessionRequest",
135+
"DeleteSessionResponse",
136+
"DidChangeDocumentNotification",
137+
"DidCloseDocumentNotification",
138+
"DidFocusDocumentNotification",
139+
"DidOpenDocumentNotification",
140+
"DidSaveDocumentNotification",
141+
"DisableProviderRequest",
142+
"DisableProviderResponse",
143+
"DisconnectMcpRequest",
144+
"DisconnectMcpResponse",
145+
"ListProvidersRequest",
146+
"ListProvidersResponse",
147+
"LogoutRequest",
148+
"LogoutResponse",
149+
"MessageMcpNotification",
150+
"MessageMcpRequest",
151+
"RejectNesNotification",
152+
"SetProviderRequest",
153+
"SetProviderResponse",
154+
"StartNesRequest",
155+
"StartNesResponse",
156+
"SuggestNesRequest",
157+
"SuggestNesResponse",
100158
"InitializeRequest",
101159
"InitializeResponse",
102160
"NewSessionRequest",

‎src/acp/agent/connection.py‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
CancelElicitationResponse,
2222
CompleteElicitationNotification,
2323
ConfigOptionUpdate,
24+
ConnectMcpRequest,
25+
ConnectMcpResponse,
2426
CreateElicitationResponse,
2527
CreateFormElicitationRequest,
2628
CreateFormRequestElicitationRequest,
@@ -32,6 +34,8 @@
3234
CreateUrlSessionElicitationRequest,
3335
CurrentModeUpdate,
3436
DeclineElicitationResponse,
37+
DisconnectMcpRequest,
38+
DisconnectMcpResponse,
3539
ElicitationFormRequestMode,
3640
ElicitationFormSessionMode,
3741
ElicitationMode,
@@ -40,6 +44,8 @@
4044
EnvVariable,
4145
KillTerminalRequest,
4246
KillTerminalResponse,
47+
MessageMcpNotification,
48+
MessageMcpRequest,
4349
PermissionOption,
4450
ReadTextFileRequest,
4551
ReadTextFileResponse,
@@ -64,7 +70,15 @@
6470
WriteTextFileRequest,
6571
WriteTextFileResponse,
6672
)
67-
from ..utils import compatible_class, notify_model, param_model, request_model, request_optional_model, serialize_params
73+
from ..utils import (
74+
compatible_class,
75+
notify_model,
76+
param_model,
77+
request_model,
78+
request_model_from_dict,
79+
request_optional_model,
80+
serialize_params,
81+
)
6882
from .router import build_agent_router
6983

7084
__all__ = ["AgentSideConnection"]
@@ -278,6 +292,47 @@ async def complete_elicitation(self, elicitation_id: str, **kwargs: Any) -> None
278292
CompleteElicitationNotification(elicitation_id=elicitation_id, field_meta=kwargs or None),
279293
)
280294

295+
@param_model(ConnectMcpRequest)
296+
async def connect_mcp(self, server_id: str, **kwargs: Any) -> ConnectMcpResponse:
297+
return await request_model(
298+
self._conn,
299+
CLIENT_METHODS["mcp_connect"],
300+
ConnectMcpRequest(server_id=server_id, field_meta=kwargs or None),
301+
ConnectMcpResponse,
302+
)
303+
304+
@param_model(DisconnectMcpRequest)
305+
async def disconnect_mcp(self, connection_id: str, **kwargs: Any) -> DisconnectMcpResponse:
306+
return await request_model_from_dict(
307+
self._conn,
308+
CLIENT_METHODS["mcp_disconnect"],
309+
DisconnectMcpRequest(connection_id=connection_id, field_meta=kwargs or None),
310+
DisconnectMcpResponse,
311+
)
312+
313+
@param_model(MessageMcpRequest)
314+
async def mcp_message(
315+
self, connection_id: str, method: str, params: dict[str, Any] | None = None, **kwargs: Any
316+
) -> Any:
317+
return await self._conn.send_request(
318+
CLIENT_METHODS["mcp_message"],
319+
serialize_params(
320+
MessageMcpRequest(connection_id=connection_id, method=method, params=params, field_meta=kwargs or None)
321+
),
322+
)
323+
324+
@param_model(MessageMcpNotification)
325+
async def notify_mcp(
326+
self, connection_id: str, method: str, params: dict[str, Any] | None = None, **kwargs: Any
327+
) -> None:
328+
await notify_model(
329+
self._conn,
330+
CLIENT_METHODS["mcp_message"],
331+
MessageMcpNotification(
332+
connection_id=connection_id, method=method, params=params, field_meta=kwargs or None
333+
),
334+
)
335+
281336
async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
282337
return await self._conn.send_request(f"_{method}", params)
283338

‎src/acp/agent/router.py‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,36 @@
99
from ..meta import AGENT_METHODS
1010
from ..router import MessageRouter, Route, _resolve_handler, _warn_legacy_handler
1111
from ..schema import (
12+
AcceptNesNotification,
1213
AuthenticateRequest,
1314
CancelNotification,
15+
CloseNesRequest,
1416
CloseSessionRequest,
17+
DeleteSessionRequest,
18+
DidChangeDocumentNotification,
19+
DidCloseDocumentNotification,
20+
DidFocusDocumentNotification,
21+
DidOpenDocumentNotification,
22+
DidSaveDocumentNotification,
23+
DisableProviderRequest,
1524
ForkSessionRequest,
1625
InitializeRequest,
26+
ListProvidersRequest,
1727
ListSessionsRequest,
1828
LoadSessionRequest,
29+
LogoutRequest,
30+
MessageMcpNotification,
31+
MessageMcpRequest,
1932
NewSessionRequest,
2033
PromptRequest,
34+
RejectNesNotification,
2135
ResumeSessionRequest,
36+
SetProviderRequest,
2237
SetSessionConfigOptionBooleanRequest,
2338
SetSessionConfigOptionSelectRequest,
2439
SetSessionModeRequest,
40+
StartNesRequest,
41+
SuggestNesRequest,
2542
)
2643
from ..utils import model_to_kwargs, normalize_result
2744

@@ -102,6 +119,56 @@ def build_agent_router(agent: Agent, use_unstable_protocol: bool = False) -> Mes
102119

103120
router.route_notification(AGENT_METHODS["session_cancel"], CancelNotification, agent, "cancel")
104121

122+
router.route_request(
123+
AGENT_METHODS["session_delete"],
124+
DeleteSessionRequest,
125+
agent,
126+
"delete_session",
127+
adapt_result=normalize_result,
128+
)
129+
router.route_request(AGENT_METHODS["providers_list"], ListProvidersRequest, agent, "list_providers", unstable=True)
130+
router.route_request(
131+
AGENT_METHODS["providers_set"],
132+
SetProviderRequest,
133+
agent,
134+
"set_provider",
135+
unstable=True,
136+
adapt_result=normalize_result,
137+
)
138+
router.route_request(
139+
AGENT_METHODS["providers_disable"],
140+
DisableProviderRequest,
141+
agent,
142+
"disable_provider",
143+
unstable=True,
144+
adapt_result=normalize_result,
145+
)
146+
router.route_request(AGENT_METHODS["logout"], LogoutRequest, agent, "logout", adapt_result=normalize_result)
147+
router.route_request(AGENT_METHODS["mcp_message"], MessageMcpRequest, agent, "mcp_message", unstable=True)
148+
router.route_notification(AGENT_METHODS["mcp_message"], MessageMcpNotification, agent, "notify_mcp", unstable=True)
149+
router.route_request(AGENT_METHODS["nes_start"], StartNesRequest, agent, "start_nes", unstable=True)
150+
router.route_request(AGENT_METHODS["nes_suggest"], SuggestNesRequest, agent, "suggest_nes", unstable=True)
151+
router.route_request(
152+
AGENT_METHODS["nes_close"], CloseNesRequest, agent, "close_nes", unstable=True, adapt_result=normalize_result
153+
)
154+
router.route_notification(AGENT_METHODS["nes_accept"], AcceptNesNotification, agent, "accept_nes", unstable=True)
155+
router.route_notification(AGENT_METHODS["nes_reject"], RejectNesNotification, agent, "reject_nes", unstable=True)
156+
router.route_notification(
157+
AGENT_METHODS["document_did_open"], DidOpenDocumentNotification, agent, "did_open", unstable=True
158+
)
159+
router.route_notification(
160+
AGENT_METHODS["document_did_change"], DidChangeDocumentNotification, agent, "did_change", unstable=True
161+
)
162+
router.route_notification(
163+
AGENT_METHODS["document_did_close"], DidCloseDocumentNotification, agent, "did_close", unstable=True
164+
)
165+
router.route_notification(
166+
AGENT_METHODS["document_did_save"], DidSaveDocumentNotification, agent, "did_save", unstable=True
167+
)
168+
router.route_notification(
169+
AGENT_METHODS["document_did_focus"], DidFocusDocumentNotification, agent, "did_focus", unstable=True
170+
)
171+
105172
@router.handle_extension_request
106173
async def _handle_extension_request(name: str, payload: dict[str, Any]) -> Any:
107174
ext = getattr(agent, "ext_method", None)

0 commit comments

Comments
 (0)