Skip to content

Commit 83b7660

Browse files
authored
feat(acp): expose unstable tool-call names (#296)
1 parent 6eb3f0a commit 83b7660

4 files changed

Lines changed: 71 additions & 2 deletions

File tree

md/protocol-v2.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,19 @@ agent:
175175
- If the agent rejects the v2 initialize request, the error is surfaced. A
176176
rejected initialize is not treated as permission to retry with v1.
177177

178-
## Draft schema changes in schema 1.5
178+
## Draft schema changes in schema 1.5 and 1.6
179179

180180
The `unstable_protocol_v2` API follows the moving draft schema. Schema 1.5 adds
181181
semantic newtypes for paths, media types, IDs, and cursors; renames
182182
`DiffPatch.diff` to `DiffPatch.text`; adds terminal state and output update
183183
types; and makes v1/v2 conversions fallible and generic. These are draft API
184184
changes rather than stable v1 wire changes. See [Migrating to
185185
v2.0](./migration_v2.0.md#draft-v2-schema-updates) for concrete source changes.
186+
187+
Schema 1.6 adds `Cancelled` tool-call and plan-entry statuses to draft v2.
188+
Programmatic tool-call names are available in both protocol versions through
189+
the separate `unstable_tool_call_name` feature. Draft v2 users must enable both
190+
`unstable_protocol_v2` and `unstable_tool_call_name`. In v2, an omitted name
191+
leaves the existing value unchanged, `null` clears it, and a string replaces
192+
it. V1 cannot clear an existing name, so converting a v2 `null` name to v1
193+
fails.

src/agent-client-protocol/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- *(unstable)* Expose programmatic tool-call names through the
8+
`unstable_tool_call_name` feature.
9+
510
### Fixed
611

712
- *(unstable-v2)* Preserve unknown initialize fields when the protocol router

src/agent-client-protocol/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,21 @@ rustdoc-args = ["--cfg", "docsrs"]
1919
default = []
2020

2121
# Forward unstable features from agent-client-protocol-schema.
22-
# Enable these to get support for the corresponding unstable ACP methods.
22+
# Enable these to get support for the corresponding unstable ACP surfaces.
2323
unstable = [
2424
"unstable_auth_methods",
2525
"unstable_elicitation",
2626
"unstable_end_turn_token_usage",
2727
"unstable_mcp_over_acp",
2828
"unstable_session_fork",
29+
"unstable_tool_call_name",
2930
]
3031
unstable_auth_methods = ["agent-client-protocol-schema/unstable_auth_methods"]
3132
unstable_elicitation = ["agent-client-protocol-schema/unstable_elicitation"]
3233
unstable_end_turn_token_usage = ["agent-client-protocol-schema/unstable_end_turn_token_usage"]
3334
unstable_mcp_over_acp = ["agent-client-protocol-schema/unstable_mcp_over_acp"]
3435
unstable_session_fork = ["agent-client-protocol-schema/unstable_session_fork"]
36+
unstable_tool_call_name = ["agent-client-protocol-schema/unstable_tool_call_name"]
3537
unstable_protocol_v2 = ["agent-client-protocol-schema/unstable_protocol_v2"]
3638

3739
[dependencies]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#![cfg(feature = "unstable_tool_call_name")]
2+
3+
#[cfg(feature = "unstable_protocol_v2")]
4+
use agent_client_protocol::schema::v1::ToolCallUpdate as V1ToolCallUpdate;
5+
use agent_client_protocol::schema::v1::{ToolCall, ToolCallUpdateFields};
6+
use serde_json::json;
7+
8+
#[test]
9+
fn v1_tool_call_name_serializes_and_updates() {
10+
let mut tool_call = ToolCall::new("call_1", "Read configuration").name("read_file");
11+
12+
assert_eq!(
13+
serde_json::to_value(&tool_call).unwrap()["name"],
14+
json!("read_file")
15+
);
16+
17+
tool_call.update(ToolCallUpdateFields::new().name("write_file"));
18+
assert_eq!(tool_call.name.as_deref(), Some("write_file"));
19+
20+
tool_call.update(ToolCallUpdateFields::new());
21+
assert_eq!(tool_call.name.as_deref(), Some("write_file"));
22+
23+
let null_name: ToolCallUpdateFields = serde_json::from_value(json!({ "name": null })).unwrap();
24+
tool_call.update(null_name);
25+
assert_eq!(tool_call.name.as_deref(), Some("write_file"));
26+
}
27+
28+
#[cfg(feature = "unstable_protocol_v2")]
29+
#[test]
30+
fn tool_call_name_preserves_v2_patch_semantics() {
31+
use agent_client_protocol::schema::{
32+
MaybeUndefined,
33+
v2::{
34+
ToolCallUpdate,
35+
conversion::{try_v1_to_v2, try_v2_to_v1},
36+
},
37+
};
38+
39+
let named: ToolCallUpdate =
40+
try_v1_to_v2(ToolCall::new("call_1", "Read configuration").name("read_file")).unwrap();
41+
assert_eq!(named.name, MaybeUndefined::Value("read_file".to_string()));
42+
43+
let omitted: ToolCallUpdate =
44+
try_v1_to_v2(V1ToolCallUpdate::new("call_1", ToolCallUpdateFields::new())).unwrap();
45+
assert_eq!(omitted.name, MaybeUndefined::Undefined);
46+
47+
let cleared = ToolCallUpdate::new("call_1").name(None::<String>);
48+
assert_eq!(cleared.name, MaybeUndefined::Null);
49+
let error = try_v2_to_v1::<_, V1ToolCallUpdate>(cleared).unwrap_err();
50+
assert_eq!(
51+
error.message(),
52+
"v2 ToolCallUpdate.name with null value cannot be represented in v1"
53+
);
54+
}

0 commit comments

Comments
 (0)