diff --git a/src/acp-agent.ts b/src/acp-agent.ts index 39231224..f2e9a6ea 100644 --- a/src/acp-agent.ts +++ b/src/acp-agent.ts @@ -34,7 +34,7 @@ import { createMcpServer } from "./mcp-server.js"; import { AddressInfo } from "node:net"; // Implement the ACP Agent interface -class ClaudeAcpAgent implements Agent { +export class ClaudeAcpAgent implements Agent { sessions: { [key: string]: { query: Query; input: Pushable }; }; @@ -70,7 +70,7 @@ class ClaudeAcpAgent implements Agent { } // todo!() auth, permissionPromptToolName - let server = await createMcpServer(); + let server = await createMcpServer(this, sessionId); let address = server.address() as AddressInfo; mcpServers["acp"] = { type: "http", @@ -292,6 +292,68 @@ function toAcpNotifications( return output; } +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "content": [ +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: { +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "type": "tool_use", +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "id": "toolu_01BZ6ZfAoU5Vw1g4YiRa8kv3", +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "name": "Bash", +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "input": { +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "command": "rm README.md.es", +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: "description": "Delete README.md.es file" +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: } +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: } +2025-08-27T11:10:22-06:00 WARN [agent_servers::acp] agent stderr: ], + +pub fn label(&self) -> String { + match &self { + Self::Task(Some(params)) => params.description.clone(), + Self::Task(None) => "Task".into(), + Self::NotebookRead(Some(params)) => { + format!("Read Notebook {}", params.notebook_path.display()) + } + Self::NotebookRead(None) => "Read Notebook".into(), + Self::NotebookEdit(Some(params)) => { + format!("Edit Notebook {}", params.notebook_path.display()) + } + Self::NotebookEdit(None) => "Edit Notebook".into(), + Self::Terminal(Some(params)) => format!("`{}`", params.command), + Self::Terminal(None) => "Terminal".into(), + Self::ReadFile(_) => "Read File".into(), + Self::Ls(Some(params)) => { + format!("List Directory {}", params.path.display()) + } + Self::Ls(None) => "List Directory".into(), + Self::Edit(Some(params)) => { + format!("Edit {}", params.abs_path.display()) + } + Self::Edit(None) => "Edit".into(), + Self::MultiEdit(Some(params)) => { + format!("Multi Edit {}", params.file_path.display()) + } + Self::MultiEdit(None) => "Multi Edit".into(), + Self::Write(Some(params)) => { + format!("Write {}", params.abs_path.display()) + } + Self::Write(None) => "Write".into(), + Self::Glob(Some(params)) => { + format!("Glob `{params}`") + } + Self::Glob(None) => "Glob".into(), + Self::Grep(Some(params)) => format!("`{params}`"), + Self::Grep(None) => "Grep".into(), + Self::WebFetch(Some(params)) => format!("Fetch {}", params.url), + Self::WebFetch(None) => "Fetch".into(), + Self::WebSearch(Some(params)) => format!("Web Search: {}", params), + Self::WebSearch(None) => "Web Search".into(), + Self::TodoWrite(Some(params)) => format!( + "Update TODOs: {}", + params.todos.iter().map(|todo| &todo.content).join(", ") + ), + Self::TodoWrite(None) => "Update TODOs".into(), + Self::ExitPlanMode(_) => "Exit Plan Mode".into(), + Self::Other { name, .. } => name.clone(), + } +} export function runAcp() { new AgentSideConnection( diff --git a/src/mcp-server.ts b/src/mcp-server.ts index e6f55e9b..aed8f572 100644 --- a/src/mcp-server.ts +++ b/src/mcp-server.ts @@ -4,8 +4,12 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { z } from "zod"; import { Server } from "node:http"; +import { ClaudeAcpAgent } from "./acp-agent.js"; -export function createMcpServer(): Promise { +export function createMcpServer( + agent: ClaudeAcpAgent, + sessionId: string, +): Promise { // Create MCP server const server = new McpServer({ name: "acp-mcp-server", @@ -26,17 +30,59 @@ export function createMcpServer(): Promise { }, async (input) => { console.error("PERMISSION TOOl", input); - return { - content: [ - { - type: "text", - text: JSON.stringify({ - behavior: "allow", - updatedInput: input.input, - }), - }, + const session = agent.sessions[sessionId]; + if (!session) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + behavior: "deny", + message: "Session not found", + }), + }, + ], + }; + } + let response = await agent.client.requestPermission({ + options: [ + { kind: "allow_once", name: "Allow", optionId: "allow" }, + { kind: "reject_once", name: "Reject", optionId: "reject" }, ], - }; + sessionId, + toolCall: { + toolCallId: input.tool_use_id!, + rawInput: input.input, + }, + }); + if ( + response.outcome?.outcome == "selected" && + response.outcome.optionId == "allow" + ) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + behavior: "allow", + updatedInput: input.input, + }), + }, + ], + }; + } else { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + behavior: "deny", + message: "User refused permission to run tool", + }), + }, + ], + }; + } }, );