@agentrq/acp-gateway 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,234 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { MCPBridge } from "../mcpClient.js";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
5
+ const mockClient = {
6
+ connect: vi.fn().mockResolvedValue(undefined),
7
+ close: vi.fn().mockResolvedValue(undefined),
8
+ callTool: vi.fn().mockResolvedValue({ result: "ok" }),
9
+ notification: vi.fn().mockResolvedValue(undefined),
10
+ setNotificationHandler: vi.fn(),
11
+ };
12
+ const mockTransport = {
13
+ close: vi.fn().mockResolvedValue(undefined),
14
+ onclose: null,
15
+ onerror: null,
16
+ _sessionId: "mock-session-id",
17
+ };
18
+ vi.mock("@modelcontextprotocol/sdk/client/index.js", () => {
19
+ return {
20
+ Client: vi.fn().mockImplementation(function () {
21
+ return mockClient;
22
+ }),
23
+ };
24
+ });
25
+ vi.mock("@modelcontextprotocol/sdk/client/streamableHttp.js", () => {
26
+ return {
27
+ StreamableHTTPClientTransport: vi.fn().mockImplementation(function () {
28
+ return mockTransport;
29
+ }),
30
+ };
31
+ });
32
+ describe("MCPBridge", () => {
33
+ const config = {
34
+ name: "agentrq",
35
+ type: "http",
36
+ url: "http://localhost:8080",
37
+ };
38
+ beforeEach(() => {
39
+ vi.clearAllMocks();
40
+ vi.useFakeTimers();
41
+ });
42
+ afterEach(() => {
43
+ vi.useRealTimers();
44
+ });
45
+ it("should initialize with correct config", () => {
46
+ const bridge = new MCPBridge(config);
47
+ expect(bridge).toBeDefined();
48
+ expect(StreamableHTTPClientTransport).toHaveBeenCalled();
49
+ expect(Client).toHaveBeenCalled();
50
+ });
51
+ it("should get session ID from transport", () => {
52
+ const bridge = new MCPBridge(config);
53
+ expect(bridge.getSessionId()).toBe("mock-session-id");
54
+ });
55
+ it("should throw error if config has no URL", () => {
56
+ expect(() => new MCPBridge({ name: "fail", type: "http" })).toThrow("has no URL");
57
+ });
58
+ describe("connect", () => {
59
+ it("should connect successfully and set up handlers", async () => {
60
+ const bridge = new MCPBridge(config);
61
+ const mClient = bridge.client;
62
+ await bridge.connect();
63
+ expect(mClient.connect).toHaveBeenCalled();
64
+ expect(mClient.setNotificationHandler).toHaveBeenCalledTimes(2);
65
+ expect(bridge.isConnected).toBe(true);
66
+ });
67
+ it("should retry on connection failure", async () => {
68
+ const bridge = new MCPBridge(config);
69
+ const mClient = bridge.client;
70
+ mClient.connect
71
+ .mockRejectedValueOnce(new Error("fail"))
72
+ .mockResolvedValueOnce(undefined);
73
+ const connectPromise = bridge.connect();
74
+ await vi.runAllTimersAsync();
75
+ await connectPromise;
76
+ expect(mClient.connect).toHaveBeenCalledTimes(2);
77
+ expect(bridge.isConnected).toBe(true);
78
+ });
79
+ it("should handle transport close by reconnecting", async () => {
80
+ const bridge = new MCPBridge(config);
81
+ await bridge.connect();
82
+ const transport = bridge.transport;
83
+ const connectSpy = vi.spyOn(bridge, "connect");
84
+ if (transport.onclose) {
85
+ transport.onclose();
86
+ }
87
+ expect(bridge.isConnected).toBe(false);
88
+ expect(connectSpy).toHaveBeenCalled();
89
+ });
90
+ it("should start connection in ensureConnected if not already connecting", async () => {
91
+ const bridge = new MCPBridge(config);
92
+ const connectSpy = vi.spyOn(bridge, "connect");
93
+ // Mock connect to immediately set isConnected = true to avoid long wait
94
+ connectSpy.mockImplementation(async () => {
95
+ bridge.isConnected = true;
96
+ });
97
+ await bridge.ensureConnected();
98
+ expect(connectSpy).toHaveBeenCalled();
99
+ });
100
+ it("should handle transport close by reconnecting even if session not previously connected", async () => {
101
+ const bridge = new MCPBridge(config);
102
+ // Initial connect
103
+ await bridge.connect();
104
+ expect(bridge.isConnected).toBe(true);
105
+ // Now replace the method on the instance
106
+ let resolveReconnect;
107
+ const reconnectPromise = new Promise((resolve) => { resolveReconnect = resolve; });
108
+ bridge._connectOnce = vi.fn().mockReturnValue(reconnectPromise);
109
+ // Manually trigger onclose
110
+ if (mockTransport.onclose) {
111
+ mockTransport.onclose();
112
+ }
113
+ // Ticking to allow onclose to call connect()
114
+ await vi.advanceTimersByTimeAsync(0);
115
+ // Reconnection should have been triggered
116
+ expect(bridge.isConnecting).toBe(true);
117
+ expect(bridge.isConnected).toBe(false);
118
+ // Clean up
119
+ resolveReconnect();
120
+ await vi.runAllTimersAsync();
121
+ });
122
+ it("should do nothing on transport close if not previously connected", async () => {
123
+ const bridge = new MCPBridge(config);
124
+ bridge.isConnected = false;
125
+ const connectSpy = vi.spyOn(bridge, "connect");
126
+ if (mockTransport.onclose) {
127
+ mockTransport.onclose();
128
+ }
129
+ expect(connectSpy).not.toHaveBeenCalled();
130
+ });
131
+ it("should log transport errors", async () => {
132
+ const consoleSpy = vi
133
+ .spyOn(console, "error")
134
+ .mockImplementation(() => { });
135
+ const bridge = new MCPBridge(config);
136
+ await bridge.connect();
137
+ const transport = bridge.transport;
138
+ if (transport.onerror) {
139
+ transport.onerror(new Error("transport error"));
140
+ }
141
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Transport error"), "transport error");
142
+ consoleSpy.mockRestore();
143
+ });
144
+ });
145
+ describe("notification handlers", () => {
146
+ it("should emit 'task' when channel notification is received", async () => {
147
+ const bridge = new MCPBridge(config);
148
+ const mClient = bridge.client;
149
+ let taskHandler;
150
+ mClient.setNotificationHandler.mockImplementation((schema, handler) => {
151
+ if (mClient.setNotificationHandler.mock.calls.length === 1) {
152
+ taskHandler = handler;
153
+ }
154
+ });
155
+ await bridge.connect();
156
+ const emitSpy = vi.spyOn(bridge, "emit");
157
+ if (taskHandler) {
158
+ taskHandler({ params: { content: "test task", meta: { foo: 1 } } });
159
+ }
160
+ expect(emitSpy).toHaveBeenCalledWith("task", {
161
+ content: "test task",
162
+ meta: { foo: 1 },
163
+ });
164
+ });
165
+ it("should emit 'verdict' when permission notification is received", async () => {
166
+ const bridge = new MCPBridge(config);
167
+ const mClient = bridge.client;
168
+ let verdictHandler;
169
+ mClient.setNotificationHandler.mockImplementation((schema, handler) => {
170
+ if (mClient.setNotificationHandler.mock.calls.length === 2) {
171
+ verdictHandler = handler;
172
+ }
173
+ });
174
+ await bridge.connect();
175
+ const emitSpy = vi.spyOn(bridge, "emit");
176
+ if (verdictHandler) {
177
+ verdictHandler({ params: { request_id: "req-1", behavior: "allow" } });
178
+ }
179
+ expect(emitSpy).toHaveBeenCalledWith("verdict", {
180
+ requestId: "req-1",
181
+ behavior: "allow",
182
+ });
183
+ });
184
+ });
185
+ describe("ensureConnected", () => {
186
+ it("should wait for connection if currently connecting", async () => {
187
+ const bridge = new MCPBridge(config);
188
+ bridge.isConnecting = true;
189
+ const toolPromise = bridge.callTool("test");
190
+ setTimeout(() => {
191
+ bridge.isConnected = true;
192
+ }, 1000);
193
+ await vi.advanceTimersByTimeAsync(1500);
194
+ const result = await toolPromise;
195
+ expect(result).toEqual({ result: "ok" });
196
+ });
197
+ it("should throw if connection times out", async () => {
198
+ const bridge = new MCPBridge(config);
199
+ bridge.isConnecting = true;
200
+ const toolPromise = bridge.callTool("test");
201
+ const expectPromise = expect(toolPromise).rejects.toThrow("MCP not connected after 10s timeout");
202
+ // Advance time enough to trigger the timeout
203
+ await vi.runAllTimersAsync();
204
+ await expectPromise;
205
+ });
206
+ });
207
+ describe("tool calls and notifications", () => {
208
+ it("should call tools correctly", async () => {
209
+ const bridge = new MCPBridge(config);
210
+ bridge.isConnected = true;
211
+ const result = await bridge.callTool("test-tool", { arg: 1 });
212
+ expect(result).toEqual({ result: "ok" });
213
+ });
214
+ it("should send notifications correctly", async () => {
215
+ const bridge = new MCPBridge(config);
216
+ bridge.isConnected = true;
217
+ await bridge.sendNotification("test-method", { foo: "bar" });
218
+ expect(bridge.client.notification).toHaveBeenCalled();
219
+ });
220
+ it("should handle transport close rejection in _connectOnce", async () => {
221
+ const bridge = new MCPBridge(config);
222
+ mockTransport.close.mockRejectedValue(new Error("close failed"));
223
+ await bridge._connectOnce();
224
+ expect(mockTransport.close).toHaveBeenCalled();
225
+ expect(bridge.isConnected).toBe(false);
226
+ });
227
+ it("should close the client", async () => {
228
+ const bridge = new MCPBridge(config);
229
+ await bridge.close();
230
+ expect(bridge.client.close).toHaveBeenCalled();
231
+ });
232
+ });
233
+ });
234
+ //# sourceMappingURL=mcpClient.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpClient.test.js","sourceRoot":"","sources":["../../src/__tests__/mcpClient.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * acpClient.ts
3
+ *
4
+ * Implements the ACP Client interface for the agentrq workspace.
5
+ * Routes permission requests to the MCP server.
6
+ */
7
+ import * as fs from "node:fs/promises";
8
+ import * as path from "node:path";
9
+ export class AgentRQACPClient {
10
+ mcpBridge;
11
+ constructor(mcpBridge) {
12
+ this.mcpBridge = mcpBridge;
13
+ }
14
+ async requestPermission(params) {
15
+ const requestId = params.toolCall.toolCallId;
16
+ const toolTitle = params.toolCall.title ?? "Unknown Tool";
17
+ // Auto-allow tool calls that contain the pattern: agentrq-<11 chars a-zA-Z0-9>
18
+ const agentrqToolPattern = /agentrq-[a-zA-Z0-9]{11}/;
19
+ if (agentrqToolPattern.test(toolTitle)) {
20
+ console.error(`\n🔓 ACP Auto-allowing tool call: ${toolTitle} (ID: ${requestId})`);
21
+ const option = params.options.find(o => o.kind.startsWith("allow") ||
22
+ o.name.toLowerCase().includes("allow") ||
23
+ o.name.toLowerCase().includes("yes") ||
24
+ o.name.toLowerCase().includes("approve"));
25
+ const optionId = option?.optionId ?? params.options[0].optionId;
26
+ return {
27
+ outcome: {
28
+ outcome: "selected",
29
+ optionId: optionId,
30
+ },
31
+ };
32
+ }
33
+ console.error(`\n🔐 ACP Permission requested: ${toolTitle} (ID: ${requestId})`);
34
+ const payload = {
35
+ request_id: requestId,
36
+ tool_name: toolTitle,
37
+ description: toolTitle,
38
+ input_preview: JSON.stringify(params.toolCall.rawInput ?? {}),
39
+ };
40
+ console.error(`[acp] Bridge Session ID: ${this.mcpBridge.getSessionId() ?? "unknown"}`);
41
+ console.error(`[acp] Sending permission request notification:`, JSON.stringify(payload, null, 2));
42
+ // 1. Forward the permission request to the MCP server as a notification.
43
+ await this.mcpBridge.sendNotification("notifications/claude/channel/permission_request", payload);
44
+ // 2. Wait for the verdict from the MCP server
45
+ console.error(`⌛ Waiting for human approval in the agentrq dashboard...`);
46
+ return new Promise((resolve) => {
47
+ const handler = (data) => {
48
+ if (data.requestId === requestId) {
49
+ // Cleanup this listener
50
+ this.mcpBridge.off("verdict", handler);
51
+ console.error(`✅ Permission verdict received: ${data.behavior}`);
52
+ // Map "allow"/"deny" to the correct ACP option
53
+ // ACP options usually include "allow_once", "allow_always", etc.
54
+ const verdictMatch = data.behavior === "allow" ? "allow" : "deny";
55
+ const option = params.options.find(o => o.kind.startsWith(verdictMatch) ||
56
+ o.name.toLowerCase().includes(verdictMatch) ||
57
+ (verdictMatch === "allow" && (o.name.toLowerCase().includes("yes") || o.name.toLowerCase().includes("approve"))));
58
+ const optionId = option?.optionId ?? params.options[0].optionId;
59
+ console.error(`[acp] Selected permission option: ${optionId} (${option?.name ?? "default"})`);
60
+ resolve({
61
+ outcome: {
62
+ outcome: "selected",
63
+ optionId: optionId,
64
+ },
65
+ });
66
+ }
67
+ };
68
+ this.mcpBridge.on("verdict", handler);
69
+ });
70
+ }
71
+ async sessionUpdate(params) {
72
+ const update = params.update;
73
+ switch (update.sessionUpdate) {
74
+ case "agent_message_chunk":
75
+ if (update.content.type === "text") {
76
+ process.stdout.write(update.content.text);
77
+ }
78
+ break;
79
+ case "tool_call":
80
+ // Skip logging for auto-allowed tools to avoid noise in the backend/logs
81
+ const agentrqToolPattern = /agentrq-[a-zA-Z0-9]{11}/;
82
+ if (update.title && agentrqToolPattern.test(update.title)) {
83
+ break;
84
+ }
85
+ console.error(`\n🔧 [ACP Agent] Tool call: ${update.title} (${update.status})`);
86
+ break;
87
+ default:
88
+ break;
89
+ }
90
+ }
91
+ async writeTextFile(params) {
92
+ const filePath = path.resolve(process.cwd(), params.path);
93
+ console.error(`[acp] Writing file: ${params.path}`);
94
+ try {
95
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
96
+ await fs.writeFile(filePath, params.content, "utf8");
97
+ console.error(`[acp] File written successfully: ${params.path}`);
98
+ return {};
99
+ }
100
+ catch (err) {
101
+ console.error(`[acp] Error writing file ${params.path}:`, err.message);
102
+ throw err;
103
+ }
104
+ }
105
+ async readTextFile(params) {
106
+ const filePath = path.resolve(process.cwd(), params.path);
107
+ console.error(`[acp] Reading file: ${params.path}`);
108
+ try {
109
+ const content = await fs.readFile(filePath, "utf8");
110
+ return { content };
111
+ }
112
+ catch (err) {
113
+ console.error(`[acp] Error reading file ${params.path}:`, err.message);
114
+ throw err;
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=acpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acpClient.js","sourceRoot":"","sources":["../src/acpClient.ts"],"names":[],"mappings":"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"}
package/dist/config.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * config.ts
3
+ *
4
+ * Reads the .mcp.json file from the workspace root and returns the first
5
+ * HTTP MCP server config (preferring any server whose name contains "agentrq").
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ /**
10
+ * Find and parse the .mcp.json config file.
11
+ * Searches CWD and then up to 3 parent directories.
12
+ */
13
+ export function loadMcpConfig(startDir = process.cwd()) {
14
+ const candidates = [
15
+ resolve(startDir, ".mcp.json"),
16
+ resolve(startDir, "..", ".mcp.json"),
17
+ resolve(startDir, "..", "..", ".mcp.json"),
18
+ resolve(startDir, "..", "..", "..", ".mcp.json"),
19
+ ];
20
+ for (const candidate of candidates) {
21
+ try {
22
+ const raw = readFileSync(candidate, "utf-8");
23
+ const parsed = JSON.parse(raw);
24
+ const servers = Object.entries(parsed.mcpServers ?? {}).map(([name, cfg]) => ({
25
+ name,
26
+ type: cfg.type ?? (cfg.url ? "http" : "stdio"),
27
+ url: cfg.url,
28
+ command: cfg.command,
29
+ args: cfg.args,
30
+ env: cfg.env,
31
+ }));
32
+ if (servers.length > 0) {
33
+ console.error(`[config] Loaded .mcp.json from ${candidate}`);
34
+ return servers;
35
+ }
36
+ }
37
+ catch {
38
+ // Not found here, try next
39
+ }
40
+ }
41
+ throw new Error("Could not find .mcp.json — run acp-gateway from your workspace root");
42
+ }
43
+ /**
44
+ * Pick the primary agentrq MCP server from the list.
45
+ * Prefers servers with "agentrq" in the name; falls back to the first HTTP server.
46
+ */
47
+ export function pickAgentrqServer(servers) {
48
+ // Prefer named agentrq server
49
+ const named = servers.find((s) => s.name.toLowerCase().includes("agentrq") && s.type === "http" && s.url);
50
+ if (named)
51
+ return named;
52
+ // Fall back to first HTTP server
53
+ const http = servers.find((s) => s.type === "http" && s.url);
54
+ if (http)
55
+ return http;
56
+ throw new Error("No HTTP MCP server found in .mcp.json — expected at least one entry with type=http and url");
57
+ }
58
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAC5D,MAAM,UAAU,GAAG;QACjB,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC;QAC9B,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC;QACpC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC;QAC1C,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC;KACjD,CAAC;IAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,OAAO,GAAsB,MAAM,CAAC,OAAO,CAC/C,MAAM,CAAC,UAAU,IAAI,EAAE,CACxB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtB,IAAI;gBACJ,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC9C,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,GAAG,EAAE,GAAG,CAAC,GAAG;aACb,CAAC,CAAC,CAAC;YAEJ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,KAAK,CAAC,kCAAkC,SAAS,EAAE,CAAC,CAAC;gBAC7D,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA0B;IAE1B,8BAA8B;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,GAAG,CAC9E,CAAC;IACF,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,iCAAiC;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7D,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;AACJ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * index.ts
4
+ *
5
+ * The main entry point for acp-gateway.
6
+ * Orchestrates the bridge between the ACP Agent and the agentrq MCP Server.
7
+ */
8
+ import { spawn } from "node:child_process";
9
+ import { Writable, Readable } from "node:stream";
10
+ import * as acp from "@agentclientprotocol/sdk";
11
+ import { loadMcpConfig, pickAgentrqServer } from "./config.js";
12
+ import { MCPBridge } from "./mcpClient.js";
13
+ import { AgentRQACPClient } from "./acpClient.js";
14
+ async function main() {
15
+ const args = process.argv.slice(2);
16
+ // Find where the command starts (after -- if provided, or just all args)
17
+ const cmdStartIndex = args.indexOf("--");
18
+ const acpCmdArgs = cmdStartIndex !== -1 ? args.slice(cmdStartIndex + 1) : args;
19
+ if (acpCmdArgs.length === 0) {
20
+ console.error("Usage: acp-gateway -- <acp-server-command> [args...]");
21
+ console.error("Example: acp-gateway -- gemini --acp");
22
+ process.exit(1);
23
+ }
24
+ // 1. Load MCP Config
25
+ const configs = loadMcpConfig();
26
+ const agentrqConfig = pickAgentrqServer(configs);
27
+ // 2. Initialize MCP Bridge
28
+ const mcpBridge = new MCPBridge(agentrqConfig);
29
+ await mcpBridge.connect();
30
+ // 3. Spawn ACP Agent Subprocess
31
+ const [cmd, ...cmdArgs] = acpCmdArgs;
32
+ console.error(`[acp] Spawning agent: ${cmd} ${cmdArgs.join(" ")}`);
33
+ const agentProcess = spawn(cmd, cmdArgs, {
34
+ stdio: ["pipe", "pipe", "inherit"],
35
+ env: { ...process.env, ...agentrqConfig.env },
36
+ });
37
+ const input = Writable.toWeb(agentProcess.stdin);
38
+ const output = Readable.toWeb(agentProcess.stdout);
39
+ // 4. Create ACP Connection
40
+ const acpClient = new AgentRQACPClient(mcpBridge);
41
+ const stream = acp.ndJsonStream(input, output);
42
+ const connection = new acp.ClientSideConnection((_agent) => acpClient, stream);
43
+ try {
44
+ // 5. Initialize ACP Connection
45
+ const initResult = await connection.initialize({
46
+ protocolVersion: acp.PROTOCOL_VERSION,
47
+ clientCapabilities: {
48
+ fs: {
49
+ readTextFile: true,
50
+ writeTextFile: true,
51
+ },
52
+ },
53
+ });
54
+ console.error(`[acp] Connected to agent (protocol v${initResult.protocolVersion})`);
55
+ // 6. Create ACP Session
56
+ // We pass our agentrq MCP server to the agent so it can use our tools directly.
57
+ // The McpServer object must follow the ACP protocol schema (type, name, url, headers).
58
+ const sessionResult = await connection.newSession({
59
+ cwd: process.cwd(),
60
+ mcpServers: [
61
+ {
62
+ type: "http",
63
+ name: agentrqConfig.name,
64
+ url: agentrqConfig.url,
65
+ headers: [],
66
+ },
67
+ ],
68
+ });
69
+ const sessionId = sessionResult.sessionId;
70
+ console.error(`[acp] Created session: ${sessionId}`);
71
+ // Bridge: MCP -> ACP
72
+ // When the MCP server sends a notification to 'notifications/claude/channel',
73
+ // it contains a new task content.
74
+ mcpBridge.on("task", async ({ content }) => {
75
+ console.error("\n[bridge] Incoming task from MCP server. Forwarding to ACP agent...");
76
+ try {
77
+ const result = await connection.prompt({
78
+ sessionId,
79
+ prompt: [{ type: "text", text: content }],
80
+ });
81
+ console.error(`\n[acp] Agent completed task. Reason: ${result.stopReason}`);
82
+ // Loop: When a task is complete, automatically ask for the next one
83
+ await checkForNextTask(mcpBridge, connection, sessionId);
84
+ }
85
+ catch (err) {
86
+ console.error("[acp] Error during prompt execution:", err);
87
+ }
88
+ });
89
+ // Initial check for a pending task
90
+ await checkForNextTask(mcpBridge, connection, sessionId);
91
+ // Keep the process alive
92
+ await new Promise(() => { });
93
+ }
94
+ catch (error) {
95
+ console.error("[acp-gateway] Error:", error);
96
+ }
97
+ finally {
98
+ agentProcess.kill();
99
+ await mcpBridge.close();
100
+ process.exit(0);
101
+ }
102
+ }
103
+ /**
104
+ * Checks for the next pending task using the 'getNextTask' tool on the MCP server.
105
+ * If found, sends it to the ACP agent.
106
+ */
107
+ async function checkForNextTask(mcpBridge, connection, sessionId) {
108
+ console.error("[bridge] Checking for next task via MCP server...");
109
+ try {
110
+ const result = await mcpBridge.callTool("getNextTask");
111
+ if (result.isError) {
112
+ console.error("[mcp] Error getting next task:", result.content);
113
+ return;
114
+ }
115
+ const contentBlock = result.content;
116
+ const content = contentBlock[0];
117
+ if (content &&
118
+ content.text &&
119
+ !content.text.includes("no pending tasks exist")) {
120
+ console.error(`[bridge] Found task: "${content.text.slice(0, 50).replace(/\n/g, " ")}..."`);
121
+ const promptResult = await connection.prompt({
122
+ sessionId,
123
+ prompt: [{ type: "text", text: content.text }],
124
+ });
125
+ console.error(`\n[acp] Agent completed with: ${promptResult.stopReason}`);
126
+ // Recursively check for next task
127
+ await checkForNextTask(mcpBridge, connection, sessionId);
128
+ }
129
+ else {
130
+ console.error("[bridge] No pending tasks available.");
131
+ }
132
+ }
133
+ catch (err) {
134
+ console.error("[bridge] Failed to check for next task:", err);
135
+ }
136
+ }
137
+ main().catch((err) => {
138
+ console.error("[fatal]", err);
139
+ process.exit(1);
140
+ });
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}