mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 04:30:59 +00:00
Permissions working... ish
This commit is contained in:
999
package-lock.json
generated
999
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/acp-agent.js",
|
"start": "node dist/index.js",
|
||||||
"dev": "npm run build && npm run start",
|
"dev": "npm run build && npm run start",
|
||||||
"lint": "eslint src --ext .ts"
|
"lint": "eslint src --ext .ts"
|
||||||
},
|
},
|
||||||
@@ -23,10 +23,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/claude-code": "^1.0.92",
|
"@anthropic-ai/claude-code": "^1.0.92",
|
||||||
"@zed-industries/agent-client-protocol": "0.0.32",
|
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||||
|
"@types/express": "^5.0.3",
|
||||||
|
"@zed-industries/agent-client-protocol": "0.1.0",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"minimist": "^1.2.8",
|
||||||
"uuid": "11.1.0"
|
"uuid": "11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import {
|
|||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
import { ContentBlock } from "@zed-industries/agent-client-protocol";
|
import { ContentBlock } from "@zed-industries/agent-client-protocol";
|
||||||
import { SessionNotification } from "@zed-industries/agent-client-protocol";
|
import { SessionNotification } from "@zed-industries/agent-client-protocol";
|
||||||
|
import { createMcpServer } from "./mcp-server.js";
|
||||||
|
import { AddressInfo } from "node:net";
|
||||||
|
|
||||||
// Implement the ACP Agent interface
|
// Implement the ACP Agent interface
|
||||||
class ClaudeAcpAgent implements Agent {
|
class ClaudeAcpAgent implements Agent {
|
||||||
@@ -68,10 +70,27 @@ class ClaudeAcpAgent implements Agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// todo!() auth, permissionPromptToolName
|
// todo!() auth, permissionPromptToolName
|
||||||
|
let server = await createMcpServer();
|
||||||
|
let address = server.address() as AddressInfo;
|
||||||
|
mcpServers["acp"] = {
|
||||||
|
type: "http",
|
||||||
|
url: "http://127.0.0.1:" + address.port + "/mcp",
|
||||||
|
headers: {
|
||||||
|
"x-acp-proxy-session-id": sessionId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.error(mcpServers);
|
||||||
|
|
||||||
let q = query({
|
let q = query({
|
||||||
prompt: input,
|
prompt: input,
|
||||||
options: { cwd: params.cwd, mcpServers },
|
options: {
|
||||||
|
cwd: params.cwd,
|
||||||
|
mcpServers,
|
||||||
|
executableArgs: ["--debug"],
|
||||||
|
strictMcpConfig: true,
|
||||||
|
permissionPromptToolName: "mcp__acp__permission",
|
||||||
|
stderr: (err) => console.error(err),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
this.sessions[sessionId] = { query: q, input: input };
|
this.sessions[sessionId] = { query: q, input: input };
|
||||||
|
|
||||||
@@ -90,7 +109,6 @@ class ClaudeAcpAgent implements Agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { query, input } = this.sessions[params.sessionId];
|
const { query, input } = this.sessions[params.sessionId];
|
||||||
query.interrupt();
|
|
||||||
input.push(promptToClaude(params));
|
input.push(promptToClaude(params));
|
||||||
while (true) {
|
while (true) {
|
||||||
let { value: message, done } = await query.next();
|
let { value: message, done } = await query.next();
|
||||||
@@ -269,20 +287,19 @@ function toAcpNotifications(
|
|||||||
default:
|
default:
|
||||||
throw new Error("unhandled chunk type: " + chunk.type);
|
throw new Error("unhandled chunk type: " + chunk.type);
|
||||||
}
|
}
|
||||||
output.push({ sessionId: message.session_id, update });
|
output.push({ sessionId, update });
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
new AgentSideConnection(
|
export function runAcp() {
|
||||||
(client) => new ClaudeAcpAgent(client),
|
new AgentSideConnection(
|
||||||
nodeToWebWritable(process.stdout),
|
(client) => new ClaudeAcpAgent(client),
|
||||||
nodeToWebReadable(process.stdin),
|
nodeToWebWritable(process.stdout),
|
||||||
);
|
nodeToWebReadable(process.stdin),
|
||||||
|
);
|
||||||
// Keep process alive
|
}
|
||||||
process.stdin.resume();
|
|
||||||
|
|
||||||
type ContentChunk =
|
type ContentChunk =
|
||||||
| { type: "text"; text: string }
|
| { type: "text"; text: string }
|
||||||
|
|||||||
5
src/index.ts
Normal file
5
src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { runAcp as runAcp } from "./acp-agent.js";
|
||||||
|
runAcp();
|
||||||
|
|
||||||
|
// Keep process alive
|
||||||
|
process.stdin.resume();
|
||||||
89
src/mcp-server.ts
Normal file
89
src/mcp-server.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
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";
|
||||||
|
|
||||||
|
export function createMcpServer(): Promise<Server> {
|
||||||
|
// Create MCP server
|
||||||
|
const server = new McpServer({
|
||||||
|
name: "acp-mcp-server",
|
||||||
|
version: "1.0.0",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register a simple echo tool (for demonstration)
|
||||||
|
server.registerTool(
|
||||||
|
"permission",
|
||||||
|
{
|
||||||
|
title: "Permission Tool",
|
||||||
|
description: "Used to request tool permissions",
|
||||||
|
inputSchema: {
|
||||||
|
tool_name: z.string(),
|
||||||
|
input: z.any(),
|
||||||
|
tool_use_id: z.string().optional(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (input) => {
|
||||||
|
console.error("PERMISSION TOOl", input);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify({
|
||||||
|
behavior: "allow",
|
||||||
|
updatedInput: input.input,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.post("/mcp", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const transport: StreamableHTTPServerTransport =
|
||||||
|
new StreamableHTTPServerTransport({
|
||||||
|
sessionIdGenerator: undefined,
|
||||||
|
});
|
||||||
|
res.on("close", () => {
|
||||||
|
transport.close();
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
await server.connect(transport);
|
||||||
|
await transport.handleRequest(req, res, req.body);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling MCP request:", error);
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.status(500).json({
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
error: {
|
||||||
|
code: -32603,
|
||||||
|
message: "Internal server error",
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const listener = app.listen(0, "127.0.0.1", (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const address = listener.address();
|
||||||
|
console.error(address);
|
||||||
|
if (typeof address === "object" && address && "port" in address) {
|
||||||
|
console.error(
|
||||||
|
`MCP HTTP server listening on http://127.0.0.1:${address.port}/mcp`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
resolve(listener);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user