Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 19 additions & 28 deletions examples/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,69 +24,60 @@
update_agent_message,
PROTOCOL_VERSION,
)
from acp.schema import AgentCapabilities, McpCapabilities, PromptCapabilities
from acp.schema import AgentCapabilities, AgentMessageChunk, Implementation


class ExampleAgent(Agent):
def __init__(self, conn: AgentSideConnection) -> None:
self._conn = conn
self._next_session_id = 0
self._sessions: set[str] = set()

async def _send_chunk(self, session_id: str, content: Any) -> None:
await self._conn.sessionUpdate(
session_notification(
session_id,
update_agent_message(content),
)
)
async def _send_agent_message(self, session_id: str, content: Any) -> None:
update = content if isinstance(content, AgentMessageChunk) else update_agent_message(content)
await self._conn.sessionUpdate(session_notification(session_id, update))

async def initialize(self, params: InitializeRequest) -> InitializeResponse: # noqa: ARG002
logging.info("Received initialize request")
mcp_caps: McpCapabilities = McpCapabilities(http=False, sse=False)
prompt_caps: PromptCapabilities = PromptCapabilities(audio=False, embeddedContext=False, image=False)
agent_caps: AgentCapabilities = AgentCapabilities(
loadSession=False,
mcpCapabilities=mcp_caps,
promptCapabilities=prompt_caps,
)
return InitializeResponse(
protocolVersion=PROTOCOL_VERSION,
agentCapabilities=agent_caps,
agentCapabilities=AgentCapabilities(),
agentInfo=Implementation(name="example-agent", title="Example Agent", version="0.1.0"),
)

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

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

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

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

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

# Notify the client what it just sent and then echo each content block back.
await self._send_chunk(
params.sessionId,
text_block("Client sent:"),
)
await self._send_agent_message(params.sessionId, text_block("Client sent:"))
for block in params.prompt:
await self._send_chunk(params.sessionId, block)
await self._send_agent_message(params.sessionId, block)

return PromptResponse(stopReason="end_turn")

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

async def extMethod(self, method: str, params: dict) -> dict: # noqa: ARG002
logging.info("Received extension method call: %s", method)
Expand Down
44 changes: 32 additions & 12 deletions examples/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
text_block,
PROTOCOL_VERSION,
)
from acp.schema import (
AgentMessageChunk,
AudioContentBlock,
ClientCapabilities,
EmbeddedResourceContentBlock,
ImageContentBlock,
Implementation,
ResourceContentBlock,
TextContentBlock,
)


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

async def sessionUpdate(self, params: SessionNotification) -> None:
update = params.update
if isinstance(update, dict):
kind = update.get("sessionUpdate")
content = update.get("content")
else:
kind = getattr(update, "sessionUpdate", None)
content = getattr(update, "content", None)

if kind != "agent_message_chunk" or content is None:
if not isinstance(update, AgentMessageChunk):
return

if isinstance(content, dict):
text = content.get("text", "<content>")
content = update.content
text: str
if isinstance(content, TextContentBlock):
text = content.text
elif isinstance(content, ImageContentBlock):
text = "<image>"
elif isinstance(content, AudioContentBlock):
text = "<audio>"
elif isinstance(content, ResourceContentBlock):
text = content.uri or "<resource>"
elif isinstance(content, EmbeddedResourceContentBlock):
text = "<resource>"
else:
text = getattr(content, "text", "<content>")
text = "<content>"

print(f"| Agent: {text}")

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

await conn.initialize(InitializeRequest(protocolVersion=PROTOCOL_VERSION, clientCapabilities=None))
await conn.initialize(
InitializeRequest(
protocolVersion=PROTOCOL_VERSION,
clientCapabilities=ClientCapabilities(),
clientInfo=Implementation(name="example-client", title="Example Client", version="0.1.0"),
)
)
session = await conn.newSession(NewSessionRequest(mcpServers=[], cwd=os.getcwd()))

await interactive_loop(conn, session.sessionId)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "agent-client-protocol"
version = "0.5.0"
version = "0.6.2"
description = "A Python implement of Agent Client Protocol (ACP, by Zed Industries)"
authors = [{ name = "Chojan Shang", email = "psiace@apache.org" }]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion schema/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
refs/tags/v0.5.0
refs/tags/v0.6.2
49 changes: 48 additions & 1 deletion schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,31 @@
],
"type": "object"
},
"Implementation": {
"description": "Describes the name and version of an MCP implementation, with an optional\ntitle for UI representation.",
"properties": {
"name": {
"description": "Intended for programmatic or logical use, but can be used as a display\nname fallback if title isn\u2019t present.",
"type": "string"
},
"title": {
"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.",
"type": [
"string",
"null"
]
},
"version": {
"description": "Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes.",
"type": "string"
}
},
"required": [
"name",
"version"
],
"type": "object"
},
"InitializeRequest": {
"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)",
"properties": {
Expand All @@ -1088,6 +1113,17 @@
},
"description": "Capabilities supported by the client."
},
"clientInfo": {
"anyOf": [
{
"$ref": "#/$defs/Implementation"
},
{
"type": "null"
}
],
"description": "Information about the Client name and version sent to the Agent.\n\nNote: in future versions of the protocol, this will be required."
},
"protocolVersion": {
"$ref": "#/$defs/ProtocolVersion",
"description": "The latest protocol version supported by the client."
Expand Down Expand Up @@ -1122,6 +1158,17 @@
},
"description": "Capabilities supported by the agent."
},
"agentInfo": {
"anyOf": [
{
"$ref": "#/$defs/Implementation"
},
{
"type": "null"
}
],
"description": "Information about the Agent name and version sent to the Client.\n\nNote: in future versions of the protocol, this will be required."
},
"authMethods": {
"default": [],
"description": "Authentication methods supported by the agent.",
Expand Down Expand Up @@ -2304,7 +2351,7 @@
"SetSessionModeResponse": {
"description": "Response to `session/set_mode` method.",
"properties": {
"meta": true
"_meta": true
},
"type": "object",
"x-method": "session/set_mode",
Expand Down
2 changes: 1 addition & 1 deletion src/acp/meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Generated from schema/meta.json. Do not edit by hand.
# Schema ref: refs/tags/v0.5.0
# Schema ref: refs/tags/v0.6.2
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'}
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'}
PROTOCOL_VERSION = 1
51 changes: 49 additions & 2 deletions src/acp/schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Generated from schema/schema.json. Do not edit by hand.
# Schema ref: refs/tags/v0.5.0
# Schema ref: refs/tags/v0.6.2

from __future__ import annotations

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


class Implementation(BaseModel):
# Intended for programmatic or logical use, but can be used as a display
# name fallback if title isn’t present.
name: Annotated[
str,
Field(
description="Intended for programmatic or logical use, but can be used as a display\nname fallback if title isn’t present."
),
]
# Intended for UI and end-user contexts — optimized to be human-readable
# and easily understood.
#
# If not provided, the name should be used for display.
title: Annotated[
Optional[str],
Field(
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."
),
] = None
# Version of the implementation. Can be displayed to the user or used
# for debugging or metrics purposes.
version: Annotated[
str,
Field(
description="Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes."
),
]


class KillTerminalCommandResponse(BaseModel):
# Extension point for implementations
field_meta: Annotated[
Expand Down Expand Up @@ -349,7 +378,7 @@ class SetSessionModeRequest(BaseModel):


class SetSessionModeResponse(BaseModel):
meta: Optional[Any] = None
field_meta: Annotated[Optional[Any], Field(alias="_meta")] = None


class SetSessionModelRequest(BaseModel):
Expand Down Expand Up @@ -767,6 +796,15 @@ class InitializeRequest(BaseModel):
Optional[ClientCapabilities],
Field(description="Capabilities supported by the client."),
] = ClientCapabilities(fs=FileSystemCapability(readTextFile=False, writeTextFile=False), terminal=False)
# Information about the Client name and version sent to the Agent.
#
# Note: in future versions of the protocol, this will be required.
clientInfo: Annotated[
Optional[Implementation],
Field(
description="Information about the Client name and version sent to the Agent.\n\nNote: in future versions of the protocol, this will be required."
),
] = None
# The latest protocol version supported by the client.
protocolVersion: Annotated[
int,
Expand All @@ -793,6 +831,15 @@ class InitializeResponse(BaseModel):
mcpCapabilities=McpCapabilities(http=False, sse=False),
promptCapabilities=PromptCapabilities(audio=False, embeddedContext=False, image=False),
)
# Information about the Agent name and version sent to the Client.
#
# Note: in future versions of the protocol, this will be required.
agentInfo: Annotated[
Optional[Implementation],
Field(
description="Information about the Agent name and version sent to the Client.\n\nNote: in future versions of the protocol, this will be required."
),
] = None
# Authentication methods supported by the agent.
authMethods: Annotated[
Optional[List[AuthMethod]],
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.