Skip to content

Commit 61bc0d7

Browse files
authored
feat: follow protocol 0.6.2 (#15)
* feat: follow protocol 0.6.2 Signed-off-by: Chojan Shang <[email protected]> * feat: update examples Signed-off-by: Chojan Shang <[email protected]> * chore: bump to 0.6.2 Signed-off-by: Chojan Shang <[email protected]> --------- Signed-off-by: Chojan Shang <[email protected]>
1 parent fa891d2 commit 61bc0d7

File tree

8 files changed

+152
-47
lines changed

8 files changed

+152
-47
lines changed

examples/agent.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,69 +24,60 @@
2424
update_agent_message,
2525
PROTOCOL_VERSION,
2626
)
27-
from acp.schema import AgentCapabilities, McpCapabilities, PromptCapabilities
27+
from acp.schema import AgentCapabilities, AgentMessageChunk, Implementation
2828

2929

3030
class ExampleAgent(Agent):
3131
def __init__(self, conn: AgentSideConnection) -> None:
3232
self._conn = conn
3333
self._next_session_id = 0
34+
self._sessions: set[str] = set()
3435

35-
async def _send_chunk(self, session_id: str, content: Any) -> None:
36-
await self._conn.sessionUpdate(
37-
session_notification(
38-
session_id,
39-
update_agent_message(content),
40-
)
41-
)
36+
async def _send_agent_message(self, session_id: str, content: Any) -> None:
37+
update = content if isinstance(content, AgentMessageChunk) else update_agent_message(content)
38+
await self._conn.sessionUpdate(session_notification(session_id, update))
4239

4340
async def initialize(self, params: InitializeRequest) -> InitializeResponse: # noqa: ARG002
4441
logging.info("Received initialize request")
45-
mcp_caps: McpCapabilities = McpCapabilities(http=False, sse=False)
46-
prompt_caps: PromptCapabilities = PromptCapabilities(audio=False, embeddedContext=False, image=False)
47-
agent_caps: AgentCapabilities = AgentCapabilities(
48-
loadSession=False,
49-
mcpCapabilities=mcp_caps,
50-
promptCapabilities=prompt_caps,
51-
)
5242
return InitializeResponse(
5343
protocolVersion=PROTOCOL_VERSION,
54-
agentCapabilities=agent_caps,
44+
agentCapabilities=AgentCapabilities(),
45+
agentInfo=Implementation(name="example-agent", title="Example Agent", version="0.1.0"),
5546
)
5647

5748
async def authenticate(self, params: AuthenticateRequest) -> AuthenticateResponse | None: # noqa: ARG002
58-
logging.info("Received authenticate request")
49+
logging.info("Received authenticate request %s", params.methodId)
5950
return AuthenticateResponse()
6051

6152
async def newSession(self, params: NewSessionRequest) -> NewSessionResponse: # noqa: ARG002
6253
logging.info("Received new session request")
6354
session_id = str(self._next_session_id)
6455
self._next_session_id += 1
65-
return NewSessionResponse(sessionId=session_id)
56+
self._sessions.add(session_id)
57+
return NewSessionResponse(sessionId=session_id, modes=None)
6658

6759
async def loadSession(self, params: LoadSessionRequest) -> LoadSessionResponse | None: # noqa: ARG002
68-
logging.info("Received load session request")
60+
logging.info("Received load session request %s", params.sessionId)
61+
self._sessions.add(params.sessionId)
6962
return LoadSessionResponse()
7063

7164
async def setSessionMode(self, params: SetSessionModeRequest) -> SetSessionModeResponse | None: # noqa: ARG002
72-
logging.info("Received set session mode request")
65+
logging.info("Received set session mode request %s -> %s", params.sessionId, params.modeId)
7366
return SetSessionModeResponse()
7467

7568
async def prompt(self, params: PromptRequest) -> PromptResponse:
76-
logging.info("Received prompt request")
69+
logging.info("Received prompt request for session %s", params.sessionId)
70+
if params.sessionId not in self._sessions:
71+
self._sessions.add(params.sessionId)
7772

78-
# Notify the client what it just sent and then echo each content block back.
79-
await self._send_chunk(
80-
params.sessionId,
81-
text_block("Client sent:"),
82-
)
73+
await self._send_agent_message(params.sessionId, text_block("Client sent:"))
8374
for block in params.prompt:
84-
await self._send_chunk(params.sessionId, block)
75+
await self._send_agent_message(params.sessionId, block)
8576

8677
return PromptResponse(stopReason="end_turn")
8778

8879
async def cancel(self, params: CancelNotification) -> None: # noqa: ARG002
89-
logging.info("Received cancel notification")
80+
logging.info("Received cancel notification for session %s", params.sessionId)
9081

9182
async def extMethod(self, method: str, params: dict) -> dict: # noqa: ARG002
9283
logging.info("Received extension method call: %s", method)

examples/client.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717
text_block,
1818
PROTOCOL_VERSION,
1919
)
20+
from acp.schema import (
21+
AgentMessageChunk,
22+
AudioContentBlock,
23+
ClientCapabilities,
24+
EmbeddedResourceContentBlock,
25+
ImageContentBlock,
26+
Implementation,
27+
ResourceContentBlock,
28+
TextContentBlock,
29+
)
2030

2131

2232
class ExampleClient(Client):
@@ -46,20 +56,24 @@ async def killTerminal(self, params): # type: ignore[override]
4656

4757
async def sessionUpdate(self, params: SessionNotification) -> None:
4858
update = params.update
49-
if isinstance(update, dict):
50-
kind = update.get("sessionUpdate")
51-
content = update.get("content")
52-
else:
53-
kind = getattr(update, "sessionUpdate", None)
54-
content = getattr(update, "content", None)
55-
56-
if kind != "agent_message_chunk" or content is None:
59+
if not isinstance(update, AgentMessageChunk):
5760
return
5861

59-
if isinstance(content, dict):
60-
text = content.get("text", "<content>")
62+
content = update.content
63+
text: str
64+
if isinstance(content, TextContentBlock):
65+
text = content.text
66+
elif isinstance(content, ImageContentBlock):
67+
text = "<image>"
68+
elif isinstance(content, AudioContentBlock):
69+
text = "<audio>"
70+
elif isinstance(content, ResourceContentBlock):
71+
text = content.uri or "<resource>"
72+
elif isinstance(content, EmbeddedResourceContentBlock):
73+
text = "<resource>"
6174
else:
62-
text = getattr(content, "text", "<content>")
75+
text = "<content>"
76+
6377
print(f"| Agent: {text}")
6478

6579
async def extMethod(self, method: str, params: dict) -> dict: # noqa: ARG002
@@ -130,7 +144,13 @@ async def main(argv: list[str]) -> int:
130144
client_impl = ExampleClient()
131145
conn = ClientSideConnection(lambda _agent: client_impl, proc.stdin, proc.stdout)
132146

133-
await conn.initialize(InitializeRequest(protocolVersion=PROTOCOL_VERSION, clientCapabilities=None))
147+
await conn.initialize(
148+
InitializeRequest(
149+
protocolVersion=PROTOCOL_VERSION,
150+
clientCapabilities=ClientCapabilities(),
151+
clientInfo=Implementation(name="example-client", title="Example Client", version="0.1.0"),
152+
)
153+
)
134154
session = await conn.newSession(NewSessionRequest(mcpServers=[], cwd=os.getcwd()))
135155

136156
await interactive_loop(conn, session.sessionId)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agent-client-protocol"
3-
version = "0.5.0"
3+
version = "0.6.2"
44
description = "A Python implement of Agent Client Protocol (ACP, by Zed Industries)"
55
authors = [{ name = "Chojan Shang", email = "[email protected]" }]
66
readme = "README.md"

schema/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
refs/tags/v0.5.0
1+
refs/tags/v0.6.2

schema/schema.json

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,31 @@
10711071
],
10721072
"type": "object"
10731073
},
1074+
"Implementation": {
1075+
"description": "Describes the name and version of an MCP implementation, with an optional\ntitle for UI representation.",
1076+
"properties": {
1077+
"name": {
1078+
"description": "Intended for programmatic or logical use, but can be used as a display\nname fallback if title isn\u2019t present.",
1079+
"type": "string"
1080+
},
1081+
"title": {
1082+
"description": "Intended for UI and end-user contexts \u2014 optimized to be human-readable\nand easily understood.\n\nIf not provided, the name should be used for display.",
1083+
"type": [
1084+
"string",
1085+
"null"
1086+
]
1087+
},
1088+
"version": {
1089+
"description": "Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes.",
1090+
"type": "string"
1091+
}
1092+
},
1093+
"required": [
1094+
"name",
1095+
"version"
1096+
],
1097+
"type": "object"
1098+
},
10741099
"InitializeRequest": {
10751100
"description": "Request parameters for the initialize method.\n\nSent by the client to establish connection and negotiate capabilities.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)",
10761101
"properties": {
@@ -1088,6 +1113,17 @@
10881113
},
10891114
"description": "Capabilities supported by the client."
10901115
},
1116+
"clientInfo": {
1117+
"anyOf": [
1118+
{
1119+
"$ref": "#/$defs/Implementation"
1120+
},
1121+
{
1122+
"type": "null"
1123+
}
1124+
],
1125+
"description": "Information about the Client name and version sent to the Agent.\n\nNote: in future versions of the protocol, this will be required."
1126+
},
10911127
"protocolVersion": {
10921128
"$ref": "#/$defs/ProtocolVersion",
10931129
"description": "The latest protocol version supported by the client."
@@ -1122,6 +1158,17 @@
11221158
},
11231159
"description": "Capabilities supported by the agent."
11241160
},
1161+
"agentInfo": {
1162+
"anyOf": [
1163+
{
1164+
"$ref": "#/$defs/Implementation"
1165+
},
1166+
{
1167+
"type": "null"
1168+
}
1169+
],
1170+
"description": "Information about the Agent name and version sent to the Client.\n\nNote: in future versions of the protocol, this will be required."
1171+
},
11251172
"authMethods": {
11261173
"default": [],
11271174
"description": "Authentication methods supported by the agent.",
@@ -2304,7 +2351,7 @@
23042351
"SetSessionModeResponse": {
23052352
"description": "Response to `session/set_mode` method.",
23062353
"properties": {
2307-
"meta": true
2354+
"_meta": true
23082355
},
23092356
"type": "object",
23102357
"x-method": "session/set_mode",

src/acp/meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Generated from schema/meta.json. Do not edit by hand.
2-
# Schema ref: refs/tags/v0.5.0
2+
# Schema ref: refs/tags/v0.6.2
33
AGENT_METHODS = {'authenticate': 'authenticate', 'initialize': 'initialize', 'session_cancel': 'session/cancel', 'session_load': 'session/load', 'session_new': 'session/new', 'session_prompt': 'session/prompt', 'session_set_mode': 'session/set_mode', 'session_set_model': 'session/set_model'}
44
CLIENT_METHODS = {'fs_read_text_file': 'fs/read_text_file', 'fs_write_text_file': 'fs/write_text_file', 'session_request_permission': 'session/request_permission', 'session_update': 'session/update', 'terminal_create': 'terminal/create', 'terminal_kill': 'terminal/kill', 'terminal_output': 'terminal/output', 'terminal_release': 'terminal/release', 'terminal_wait_for_exit': 'terminal/wait_for_exit'}
55
PROTOCOL_VERSION = 1

src/acp/schema.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Generated from schema/schema.json. Do not edit by hand.
2-
# Schema ref: refs/tags/v0.5.0
2+
# Schema ref: refs/tags/v0.6.2
33

44
from __future__ import annotations
55

@@ -151,6 +151,35 @@ class HttpHeader(BaseModel):
151151
value: Annotated[str, Field(description="The value to set for the HTTP header.")]
152152

153153

154+
class Implementation(BaseModel):
155+
# Intended for programmatic or logical use, but can be used as a display
156+
# name fallback if title isn’t present.
157+
name: Annotated[
158+
str,
159+
Field(
160+
description="Intended for programmatic or logical use, but can be used as a display\nname fallback if title isn’t present."
161+
),
162+
]
163+
# Intended for UI and end-user contexts — optimized to be human-readable
164+
# and easily understood.
165+
#
166+
# If not provided, the name should be used for display.
167+
title: Annotated[
168+
Optional[str],
169+
Field(
170+
description="Intended for UI and end-user contexts — optimized to be human-readable\nand easily understood.\n\nIf not provided, the name should be used for display."
171+
),
172+
] = None
173+
# Version of the implementation. Can be displayed to the user or used
174+
# for debugging or metrics purposes.
175+
version: Annotated[
176+
str,
177+
Field(
178+
description="Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes."
179+
),
180+
]
181+
182+
154183
class KillTerminalCommandResponse(BaseModel):
155184
# Extension point for implementations
156185
field_meta: Annotated[
@@ -349,7 +378,7 @@ class SetSessionModeRequest(BaseModel):
349378

350379

351380
class SetSessionModeResponse(BaseModel):
352-
meta: Optional[Any] = None
381+
field_meta: Annotated[Optional[Any], Field(alias="_meta")] = None
353382

354383

355384
class SetSessionModelRequest(BaseModel):
@@ -767,6 +796,15 @@ class InitializeRequest(BaseModel):
767796
Optional[ClientCapabilities],
768797
Field(description="Capabilities supported by the client."),
769798
] = ClientCapabilities(fs=FileSystemCapability(readTextFile=False, writeTextFile=False), terminal=False)
799+
# Information about the Client name and version sent to the Agent.
800+
#
801+
# Note: in future versions of the protocol, this will be required.
802+
clientInfo: Annotated[
803+
Optional[Implementation],
804+
Field(
805+
description="Information about the Client name and version sent to the Agent.\n\nNote: in future versions of the protocol, this will be required."
806+
),
807+
] = None
770808
# The latest protocol version supported by the client.
771809
protocolVersion: Annotated[
772810
int,
@@ -793,6 +831,15 @@ class InitializeResponse(BaseModel):
793831
mcpCapabilities=McpCapabilities(http=False, sse=False),
794832
promptCapabilities=PromptCapabilities(audio=False, embeddedContext=False, image=False),
795833
)
834+
# Information about the Agent name and version sent to the Client.
835+
#
836+
# Note: in future versions of the protocol, this will be required.
837+
agentInfo: Annotated[
838+
Optional[Implementation],
839+
Field(
840+
description="Information about the Agent name and version sent to the Client.\n\nNote: in future versions of the protocol, this will be required."
841+
),
842+
] = None
796843
# Authentication methods supported by the agent.
797844
authMethods: Annotated[
798845
Optional[List[AuthMethod]],

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)