diff --git a/examples/agent.py b/examples/agent.py index 3366817..c53ad56 100644 --- a/examples/agent.py +++ b/examples/agent.py @@ -28,7 +28,7 @@ Implementation, ResourceContentBlock, SseMcpServer, - StdioMcpServer, + McpServerStdio, TextContentBlock, ) @@ -66,7 +66,7 @@ async def authenticate(self, method_id: str, **kwargs: Any) -> AuthenticateRespo return AuthenticateResponse() async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: logging.info("Received new session request") session_id = str(self._next_session_id) @@ -75,7 +75,7 @@ async def new_session( return NewSessionResponse(session_id=session_id, modes=None) async def load_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], session_id: str, **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], session_id: str, **kwargs: Any ) -> LoadSessionResponse | None: logging.info("Received load session request %s", session_id) self._sessions.add(session_id) diff --git a/examples/echo_agent.py b/examples/echo_agent.py index 282a205..a096677 100644 --- a/examples/echo_agent.py +++ b/examples/echo_agent.py @@ -22,7 +22,7 @@ Implementation, ResourceContentBlock, SseMcpServer, - StdioMcpServer, + McpServerStdio, TextContentBlock, ) @@ -43,7 +43,7 @@ async def initialize( return InitializeResponse(protocol_version=protocol_version) async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: return NewSessionResponse(session_id=uuid4().hex) diff --git a/schema/VERSION b/schema/VERSION index 75451e3..4803c54 100644 --- a/schema/VERSION +++ b/schema/VERSION @@ -1 +1 @@ -refs/tags/v0.6.3 +refs/tags/v0.7.0 diff --git a/schema/meta.json b/schema/meta.json index 0f0c6c4..7fad892 100644 --- a/schema/meta.json +++ b/schema/meta.json @@ -3,6 +3,7 @@ "authenticate": "authenticate", "initialize": "initialize", "session_cancel": "session/cancel", + "session_list": "session/list", "session_load": "session/load", "session_new": "session/new", "session_prompt": "session/prompt", diff --git a/schema/schema.json b/schema/schema.json index 9b39020..88f286e 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -12,7 +12,11 @@ "type": "boolean" }, "mcpCapabilities": { - "$ref": "#/$defs/McpCapabilities", + "allOf": [ + { + "$ref": "#/$defs/McpCapabilities" + } + ], "default": { "http": false, "sse": false @@ -20,13 +24,25 @@ "description": "MCP capabilities supported by the agent." }, "promptCapabilities": { - "$ref": "#/$defs/PromptCapabilities", + "allOf": [ + { + "$ref": "#/$defs/PromptCapabilities" + } + ], "default": { "audio": false, "embeddedContext": false, "image": false }, "description": "Prompt capabilities supported by the agent." + }, + "sessionCapabilities": { + "allOf": [ + { + "$ref": "#/$defs/SessionCapabilities" + } + ], + "default": {} } }, "type": "object" @@ -34,13 +50,15 @@ "AgentNotification": { "anyOf": [ { - "$ref": "#/$defs/SessionNotification", - "description": "Handles session update notifications from the agent.\n\nThis is a notification endpoint (no response expected) that receives\nreal-time updates about session progress, including message chunks,\ntool calls, and execution plans.\n\nNote: Clients SHOULD continue accepting tool call updates even after\nsending a `session/cancel` notification, as the agent may send final\nupdates before responding with the cancelled stop reason.\n\nSee protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)", - "title": "SessionNotification" + "allOf": [ + { + "$ref": "#/$defs/SessionNotification" + } + ], + "description": "Handles session update notifications from the agent.\n\nThis is a notification endpoint (no response expected) that receives\nreal-time updates about session progress, including message chunks,\ntool calls, and execution plans.\n\nNote: Clients SHOULD continue accepting tool call updates even after\nsending a `session/cancel` notification, as the agent may send final\nupdates before responding with the cancelled stop reason.\n\nSee protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)" }, { - "description": "Handles extension notifications from the agent.\n\nAllows the Agent to send an arbitrary notification that is not part of the ACP spec.\nExtension notifications provide a way to send one-way messages for custom functionality\nwhile maintaining protocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "title": "ExtNotification" + "description": "Handles extension notifications from the agent.\n\nAllows the Agent to send an arbitrary notification that is not part of the ACP spec.\nExtension notifications provide a way to send one-way messages for custom functionality\nwhile maintaining protocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)" } ], "description": "All possible notifications that an agent can send to a client.\n\nThis enum is used internally for routing RPC notifications. You typically won't need\nto use this directly - use the notification methods on the [`Client`] trait instead.\n\nNotifications do not expect a response.", @@ -53,16 +71,13 @@ "id": { "anyOf": [ { - "title": "null", "type": "null" }, { "format": "int64", - "title": "number", "type": "integer" }, { - "title": "string", "type": "string" } ], @@ -86,7 +101,6 @@ "id", "method" ], - "title": "Request", "type": "object" }, { @@ -118,16 +132,13 @@ "id": { "anyOf": [ { - "title": "null", "type": "null" }, { "format": "int64", - "title": "number", "type": "integer" }, { - "title": "string", "type": "string" } ], @@ -137,7 +148,6 @@ "required": [ "id" ], - "title": "Response", "type": "object" }, { @@ -159,7 +169,6 @@ "required": [ "method" ], - "title": "Notification", "type": "object" } ], @@ -181,48 +190,71 @@ "AgentRequest": { "anyOf": [ { - "$ref": "#/$defs/WriteTextFileRequest", - "description": "Writes content to a text file in the client's file system.\n\nOnly available if the client advertises the `fs.writeTextFile` capability.\nAllows the agent to create or modify files within the client's environment.\n\nSee protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)", - "title": "WriteTextFileRequest" + "allOf": [ + { + "$ref": "#/$defs/WriteTextFileRequest" + } + ], + "description": "Writes content to a text file in the client's file system.\n\nOnly available if the client advertises the `fs.writeTextFile` capability.\nAllows the agent to create or modify files within the client's environment.\n\nSee protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)" }, { - "$ref": "#/$defs/ReadTextFileRequest", - "description": "Reads content from a text file in the client's file system.\n\nOnly available if the client advertises the `fs.readTextFile` capability.\nAllows the agent to access file contents within the client's environment.\n\nSee protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)", - "title": "ReadTextFileRequest" + "allOf": [ + { + "$ref": "#/$defs/ReadTextFileRequest" + } + ], + "description": "Reads content from a text file in the client's file system.\n\nOnly available if the client advertises the `fs.readTextFile` capability.\nAllows the agent to access file contents within the client's environment.\n\nSee protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)" }, { - "$ref": "#/$defs/RequestPermissionRequest", - "description": "Requests permission from the user for a tool call operation.\n\nCalled by the agent when it needs user authorization before executing\na potentially sensitive operation. The client should present the options\nto the user and return their decision.\n\nIf the client cancels the prompt turn via `session/cancel`, it MUST\nrespond to this request with `RequestPermissionOutcome::Cancelled`.\n\nSee protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)", - "title": "RequestPermissionRequest" + "allOf": [ + { + "$ref": "#/$defs/RequestPermissionRequest" + } + ], + "description": "Requests permission from the user for a tool call operation.\n\nCalled by the agent when it needs user authorization before executing\na potentially sensitive operation. The client should present the options\nto the user and return their decision.\n\nIf the client cancels the prompt turn via `session/cancel`, it MUST\nrespond to this request with `RequestPermissionOutcome::Cancelled`.\n\nSee protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)" }, { - "$ref": "#/$defs/CreateTerminalRequest", - "description": "Executes a command in a new terminal\n\nOnly available if the `terminal` Client capability is set to `true`.\n\nReturns a `TerminalId` that can be used with other terminal methods\nto get the current output, wait for exit, and kill the command.\n\nThe `TerminalId` can also be used to embed the terminal in a tool call\nby using the `ToolCallContent::Terminal` variant.\n\nThe Agent is responsible for releasing the terminal by using the `terminal/release`\nmethod.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)", - "title": "CreateTerminalRequest" + "allOf": [ + { + "$ref": "#/$defs/CreateTerminalRequest" + } + ], + "description": "Executes a command in a new terminal\n\nOnly available if the `terminal` Client capability is set to `true`.\n\nReturns a `TerminalId` that can be used with other terminal methods\nto get the current output, wait for exit, and kill the command.\n\nThe `TerminalId` can also be used to embed the terminal in a tool call\nby using the `ToolCallContent::Terminal` variant.\n\nThe Agent is responsible for releasing the terminal by using the `terminal/release`\nmethod.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)" }, { - "$ref": "#/$defs/TerminalOutputRequest", - "description": "Gets the terminal output and exit status\n\nReturns the current content in the terminal without waiting for the command to exit.\nIf the command has already exited, the exit status is included.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)", - "title": "TerminalOutputRequest" + "allOf": [ + { + "$ref": "#/$defs/TerminalOutputRequest" + } + ], + "description": "Gets the terminal output and exit status\n\nReturns the current content in the terminal without waiting for the command to exit.\nIf the command has already exited, the exit status is included.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)" }, { - "$ref": "#/$defs/ReleaseTerminalRequest", - "description": "Releases a terminal\n\nThe command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`\nto wait for the command to exit before releasing the terminal.\n\nAfter release, the `TerminalId` can no longer be used with other `terminal/*` methods,\nbut tool calls that already contain it, continue to display its output.\n\nThe `terminal/kill` method can be used to terminate the command without releasing\nthe terminal, allowing the Agent to call `terminal/output` and other methods.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)", - "title": "ReleaseTerminalRequest" + "allOf": [ + { + "$ref": "#/$defs/ReleaseTerminalRequest" + } + ], + "description": "Releases a terminal\n\nThe command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`\nto wait for the command to exit before releasing the terminal.\n\nAfter release, the `TerminalId` can no longer be used with other `terminal/*` methods,\nbut tool calls that already contain it, continue to display its output.\n\nThe `terminal/kill` method can be used to terminate the command without releasing\nthe terminal, allowing the Agent to call `terminal/output` and other methods.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)" }, { - "$ref": "#/$defs/WaitForTerminalExitRequest", - "description": "Waits for the terminal command to exit and return its exit status\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)", - "title": "WaitForTerminalExitRequest" + "allOf": [ + { + "$ref": "#/$defs/WaitForTerminalExitRequest" + } + ], + "description": "Waits for the terminal command to exit and return its exit status\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)" }, { - "$ref": "#/$defs/KillTerminalCommandRequest", - "description": "Kills the terminal command without releasing the terminal\n\nWhile `terminal/release` will also kill the command, this method will keep\nthe `TerminalId` valid so it can be used with other methods.\n\nThis method can be helpful when implementing command timeouts which terminate\nthe command as soon as elapsed, and then get the final output so it can be sent\nto the model.\n\nNote: `terminal/release` when `TerminalId` is no longer needed.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)", - "title": "KillTerminalCommandRequest" + "allOf": [ + { + "$ref": "#/$defs/KillTerminalCommandRequest" + } + ], + "description": "Kills the terminal command without releasing the terminal\n\nWhile `terminal/release` will also kill the command, this method will keep\nthe `TerminalId` valid so it can be used with other methods.\n\nThis method can be helpful when implementing command timeouts which terminate\nthe command as soon as elapsed, and then get the final output so it can be sent\nto the model.\n\nNote: `terminal/release` when `TerminalId` is no longer needed.\n\nSee protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)" }, { - "description": "Handles extension method requests from the agent.\n\nAllows the Agent to send an arbitrary request that is not part of the ACP spec.\nExtension methods provide a way to add custom functionality while maintaining\nprotocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "title": "ExtMethodRequest" + "description": "Handles extension method requests from the agent.\n\nAllows the Agent to send an arbitrary request that is not part of the ACP spec.\nExtension methods provide a way to add custom functionality while maintaining\nprotocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)" } ], "description": "All possible requests that an agent can send to a client.\n\nThis enum is used internally for routing RPC requests. You typically won't need\nto use this directly - instead, use the methods on the [`Client`] trait.\n\nThis enum encompasses all method calls from agent to client.", @@ -231,36 +263,30 @@ "AgentResponse": { "anyOf": [ { - "$ref": "#/$defs/InitializeResponse", - "title": "InitializeResponse" + "$ref": "#/$defs/InitializeResponse" }, { - "$ref": "#/$defs/AuthenticateResponse", - "title": "AuthenticateResponse" + "$ref": "#/$defs/AuthenticateResponse" }, { - "$ref": "#/$defs/NewSessionResponse", - "title": "NewSessionResponse" + "$ref": "#/$defs/NewSessionResponse" }, { - "$ref": "#/$defs/LoadSessionResponse", - "title": "LoadSessionResponse" + "$ref": "#/$defs/LoadSessionResponse" }, { - "$ref": "#/$defs/SetSessionModeResponse", - "title": "SetSessionModeResponse" + "$ref": "#/$defs/ListSessionsResponse" }, { - "$ref": "#/$defs/PromptResponse", - "title": "PromptResponse" + "$ref": "#/$defs/SetSessionModeResponse" }, { - "$ref": "#/$defs/SetSessionModelResponse", - "title": "SetSessionModelResponse" + "$ref": "#/$defs/PromptResponse" }, { - "title": "ExtMethodResponse" - } + "$ref": "#/$defs/SetSessionModelResponse" + }, + {} ], "description": "All possible responses that an agent can send to a client.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `ClientRequest` variants.", "x-docs-ignore": true @@ -296,6 +322,35 @@ }, "type": "object" }, + "AudioContent": { + "description": "Audio provided to or from an LLM.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "annotations": { + "anyOf": [ + { + "$ref": "#/$defs/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + } + }, + "required": [ + "data", + "mimeType" + ], + "type": "object" + }, "AuthMethod": { "description": "Describes an available authentication method.", "properties": { @@ -310,8 +365,8 @@ ] }, "id": { - "$ref": "#/$defs/AuthMethodId", - "description": "Unique identifier for this authentication method." + "description": "Unique identifier for this authentication method.", + "type": "string" }, "name": { "description": "Human-readable name of the authentication method.", @@ -324,10 +379,6 @@ ], "type": "object" }, - "AuthMethodId": { - "description": "Unique identifier for an authentication method.", - "type": "string" - }, "AuthenticateRequest": { "description": "Request parameters for the authenticate method.\n\nSpecifies which authentication method to use.", "properties": { @@ -335,8 +386,8 @@ "description": "Extension point for implementations" }, "methodId": { - "$ref": "#/$defs/AuthMethodId", - "description": "The ID of the authentication method to use.\nMust be one of the methods advertised in the initialize response." + "description": "The ID of the authentication method to use.\nMust be one of the methods advertised in the initialize response.", + "type": "string" } }, "required": [ @@ -347,7 +398,7 @@ "x-side": "agent" }, "AuthenticateResponse": { - "description": "Response to authenticate method", + "description": "Response to the `authenticate` method.", "properties": { "_meta": { "description": "Extension point for implementations" @@ -392,22 +443,35 @@ "AvailableCommandInput": { "anyOf": [ { - "description": "All text that was typed after the command name is provided as input.", - "properties": { - "hint": { - "description": "A hint to display when the input hasn't been provided yet", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/UnstructuredCommandInput" } - }, - "required": [ - "hint" ], - "title": "UnstructuredCommandInput", - "type": "object" + "description": "All text that was typed after the command name is provided as input." } ], "description": "The input specification for a command." }, + "AvailableCommandsUpdate": { + "description": "Available commands are ready or have changed", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "availableCommands": { + "description": "Commands the agent can execute", + "items": { + "$ref": "#/$defs/AvailableCommand" + }, + "type": "array" + } + }, + "required": [ + "availableCommands" + ], + "type": "object" + }, "BlobResourceContents": { "description": "Binary resource contents.", "properties": { @@ -440,7 +504,11 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session to cancel operations for." } }, @@ -458,7 +526,11 @@ "description": "Extension point for implementations" }, "fs": { - "$ref": "#/$defs/FileSystemCapability", + "allOf": [ + { + "$ref": "#/$defs/FileSystemCapability" + } + ], "default": { "readTextFile": false, "writeTextFile": false @@ -476,13 +548,15 @@ "ClientNotification": { "anyOf": [ { - "$ref": "#/$defs/CancelNotification", - "description": "Cancels ongoing operations for a session.\n\nThis is a notification sent by the client to cancel an ongoing prompt turn.\n\nUpon receiving this notification, the Agent SHOULD:\n- Stop all language model requests as soon as possible\n- Abort all tool call invocations in progress\n- Send any pending `session/update` notifications\n- Respond to the original `session/prompt` request with `StopReason::Cancelled`\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)", - "title": "CancelNotification" + "allOf": [ + { + "$ref": "#/$defs/CancelNotification" + } + ], + "description": "Cancels ongoing operations for a session.\n\nThis is a notification sent by the client to cancel an ongoing prompt turn.\n\nUpon receiving this notification, the Agent SHOULD:\n- Stop all language model requests as soon as possible\n- Abort all tool call invocations in progress\n- Send any pending `session/update` notifications\n- Respond to the original `session/prompt` request with `StopReason::Cancelled`\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)" }, { - "description": "Handles extension notifications from the client.\n\nExtension notifications provide a way to send one-way messages for custom functionality\nwhile maintaining protocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "title": "ExtNotification" + "description": "Handles extension notifications from the client.\n\nExtension notifications provide a way to send one-way messages for custom functionality\nwhile maintaining protocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)" } ], "description": "All possible notifications that a client can send to an agent.\n\nThis enum is used internally for routing RPC notifications. You typically won't need\nto use this directly - use the notification methods on the [`Agent`] trait instead.\n\nNotifications do not expect a response.", @@ -495,16 +569,13 @@ "id": { "anyOf": [ { - "title": "null", "type": "null" }, { "format": "int64", - "title": "number", "type": "integer" }, { - "title": "string", "type": "string" } ], @@ -528,7 +599,6 @@ "id", "method" ], - "title": "Request", "type": "object" }, { @@ -560,16 +630,13 @@ "id": { "anyOf": [ { - "title": "null", "type": "null" }, { "format": "int64", - "title": "number", "type": "integer" }, { - "title": "string", "type": "string" } ], @@ -579,7 +646,6 @@ "required": [ "id" ], - "title": "Response", "type": "object" }, { @@ -601,7 +667,6 @@ "required": [ "method" ], - "title": "Notification", "type": "object" } ], @@ -623,43 +688,71 @@ "ClientRequest": { "anyOf": [ { - "$ref": "#/$defs/InitializeRequest", - "description": "Establishes the connection with a client and negotiates protocol capabilities.\n\nThis method is called once at the beginning of the connection to:\n- Negotiate the protocol version to use\n- Exchange capability information between client and agent\n- Determine available authentication methods\n\nThe agent should respond with its supported protocol version and capabilities.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)", - "title": "InitializeRequest" + "allOf": [ + { + "$ref": "#/$defs/InitializeRequest" + } + ], + "description": "Establishes the connection with a client and negotiates protocol capabilities.\n\nThis method is called once at the beginning of the connection to:\n- Negotiate the protocol version to use\n- Exchange capability information between client and agent\n- Determine available authentication methods\n\nThe agent should respond with its supported protocol version and capabilities.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)" + }, + { + "allOf": [ + { + "$ref": "#/$defs/AuthenticateRequest" + } + ], + "description": "Authenticates the client using the specified authentication method.\n\nCalled when the agent requires authentication before allowing session creation.\nThe client provides the authentication method ID that was advertised during initialization.\n\nAfter successful authentication, the client can proceed to create sessions with\n`new_session` without receiving an `auth_required` error.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)" }, { - "$ref": "#/$defs/AuthenticateRequest", - "description": "Authenticates the client using the specified authentication method.\n\nCalled when the agent requires authentication before allowing session creation.\nThe client provides the authentication method ID that was advertised during initialization.\n\nAfter successful authentication, the client can proceed to create sessions with\n`new_session` without receiving an `auth_required` error.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)", - "title": "AuthenticateRequest" + "allOf": [ + { + "$ref": "#/$defs/NewSessionRequest" + } + ], + "description": "Creates a new conversation session with the agent.\n\nSessions represent independent conversation contexts with their own history and state.\n\nThe agent should:\n- Create a new session context\n- Connect to any specified MCP servers\n- Return a unique session ID for future requests\n\nMay return an `auth_required` error if the agent requires authentication.\n\nSee protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)" }, { - "$ref": "#/$defs/NewSessionRequest", - "description": "Creates a new conversation session with the agent.\n\nSessions represent independent conversation contexts with their own history and state.\n\nThe agent should:\n- Create a new session context\n- Connect to any specified MCP servers\n- Return a unique session ID for future requests\n\nMay return an `auth_required` error if the agent requires authentication.\n\nSee protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)", - "title": "NewSessionRequest" + "allOf": [ + { + "$ref": "#/$defs/LoadSessionRequest" + } + ], + "description": "Loads an existing session to resume a previous conversation.\n\nThis method is only available if the agent advertises the `loadSession` capability.\n\nThe agent should:\n- Restore the session context and conversation history\n- Connect to the specified MCP servers\n- Stream the entire conversation history back to the client via notifications\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)" }, { - "$ref": "#/$defs/LoadSessionRequest", - "description": "Loads an existing session to resume a previous conversation.\n\nThis method is only available if the agent advertises the `loadSession` capability.\n\nThe agent should:\n- Restore the session context and conversation history\n- Connect to the specified MCP servers\n- Stream the entire conversation history back to the client via notifications\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)", - "title": "LoadSessionRequest" + "allOf": [ + { + "$ref": "#/$defs/ListSessionsRequest" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nLists existing sessions known to the agent.\n\nThis method is only available if the agent advertises the `listSessions` capability.\n\nThe agent should return metadata about sessions with optional filtering and pagination support." }, { - "$ref": "#/$defs/SetSessionModeRequest", - "description": "Sets the current mode for a session.\n\nAllows switching between different agent modes (e.g., \"ask\", \"architect\", \"code\")\nthat affect system prompts, tool availability, and permission behaviors.\n\nThe mode must be one of the modes advertised in `availableModes` during session\ncreation or loading. Agents may also change modes autonomously and notify the\nclient via `current_mode_update` notifications.\n\nThis method can be called at any time during a session, whether the Agent is\nidle or actively generating a response.\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", - "title": "SetSessionModeRequest" + "allOf": [ + { + "$ref": "#/$defs/SetSessionModeRequest" + } + ], + "description": "Sets the current mode for a session.\n\nAllows switching between different agent modes (e.g., \"ask\", \"architect\", \"code\")\nthat affect system prompts, tool availability, and permission behaviors.\n\nThe mode must be one of the modes advertised in `availableModes` during session\ncreation or loading. Agents may also change modes autonomously and notify the\nclient via `current_mode_update` notifications.\n\nThis method can be called at any time during a session, whether the Agent is\nidle or actively generating a response.\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)" }, { - "$ref": "#/$defs/PromptRequest", - "description": "Processes a user prompt within a session.\n\nThis method handles the whole lifecycle of a prompt:\n- Receives user messages with optional context (files, images, etc.)\n- Processes the prompt using language models\n- Reports language model content and tool calls to the Clients\n- Requests permission to run tools\n- Executes any requested tool calls\n- Returns when the turn is complete with a stop reason\n\nSee protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)", - "title": "PromptRequest" + "allOf": [ + { + "$ref": "#/$defs/PromptRequest" + } + ], + "description": "Processes a user prompt within a session.\n\nThis method handles the whole lifecycle of a prompt:\n- Receives user messages with optional context (files, images, etc.)\n- Processes the prompt using language models\n- Reports language model content and tool calls to the Clients\n- Requests permission to run tools\n- Executes any requested tool calls\n- Returns when the turn is complete with a stop reason\n\nSee protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)" }, { - "$ref": "#/$defs/SetSessionModelRequest", - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSelect a model for a given session.", - "title": "SetSessionModelRequest" + "allOf": [ + { + "$ref": "#/$defs/SetSessionModelRequest" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSelect a model for a given session." }, { - "description": "Handles extension method requests from the client.\n\nExtension methods provide a way to add custom functionality while maintaining\nprotocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "title": "ExtMethodRequest" + "description": "Handles extension method requests from the client.\n\nExtension methods provide a way to add custom functionality while maintaining\nprotocol compatibility.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)" } ], "description": "All possible requests that a client can send to an agent.\n\nThis enum is used internally for routing RPC requests. You typically won't need\nto use this directly - instead, use the methods on the [`Agent`] trait.\n\nThis enum encompasses all method calls from client to agent.", @@ -668,44 +761,54 @@ "ClientResponse": { "anyOf": [ { - "$ref": "#/$defs/WriteTextFileResponse", - "title": "WriteTextFileResponse" + "$ref": "#/$defs/WriteTextFileResponse" }, { - "$ref": "#/$defs/ReadTextFileResponse", - "title": "ReadTextFileResponse" + "$ref": "#/$defs/ReadTextFileResponse" }, { - "$ref": "#/$defs/RequestPermissionResponse", - "title": "RequestPermissionResponse" + "$ref": "#/$defs/RequestPermissionResponse" }, { - "$ref": "#/$defs/CreateTerminalResponse", - "title": "CreateTerminalResponse" + "$ref": "#/$defs/CreateTerminalResponse" }, { - "$ref": "#/$defs/TerminalOutputResponse", - "title": "TerminalOutputResponse" + "$ref": "#/$defs/TerminalOutputResponse" }, { - "$ref": "#/$defs/ReleaseTerminalResponse", - "title": "ReleaseTerminalResponse" + "$ref": "#/$defs/ReleaseTerminalResponse" }, { - "$ref": "#/$defs/WaitForTerminalExitResponse", - "title": "WaitForTerminalExitResponse" + "$ref": "#/$defs/WaitForTerminalExitResponse" }, { - "$ref": "#/$defs/KillTerminalCommandResponse", - "title": "KillTerminalResponse" + "$ref": "#/$defs/KillTerminalCommandResponse" }, - { - "title": "ExtMethodResponse" - } + {} ], "description": "All possible responses that a client can send to an agent.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `AgentRequest` variants.", "x-docs-ignore": true }, + "Content": { + "description": "Standard content block (text, images, resources).", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "content": { + "allOf": [ + { + "$ref": "#/$defs/ContentBlock" + } + ], + "description": "The actual content block." + } + }, + "required": [ + "content" + ], + "type": "object" + }, "ContentBlock": { "description": "Content blocks represent displayable information in the Agent Client Protocol.\n\nThey provide a structured way to handle various types of user-facing content\u2014whether\nit's text from language models, images for analysis, or embedded resources for context.\n\nContent blocks appear in:\n- User prompts sent via `session/prompt`\n- Language model output streamed through `session/update` notifications\n- Progress updates and results from tool calls\n\nThis structure is compatible with the Model Context Protocol (MCP), enabling\nagents to seamlessly forward content from MCP tool outputs without transformation.\n\nSee protocol docs: [Content](https://agentclientprotocol.com/protocol/content)", "discriminator": { @@ -713,200 +816,117 @@ }, "oneOf": [ { + "allOf": [ + { + "$ref": "#/$defs/TextContent" + } + ], "description": "Text content. May be plain text or formatted with Markdown.\n\nAll agents MUST support text content blocks in prompts.\nClients SHOULD render this text as Markdown.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "annotations": { - "anyOf": [ - { - "$ref": "#/$defs/Annotations" - }, - { - "type": "null" - } - ] - }, - "text": { - "type": "string" - }, "type": { "const": "text", "type": "string" } }, "required": [ - "type", - "text" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ImageContent" + } + ], "description": "Images for visual context or analysis.\n\nRequires the `image` prompt capability when included in prompts.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "annotations": { - "anyOf": [ - { - "$ref": "#/$defs/Annotations" - }, - { - "type": "null" - } - ] - }, - "data": { - "type": "string" - }, - "mimeType": { - "type": "string" - }, "type": { "const": "image", "type": "string" - }, - "uri": { - "type": [ - "string", - "null" - ] } }, "required": [ - "type", - "data", - "mimeType" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/AudioContent" + } + ], "description": "Audio data for transcription or analysis.\n\nRequires the `audio` prompt capability when included in prompts.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "annotations": { - "anyOf": [ - { - "$ref": "#/$defs/Annotations" - }, - { - "type": "null" - } - ] - }, - "data": { - "type": "string" - }, - "mimeType": { - "type": "string" - }, "type": { "const": "audio", "type": "string" } }, "required": [ - "type", - "data", - "mimeType" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ResourceLink" + } + ], "description": "References to resources that the agent can access.\n\nAll agents MUST support resource links in prompts.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "annotations": { - "anyOf": [ - { - "$ref": "#/$defs/Annotations" - }, - { - "type": "null" - } - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "mimeType": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": "string" - }, - "size": { - "format": "int64", - "type": [ - "integer", - "null" - ] - }, - "title": { - "type": [ - "string", - "null" - ] - }, "type": { "const": "resource_link", "type": "string" - }, - "uri": { - "type": "string" } }, "required": [ - "type", - "name", - "uri" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/EmbeddedResource" + } + ], "description": "Complete resource contents embedded directly in the message.\n\nPreferred for including context as it avoids extra round-trips.\n\nRequires the `embeddedContext` prompt capability when included in prompts.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "annotations": { - "anyOf": [ - { - "$ref": "#/$defs/Annotations" - }, - { - "type": "null" - } - ] - }, - "resource": { - "$ref": "#/$defs/EmbeddedResourceResource" - }, "type": { "const": "resource", "type": "string" } }, "required": [ - "type", - "resource" + "type" ], "type": "object" } ] }, + "ContentChunk": { + "description": "A streamed item of content", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "content": { + "allOf": [ + { + "$ref": "#/$defs/ContentBlock" + } + ], + "description": "A single item of content" + } + }, + "required": [ + "content" + ], + "type": "object" + }, "CreateTerminalRequest": { "description": "Request to create a new terminal and execute a command.", "properties": { @@ -948,7 +968,11 @@ ] }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." } }, @@ -978,30 +1002,101 @@ "x-method": "terminal/create", "x-side": "client" }, - "EmbeddedResourceResource": { - "anyOf": [ - { - "$ref": "#/$defs/TextResourceContents", - "title": "TextResourceContents" - }, - { - "$ref": "#/$defs/BlobResourceContents", - "title": "BlobResourceContents" - } - ], - "description": "Resource content that can be embedded in a message." - }, - "EnvVariable": { - "description": "An environment variable to set when launching an MCP server.", + "CurrentModeUpdate": { + "description": "The current mode of the session has changed\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", "properties": { "_meta": { "description": "Extension point for implementations" }, - "name": { - "description": "The name of the environment variable.", - "type": "string" - }, - "value": { + "currentModeId": { + "allOf": [ + { + "$ref": "#/$defs/SessionModeId" + } + ], + "description": "The ID of the current mode" + } + }, + "required": [ + "currentModeId" + ], + "type": "object" + }, + "Diff": { + "description": "A diff representing file modifications.\n\nShows changes to files in a format suitable for display in the client UI.\n\nSee protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "newText": { + "description": "The new content after modification.", + "type": "string" + }, + "oldText": { + "description": "The original content (None for new files).", + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "The file path being modified.", + "type": "string" + } + }, + "required": [ + "path", + "newText" + ], + "type": "object" + }, + "EmbeddedResource": { + "description": "The contents of a resource, embedded into a prompt or tool call result.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "annotations": { + "anyOf": [ + { + "$ref": "#/$defs/Annotations" + }, + { + "type": "null" + } + ] + }, + "resource": { + "$ref": "#/$defs/EmbeddedResourceResource" + } + }, + "required": [ + "resource" + ], + "type": "object" + }, + "EmbeddedResourceResource": { + "anyOf": [ + { + "$ref": "#/$defs/TextResourceContents" + }, + { + "$ref": "#/$defs/BlobResourceContents" + } + ], + "description": "Resource content that can be embedded in a message." + }, + "EnvVariable": { + "description": "An environment variable to set when launching an MCP server.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "name": { + "description": "The name of the environment variable.", + "type": "string" + }, + "value": { "description": "The value to set for the environment variable.", "type": "string" } @@ -1035,7 +1130,7 @@ "type": "object" }, "FileSystemCapability": { - "description": "File system capabilities that a client may support.\n\nSee protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)", + "description": "Filesystem capabilities supported by the client.\nFile system capabilities that a client may support.\n\nSee protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)", "properties": { "_meta": { "description": "Extension point for implementations" @@ -1074,9 +1169,47 @@ ], "type": "object" }, + "ImageContent": { + "description": "An image provided to or from an LLM.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "annotations": { + "anyOf": [ + { + "$ref": "#/$defs/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "uri": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "data", + "mimeType" + ], + "type": "object" + }, "Implementation": { - "description": "Describes the name and version of an MCP implementation, with an optional\ntitle for UI representation.", + "description": "Metadata about the implementation of the client or agent.\nDescribes the name and version of an MCP implementation, with an optional\ntitle for UI representation.", "properties": { + "_meta": { + "description": "Extension point for implementations" + }, "name": { "description": "Intended for programmatic or logical use, but can be used as a display\nname fallback if title isn\u2019t present.", "type": "string" @@ -1089,7 +1222,7 @@ ] }, "version": { - "description": "Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes.", + "description": "Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes. (e.g. \"1.0.0\").", "type": "string" } }, @@ -1106,7 +1239,11 @@ "description": "Extension point for implementations" }, "clientCapabilities": { - "$ref": "#/$defs/ClientCapabilities", + "allOf": [ + { + "$ref": "#/$defs/ClientCapabilities" + } + ], "default": { "fs": { "readTextFile": false, @@ -1128,7 +1265,11 @@ "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", + "allOf": [ + { + "$ref": "#/$defs/ProtocolVersion" + } + ], "description": "The latest protocol version supported by the client." } }, @@ -1140,13 +1281,17 @@ "x-side": "agent" }, "InitializeResponse": { - "description": "Response from the initialize method.\n\nContains the negotiated protocol version and agent capabilities.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)", + "description": "Response to the `initialize` method.\n\nContains the negotiated protocol version and agent capabilities.\n\nSee protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)", "properties": { "_meta": { "description": "Extension point for implementations" }, "agentCapabilities": { - "$ref": "#/$defs/AgentCapabilities", + "allOf": [ + { + "$ref": "#/$defs/AgentCapabilities" + } + ], "default": { "loadSession": false, "mcpCapabilities": { @@ -1157,7 +1302,8 @@ "audio": false, "embeddedContext": false, "image": false - } + }, + "sessionCapabilities": {} }, "description": "Capabilities supported by the agent." }, @@ -1181,7 +1327,11 @@ "type": "array" }, "protocolVersion": { - "$ref": "#/$defs/ProtocolVersion", + "allOf": [ + { + "$ref": "#/$defs/ProtocolVersion" + } + ], "description": "The protocol version the client specified if supported by the agent,\nor the latest protocol version supported by the agent.\n\nThe client should disconnect, if it doesn't support this version." } }, @@ -1199,7 +1349,11 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." }, "terminalId": { @@ -1226,6 +1380,59 @@ "x-method": "terminal/kill", "x-side": "client" }, + "ListSessionsRequest": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for listing existing sessions.\n\nOnly available if the Agent supports the `listSessions` capability.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "cursor": { + "description": "Opaque cursor token from a previous response's nextCursor field for cursor-based pagination", + "type": [ + "string", + "null" + ] + }, + "cwd": { + "description": "Filter sessions by working directory. Must be an absolute path.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object", + "x-method": "session/list", + "x-side": "agent" + }, + "ListSessionsResponse": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nResponse from listing sessions.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "nextCursor": { + "description": "Opaque cursor token. If present, pass this in the next request's cursor parameter\nto fetch the next page. If absent, there are no more results.", + "type": [ + "string", + "null" + ] + }, + "sessions": { + "description": "Array of session information objects", + "items": { + "$ref": "#/$defs/SessionInfo" + }, + "type": "array" + } + }, + "required": [ + "sessions" + ], + "type": "object", + "x-method": "session/list", + "x-side": "agent" + }, "LoadSessionRequest": { "description": "Request parameters for loading an existing session.\n\nOnly available if the Agent supports the `loadSession` capability.\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)", "properties": { @@ -1244,7 +1451,11 @@ "type": "array" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session to load." } }, @@ -1312,101 +1523,48 @@ "McpServer": { "anyOf": [ { + "allOf": [ + { + "$ref": "#/$defs/McpServerHttp" + } + ], "description": "HTTP transport configuration\n\nOnly available when the Agent capabilities indicate `mcp_capabilities.http` is `true`.", "properties": { - "headers": { - "description": "HTTP headers to set when making requests to the MCP server.", - "items": { - "$ref": "#/$defs/HttpHeader" - }, - "type": "array" - }, - "name": { - "description": "Human-readable name identifying this MCP server.", - "type": "string" - }, "type": { "const": "http", "type": "string" - }, - "url": { - "description": "URL to the MCP server.", - "type": "string" } }, "required": [ - "type", - "name", - "url", - "headers" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/McpServerSse" + } + ], "description": "SSE transport configuration\n\nOnly available when the Agent capabilities indicate `mcp_capabilities.sse` is `true`.", "properties": { - "headers": { - "description": "HTTP headers to set when making requests to the MCP server.", - "items": { - "$ref": "#/$defs/HttpHeader" - }, - "type": "array" - }, - "name": { - "description": "Human-readable name identifying this MCP server.", - "type": "string" - }, "type": { "const": "sse", "type": "string" - }, - "url": { - "description": "URL to the MCP server.", - "type": "string" } }, "required": [ - "type", - "name", - "url", - "headers" + "type" ], "type": "object" }, { - "description": "Stdio transport configuration\n\nAll Agents MUST support this transport.", - "properties": { - "args": { - "description": "Command-line arguments to pass to the MCP server.", - "items": { - "type": "string" - }, - "type": "array" - }, - "command": { - "description": "Path to the MCP server executable.", - "type": "string" - }, - "env": { - "description": "Environment variables to set when launching the MCP server.", - "items": { - "$ref": "#/$defs/EnvVariable" - }, - "type": "array" - }, - "name": { - "description": "Human-readable name identifying this MCP server.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/McpServerStdio" } - }, - "required": [ - "name", - "command", - "args", - "env" ], - "title": "stdio", - "type": "object" + "description": "Stdio transport configuration\n\nAll Agents MUST support this transport." } ], "description": "Configuration for connecting to an MCP (Model Context Protocol) server.\n\nMCP servers provide tools and context that the agent can use when\nprocessing prompts.\n\nSee protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/session-setup#mcp-servers)", @@ -1414,6 +1572,101 @@ "propertyName": "type" } }, + "McpServerHttp": { + "description": "HTTP transport configuration for MCP.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "headers": { + "description": "HTTP headers to set when making requests to the MCP server.", + "items": { + "$ref": "#/$defs/HttpHeader" + }, + "type": "array" + }, + "name": { + "description": "Human-readable name identifying this MCP server.", + "type": "string" + }, + "url": { + "description": "URL to the MCP server.", + "type": "string" + } + }, + "required": [ + "name", + "url", + "headers" + ], + "type": "object" + }, + "McpServerSse": { + "description": "SSE transport configuration for MCP.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "headers": { + "description": "HTTP headers to set when making requests to the MCP server.", + "items": { + "$ref": "#/$defs/HttpHeader" + }, + "type": "array" + }, + "name": { + "description": "Human-readable name identifying this MCP server.", + "type": "string" + }, + "url": { + "description": "URL to the MCP server.", + "type": "string" + } + }, + "required": [ + "name", + "url", + "headers" + ], + "type": "object" + }, + "McpServerStdio": { + "description": "Stdio transport configuration for MCP.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "args": { + "description": "Command-line arguments to pass to the MCP server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "command": { + "description": "Path to the MCP server executable.", + "type": "string" + }, + "env": { + "description": "Environment variables to set when launching the MCP server.", + "items": { + "$ref": "#/$defs/EnvVariable" + }, + "type": "array" + }, + "name": { + "description": "Human-readable name identifying this MCP server.", + "type": "string" + } + }, + "required": [ + "name", + "command", + "args", + "env" + ], + "type": "object" + }, "ModelId": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA unique identifier for a model.", "type": "string" @@ -1432,7 +1685,11 @@ ] }, "modelId": { - "$ref": "#/$defs/ModelId", + "allOf": [ + { + "$ref": "#/$defs/ModelId" + } + ], "description": "Unique identifier for the model." }, "name": { @@ -1501,7 +1758,11 @@ "description": "Initial mode state if supported by the Agent\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "Unique identifier for the created session.\n\nUsed in all subsequent requests for this conversation." } }, @@ -1519,7 +1780,11 @@ "description": "Extension point for implementations" }, "kind": { - "$ref": "#/$defs/PermissionOptionKind", + "allOf": [ + { + "$ref": "#/$defs/PermissionOptionKind" + } + ], "description": "Hint about the nature of this permission option." }, "name": { @@ -1527,7 +1792,11 @@ "type": "string" }, "optionId": { - "$ref": "#/$defs/PermissionOptionId", + "allOf": [ + { + "$ref": "#/$defs/PermissionOptionId" + } + ], "description": "Unique identifier for this permission option." } }, @@ -1567,6 +1836,25 @@ } ] }, + "Plan": { + "description": "An execution plan for accomplishing complex tasks.\n\nPlans consist of multiple entries representing individual tasks or goals.\nAgents report plans to clients to provide visibility into their execution strategy.\nPlans can evolve during execution as the agent discovers new requirements or completes tasks.\n\nSee protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "entries": { + "description": "The list of tasks to be accomplished.\n\nWhen updating a plan, the agent must send a complete list of all entries\nwith their current status. The client replaces the entire plan with each update.", + "items": { + "$ref": "#/$defs/PlanEntry" + }, + "type": "array" + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "PlanEntry": { "description": "A single entry in the execution plan.\n\nRepresents a task or goal that the assistant intends to accomplish\nas part of fulfilling the user's request.\nSee protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent-plan#plan-entries)", "properties": { @@ -1578,11 +1866,19 @@ "type": "string" }, "priority": { - "$ref": "#/$defs/PlanEntryPriority", + "allOf": [ + { + "$ref": "#/$defs/PlanEntryPriority" + } + ], "description": "The relative importance of this task.\nUsed to indicate which tasks are most critical to the overall goal." }, "status": { - "$ref": "#/$defs/PlanEntryStatus", + "allOf": [ + { + "$ref": "#/$defs/PlanEntryStatus" + } + ], "description": "Current execution status of this task." } }, @@ -1671,7 +1967,11 @@ "type": "array" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session to send this user message to" } }, @@ -1690,7 +1990,11 @@ "description": "Extension point for implementations" }, "stopReason": { - "$ref": "#/$defs/StopReason", + "allOf": [ + { + "$ref": "#/$defs/StopReason" + } + ], "description": "Indicates why the agent stopped processing the turn." } }, @@ -1737,7 +2041,11 @@ "type": "string" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." } }, @@ -1773,7 +2081,11 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." }, "terminalId": { @@ -1820,20 +2132,20 @@ "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/SelectedPermissionOutcome" + } + ], "description": "The user selected one of the provided options.", "properties": { - "optionId": { - "$ref": "#/$defs/PermissionOptionId", - "description": "The ID of the option the user selected." - }, "outcome": { "const": "selected", "type": "string" } }, "required": [ - "outcome", - "optionId" + "outcome" ], "type": "object" } @@ -1853,79 +2165,20 @@ "type": "array" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." }, "toolCall": { - "description": "Details about the tool call requiring permission.", - "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "description": "Replace the content collection.", - "items": { - "$ref": "#/$defs/ToolCallContent" - }, - "type": [ - "array", - "null" - ] - }, - "kind": { - "anyOf": [ - { - "$ref": "#/$defs/ToolKind" - }, - { - "type": "null" - } - ], - "description": "Update the tool kind." - }, - "locations": { - "description": "Replace the locations collection.", - "items": { - "$ref": "#/$defs/ToolCallLocation" - }, - "type": [ - "array", - "null" - ] - }, - "rawInput": { - "description": "Update the raw input." - }, - "rawOutput": { - "description": "Update the raw output." - }, - "status": { - "anyOf": [ - { - "$ref": "#/$defs/ToolCallStatus" - }, - { - "type": "null" - } - ], - "description": "Update the execution status." - }, - "title": { - "description": "Update the human-readable title.", - "type": [ - "string", - "null" - ] - }, - "toolCallId": { - "$ref": "#/$defs/ToolCallId", - "description": "The ID of the tool call being updated." + "allOf": [ + { + "$ref": "#/$defs/ToolCallUpdate" } - }, - "required": [ - "toolCallId" ], - "type": "object" + "description": "Details about the tool call requiring permission." } }, "required": [ @@ -1944,7 +2197,11 @@ "description": "Extension point for implementations" }, "outcome": { - "$ref": "#/$defs/RequestPermissionOutcome", + "allOf": [ + { + "$ref": "#/$defs/RequestPermissionOutcome" + } + ], "description": "The user's decision on the permission request." } }, @@ -1955,6 +2212,60 @@ "x-method": "session/request_permission", "x-side": "client" }, + "ResourceLink": { + "description": "A resource that the server is capable of reading, included in a prompt or tool call result.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "annotations": { + "anyOf": [ + { + "$ref": "#/$defs/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size": { + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "uri": { + "type": "string" + } + }, + "required": [ + "name", + "uri" + ], + "type": "object" + }, "Role": { "description": "The sender or recipient of messages and data in a conversation.", "enum": [ @@ -1963,10 +2274,98 @@ ], "type": "string" }, + "SelectedPermissionOutcome": { + "description": "The user selected one of the provided options.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "optionId": { + "allOf": [ + { + "$ref": "#/$defs/PermissionOptionId" + } + ], + "description": "The ID of the option the user selected." + } + }, + "required": [ + "optionId" + ], + "type": "object" + }, + "SessionCapabilities": { + "description": "Session capabilities supported by the agent.\n\nAs a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`.\n\nOptionally, they **MAY** support other session methods and notifications by specifying additional capabilities.\n\nNote: `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "list": { + "anyOf": [ + { + "$ref": "#/$defs/SessionListCapabilities" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/list`." + } + }, + "type": "object" + }, "SessionId": { "description": "A unique identifier for a conversation session between a client and agent.\n\nSessions maintain their own context, conversation history, and state,\nallowing multiple independent interactions with the same agent.\n\n# Example\n\n```\nuse agent_client_protocol::SessionId;\nuse std::sync::Arc;\n\nlet session_id = SessionId(Arc::from(\"sess_abc123def456\"));\n```\n\nSee protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session-setup#session-id)", "type": "string" }, + "SessionInfo": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInformation about a session returned by session/list", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "cwd": { + "description": "The working directory for this session. Must be an absolute path.", + "type": "string" + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "Unique identifier for the session" + }, + "title": { + "description": "Human-readable title for the session", + "type": [ + "string", + "null" + ] + }, + "updatedAt": { + "description": "ISO 8601 timestamp of last activity", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "sessionId", + "cwd" + ], + "type": "object" + }, + "SessionListCapabilities": { + "description": "Capabilities for the `session/list` method.\n\nBy supplying `{}` it means that the agent supports listing of sessions.\n\nFurther capabilities can be added in the future for other means of filtering or searching the list.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + } + }, + "type": "object" + }, "SessionMode": { "description": "A mode the agent can operate in.\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", "properties": { @@ -2010,7 +2409,11 @@ "type": "array" }, "currentModeId": { - "$ref": "#/$defs/SessionModeId", + "allOf": [ + { + "$ref": "#/$defs/SessionModeId" + } + ], "description": "The current mode the Agent is in." } }, @@ -2034,7 +2437,11 @@ "type": "array" }, "currentModelId": { - "$ref": "#/$defs/ModelId", + "allOf": [ + { + "$ref": "#/$defs/ModelId" + } + ], "description": "The current model the Agent is in." } }, @@ -2051,11 +2458,19 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session this update pertains to." }, "update": { - "$ref": "#/$defs/SessionUpdate", + "allOf": [ + { + "$ref": "#/$defs/SessionUpdate" + } + ], "description": "The actual update content." } }, @@ -2074,264 +2489,146 @@ }, "oneOf": [ { + "allOf": [ + { + "$ref": "#/$defs/ContentChunk" + } + ], "description": "A chunk of the user's message being streamed.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "$ref": "#/$defs/ContentBlock", - "description": "A single item of content" - }, "sessionUpdate": { "const": "user_message_chunk", "type": "string" } }, "required": [ - "sessionUpdate", - "content" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ContentChunk" + } + ], "description": "A chunk of the agent's response being streamed.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "$ref": "#/$defs/ContentBlock", - "description": "A single item of content" - }, "sessionUpdate": { "const": "agent_message_chunk", "type": "string" } }, "required": [ - "sessionUpdate", - "content" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ContentChunk" + } + ], "description": "A chunk of the agent's internal reasoning being streamed.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "$ref": "#/$defs/ContentBlock", - "description": "A single item of content" - }, "sessionUpdate": { "const": "agent_thought_chunk", "type": "string" } }, "required": [ - "sessionUpdate", - "content" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ToolCall" + } + ], "description": "Notification that a new tool call has been initiated.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "description": "Content produced by the tool call.", - "items": { - "$ref": "#/$defs/ToolCallContent" - }, - "type": "array" - }, - "kind": { - "$ref": "#/$defs/ToolKind", - "description": "The category of tool being invoked.\nHelps clients choose appropriate icons and UI treatment." - }, - "locations": { - "description": "File locations affected by this tool call.\nEnables \"follow-along\" features in clients.", - "items": { - "$ref": "#/$defs/ToolCallLocation" - }, - "type": "array" - }, - "rawInput": { - "description": "Raw input parameters sent to the tool." - }, - "rawOutput": { - "description": "Raw output returned by the tool." - }, "sessionUpdate": { "const": "tool_call", "type": "string" - }, - "status": { - "$ref": "#/$defs/ToolCallStatus", - "description": "Current execution status of the tool call." - }, - "title": { - "description": "Human-readable title describing what the tool is doing.", - "type": "string" - }, - "toolCallId": { - "$ref": "#/$defs/ToolCallId", - "description": "Unique identifier for this tool call within the session." } }, "required": [ - "sessionUpdate", - "toolCallId", - "title" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/ToolCallUpdate" + } + ], "description": "Update on the status or results of a tool call.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "content": { - "description": "Replace the content collection.", - "items": { - "$ref": "#/$defs/ToolCallContent" - }, - "type": [ - "array", - "null" - ] - }, - "kind": { - "anyOf": [ - { - "$ref": "#/$defs/ToolKind" - }, - { - "type": "null" - } - ], - "description": "Update the tool kind." - }, - "locations": { - "description": "Replace the locations collection.", - "items": { - "$ref": "#/$defs/ToolCallLocation" - }, - "type": [ - "array", - "null" - ] - }, - "rawInput": { - "description": "Update the raw input." - }, - "rawOutput": { - "description": "Update the raw output." - }, "sessionUpdate": { "const": "tool_call_update", "type": "string" - }, - "status": { - "anyOf": [ - { - "$ref": "#/$defs/ToolCallStatus" - }, - { - "type": "null" - } - ], - "description": "Update the execution status." - }, - "title": { - "description": "Update the human-readable title.", - "type": [ - "string", - "null" - ] - }, - "toolCallId": { - "$ref": "#/$defs/ToolCallId", - "description": "The ID of the tool call being updated." } }, "required": [ - "sessionUpdate", - "toolCallId" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/Plan" + } + ], "description": "The agent's execution plan for complex tasks.\nSee protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "entries": { - "description": "The list of tasks to be accomplished.\n\nWhen updating a plan, the agent must send a complete list of all entries\nwith their current status. The client replaces the entire plan with each update.", - "items": { - "$ref": "#/$defs/PlanEntry" - }, - "type": "array" - }, "sessionUpdate": { "const": "plan", "type": "string" } }, "required": [ - "sessionUpdate", - "entries" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/AvailableCommandsUpdate" + } + ], "description": "Available commands are ready or have changed", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "availableCommands": { - "description": "Commands the agent can execute", - "items": { - "$ref": "#/$defs/AvailableCommand" - }, - "type": "array" - }, "sessionUpdate": { "const": "available_commands_update", "type": "string" } }, "required": [ - "sessionUpdate", - "availableCommands" + "sessionUpdate" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/CurrentModeUpdate" + } + ], "description": "The current mode of the session has changed\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "currentModeId": { - "$ref": "#/$defs/SessionModeId", - "description": "The ID of the current mode" - }, "sessionUpdate": { "const": "current_mode_update", "type": "string" } }, "required": [ - "sessionUpdate", - "currentModeId" + "sessionUpdate" ], "type": "object" } @@ -2344,11 +2641,19 @@ "description": "Extension point for implementations" }, "modeId": { - "$ref": "#/$defs/SessionModeId", + "allOf": [ + { + "$ref": "#/$defs/SessionModeId" + } + ], "description": "The ID of the mode to set." }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session to set the mode for." } }, @@ -2363,7 +2668,7 @@ "SetSessionModeResponse": { "description": "Response to `session/set_mode` method.", "properties": { - "_meta": true + "_meta": {} }, "type": "object", "x-method": "session/set_mode", @@ -2376,11 +2681,19 @@ "description": "Extension point for implementations" }, "modelId": { - "$ref": "#/$defs/ModelId", + "allOf": [ + { + "$ref": "#/$defs/ModelId" + } + ], "description": "The ID of the model to set." }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The ID of the session to set the model for." } }, @@ -2433,6 +2746,21 @@ } ] }, + "Terminal": { + "description": "Embed a terminal created with `terminal/create` by its id.\n\nThe terminal must be added before calling `terminal/release`.\n\nSee protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminals)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "terminalId" + ], + "type": "object" + }, "TerminalExitStatus": { "description": "Exit status of a terminal command.", "properties": { @@ -2465,7 +2793,11 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." }, "terminalId": { @@ -2515,6 +2847,31 @@ "x-method": "terminal/output", "x-side": "client" }, + "TextContent": { + "description": "Text provided to or from an LLM.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "annotations": { + "anyOf": [ + { + "$ref": "#/$defs/Annotations" + }, + { + "type": "null" + } + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "text" + ], + "type": "object" + }, "TextResourceContents": { "description": "Text-based resource contents.", "properties": { @@ -2540,6 +2897,67 @@ ], "type": "object" }, + "ToolCall": { + "description": "Represents a tool call that the language model has requested.\n\nTool calls are actions that the agent executes on behalf of the language model,\nsuch as reading files, executing code, or fetching data from external sources.\n\nSee protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/tool-calls)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "content": { + "description": "Content produced by the tool call.", + "items": { + "$ref": "#/$defs/ToolCallContent" + }, + "type": "array" + }, + "kind": { + "allOf": [ + { + "$ref": "#/$defs/ToolKind" + } + ], + "description": "The category of tool being invoked.\nHelps clients choose appropriate icons and UI treatment." + }, + "locations": { + "description": "File locations affected by this tool call.\nEnables \"follow-along\" features in clients.", + "items": { + "$ref": "#/$defs/ToolCallLocation" + }, + "type": "array" + }, + "rawInput": { + "description": "Raw input parameters sent to the tool." + }, + "rawOutput": { + "description": "Raw output returned by the tool." + }, + "status": { + "allOf": [ + { + "$ref": "#/$defs/ToolCallStatus" + } + ], + "description": "Current execution status of the tool call." + }, + "title": { + "description": "Human-readable title describing what the tool is doing.", + "type": "string" + }, + "toolCallId": { + "allOf": [ + { + "$ref": "#/$defs/ToolCallId" + } + ], + "description": "Unique identifier for this tool call within the session." + } + }, + "required": [ + "toolCallId", + "title" + ], + "type": "object" + }, "ToolCallContent": { "description": "Content produced by a tool call.\n\nTool calls can produce different types of content including\nstandard content blocks (text, images) or file diffs.\n\nSee protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)", "discriminator": { @@ -2547,70 +2965,56 @@ }, "oneOf": [ { + "allOf": [ + { + "$ref": "#/$defs/Content" + } + ], "description": "Standard content block (text, images, resources).", "properties": { - "content": { - "$ref": "#/$defs/ContentBlock", - "description": "The actual content block." - }, "type": { "const": "content", "type": "string" } }, "required": [ - "type", - "content" + "type" ], "type": "object" }, { + "allOf": [ + { + "$ref": "#/$defs/Diff" + } + ], "description": "File modification shown as a diff.", "properties": { - "_meta": { - "description": "Extension point for implementations" - }, - "newText": { - "description": "The new content after modification.", - "type": "string" - }, - "oldText": { - "description": "The original content (None for new files).", - "type": [ - "string", - "null" - ] - }, - "path": { - "description": "The file path being modified.", - "type": "string" - }, "type": { "const": "diff", "type": "string" } }, "required": [ - "type", - "path", - "newText" + "type" ], "type": "object" }, { - "description": "Embed a terminal created with `terminal/create` by its id.\n\nThe terminal must be added before calling `terminal/release`.\n\nSee protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminal)", + "allOf": [ + { + "$ref": "#/$defs/Terminal" + } + ], + "description": "Embed a terminal created with `terminal/create` by its id.\n\nThe terminal must be added before calling `terminal/release`.\n\nSee protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminals)", "properties": { - "terminalId": { - "type": "string" - }, "type": { "const": "terminal", "type": "string" } }, "required": [ - "type", - "terminalId" + "type" ], "type": "object" } @@ -2670,6 +3074,81 @@ } ] }, + "ToolCallUpdate": { + "description": "An update to an existing tool call.\n\nUsed to report progress and results as tools execute. All fields except\nthe tool call ID are optional - only changed fields need to be included.\n\nSee protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-calls#updating)", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "content": { + "description": "Replace the content collection.", + "items": { + "$ref": "#/$defs/ToolCallContent" + }, + "type": [ + "array", + "null" + ] + }, + "kind": { + "anyOf": [ + { + "$ref": "#/$defs/ToolKind" + }, + { + "type": "null" + } + ], + "description": "Update the tool kind." + }, + "locations": { + "description": "Replace the locations collection.", + "items": { + "$ref": "#/$defs/ToolCallLocation" + }, + "type": [ + "array", + "null" + ] + }, + "rawInput": { + "description": "Update the raw input." + }, + "rawOutput": { + "description": "Update the raw output." + }, + "status": { + "anyOf": [ + { + "$ref": "#/$defs/ToolCallStatus" + }, + { + "type": "null" + } + ], + "description": "Update the execution status." + }, + "title": { + "description": "Update the human-readable title.", + "type": [ + "string", + "null" + ] + }, + "toolCallId": { + "allOf": [ + { + "$ref": "#/$defs/ToolCallId" + } + ], + "description": "The ID of the tool call being updated." + } + }, + "required": [ + "toolCallId" + ], + "type": "object" + }, "ToolKind": { "description": "Categories of tools that can be invoked.\n\nTool kinds help clients choose appropriate icons and optimize how they\ndisplay tool execution progress.\n\nSee protocol docs: [Creating](https://agentclientprotocol.com/protocol/tool-calls#creating)", "oneOf": [ @@ -2725,6 +3204,22 @@ } ] }, + "UnstructuredCommandInput": { + "description": "All text that was typed after the command name is provided as input.", + "properties": { + "_meta": { + "description": "Extension point for implementations" + }, + "hint": { + "description": "A hint to display when the input hasn't been provided yet", + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, "WaitForTerminalExitRequest": { "description": "Request to wait for a terminal command to exit.", "properties": { @@ -2732,7 +3227,11 @@ "description": "Extension point for implementations" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." }, "terminalId": { @@ -2790,7 +3289,11 @@ "type": "string" }, "sessionId": { - "$ref": "#/$defs/SessionId", + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], "description": "The session ID for this request." } }, @@ -2818,12 +3321,10 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ { - "$ref": "#/$defs/AgentOutgoingMessage", - "title": "AgentOutgoingMessage" + "$ref": "#/$defs/AgentOutgoingMessage" }, { - "$ref": "#/$defs/ClientOutgoingMessage", - "title": "ClientOutgoingMessage" + "$ref": "#/$defs/ClientOutgoingMessage" } ] } diff --git a/scripts/gen_all.py b/scripts/gen_all.py index 872a98a..3414e12 100644 --- a/scripts/gen_all.py +++ b/scripts/gen_all.py @@ -109,8 +109,8 @@ def resolve_ref(version: str | None) -> str: def download_schema(repo: str, ref: str) -> None: SCHEMA_DIR.mkdir(parents=True, exist_ok=True) - schema_url = f"https://raw.githubusercontent.com/{repo}/{ref}/schema/schema.json" - meta_url = f"https://raw.githubusercontent.com/{repo}/{ref}/schema/meta.json" + schema_url = f"https://raw.githubusercontent.com/{repo}/{ref}/schema/schema.unstable.json" + meta_url = f"https://raw.githubusercontent.com/{repo}/{ref}/schema/meta.unstable.json" try: schema_data = fetch_json(schema_url) meta_data = fetch_json(meta_url) diff --git a/scripts/gen_schema.py b/scripts/gen_schema.py index d340426..b8f9e9a 100644 --- a/scripts/gen_schema.py +++ b/scripts/gen_schema.py @@ -28,7 +28,7 @@ STDIO_TYPE_LITERAL = 'Literal["2#-datamodel-code-generator-#-object-#-special-#"]' STDIO_TYPE_PATTERN = re.compile( - r"^ type:\s*Literal\[['\"]2#-datamodel-code-generator-#-object-#-special-#['\"]\]" + r"^ type:\s*Literal\[['\"]McpServerStdio['\"]\]" r"(?:\s*=\s*['\"][^'\"]+['\"])?\s*$", re.MULTILINE, ) @@ -40,7 +40,6 @@ "AgentOutgoingMessage2": "AgentResponseMessage", "AgentOutgoingMessage3": "AgentErrorMessage", "AgentOutgoingMessage4": "AgentNotificationMessage", - "AvailableCommandInput1": "CommandInputHint", "ClientOutgoingMessage1": "ClientRequestMessage", "ClientOutgoingMessage2": "ClientResponseMessage", "ClientOutgoingMessage3": "ClientErrorMessage", @@ -52,7 +51,6 @@ "ContentBlock5": "EmbeddedResourceContentBlock", "McpServer1": "HttpMcpServer", "McpServer2": "SseMcpServer", - "McpServer3": "StdioMcpServer", "RequestPermissionOutcome1": "DeniedOutcome", "RequestPermissionOutcome2": "AllowedOutcome", "SessionUpdate1": "UserMessageChunk", @@ -68,6 +66,10 @@ "ToolCallContent3": "TerminalToolCallContent", } +ALIASES_MAP = { + "StdioMcpServer": "McpServerStdio", +} + ENUM_LITERAL_MAP: dict[str, tuple[str, ...]] = { "PermissionOptionKind": ( "allow_once", @@ -87,16 +89,15 @@ ("PlanEntry", "priority", "PlanEntryPriority", False), ("PlanEntry", "status", "PlanEntryStatus", False), ("PromptResponse", "stop_reason", "StopReason", False), - ("ToolCallProgress", "kind", "ToolKind", True), - ("ToolCallProgress", "status", "ToolCallStatus", True), - ("ToolCallStart", "kind", "ToolKind", True), - ("ToolCallStart", "status", "ToolCallStatus", True), ("ToolCall", "kind", "ToolKind", True), ("ToolCall", "status", "ToolCallStatus", True), + ("ToolCallUpdate", "kind", "ToolKind", True), + ("ToolCallUpdate", "status", "ToolCallStatus", True), ) DEFAULT_VALUE_OVERRIDES: tuple[tuple[str, str, str], ...] = ( ("AgentCapabilities", "mcp_capabilities", "McpCapabilities()"), + ("AgentCapabilities", "session_capabilities", "SessionCapabilities()"), ( "AgentCapabilities", "prompt_capabilities", @@ -222,6 +223,7 @@ def _build_header_block() -> str: def _build_alias_block() -> str: alias_lines = [f"{old} = {new}" for old, new in sorted(RENAME_MAP.items())] + alias_lines += [f"{old} = {new}" for old, new in sorted(ALIASES_MAP.items())] return BACKCOMPAT_MARKER + "\n" + "\n".join(alias_lines) + "\n" @@ -421,6 +423,7 @@ def _normalize_stdio_model(content: str) -> str: replacement_line = ' type: Literal["stdio"] = "stdio"' new_content, count = STDIO_TYPE_PATTERN.subn(replacement_line, content) if count == 0: + print("Warning: stdio type placeholder not found; no replacements made.", file=sys.stderr) return content if count > 1: print( diff --git a/src/acp/agent/connection.py b/src/acp/agent/connection.py index 86bd29f..5a96af5 100644 --- a/src/acp/agent/connection.py +++ b/src/acp/agent/connection.py @@ -28,9 +28,9 @@ SessionNotification, TerminalOutputRequest, TerminalOutputResponse, - ToolCall, ToolCallProgress, ToolCallStart, + ToolCallUpdate, UserMessageChunk, WaitForTerminalExitRequest, WaitForTerminalExitResponse, @@ -56,12 +56,14 @@ def __init__( input_stream: Any, output_stream: Any, listening: bool = True, + *, + use_unstable_protocol: bool = False, **connection_kwargs: Any, ) -> None: agent = to_agent(cast(Client, self)) if callable(to_agent) else to_agent if not isinstance(input_stream, asyncio.StreamWriter) or not isinstance(output_stream, asyncio.StreamReader): raise TypeError(_AGENT_CONNECTION_ERROR) - handler = build_agent_router(cast(Agent, agent)) + handler = build_agent_router(cast(Agent, agent), use_unstable_protocol=use_unstable_protocol) self._conn = Connection(handler, input_stream, output_stream, listening=listening, **connection_kwargs) if on_connect := getattr(agent, "on_connect", None): on_connect(self) @@ -92,7 +94,7 @@ async def session_update( @param_model(RequestPermissionRequest) async def request_permission( - self, options: list[PermissionOption], session_id: str, tool_call: ToolCall, **kwargs: Any + self, options: list[PermissionOption], session_id: str, tool_call: ToolCallUpdate, **kwargs: Any ) -> RequestPermissionResponse: return await request_model( self._conn, diff --git a/src/acp/agent/router.py b/src/acp/agent/router.py index 9934b40..c80b42c 100644 --- a/src/acp/agent/router.py +++ b/src/acp/agent/router.py @@ -10,6 +10,7 @@ AuthenticateRequest, CancelNotification, InitializeRequest, + ListSessionsRequest, LoadSessionRequest, NewSessionRequest, PromptRequest, @@ -21,8 +22,8 @@ __all__ = ["build_agent_router"] -def build_agent_router(agent: Agent) -> MessageRouter: - router = MessageRouter() +def build_agent_router(agent: Agent, use_unstable_protocol: bool = False) -> MessageRouter: + router = MessageRouter(use_unstable_protocol=use_unstable_protocol) router.route_request(AGENT_METHODS["initialize"], InitializeRequest, agent, "initialize") router.route_request(AGENT_METHODS["session_new"], NewSessionRequest, agent, "new_session") @@ -33,6 +34,7 @@ def build_agent_router(agent: Agent) -> MessageRouter: "load_session", adapt_result=normalize_result, ) + router.route_request(AGENT_METHODS["session_list"], ListSessionsRequest, agent, "list_sessions", unstable=True) router.route_request( AGENT_METHODS["session_set_mode"], SetSessionModeRequest, @@ -47,6 +49,7 @@ def build_agent_router(agent: Agent) -> MessageRouter: agent, "set_session_model", adapt_result=normalize_result, + unstable=True, ) router.route_request( AGENT_METHODS["authenticate"], diff --git a/src/acp/client/connection.py b/src/acp/client/connection.py index 292755a..88cf2ac 100644 --- a/src/acp/client/connection.py +++ b/src/acp/client/connection.py @@ -19,8 +19,11 @@ Implementation, InitializeRequest, InitializeResponse, + ListSessionsRequest, + ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, + McpServerStdio, NewSessionRequest, NewSessionResponse, PromptRequest, @@ -31,7 +34,6 @@ SetSessionModeRequest, SetSessionModeResponse, SseMcpServer, - StdioMcpServer, TextContentBlock, ) from ..utils import compatible_class, notify_model, param_model, request_model, request_model_from_dict @@ -51,12 +53,14 @@ def __init__( to_client: Callable[[Agent], Client] | Client, input_stream: Any, output_stream: Any, + *, + use_unstable_protocol: bool = False, **connection_kwargs: Any, ) -> None: if not isinstance(input_stream, asyncio.StreamWriter) or not isinstance(output_stream, asyncio.StreamReader): raise TypeError(_CLIENT_CONNECTION_ERROR) client = to_client(cast(Agent, self)) if callable(to_client) else to_client - handler = build_client_router(cast(Client, client)) + handler = build_client_router(cast(Client, client), use_unstable_protocol=use_unstable_protocol) self._conn = Connection(handler, input_stream, output_stream, **connection_kwargs) if on_connect := getattr(client, "on_connect", None): on_connect(self) @@ -83,7 +87,7 @@ async def initialize( @param_model(NewSessionRequest) async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: return await request_model( self._conn, @@ -94,7 +98,7 @@ async def new_session( @param_model(LoadSessionRequest) async def load_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], session_id: str, **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], session_id: str, **kwargs: Any ) -> LoadSessionResponse: return await request_model_from_dict( self._conn, @@ -103,6 +107,17 @@ async def load_session( LoadSessionResponse, ) + @param_model(ListSessionsRequest) + async def list_sessions( + self, cursor: str | None = None, cwd: str | None = None, **kwargs: Any + ) -> ListSessionsResponse: + return await request_model_from_dict( + self._conn, + AGENT_METHODS["session_list"], + ListSessionsRequest(cursor=cursor, cwd=cwd, field_meta=kwargs or None), + ListSessionsResponse, + ) + @param_model(SetSessionModeRequest) async def set_session_mode(self, mode_id: str, session_id: str, **kwargs: Any) -> SetSessionModeResponse: return await request_model_from_dict( diff --git a/src/acp/client/router.py b/src/acp/client/router.py index 488d42c..4bab2d9 100644 --- a/src/acp/client/router.py +++ b/src/acp/client/router.py @@ -22,8 +22,8 @@ __all__ = ["build_client_router"] -def build_client_router(client: Client) -> MessageRouter: - router = MessageRouter() +def build_client_router(client: Client, use_unstable_protocol: bool = False) -> MessageRouter: + router = MessageRouter(use_unstable_protocol=use_unstable_protocol) router.route_request(CLIENT_METHODS["fs_write_text_file"], WriteTextFileRequest, client, "write_text_file") router.route_request(CLIENT_METHODS["fs_read_text_file"], ReadTextFileRequest, client, "read_text_file") diff --git a/src/acp/contrib/permissions.py b/src/acp/contrib/permissions.py index 462be9d..7dfc8e8 100644 --- a/src/acp/contrib/permissions.py +++ b/src/acp/contrib/permissions.py @@ -4,7 +4,7 @@ from typing import Any from ..helpers import text_block, tool_content -from ..schema import PermissionOption, RequestPermissionRequest, RequestPermissionResponse, ToolCall +from ..schema import PermissionOption, RequestPermissionRequest, RequestPermissionResponse, ToolCallUpdate from .tool_calls import ToolCallTracker, _copy_model_list @@ -60,7 +60,7 @@ async def request_for( description: str | None = None, options: Sequence[PermissionOption] | None = None, content: Sequence[Any] | None = None, - tool_call: ToolCall | None = None, + tool_call: ToolCallUpdate | None = None, ) -> RequestPermissionResponse: """Request user approval for a tool call.""" if tool_call is None: diff --git a/src/acp/contrib/tool_calls.py b/src/acp/contrib/tool_calls.py index 1449d6d..107d134 100644 --- a/src/acp/contrib/tool_calls.py +++ b/src/acp/contrib/tool_calls.py @@ -7,7 +7,14 @@ from pydantic import BaseModel, ConfigDict from ..helpers import text_block, tool_content -from ..schema import ToolCall, ToolCallLocation, ToolCallProgress, ToolCallStart, ToolCallStatus, ToolKind +from ..schema import ( + ToolCallLocation, + ToolCallProgress, + ToolCallStart, + ToolCallStatus, + ToolCallUpdate, + ToolKind, +) class _MissingToolCallTitleError(ValueError): @@ -91,8 +98,8 @@ def to_view(self) -> TrackedToolCallView: raw_output=self.raw_output, ) - def to_tool_call_model(self) -> ToolCall: - return ToolCall( + def to_tool_call_model(self) -> ToolCallUpdate: + return ToolCallUpdate( tool_call_id=self.tool_call_id, title=self.title, kind=self.kind, @@ -249,7 +256,7 @@ def view(self, external_id: str) -> TrackedToolCallView: state = self._require_call(external_id) return state.to_view() - def tool_call_model(self, external_id: str) -> ToolCall: + def tool_call_model(self, external_id: str) -> ToolCallUpdate: """Return a deep copy of the tool call suitable for permission requests.""" state = self._require_call(external_id) return state.to_tool_call_model() diff --git a/src/acp/core.py b/src/acp/core.py index a3d38b6..1d440de 100644 --- a/src/acp/core.py +++ b/src/acp/core.py @@ -32,7 +32,12 @@ async def run_agent( - agent: Agent, input_stream: Any = None, output_stream: Any = None, **connection_kwargs: Any + agent: Agent, + input_stream: Any = None, + output_stream: Any = None, + *, + use_unstable_protocol: bool = False, + **connection_kwargs: Any, ) -> None: """Run an ACP agent over the given input/output streams. @@ -43,6 +48,7 @@ async def run_agent( agent: The agent implementation to run. input_stream: The (client) input stream to write to (defaults: ``sys.stdin``). output_stream: The (client) output stream to read from (defaults: ``sys.stdout``). + use_unstable_protocol: Whether to enable unstable protocol features. **connection_kwargs: Additional keyword arguments to pass to the :class:`AgentSideConnection` constructor. """ @@ -50,12 +56,24 @@ async def run_agent( if input_stream is None and output_stream is None: output_stream, input_stream = await stdio_streams() - conn = AgentSideConnection(agent, input_stream, output_stream, listening=False, **connection_kwargs) + conn = AgentSideConnection( + agent, + input_stream, + output_stream, + listening=False, + use_unstable_protocol=use_unstable_protocol, + **connection_kwargs, + ) await conn.listen() def connect_to_agent( - client: Client, input_stream: Any, output_stream: Any, **connection_kwargs: Any + client: Client, + input_stream: Any, + output_stream: Any, + *, + use_unstable_protocol: bool = False, + **connection_kwargs: Any, ) -> ClientSideConnection: """Create a ClientSideConnection to an ACP agent over the given input/output streams. @@ -63,10 +81,13 @@ def connect_to_agent( client: The client implementation to use. input_stream: The (agent) input stream to write to (default: ``sys.stdin``). output_stream: The (agent) output stream to read from (default: ``sys.stdout``). + use_unstable_protocol: Whether to enable unstable protocol features. **connection_kwargs: Additional keyword arguments to pass to the :class:`ClientSideConnection` constructor. Returns: A :class:`ClientSideConnection` instance connected to the agent. """ - return ClientSideConnection(client, input_stream, output_stream, **connection_kwargs) + return ClientSideConnection( + client, input_stream, output_stream, use_unstable_protocol=use_unstable_protocol, **connection_kwargs + ) diff --git a/src/acp/interfaces.py b/src/acp/interfaces.py index 26021cb..f0049e0 100644 --- a/src/acp/interfaces.py +++ b/src/acp/interfaces.py @@ -24,8 +24,11 @@ InitializeResponse, KillTerminalCommandRequest, KillTerminalCommandResponse, + ListSessionsRequest, + ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, + McpServerStdio, NewSessionRequest, NewSessionResponse, PermissionOption, @@ -44,13 +47,12 @@ SetSessionModeRequest, SetSessionModeResponse, SseMcpServer, - StdioMcpServer, TerminalOutputRequest, TerminalOutputResponse, TextContentBlock, - ToolCall, ToolCallProgress, ToolCallStart, + ToolCallUpdate, UserMessageChunk, WaitForTerminalExitRequest, WaitForTerminalExitResponse, @@ -65,7 +67,7 @@ class Client(Protocol): @param_model(RequestPermissionRequest) async def request_permission( - self, options: list[PermissionOption], session_id: str, tool_call: ToolCall, **kwargs: Any + self, options: list[PermissionOption], session_id: str, tool_call: ToolCallUpdate, **kwargs: Any ) -> RequestPermissionResponse: ... @param_model(SessionNotification) @@ -142,14 +144,19 @@ async def initialize( @param_model(NewSessionRequest) async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: ... @param_model(LoadSessionRequest) async def load_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], session_id: str, **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], session_id: str, **kwargs: Any ) -> LoadSessionResponse | None: ... + @param_model(ListSessionsRequest) + async def list_sessions( + self, cursor: str | None = None, cwd: str | None = None, **kwargs: Any + ) -> ListSessionsResponse: ... + @param_model(SetSessionModeRequest) async def set_session_mode(self, mode_id: str, session_id: str, **kwargs: Any) -> SetSessionModeResponse | None: ... diff --git a/src/acp/meta.py b/src/acp/meta.py index 82deb0e..900e195 100644 --- a/src/acp/meta.py +++ b/src/acp/meta.py @@ -1,9 +1,10 @@ # Generated from schema/meta.json. Do not edit by hand. -# Schema ref: refs/tags/v0.6.3 +# Schema ref: refs/tags/v0.7.0 AGENT_METHODS = { "authenticate": "authenticate", "initialize": "initialize", "session_cancel": "session/cancel", + "session_list": "session/list", "session_load": "session/load", "session_new": "session/new", "session_prompt": "session/prompt", diff --git a/src/acp/router.py b/src/acp/router.py index e3e0929..2aa3c24 100644 --- a/src/acp/router.py +++ b/src/acp/router.py @@ -28,12 +28,20 @@ class Route: optional: bool = False default_result: Any = None adapt_result: Callable[[Any | None], Any] | None = None + warn_unstable: bool = False async def handle(self, params: Any) -> Any: if self.func is None: if self.optional: return self.default_result raise RequestError.method_not_found(self.method) + if self.warn_unstable: + warnings.warn( + f"The method {self.method} is part of the unstable protocol, please enable `use_unstable_protocol` flag to use it.", + UserWarning, + stacklevel=3, + ) + raise RequestError.method_not_found(self.method) result = await self.func(params) if self.adapt_result is not None and self.kind == "request": return self.adapt_result(result) @@ -41,11 +49,12 @@ async def handle(self, params: Any) -> Any: class MessageRouter: - def __init__(self) -> None: + def __init__(self, use_unstable_protocol: bool = False) -> None: self._requests: dict[str, Route] = {} self._notifications: dict[str, Route] = {} self._request_extensions: RequestHandler | None = None self._notification_extensions: RequestHandler | None = None + self._use_unstable_protocol = use_unstable_protocol def add_route(self, route: Route) -> None: if route.kind == "request": @@ -98,6 +107,7 @@ def route_request( optional: bool = False, default_result: Any = None, adapt_result: Callable[[Any | None], Any] | None = None, + unstable: bool = False, ) -> Route: """Register a request route with obj and attribute name.""" route = Route( @@ -107,6 +117,7 @@ def route_request( optional=optional, default_result=default_result, adapt_result=adapt_result, + warn_unstable=unstable and not self._use_unstable_protocol, ) self.add_route(route) return route @@ -118,6 +129,7 @@ def route_notification( obj: Any, attr: str, optional: bool = False, + unstable: bool = False, ) -> Route: """Register a notification route with obj and attribute name.""" route = Route( @@ -125,6 +137,7 @@ def route_notification( func=self._make_func(model, obj, attr), kind="notification", optional=optional, + warn_unstable=unstable and not self._use_unstable_protocol, ) self.add_route(route) return route diff --git a/src/acp/schema.py b/src/acp/schema.py index 3d8ae81..2576f48 100644 --- a/src/acp/schema.py +++ b/src/acp/schema.py @@ -1,5 +1,5 @@ # Generated from schema/schema.json. Do not edit by hand. -# Schema ref: refs/tags/v0.6.3 +# Schema ref: refs/tags/v0.7.0 from __future__ import annotations @@ -30,6 +30,23 @@ class Jsonrpc(Enum): field_2_0 = "2.0" +class AuthMethod(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # Optional description providing more details about this authentication method. + description: Annotated[ + Optional[str], + Field(description="Optional description providing more details about this authentication method."), + ] = None + # Unique identifier for this authentication method. + id: Annotated[str, Field(description="Unique identifier for this authentication method.")] + # Human-readable name of the authentication method. + name: Annotated[str, Field(description="Human-readable name of the authentication method.")] + + class AuthenticateRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -55,22 +72,6 @@ class AuthenticateResponse(BaseModel): ] = None -class CommandInputHint(BaseModel): - # A hint to display when the input hasn't been provided yet - hint: Annotated[ - str, - Field(description="A hint to display when the input hasn't been provided yet"), - ] - - -class AvailableCommandInput(RootModel[CommandInputHint]): - # The input specification for a command. - root: Annotated[ - CommandInputHint, - Field(description="The input specification for a command."), - ] - - class BlobResourceContents(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -98,6 +99,23 @@ class CreateTerminalResponse(BaseModel): ] +class Diff(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # The new content after modification. + new_text: Annotated[str, Field(alias="newText", description="The new content after modification.")] + # The original content (None for new files). + old_text: Annotated[ + Optional[str], + Field(alias="oldText", description="The original content (None for new files)."), + ] = None + # The file path being modified. + path: Annotated[str, Field(description="The file path being modified.")] + + class EnvVariable(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -174,6 +192,11 @@ class HttpHeader(BaseModel): class Implementation(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None # Intended for programmatic or logical use, but can be used as a display # name fallback if title isn’t present. name: Annotated[ @@ -193,11 +216,11 @@ class Implementation(BaseModel): ), ] = None # Version of the implementation. Can be displayed to the user or used - # for debugging or metrics purposes. + # for debugging or metrics purposes. (e.g. "1.0.0"). version: Annotated[ str, Field( - description="Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes." + description='Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes. (e.g. "1.0.0").' ), ] @@ -210,6 +233,26 @@ class KillTerminalCommandResponse(BaseModel): ] = None +class ListSessionsRequest(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # Opaque cursor token from a previous response's nextCursor field for cursor-based pagination + cursor: Annotated[ + Optional[str], + Field( + description="Opaque cursor token from a previous response's nextCursor field for cursor-based pagination" + ), + ] = None + # Filter sessions by working directory. Must be an absolute path. + cwd: Annotated[ + Optional[str], + Field(description="Filter sessions by working directory. Must be an absolute path."), + ] = None + + class McpCapabilities(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -222,7 +265,12 @@ class McpCapabilities(BaseModel): sse: Annotated[Optional[bool], Field(description="Agent supports [`McpServer::Sse`].")] = False -class HttpMcpServer(BaseModel): +class McpServerHttp(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None # HTTP headers to set when making requests to the MCP server. headers: Annotated[ List[HttpHeader], @@ -230,12 +278,16 @@ class HttpMcpServer(BaseModel): ] # Human-readable name identifying this MCP server. name: Annotated[str, Field(description="Human-readable name identifying this MCP server.")] - type: Literal["http"] # URL to the MCP server. url: Annotated[str, Field(description="URL to the MCP server.")] -class SseMcpServer(BaseModel): +class McpServerSse(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None # HTTP headers to set when making requests to the MCP server. headers: Annotated[ List[HttpHeader], @@ -243,12 +295,16 @@ class SseMcpServer(BaseModel): ] # Human-readable name identifying this MCP server. name: Annotated[str, Field(description="Human-readable name identifying this MCP server.")] - type: Literal["sse"] # URL to the MCP server. url: Annotated[str, Field(description="URL to the MCP server.")] -class StdioMcpServer(BaseModel): +class McpServerStdio(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None # Command-line arguments to pass to the MCP server. args: Annotated[ List[str], @@ -280,27 +336,6 @@ class ModelInfo(BaseModel): name: Annotated[str, Field(description="Human-readable name of the model.")] -class NewSessionRequest(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # The working directory for this session. Must be an absolute path. - cwd: Annotated[ - str, - Field(description="The working directory for this session. Must be an absolute path."), - ] - # List of MCP (Model Context Protocol) servers the agent should connect to. - mcp_servers: Annotated[ - List[Union[HttpMcpServer, SseMcpServer, StdioMcpServer]], - Field( - alias="mcpServers", - description="List of MCP (Model Context Protocol) servers the agent should connect to.", - ), - ] - - class PromptCapabilities(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -345,34 +380,52 @@ class DeniedOutcome(BaseModel): outcome: Literal["cancelled"] -class AllowedOutcome(BaseModel): +class Role(Enum): + assistant = "assistant" + user = "user" + + +class SelectedPermissionOutcome(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None # The ID of the option the user selected. option_id: Annotated[ str, Field(alias="optionId", description="The ID of the option the user selected."), ] - outcome: Literal["selected"] -class RequestPermissionResponse(BaseModel): +class SessionInfo(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # The user's decision on the permission request. - outcome: Annotated[ - Union[DeniedOutcome, AllowedOutcome], - Field( - description="The user's decision on the permission request.", - discriminator="outcome", - ), + # The working directory for this session. Must be an absolute path. + cwd: Annotated[ + str, + Field(description="The working directory for this session. Must be an absolute path."), ] + # Unique identifier for the session + session_id: Annotated[str, Field(alias="sessionId", description="Unique identifier for the session")] + # Human-readable title for the session + title: Annotated[Optional[str], Field(description="Human-readable title for the session")] = None + # ISO 8601 timestamp of last activity + updated_at: Annotated[ + Optional[str], + Field(alias="updatedAt", description="ISO 8601 timestamp of last activity"), + ] = None -class Role(Enum): - assistant = "assistant" - user = "user" +class SessionListCapabilities(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None class SessionModelState(BaseModel): @@ -396,17 +449,6 @@ class SessionModelState(BaseModel): ] -class CurrentModeUpdate(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # The ID of the current mode - current_mode_id: Annotated[str, Field(alias="currentModeId", description="The ID of the current mode")] - session_update: Annotated[Literal["current_mode_update"], Field(alias="sessionUpdate")] - - class SetSessionModeRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -449,6 +491,15 @@ class SetSessionModelResponse(BaseModel): ] = None +class Terminal(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + terminal_id: Annotated[str, Field(alias="terminalId")] + + class TerminalExitStatus(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -514,26 +565,11 @@ class TextResourceContents(BaseModel): uri: str -class FileEditToolCallContent(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # The new content after modification. - new_text: Annotated[str, Field(alias="newText", description="The new content after modification.")] - # The original content (None for new files). - old_text: Annotated[ - Optional[str], - Field(alias="oldText", description="The original content (None for new files)."), - ] = None - # The file path being modified. - path: Annotated[str, Field(description="The file path being modified.")] +class FileEditToolCallContent(Diff): type: Literal["diff"] -class TerminalToolCallContent(BaseModel): - terminal_id: Annotated[str, Field(alias="terminalId")] +class TerminalToolCallContent(Terminal): type: Literal["terminal"] @@ -549,6 +585,19 @@ class ToolCallLocation(BaseModel): path: Annotated[str, Field(description="The file path being accessed or modified.")] +class UnstructuredCommandInput(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # A hint to display when the input hasn't been provided yet + hint: Annotated[ + str, + Field(description="A hint to display when the input hasn't been provided yet"), + ] + + class WaitForTerminalExitRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -608,38 +657,6 @@ class WriteTextFileResponse(BaseModel): ] = None -class AgentCapabilities(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # Whether the agent supports `session/load`. - load_session: Annotated[ - Optional[bool], - Field( - alias="loadSession", - description="Whether the agent supports `session/load`.", - ), - ] = False - # MCP capabilities supported by the agent. - mcp_capabilities: Annotated[ - Optional[McpCapabilities], - Field( - alias="mcpCapabilities", - description="MCP capabilities supported by the agent.", - ), - ] = McpCapabilities() - # Prompt capabilities supported by the agent. - prompt_capabilities: Annotated[ - Optional[PromptCapabilities], - Field( - alias="promptCapabilities", - description="Prompt capabilities supported by the agent.", - ), - ] = PromptCapabilities() - - class AgentErrorMessage(BaseModel): jsonrpc: Jsonrpc # JSON RPC Request Id @@ -671,40 +688,22 @@ class Annotations(BaseModel): priority: Optional[float] = None -class AuthMethod(BaseModel): +class AudioContent(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # Optional description providing more details about this authentication method. - description: Annotated[ - Optional[str], - Field(description="Optional description providing more details about this authentication method."), - ] = None - # Unique identifier for this authentication method. - id: Annotated[str, Field(description="Unique identifier for this authentication method.")] - # Human-readable name of the authentication method. - name: Annotated[str, Field(description="Human-readable name of the authentication method.")] + annotations: Optional[Annotations] = None + data: str + mime_type: Annotated[str, Field(alias="mimeType")] -class AvailableCommand(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # Human-readable description of what the command does. - description: Annotated[str, Field(description="Human-readable description of what the command does.")] - # Input for the command if required - input: Annotated[ - Optional[AvailableCommandInput], - Field(description="Input for the command if required"), - ] = None - # Command name (e.g., `create_plan`, `research_codebase`). - name: Annotated[ - str, - Field(description="Command name (e.g., `create_plan`, `research_codebase`)."), +class AvailableCommandInput(RootModel[UnstructuredCommandInput]): + # The input specification for a command. + root: Annotated[ + UnstructuredCommandInput, + Field(description="The input specification for a command."), ] @@ -771,59 +770,11 @@ class ClientNotificationMessage(BaseModel): params: Optional[Union[CancelNotification, Any]] = None -class TextContentBlock(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - annotations: Optional[Annotations] = None - text: str - type: Literal["text"] +class AudioContentBlock(AudioContent): + type: Literal["audio"] -class ImageContentBlock(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - annotations: Optional[Annotations] = None - data: str - mime_type: Annotated[str, Field(alias="mimeType")] - type: Literal["image"] - uri: Optional[str] = None - - -class AudioContentBlock(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - annotations: Optional[Annotations] = None - data: str - mime_type: Annotated[str, Field(alias="mimeType")] - type: Literal["audio"] - - -class ResourceContentBlock(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - annotations: Optional[Annotations] = None - description: Optional[str] = None - mime_type: Annotated[Optional[str], Field(alias="mimeType")] = None - name: str - size: Optional[int] = None - title: Optional[str] = None - type: Literal["resource_link"] - uri: str - - -class CreateTerminalRequest(BaseModel): +class CreateTerminalRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], @@ -863,6 +814,28 @@ class CreateTerminalRequest(BaseModel): session_id: Annotated[str, Field(alias="sessionId", description="The session ID for this request.")] +class CurrentModeUpdate(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # The ID of the current mode + current_mode_id: Annotated[str, Field(alias="currentModeId", description="The ID of the current mode")] + + +class ImageContent(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + annotations: Optional[Annotations] = None + data: str + mime_type: Annotated[str, Field(alias="mimeType")] + uri: Optional[str] = None + + class InitializeRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -899,83 +872,64 @@ class InitializeRequest(BaseModel): ] -class InitializeResponse(BaseModel): +class KillTerminalCommandRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # Capabilities supported by the agent. - agent_capabilities: Annotated[ - Optional[AgentCapabilities], - Field( - alias="agentCapabilities", - description="Capabilities supported by the agent.", - ), - ] = AgentCapabilities() - # Information about the Agent name and version sent to the Client. - # - # Note: in future versions of the protocol, this will be required. - agent_info: Annotated[ - Optional[Implementation], - Field( - alias="agentInfo", - 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. - auth_methods: Annotated[ - Optional[List[AuthMethod]], - Field( - alias="authMethods", - description="Authentication methods supported by the agent.", - ), - ] = [] - # The protocol version the client specified if supported by the agent, - # or the latest protocol version supported by the agent. - # - # The client should disconnect, if it doesn't support this version. - protocol_version: Annotated[ - int, - Field( - alias="protocolVersion", - description="The protocol version the client specified if supported by the agent,\nor the latest protocol version supported by the agent.\n\nThe client should disconnect, if it doesn't support this version.", - ge=0, - le=65535, - ), - ] + # The session ID for this request. + session_id: Annotated[str, Field(alias="sessionId", description="The session ID for this request.")] + # The ID of the terminal to kill. + terminal_id: Annotated[str, Field(alias="terminalId", description="The ID of the terminal to kill.")] -class KillTerminalCommandRequest(BaseModel): +class ListSessionsResponse(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # The session ID for this request. - session_id: Annotated[str, Field(alias="sessionId", description="The session ID for this request.")] - # The ID of the terminal to kill. - terminal_id: Annotated[str, Field(alias="terminalId", description="The ID of the terminal to kill.")] + # Opaque cursor token. If present, pass this in the next request's cursor parameter + # to fetch the next page. If absent, there are no more results. + next_cursor: Annotated[ + Optional[str], + Field( + alias="nextCursor", + description="Opaque cursor token. If present, pass this in the next request's cursor parameter\nto fetch the next page. If absent, there are no more results.", + ), + ] = None + # Array of session information objects + sessions: Annotated[List[SessionInfo], Field(description="Array of session information objects")] -class LoadSessionRequest(BaseModel): +class HttpMcpServer(McpServerHttp): + type: Literal["http"] + + +class SseMcpServer(McpServerSse): + type: Literal["sse"] + + +class NewSessionRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # The working directory for this session. - cwd: Annotated[str, Field(description="The working directory for this session.")] - # List of MCP servers to connect to for this session. + # The working directory for this session. Must be an absolute path. + cwd: Annotated[ + str, + Field(description="The working directory for this session. Must be an absolute path."), + ] + # List of MCP (Model Context Protocol) servers the agent should connect to. mcp_servers: Annotated[ - List[Union[HttpMcpServer, SseMcpServer, StdioMcpServer]], + List[Union[HttpMcpServer, SseMcpServer, McpServerStdio]], Field( alias="mcpServers", - description="List of MCP servers to connect to for this session.", + description="List of MCP (Model Context Protocol) servers the agent should connect to.", ), ] - # The ID of the session to load. - session_id: Annotated[str, Field(alias="sessionId", description="The ID of the session to load.")] class PermissionOption(BaseModel): @@ -1068,6 +1022,60 @@ class ReleaseTerminalRequest(BaseModel): terminal_id: Annotated[str, Field(alias="terminalId", description="The ID of the terminal to release.")] +class AllowedOutcome(SelectedPermissionOutcome): + outcome: Literal["selected"] + + +class RequestPermissionResponse(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # The user's decision on the permission request. + outcome: Annotated[ + Union[DeniedOutcome, AllowedOutcome], + Field( + description="The user's decision on the permission request.", + discriminator="outcome", + ), + ] + + +class ResourceLink(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + annotations: Optional[Annotations] = None + description: Optional[str] = None + mime_type: Annotated[Optional[str], Field(alias="mimeType")] = None + name: str + size: Optional[int] = None + title: Optional[str] = None + uri: str + + +class SessionCapabilities(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # **UNSTABLE** + # + # This capability is not part of the spec yet, and may be removed or changed at any point. + # + # Whether the agent supports `session/list`. + list: Annotated[ + Optional[SessionListCapabilities], + Field( + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/list`." + ), + ] = None + + class SessionMode(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -1101,23 +1109,73 @@ class SessionModeState(BaseModel): ] -class AgentPlanUpdate(BaseModel): +class CurrentModeUpdate(CurrentModeUpdate): + session_update: Annotated[Literal["current_mode_update"], Field(alias="sessionUpdate")] + + +class TextContent(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # The list of tasks to be accomplished. - # - # When updating a plan, the agent must send a complete list of all entries - # with their current status. The client replaces the entire plan with each update. - entries: Annotated[ - List[PlanEntry], + annotations: Optional[Annotations] = None + text: str + + +class AgentCapabilities(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # Whether the agent supports `session/load`. + load_session: Annotated[ + Optional[bool], Field( - description="The list of tasks to be accomplished.\n\nWhen updating a plan, the agent must send a complete list of all entries\nwith their current status. The client replaces the entire plan with each update." + alias="loadSession", + description="Whether the agent supports `session/load`.", + ), + ] = False + # MCP capabilities supported by the agent. + mcp_capabilities: Annotated[ + Optional[McpCapabilities], + Field( + alias="mcpCapabilities", + description="MCP capabilities supported by the agent.", + ), + ] = McpCapabilities() + # Prompt capabilities supported by the agent. + prompt_capabilities: Annotated[ + Optional[PromptCapabilities], + Field( + alias="promptCapabilities", + description="Prompt capabilities supported by the agent.", ), + ] = PromptCapabilities() + session_capabilities: Annotated[Optional[SessionCapabilities], Field(alias="sessionCapabilities")] = ( + SessionCapabilities() + ) + + +class AvailableCommand(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # Human-readable description of what the command does. + description: Annotated[str, Field(description="Human-readable description of what the command does.")] + # Input for the command if required + input: Annotated[ + Optional[AvailableCommandInput], + Field(description="Input for the command if required"), + ] = None + # Command name (e.g., `create_plan`, `research_codebase`). + name: Annotated[ + str, + Field(description="Command name (e.g., `create_plan`, `research_codebase`)."), ] - session_update: Annotated[Literal["plan"], Field(alias="sessionUpdate")] class AvailableCommandsUpdate(BaseModel): @@ -1131,63 +1189,99 @@ class AvailableCommandsUpdate(BaseModel): List[AvailableCommand], Field(alias="availableCommands", description="Commands the agent can execute"), ] - session_update: Annotated[Literal["available_commands_update"], Field(alias="sessionUpdate")] -class ClientResponseMessage(BaseModel): - jsonrpc: Jsonrpc - # JSON RPC Request Id - # - # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] - # - # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. - # - # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. - # - # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. - id: Annotated[ - Optional[Union[int, str]], +class TextContentBlock(TextContent): + type: Literal["text"] + + +class ImageContentBlock(ImageContent): + type: Literal["image"] + + +class ResourceContentBlock(ResourceLink): + type: Literal["resource_link"] + + +class EmbeddedResource(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + annotations: Optional[Annotations] = None + # Resource content that can be embedded in a message. + resource: Annotated[ + Union[TextResourceContents, BlobResourceContents], + Field(description="Resource content that can be embedded in a message."), + ] + + +class InitializeResponse(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # Capabilities supported by the agent. + agent_capabilities: Annotated[ + Optional[AgentCapabilities], Field( - description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." + alias="agentCapabilities", + description="Capabilities supported by the agent.", ), - ] = None - # All possible responses that a client can send to an agent. + ] = AgentCapabilities() + # Information about the Agent name and version sent to the Client. # - # This enum is used internally for routing RPC responses. You typically won't need - # to use this directly - the responses are handled automatically by the connection. + # Note: in future versions of the protocol, this will be required. + agent_info: Annotated[ + Optional[Implementation], + Field( + alias="agentInfo", + 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. + auth_methods: Annotated[ + Optional[List[AuthMethod]], + Field( + alias="authMethods", + description="Authentication methods supported by the agent.", + ), + ] = [] + # The protocol version the client specified if supported by the agent, + # or the latest protocol version supported by the agent. # - # These are responses to the corresponding `AgentRequest` variants. - result: Annotated[ - Union[ - WriteTextFileResponse, - ReadTextFileResponse, - RequestPermissionResponse, - CreateTerminalResponse, - TerminalOutputResponse, - ReleaseTerminalResponse, - WaitForTerminalExitResponse, - KillTerminalCommandResponse, - Any, - ], + # The client should disconnect, if it doesn't support this version. + protocol_version: Annotated[ + int, Field( - description="All possible responses that a client can send to an agent.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `AgentRequest` variants." + alias="protocolVersion", + description="The protocol version the client specified if supported by the agent,\nor the latest protocol version supported by the agent.\n\nThe client should disconnect, if it doesn't support this version.", + ge=0, + le=65535, ), ] -class EmbeddedResourceContentBlock(BaseModel): +class LoadSessionRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - annotations: Optional[Annotations] = None - # Resource content that can be embedded in a message. - resource: Annotated[ - Union[TextResourceContents, BlobResourceContents], - Field(description="Resource content that can be embedded in a message."), + # The working directory for this session. + cwd: Annotated[str, Field(description="The working directory for this session.")] + # List of MCP servers to connect to for this session. + mcp_servers: Annotated[ + List[Union[HttpMcpServer, SseMcpServer, McpServerStdio]], + Field( + alias="mcpServers", + description="List of MCP servers to connect to for this session.", + ), ] - type: Literal["resource"] + # The ID of the session to load. + session_id: Annotated[str, Field(alias="sessionId", description="The ID of the session to load.")] class LoadSessionResponse(BaseModel): @@ -1256,6 +1350,92 @@ class NewSessionResponse(BaseModel): ] +class Plan(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # The list of tasks to be accomplished. + # + # When updating a plan, the agent must send a complete list of all entries + # with their current status. The client replaces the entire plan with each update. + entries: Annotated[ + List[PlanEntry], + Field( + description="The list of tasks to be accomplished.\n\nWhen updating a plan, the agent must send a complete list of all entries\nwith their current status. The client replaces the entire plan with each update." + ), + ] + + +class AgentPlanUpdate(Plan): + session_update: Annotated[Literal["plan"], Field(alias="sessionUpdate")] + + +class AvailableCommandsUpdate(AvailableCommandsUpdate): + session_update: Annotated[Literal["available_commands_update"], Field(alias="sessionUpdate")] + + +class ClientResponseMessage(BaseModel): + jsonrpc: Jsonrpc + # JSON RPC Request Id + # + # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] + # + # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. + # + # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. + # + # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. + id: Annotated[ + Optional[Union[int, str]], + Field( + description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." + ), + ] = None + # All possible responses that a client can send to an agent. + # + # This enum is used internally for routing RPC responses. You typically won't need + # to use this directly - the responses are handled automatically by the connection. + # + # These are responses to the corresponding `AgentRequest` variants. + result: Annotated[ + Union[ + WriteTextFileResponse, + ReadTextFileResponse, + RequestPermissionResponse, + CreateTerminalResponse, + TerminalOutputResponse, + ReleaseTerminalResponse, + WaitForTerminalExitResponse, + KillTerminalCommandResponse, + Any, + ], + Field( + description="All possible responses that a client can send to an agent.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `AgentRequest` variants." + ), + ] + + +class EmbeddedResourceContentBlock(EmbeddedResource): + type: Literal["resource"] + + +class ContentChunk(BaseModel): + # Extension point for implementations + field_meta: Annotated[ + Optional[Any], + Field(alias="_meta", description="Extension point for implementations"), + ] = None + # A single item of content + content: Annotated[ + Union[ + TextContentBlock, ImageContentBlock, AudioContentBlock, ResourceContentBlock, EmbeddedResourceContentBlock + ], + Field(description="A single item of content", discriminator="type"), + ] + + class PromptRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -1299,55 +1479,65 @@ class PromptRequest(BaseModel): ] -class UserMessageChunk(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # A single item of content - content: Annotated[ - Union[ - TextContentBlock, ImageContentBlock, AudioContentBlock, ResourceContentBlock, EmbeddedResourceContentBlock - ], - Field(description="A single item of content", discriminator="type"), - ] +class UserMessageChunk(ContentChunk): session_update: Annotated[Literal["user_message_chunk"], Field(alias="sessionUpdate")] -class AgentMessageChunk(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), +class AgentMessageChunk(ContentChunk): + session_update: Annotated[Literal["agent_message_chunk"], Field(alias="sessionUpdate")] + + +class AgentThoughtChunk(ContentChunk): + session_update: Annotated[Literal["agent_thought_chunk"], Field(alias="sessionUpdate")] + + +class AgentResponseMessage(BaseModel): + jsonrpc: Jsonrpc + # JSON RPC Request Id + # + # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] + # + # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. + # + # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. + # + # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. + id: Annotated[ + Optional[Union[int, str]], + Field( + description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." + ), ] = None - # A single item of content - content: Annotated[ + # All possible responses that an agent can send to a client. + # + # This enum is used internally for routing RPC responses. You typically won't need + # to use this directly - the responses are handled automatically by the connection. + # + # These are responses to the corresponding `ClientRequest` variants. + result: Annotated[ Union[ - TextContentBlock, ImageContentBlock, AudioContentBlock, ResourceContentBlock, EmbeddedResourceContentBlock + InitializeResponse, + AuthenticateResponse, + NewSessionResponse, + LoadSessionResponse, + ListSessionsResponse, + SetSessionModeResponse, + PromptResponse, + SetSessionModelResponse, + Any, ], - Field(description="A single item of content", discriminator="type"), + Field( + description="All possible responses that an agent can send to a client.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `ClientRequest` variants." + ), ] - session_update: Annotated[Literal["agent_message_chunk"], Field(alias="sessionUpdate")] -class AgentThoughtChunk(BaseModel): +class Content(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], Field(alias="_meta", description="Extension point for implementations"), ] = None - # A single item of content - content: Annotated[ - Union[ - TextContentBlock, ImageContentBlock, AudioContentBlock, ResourceContentBlock, EmbeddedResourceContentBlock - ], - Field(description="A single item of content", discriminator="type"), - ] - session_update: Annotated[Literal["agent_thought_chunk"], Field(alias="sessionUpdate")] - - -class ContentToolCallContent(BaseModel): # The actual content block. content: Annotated[ Union[ @@ -1355,10 +1545,13 @@ class ContentToolCallContent(BaseModel): ], Field(description="The actual content block.", discriminator="type"), ] + + +class ContentToolCallContent(Content): type: Literal["content"] -class ToolCall(BaseModel): +class ToolCallUpdate(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], @@ -1391,6 +1584,39 @@ class ToolCall(BaseModel): ] +class ClientRequestMessage(BaseModel): + jsonrpc: Jsonrpc + # JSON RPC Request Id + # + # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] + # + # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. + # + # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. + # + # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. + id: Annotated[ + Optional[Union[int, str]], + Field( + description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." + ), + ] = None + method: str + params: Optional[ + Union[ + InitializeRequest, + AuthenticateRequest, + NewSessionRequest, + LoadSessionRequest, + ListSessionsRequest, + SetSessionModeRequest, + PromptRequest, + SetSessionModelRequest, + Any, + ] + ] = None + + class RequestPermissionRequest(BaseModel): # Extension point for implementations field_meta: Annotated[ @@ -1406,7 +1632,7 @@ class RequestPermissionRequest(BaseModel): session_id: Annotated[str, Field(alias="sessionId", description="The session ID for this request.")] # Details about the tool call requiring permission. tool_call: Annotated[ - ToolCall, + ToolCallUpdate, Field( alias="toolCall", description="Details about the tool call requiring permission.", @@ -1414,7 +1640,11 @@ class RequestPermissionRequest(BaseModel): ] -class ToolCallStart(BaseModel): +class ToolCallProgress(ToolCallUpdate): + session_update: Annotated[Literal["tool_call_update"], Field(alias="sessionUpdate")] + + +class ToolCall(BaseModel): # Extension point for implementations field_meta: Annotated[ Optional[Any], @@ -1449,7 +1679,6 @@ class ToolCallStart(BaseModel): Optional[Any], Field(alias="rawOutput", description="Raw output returned by the tool."), ] = None - session_update: Annotated[Literal["tool_call"], Field(alias="sessionUpdate")] # Current execution status of the tool call. status: Annotated[Optional[ToolCallStatus], Field(description="Current execution status of the tool call.")] = None # Human-readable title describing what the tool is doing. @@ -1467,81 +1696,11 @@ class ToolCallStart(BaseModel): ] -class ToolCallProgress(BaseModel): - # Extension point for implementations - field_meta: Annotated[ - Optional[Any], - Field(alias="_meta", description="Extension point for implementations"), - ] = None - # Replace the content collection. - content: Annotated[ - Optional[List[Union[ContentToolCallContent, FileEditToolCallContent, TerminalToolCallContent]]], - Field(description="Replace the content collection."), - ] = None - # Update the tool kind. - kind: Annotated[Optional[ToolKind], Field(description="Update the tool kind.")] = None - # Replace the locations collection. - locations: Annotated[ - Optional[List[ToolCallLocation]], - Field(description="Replace the locations collection."), - ] = None - # Update the raw input. - raw_input: Annotated[Optional[Any], Field(alias="rawInput", description="Update the raw input.")] = None - # Update the raw output. - raw_output: Annotated[Optional[Any], Field(alias="rawOutput", description="Update the raw output.")] = None - session_update: Annotated[Literal["tool_call_update"], Field(alias="sessionUpdate")] - # Update the execution status. - status: Annotated[Optional[ToolCallStatus], Field(description="Update the execution status.")] = None - # Update the human-readable title. - title: Annotated[Optional[str], Field(description="Update the human-readable title.")] = None - # The ID of the tool call being updated. - tool_call_id: Annotated[ - str, - Field(alias="toolCallId", description="The ID of the tool call being updated."), - ] - - -class AgentResponseMessage(BaseModel): - jsonrpc: Jsonrpc - # JSON RPC Request Id - # - # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] - # - # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. - # - # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. - # - # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. - id: Annotated[ - Optional[Union[int, str]], - Field( - description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." - ), - ] = None - # All possible responses that an agent can send to a client. - # - # This enum is used internally for routing RPC responses. You typically won't need - # to use this directly - the responses are handled automatically by the connection. - # - # These are responses to the corresponding `ClientRequest` variants. - result: Annotated[ - Union[ - InitializeResponse, - AuthenticateResponse, - NewSessionResponse, - LoadSessionResponse, - SetSessionModeResponse, - PromptResponse, - SetSessionModelResponse, - Any, - ], - Field( - description="All possible responses that an agent can send to a client.\n\nThis enum is used internally for routing RPC responses. You typically won't need\nto use this directly - the responses are handled automatically by the connection.\n\nThese are responses to the corresponding `ClientRequest` variants." - ), - ] +class ToolCallStart(ToolCall): + session_update: Annotated[Literal["tool_call"], Field(alias="sessionUpdate")] -class ClientRequestMessage(BaseModel): +class AgentRequestMessage(BaseModel): jsonrpc: Jsonrpc # JSON RPC Request Id # @@ -1561,13 +1720,14 @@ class ClientRequestMessage(BaseModel): method: str params: Optional[ Union[ - InitializeRequest, - AuthenticateRequest, - NewSessionRequest, - LoadSessionRequest, - SetSessionModeRequest, - PromptRequest, - SetSessionModelRequest, + WriteTextFileRequest, + ReadTextFileRequest, + RequestPermissionRequest, + CreateTerminalRequest, + TerminalOutputRequest, + ReleaseTerminalRequest, + WaitForTerminalExitRequest, + KillTerminalCommandRequest, Any, ] ] = None @@ -1603,39 +1763,6 @@ class SessionNotification(BaseModel): ] -class AgentRequestMessage(BaseModel): - jsonrpc: Jsonrpc - # JSON RPC Request Id - # - # An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] - # - # The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. - # - # [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. - # - # [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. - id: Annotated[ - Optional[Union[int, str]], - Field( - description="JSON RPC Request Id\n\nAn identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]\n\nThe Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.\n\n[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.\n\n[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions." - ), - ] = None - method: str - params: Optional[ - Union[ - WriteTextFileRequest, - ReadTextFileRequest, - RequestPermissionRequest, - CreateTerminalRequest, - TerminalOutputRequest, - ReleaseTerminalRequest, - WaitForTerminalExitRequest, - KillTerminalCommandRequest, - Any, - ] - ] = None - - class AgentNotificationMessage(BaseModel): jsonrpc: Jsonrpc method: str @@ -1677,7 +1804,6 @@ class Model( AgentOutgoingMessage2 = AgentResponseMessage AgentOutgoingMessage3 = AgentErrorMessage AgentOutgoingMessage4 = AgentNotificationMessage -AvailableCommandInput1 = CommandInputHint ClientOutgoingMessage1 = ClientRequestMessage ClientOutgoingMessage2 = ClientResponseMessage ClientOutgoingMessage3 = ClientErrorMessage @@ -1689,7 +1815,6 @@ class Model( ContentBlock5 = EmbeddedResourceContentBlock McpServer1 = HttpMcpServer McpServer2 = SseMcpServer -McpServer3 = StdioMcpServer RequestPermissionOutcome1 = DeniedOutcome RequestPermissionOutcome2 = AllowedOutcome SessionUpdate1 = UserMessageChunk @@ -1703,3 +1828,4 @@ class Model( ToolCallContent1 = ContentToolCallContent ToolCallContent2 = FileEditToolCallContent ToolCallContent3 = TerminalToolCallContent +StdioMcpServer = McpServerStdio diff --git a/tests/conftest.py b/tests/conftest.py index 5f5cd8e..72fb232 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,14 +42,15 @@ HttpMcpServer, ImageContentBlock, Implementation, + ListSessionsResponse, + McpServerStdio, PermissionOption, ResourceContentBlock, SseMcpServer, - StdioMcpServer, TextContentBlock, - ToolCall, ToolCallProgress, ToolCallStart, + ToolCallUpdate, UserMessageChunk, ) @@ -140,7 +141,7 @@ def queue_permission_selected(self, option_id: str) -> None: ) async def request_permission( - self, options: list[PermissionOption], session_id: str, tool_call: ToolCall, **kwargs: Any + self, options: list[PermissionOption], session_id: str, tool_call: ToolCallUpdate, **kwargs: Any ) -> RequestPermissionResponse: if self.permission_outcomes: return self.permission_outcomes.pop() @@ -236,15 +237,20 @@ async def initialize( return InitializeResponse(protocol_version=protocol_version, agent_capabilities=None, auth_methods=[]) async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: return NewSessionResponse(session_id="test-session-123") async def load_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], session_id: str, **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], session_id: str, **kwargs: Any ) -> LoadSessionResponse | None: return LoadSessionResponse() + async def list_sessions( + self, cursor: str | None = None, cwd: str | None = None, **kwargs: Any + ) -> ListSessionsResponse: + return ListSessionsResponse(sessions=[], next_cursor=None) + async def authenticate(self, method_id: str, **kwargs: Any) -> AuthenticateResponse | None: return AuthenticateResponse() @@ -295,14 +301,22 @@ def client_fixture() -> TestClient: @pytest.fixture(name="connect") def connect_func(server, agent, client) -> Callable[[bool, bool], tuple[AgentSideConnection, ClientSideConnection]]: def _connect( - connect_agent: bool = True, connect_client: bool = True + connect_agent: bool = True, connect_client: bool = True, use_unstable_protocol: bool = False ) -> tuple[AgentSideConnection, ClientSideConnection]: agent_conn = None client_conn = None if connect_agent: - agent_conn = AgentSideConnection(agent, server.server_writer, server.server_reader, listening=True) + agent_conn = AgentSideConnection( + agent, + server.server_writer, + server.server_reader, + listening=True, + use_unstable_protocol=use_unstable_protocol, + ) if connect_client: - client_conn = ClientSideConnection(client, server.client_writer, server.client_reader) + client_conn = ClientSideConnection( + client, server.client_writer, server.client_reader, use_unstable_protocol=use_unstable_protocol + ) return agent_conn, client_conn # type: ignore[return-value] return _connect diff --git a/tests/real_user/test_permission_flow.py b/tests/real_user/test_permission_flow.py index c2a2494..95b10ce 100644 --- a/tests/real_user/test_permission_flow.py +++ b/tests/real_user/test_permission_flow.py @@ -12,7 +12,7 @@ PermissionOption, ResourceContentBlock, TextContentBlock, - ToolCall, + ToolCallUpdate, ) from tests.conftest import TestAgent, TestClient @@ -45,7 +45,7 @@ async def prompt( PermissionOption(option_id="allow", name="Allow", kind="allow_once"), PermissionOption(option_id="deny", name="Deny", kind="reject_once"), ], - tool_call=ToolCall(tool_call_id="call-1", title="Write File"), + tool_call=ToolCallUpdate(tool_call_id="call-1", title="Write File"), ) self.permission_responses.append(permission) return await super().prompt(prompt, session_id, **kwargs) diff --git a/tests/test_rpc.py b/tests/test_rpc.py index e45a881..25b5457 100644 --- a/tests/test_rpc.py +++ b/tests/test_rpc.py @@ -17,7 +17,6 @@ PromptResponse, RequestPermissionRequest, RequestPermissionResponse, - SetSessionModelResponse, SetSessionModeResponse, WriteTextFileResponse, spawn_agent_process, @@ -25,6 +24,7 @@ update_agent_message_text, update_tool_call, ) +from acp.exceptions import RequestError from acp.schema import ( AgentMessageChunk, AllowedOutcome, @@ -35,15 +35,17 @@ HttpMcpServer, ImageContentBlock, Implementation, + ListSessionsResponse, + McpServerStdio, PermissionOption, ResourceContentBlock, + SetSessionModelResponse, SseMcpServer, - StdioMcpServer, TextContentBlock, - ToolCall, ToolCallLocation, ToolCallProgress, ToolCallStart, + ToolCallUpdate, UserMessageChunk, ) from tests.conftest import TestClient @@ -71,8 +73,9 @@ async def test_initialize_and_new_session(connect): mode_resp = await agent_conn.set_session_mode(session_id=new_sess.session_id, mode_id="ask") assert isinstance(mode_resp, SetSessionModeResponse) - model_resp = await agent_conn.set_session_model(session_id=new_sess.session_id, model_id="gpt-4o") - assert isinstance(model_resp, SetSessionModelResponse) + with pytest.raises(RequestError), pytest.warns(UserWarning) as record: + await agent_conn.set_session_model(session_id=new_sess.session_id, model_id="gpt-4o") + assert len(record) == 1 @pytest.mark.asyncio @@ -188,8 +191,9 @@ async def test_set_session_mode_and_extensions(connect, agent, client): resp = await agent_conn.set_session_mode(session_id="sess", mode_id="yolo") assert isinstance(resp, SetSessionModeResponse) - model_resp = await agent_conn.set_session_model(session_id="sess", model_id="gpt-4o-mini") - assert isinstance(model_resp, SetSessionModelResponse) + with pytest.raises(RequestError), pytest.warns(UserWarning) as record: + await agent_conn.set_session_model(session_id="sess", model_id="gpt-4o-mini") + assert len(record) == 1 # extMethod echo = await agent_conn.ext_method("example.com/echo", {"x": 1}) @@ -247,7 +251,7 @@ async def initialize( return InitializeResponse(protocol_version=protocol_version) async def new_session( - self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | StdioMcpServer], **kwargs: Any + self, cwd: str, mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio], **kwargs: Any ) -> NewSessionResponse: return NewSessionResponse(session_id="sess_demo") @@ -285,7 +289,7 @@ async def prompt( permission_request = { "session_id": session_id, - "tool_call": ToolCall( + "tool_call": ToolCallUpdate( tool_call_id="call_1", title="Modifying configuration", kind="edit", @@ -329,7 +333,7 @@ async def request_permission( self, options: list[PermissionOption] | RequestPermissionRequest, session_id: str | None = None, - tool_call: ToolCall | None = None, + tool_call: ToolCallUpdate | None = None, **kwargs: Any, ) -> RequestPermissionResponse: if isinstance(options, RequestPermissionRequest): @@ -428,3 +432,14 @@ async def test_spawn_agent_process_roundtrip(tmp_path): assert test_client.notifications assert process.returncode is not None + + +@pytest.mark.asyncio +async def test_call_unstable_protocol(connect): + _, agent_conn = connect(use_unstable_protocol=True) + + resp = await agent_conn.list_sessions() + assert isinstance(resp, ListSessionsResponse) + + resp = await agent_conn.set_session_model(session_id="sess", model_id="gpt-4o-mini") + assert isinstance(resp, SetSessionModelResponse)