Working permissions

This commit is contained in:
Conrad Irwin
2025-08-27 11:16:37 -06:00
parent 96d0b838d4
commit 42b4daaf62
2 changed files with 121 additions and 13 deletions

View File

@@ -34,7 +34,7 @@ import { createMcpServer } from "./mcp-server.js";
import { AddressInfo } from "node:net"; import { AddressInfo } from "node:net";
// Implement the ACP Agent interface // Implement the ACP Agent interface
class ClaudeAcpAgent implements Agent { export class ClaudeAcpAgent implements Agent {
sessions: { sessions: {
[key: string]: { query: Query; input: Pushable<SDKUserMessage> }; [key: string]: { query: Query; input: Pushable<SDKUserMessage> };
}; };
@@ -70,7 +70,7 @@ class ClaudeAcpAgent implements Agent {
} }
// todo!() auth, permissionPromptToolName // todo!() auth, permissionPromptToolName
let server = await createMcpServer(); let server = await createMcpServer(this, sessionId);
let address = server.address() as AddressInfo; let address = server.address() as AddressInfo;
mcpServers["acp"] = { mcpServers["acp"] = {
type: "http", type: "http",
@@ -292,6 +292,68 @@ function toAcpNotifications(
return output; 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() { export function runAcp() {
new AgentSideConnection( new AgentSideConnection(

View File

@@ -4,8 +4,12 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod"; import { z } from "zod";
import { Server } from "node:http"; import { Server } from "node:http";
import { ClaudeAcpAgent } from "./acp-agent.js";
export function createMcpServer(): Promise<Server> { export function createMcpServer(
agent: ClaudeAcpAgent,
sessionId: string,
): Promise<Server> {
// Create MCP server // Create MCP server
const server = new McpServer({ const server = new McpServer({
name: "acp-mcp-server", name: "acp-mcp-server",
@@ -26,17 +30,59 @@ export function createMcpServer(): Promise<Server> {
}, },
async (input) => { async (input) => {
console.error("PERMISSION TOOl", input); console.error("PERMISSION TOOl", input);
return { const session = agent.sessions[sessionId];
content: [ if (!session) {
{ return {
type: "text", content: [
text: JSON.stringify({ {
behavior: "allow", type: "text",
updatedInput: input.input, 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",
}),
},
],
};
}
}, },
); );