@thinkwell/acp 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.
- package/dist/connection.d.ts +71 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +263 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +13 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +2 -0
- package/dist/json.js.map +1 -0
- package/dist/mcp-over-acp-handler.d.ts +77 -0
- package/dist/mcp-over-acp-handler.d.ts.map +1 -0
- package/dist/mcp-over-acp-handler.js +183 -0
- package/dist/mcp-over-acp-handler.js.map +1 -0
- package/dist/mcp-server.d.ts +56 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +148 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/session.d.ts +112 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +165 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +196 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { ClientSideConnection, type SessionNotification, type PromptResponse } from "@agentclientprotocol/sdk";
|
|
3
|
+
import { McpOverAcpHandler } from "./mcp-over-acp-handler.js";
|
|
4
|
+
import type { ActiveSession } from "./session.js";
|
|
5
|
+
import type { McpServerConfig } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Session creation options
|
|
8
|
+
*/
|
|
9
|
+
export interface SessionOptions {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
systemPrompt?: string;
|
|
12
|
+
mcpServers?: McpServerConfig[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Connection to the SACP conductor via the ACP SDK.
|
|
16
|
+
*
|
|
17
|
+
* This class wraps the official @agentclientprotocol/sdk's ClientSideConnection
|
|
18
|
+
* and adds SACP-specific functionality (MCP-over-ACP handling).
|
|
19
|
+
*/
|
|
20
|
+
export declare class SacpConnection {
|
|
21
|
+
private readonly _process;
|
|
22
|
+
private readonly _connection;
|
|
23
|
+
private readonly _mcpHandler;
|
|
24
|
+
private readonly _sessionHandlers;
|
|
25
|
+
private _initialized;
|
|
26
|
+
constructor(process: ChildProcess, connection: ClientSideConnection, mcpHandler: McpOverAcpHandler);
|
|
27
|
+
/**
|
|
28
|
+
* Get the MCP handler for registering servers
|
|
29
|
+
*/
|
|
30
|
+
get mcpHandler(): McpOverAcpHandler;
|
|
31
|
+
/**
|
|
32
|
+
* Initialize the connection (negotiate protocol version and capabilities)
|
|
33
|
+
*/
|
|
34
|
+
initialize(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Create a new session
|
|
37
|
+
*/
|
|
38
|
+
createSession(options: SessionOptions): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Send a prompt to an existing session
|
|
41
|
+
*/
|
|
42
|
+
sendPrompt(sessionId: string, content: string): Promise<PromptResponse>;
|
|
43
|
+
/**
|
|
44
|
+
* Set the handler for session updates
|
|
45
|
+
*/
|
|
46
|
+
setSessionHandler(sessionId: string, handler: ActiveSession): void;
|
|
47
|
+
/**
|
|
48
|
+
* Remove the handler for session updates
|
|
49
|
+
*/
|
|
50
|
+
removeSessionHandler(sessionId: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Handle a session update notification from the agent
|
|
53
|
+
*/
|
|
54
|
+
handleSessionUpdate(notification: SessionNotification): void;
|
|
55
|
+
private _convertNotification;
|
|
56
|
+
/**
|
|
57
|
+
* Close the connection
|
|
58
|
+
*/
|
|
59
|
+
close(): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Connect to a conductor process
|
|
63
|
+
*/
|
|
64
|
+
export declare function connect(command: string[]): Promise<SacpConnection>;
|
|
65
|
+
/**
|
|
66
|
+
* Create a session builder for this connection
|
|
67
|
+
*/
|
|
68
|
+
export declare function session(connection: SacpConnection): SessionBuilder;
|
|
69
|
+
import { SessionBuilder } from "./session.js";
|
|
70
|
+
export { SessionBuilder };
|
|
71
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,EACL,oBAAoB,EAIpB,KAAK,mBAAmB,EAKxB,KAAK,cAAc,EAEpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyC;IAC1E,OAAO,CAAC,YAAY,CAAkB;gBAGpC,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,oBAAoB,EAChC,UAAU,EAAE,iBAAiB;IAO/B;;OAEG;IACH,IAAI,UAAU,IAAI,iBAAiB,CAElC;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB7D;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAS7E;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAIlE;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI7C;;OAEG;IACH,mBAAmB,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI;IAe5D,OAAO,CAAC,oBAAoB;IAmC5B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AA2GD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CA+CxE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,cAAc,GAAG,cAAc,CAElE;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { ClientSideConnection, ndJsonStream, } from "@agentclientprotocol/sdk";
|
|
3
|
+
import { McpOverAcpHandler } from "./mcp-over-acp-handler.js";
|
|
4
|
+
/**
|
|
5
|
+
* Connection to the SACP conductor via the ACP SDK.
|
|
6
|
+
*
|
|
7
|
+
* This class wraps the official @agentclientprotocol/sdk's ClientSideConnection
|
|
8
|
+
* and adds SACP-specific functionality (MCP-over-ACP handling).
|
|
9
|
+
*/
|
|
10
|
+
export class SacpConnection {
|
|
11
|
+
_process;
|
|
12
|
+
_connection;
|
|
13
|
+
_mcpHandler;
|
|
14
|
+
_sessionHandlers = new Map();
|
|
15
|
+
_initialized = false;
|
|
16
|
+
constructor(process, connection, mcpHandler) {
|
|
17
|
+
this._process = process;
|
|
18
|
+
this._connection = connection;
|
|
19
|
+
this._mcpHandler = mcpHandler;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the MCP handler for registering servers
|
|
23
|
+
*/
|
|
24
|
+
get mcpHandler() {
|
|
25
|
+
return this._mcpHandler;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the connection (negotiate protocol version and capabilities)
|
|
29
|
+
*/
|
|
30
|
+
async initialize() {
|
|
31
|
+
if (this._initialized)
|
|
32
|
+
return;
|
|
33
|
+
await this._connection.initialize({
|
|
34
|
+
protocolVersion: 1,
|
|
35
|
+
clientCapabilities: {},
|
|
36
|
+
});
|
|
37
|
+
this._initialized = true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a new session
|
|
41
|
+
*/
|
|
42
|
+
async createSession(options) {
|
|
43
|
+
await this.initialize();
|
|
44
|
+
// Convert our McpServerConfig to the SDK's McpServer type
|
|
45
|
+
const mcpServers = (options.mcpServers ?? []).map((s) => ({
|
|
46
|
+
type: "http",
|
|
47
|
+
name: s.name,
|
|
48
|
+
url: s.url,
|
|
49
|
+
headers: [],
|
|
50
|
+
}));
|
|
51
|
+
const request = {
|
|
52
|
+
cwd: options.cwd ?? process.cwd(),
|
|
53
|
+
mcpServers,
|
|
54
|
+
};
|
|
55
|
+
const response = await this._connection.newSession(request);
|
|
56
|
+
return response.sessionId;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Send a prompt to an existing session
|
|
60
|
+
*/
|
|
61
|
+
async sendPrompt(sessionId, content) {
|
|
62
|
+
const request = {
|
|
63
|
+
sessionId,
|
|
64
|
+
prompt: [{ type: "text", text: content }],
|
|
65
|
+
};
|
|
66
|
+
return this._connection.prompt(request);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Set the handler for session updates
|
|
70
|
+
*/
|
|
71
|
+
setSessionHandler(sessionId, handler) {
|
|
72
|
+
this._sessionHandlers.set(sessionId, handler);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Remove the handler for session updates
|
|
76
|
+
*/
|
|
77
|
+
removeSessionHandler(sessionId) {
|
|
78
|
+
this._sessionHandlers.delete(sessionId);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Handle a session update notification from the agent
|
|
82
|
+
*/
|
|
83
|
+
handleSessionUpdate(notification) {
|
|
84
|
+
const { sessionId } = notification;
|
|
85
|
+
const handler = this._sessionHandlers.get(sessionId);
|
|
86
|
+
if (!handler) {
|
|
87
|
+
console.error(`No handler for session: ${sessionId}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Convert ACP notification to our SessionUpdate type
|
|
91
|
+
const update = this._convertNotification(notification);
|
|
92
|
+
if (update) {
|
|
93
|
+
handler.pushUpdate(update);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
_convertNotification(notification) {
|
|
97
|
+
const { update } = notification;
|
|
98
|
+
// Use the sessionUpdate discriminator to determine type
|
|
99
|
+
switch (update.sessionUpdate) {
|
|
100
|
+
case "agent_message_chunk":
|
|
101
|
+
case "user_message_chunk":
|
|
102
|
+
case "agent_thought_chunk": {
|
|
103
|
+
const content = update.content;
|
|
104
|
+
if (content.type === "text") {
|
|
105
|
+
return { type: "text", content: content.text };
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "tool_call": {
|
|
110
|
+
return {
|
|
111
|
+
type: "tool_use",
|
|
112
|
+
id: update.toolCallId,
|
|
113
|
+
name: update.title,
|
|
114
|
+
input: update.rawInput ?? {},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
case "plan":
|
|
118
|
+
case "tool_call_update":
|
|
119
|
+
case "available_commands_update":
|
|
120
|
+
case "current_mode_update":
|
|
121
|
+
// These are informational updates we don't need to handle
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Close the connection
|
|
128
|
+
*/
|
|
129
|
+
close() {
|
|
130
|
+
this._process.kill();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a Client implementation that handles incoming agent requests
|
|
135
|
+
*/
|
|
136
|
+
function createClient(connectionHolder, mcpHandler) {
|
|
137
|
+
return {
|
|
138
|
+
// Required: Handle session updates from the agent
|
|
139
|
+
sessionUpdate(notification) {
|
|
140
|
+
if (!connectionHolder.connection) {
|
|
141
|
+
console.error("Connection not yet initialized, dropping session update");
|
|
142
|
+
return Promise.resolve();
|
|
143
|
+
}
|
|
144
|
+
connectionHolder.connection.handleSessionUpdate(notification);
|
|
145
|
+
return Promise.resolve();
|
|
146
|
+
},
|
|
147
|
+
// Required: Handle permission requests
|
|
148
|
+
requestPermission(request) {
|
|
149
|
+
// For now, auto-approve by selecting the first option
|
|
150
|
+
// In a real client, this would prompt the user
|
|
151
|
+
const firstOption = request.options[0];
|
|
152
|
+
return Promise.resolve({
|
|
153
|
+
outcome: {
|
|
154
|
+
outcome: "selected",
|
|
155
|
+
optionId: firstOption?.optionId ?? "approve",
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
// Extension method handler for _mcp/* requests
|
|
160
|
+
async extMethod(method, params) {
|
|
161
|
+
if (mcpHandler.isMcpRequest(method)) {
|
|
162
|
+
const result = await mcpHandler.routeRequest(method, params);
|
|
163
|
+
return result ?? {};
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`Unknown extension method: ${method}`);
|
|
166
|
+
},
|
|
167
|
+
// Extension notification handler
|
|
168
|
+
async extNotification(method, params) {
|
|
169
|
+
if (mcpHandler.isMcpRequest(method)) {
|
|
170
|
+
await mcpHandler.routeRequest(method, params);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Convert Node.js streams to Web Streams for the ACP SDK
|
|
177
|
+
*/
|
|
178
|
+
function nodeToWebStreams(stdout, stdin) {
|
|
179
|
+
// Convert Node readable to Web ReadableStream
|
|
180
|
+
const readable = new ReadableStream({
|
|
181
|
+
start(controller) {
|
|
182
|
+
stdout.on("data", (chunk) => {
|
|
183
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
184
|
+
});
|
|
185
|
+
stdout.on("end", () => {
|
|
186
|
+
controller.close();
|
|
187
|
+
});
|
|
188
|
+
stdout.on("error", (err) => {
|
|
189
|
+
controller.error(err);
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
cancel() {
|
|
193
|
+
stdout.destroy();
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
// Convert Node writable to Web WritableStream
|
|
197
|
+
const writable = new WritableStream({
|
|
198
|
+
write(chunk) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
stdin.write(Buffer.from(chunk), (err) => {
|
|
201
|
+
if (err)
|
|
202
|
+
reject(err);
|
|
203
|
+
else
|
|
204
|
+
resolve();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
close() {
|
|
209
|
+
return new Promise((resolve) => {
|
|
210
|
+
stdin.end(() => resolve());
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
abort(reason) {
|
|
214
|
+
stdin.destroy(reason instanceof Error ? reason : new Error(String(reason)));
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
return { readable, writable };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Connect to a conductor process
|
|
221
|
+
*/
|
|
222
|
+
export async function connect(command) {
|
|
223
|
+
if (command.length === 0) {
|
|
224
|
+
throw new Error("Conductor command cannot be empty");
|
|
225
|
+
}
|
|
226
|
+
const [cmd, ...args] = command;
|
|
227
|
+
const childProcess = spawn(cmd, args, {
|
|
228
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
229
|
+
});
|
|
230
|
+
if (!childProcess.stdout || !childProcess.stdin) {
|
|
231
|
+
throw new Error("Conductor process must have stdio");
|
|
232
|
+
}
|
|
233
|
+
// Log stderr for debugging
|
|
234
|
+
childProcess.stderr?.on("data", (data) => {
|
|
235
|
+
console.error("[conductor stderr]", data.toString());
|
|
236
|
+
});
|
|
237
|
+
// Convert Node streams to Web streams for the SDK
|
|
238
|
+
const { readable, writable } = nodeToWebStreams(childProcess.stdout, childProcess.stdin);
|
|
239
|
+
// Create the ndjson stream
|
|
240
|
+
const stream = ndJsonStream(writable, readable);
|
|
241
|
+
// Create the MCP handler
|
|
242
|
+
const mcpHandler = new McpOverAcpHandler();
|
|
243
|
+
// Use a holder object to break the circular reference.
|
|
244
|
+
// The client factory is called lazily by ClientSideConnection,
|
|
245
|
+
// so we need the holder to be populated before any messages arrive.
|
|
246
|
+
const connectionHolder = { connection: null };
|
|
247
|
+
// Create the ACP client connection
|
|
248
|
+
const clientConnection = new ClientSideConnection((_agent) => createClient(connectionHolder, mcpHandler), stream);
|
|
249
|
+
// Create our wrapper connection and store it in the holder
|
|
250
|
+
const sacpConnection = new SacpConnection(childProcess, clientConnection, mcpHandler);
|
|
251
|
+
connectionHolder.connection = sacpConnection;
|
|
252
|
+
return sacpConnection;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Create a session builder for this connection
|
|
256
|
+
*/
|
|
257
|
+
export function session(connection) {
|
|
258
|
+
return new SessionBuilder(connection, connection.mcpHandler);
|
|
259
|
+
}
|
|
260
|
+
// Re-export for convenience
|
|
261
|
+
import { SessionBuilder } from "./session.js";
|
|
262
|
+
export { SessionBuilder };
|
|
263
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { JsonSchema, SchemaProvider, McpContext, McpServerConfig, McpConnectRequest, McpConnectResponse, McpMessageRequest, McpMessageResponse, McpContent, McpError, McpDisconnectNotification, McpToolsListResult, McpToolDefinition, McpToolsCallParams, McpToolsCallResult, SessionMessage, StopReason, ToolHandler, RegisteredTool, } from "./types.js";
|
|
2
|
+
export type { JsonValue, JsonObject } from "./json.js";
|
|
3
|
+
export { McpServer, McpServerBuilder, mcpServer } from "./mcp-server.js";
|
|
4
|
+
export { McpOverAcpHandler } from "./mcp-over-acp-handler.js";
|
|
5
|
+
export { ActiveSession, SessionBuilder } from "./session.js";
|
|
6
|
+
export type { SessionUpdate, PromptMessage, McpReadyOptions } from "./session.js";
|
|
7
|
+
export { SacpConnection, connect } from "./connection.js";
|
|
8
|
+
export type { SessionOptions } from "./connection.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,yBAAyB,EACzB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGlF,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// MCP Server
|
|
2
|
+
export { McpServer, McpServerBuilder, mcpServer } from "./mcp-server.js";
|
|
3
|
+
// MCP-over-ACP Handler
|
|
4
|
+
export { McpOverAcpHandler } from "./mcp-over-acp-handler.js";
|
|
5
|
+
// Session
|
|
6
|
+
export { ActiveSession, SessionBuilder } from "./session.js";
|
|
7
|
+
// Connection
|
|
8
|
+
export { SacpConnection, connect } from "./connection.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEzE,uBAAuB;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG7D,aAAa;AACb,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON value types
|
|
3
|
+
*/
|
|
4
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* JSON object type
|
|
9
|
+
*/
|
|
10
|
+
export type JsonObject = {
|
|
11
|
+
[key: string]: JsonValue;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC"}
|
package/dist/json.js
ADDED
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { McpServer } from "./mcp-server.js";
|
|
2
|
+
import type { McpConnectResponse } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Handler for MCP-over-ACP protocol messages.
|
|
5
|
+
*
|
|
6
|
+
* This class manages the lifecycle of MCP connections tunneled through ACP:
|
|
7
|
+
* - mcp/connect: Establishes a new MCP connection
|
|
8
|
+
* - mcp/message: Routes MCP requests to the appropriate server
|
|
9
|
+
* - mcp/disconnect: Tears down an MCP connection
|
|
10
|
+
*
|
|
11
|
+
* Note: The ACP SDK strips the underscore prefix from extension methods,
|
|
12
|
+
* so we receive "mcp/connect" even though the wire format is "_mcp/connect".
|
|
13
|
+
*/
|
|
14
|
+
export declare class McpOverAcpHandler {
|
|
15
|
+
/** Maps acp:uuid URLs to registered MCP servers */
|
|
16
|
+
private readonly _serversByUrl;
|
|
17
|
+
/** Maps connection IDs to active connections */
|
|
18
|
+
private readonly _connections;
|
|
19
|
+
/** Current session ID for context */
|
|
20
|
+
private _sessionId;
|
|
21
|
+
/** Maps session IDs to pending tools discovery promises */
|
|
22
|
+
private readonly _toolsDiscoveryBySession;
|
|
23
|
+
/**
|
|
24
|
+
* Register an MCP server to handle requests for its acp: URL
|
|
25
|
+
*/
|
|
26
|
+
register(server: McpServer): void;
|
|
27
|
+
/**
|
|
28
|
+
* Unregister an MCP server
|
|
29
|
+
*/
|
|
30
|
+
unregister(server: McpServer): void;
|
|
31
|
+
/**
|
|
32
|
+
* Set the current session ID for context
|
|
33
|
+
*/
|
|
34
|
+
setSessionId(sessionId: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Handle an incoming mcp/connect request
|
|
37
|
+
*/
|
|
38
|
+
handleConnect(params: Record<string, unknown>): McpConnectResponse;
|
|
39
|
+
/**
|
|
40
|
+
* Handle an incoming mcp/message request.
|
|
41
|
+
*
|
|
42
|
+
* IMPORTANT: The response to _mcp/message is just the raw MCP result,
|
|
43
|
+
* NOT wrapped in {connectionId, result}. The conductor expects the raw
|
|
44
|
+
* MCP response (e.g., InitializeResult, ToolsListResult) directly.
|
|
45
|
+
*/
|
|
46
|
+
handleMessage(params: Record<string, unknown>): Promise<unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* Wait for the agent to discover tools via tools/list.
|
|
49
|
+
*
|
|
50
|
+
* This is used to avoid a race condition where the client sends a prompt
|
|
51
|
+
* before the agent has finished MCP initialization. The promise resolves
|
|
52
|
+
* when tools/list is called or when the timeout expires.
|
|
53
|
+
*
|
|
54
|
+
* @param sessionId - The session to wait for
|
|
55
|
+
* @param timeout - Maximum time to wait in milliseconds (default: 2000ms)
|
|
56
|
+
*/
|
|
57
|
+
waitForToolsDiscovery(sessionId: string, timeout?: number): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the tools discovery promise for a session
|
|
60
|
+
*/
|
|
61
|
+
private _resolveToolsDiscovery;
|
|
62
|
+
/**
|
|
63
|
+
* Handle an incoming mcp/disconnect notification
|
|
64
|
+
*/
|
|
65
|
+
handleDisconnect(params: Record<string, unknown>): void;
|
|
66
|
+
/**
|
|
67
|
+
* Check if this is an MCP-over-ACP request.
|
|
68
|
+
* Note: The ACP SDK strips the underscore prefix, so we check for "mcp/".
|
|
69
|
+
*/
|
|
70
|
+
isMcpRequest(method: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Route an MCP-over-ACP request to the appropriate handler.
|
|
73
|
+
* Note: Methods arrive without the underscore prefix (e.g., "mcp/connect" not "_mcp/connect").
|
|
74
|
+
*/
|
|
75
|
+
routeRequest(method: string, params: Record<string, unknown>): Promise<unknown>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=mcp-over-acp-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-over-acp-handler.d.ts","sourceRoot":"","sources":["../src/mcp-over-acp-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAEV,kBAAkB,EAInB,MAAM,YAAY,CAAC;AAmBpB;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;IAC5B,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqC;IACnE,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IACtE,qCAAqC;IACrC,OAAO,CAAC,UAAU,CAAc;IAChC,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA+C;IAExF;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIjC;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAInC;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB;IAuClE;;;;;;OAMG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAyBtE;;;;;;;;;OASG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B/E;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAO9B;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAKvD;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIrC;;;OAGG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC;CAapB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler for MCP-over-ACP protocol messages.
|
|
3
|
+
*
|
|
4
|
+
* This class manages the lifecycle of MCP connections tunneled through ACP:
|
|
5
|
+
* - mcp/connect: Establishes a new MCP connection
|
|
6
|
+
* - mcp/message: Routes MCP requests to the appropriate server
|
|
7
|
+
* - mcp/disconnect: Tears down an MCP connection
|
|
8
|
+
*
|
|
9
|
+
* Note: The ACP SDK strips the underscore prefix from extension methods,
|
|
10
|
+
* so we receive "mcp/connect" even though the wire format is "_mcp/connect".
|
|
11
|
+
*/
|
|
12
|
+
export class McpOverAcpHandler {
|
|
13
|
+
/** Maps acp:uuid URLs to registered MCP servers */
|
|
14
|
+
_serversByUrl = new Map();
|
|
15
|
+
/** Maps connection IDs to active connections */
|
|
16
|
+
_connections = new Map();
|
|
17
|
+
/** Current session ID for context */
|
|
18
|
+
_sessionId = "";
|
|
19
|
+
/** Maps session IDs to pending tools discovery promises */
|
|
20
|
+
_toolsDiscoveryBySession = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Register an MCP server to handle requests for its acp: URL
|
|
23
|
+
*/
|
|
24
|
+
register(server) {
|
|
25
|
+
this._serversByUrl.set(server.acpUrl, server);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Unregister an MCP server
|
|
29
|
+
*/
|
|
30
|
+
unregister(server) {
|
|
31
|
+
this._serversByUrl.delete(server.acpUrl);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Set the current session ID for context
|
|
35
|
+
*/
|
|
36
|
+
setSessionId(sessionId) {
|
|
37
|
+
this._sessionId = sessionId;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handle an incoming mcp/connect request
|
|
41
|
+
*/
|
|
42
|
+
handleConnect(params) {
|
|
43
|
+
// The protocol uses "acp_url" as the parameter name
|
|
44
|
+
// Generate connectionId if not provided by conductor
|
|
45
|
+
const connectionId = params.connectionId ?? `conn-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
46
|
+
const url = (params.acp_url ?? params.url);
|
|
47
|
+
const server = this._serversByUrl.get(url);
|
|
48
|
+
if (!server) {
|
|
49
|
+
throw new Error(`No MCP server registered for URL: ${url}`);
|
|
50
|
+
}
|
|
51
|
+
// Store the connection
|
|
52
|
+
this._connections.set(connectionId, {
|
|
53
|
+
connectionId,
|
|
54
|
+
server,
|
|
55
|
+
sessionId: this._sessionId,
|
|
56
|
+
});
|
|
57
|
+
// Include tool definitions in the connect response
|
|
58
|
+
// The conductor bridge may use this to provide tool info to the agent
|
|
59
|
+
const tools = server.getToolDefinitions();
|
|
60
|
+
// Return response with snake_case field names to match the Rust conductor's expectations
|
|
61
|
+
// Note: The conductor only requires connection_id, but we include extra info for potential use
|
|
62
|
+
return {
|
|
63
|
+
connection_id: connectionId,
|
|
64
|
+
connectionId, // Also include camelCase for backwards compatibility
|
|
65
|
+
serverInfo: {
|
|
66
|
+
name: server.name,
|
|
67
|
+
version: "0.1.0",
|
|
68
|
+
},
|
|
69
|
+
capabilities: {
|
|
70
|
+
tools: {},
|
|
71
|
+
},
|
|
72
|
+
// Include tools directly - the bridge may forward this to the agent
|
|
73
|
+
tools,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle an incoming mcp/message request.
|
|
78
|
+
*
|
|
79
|
+
* IMPORTANT: The response to _mcp/message is just the raw MCP result,
|
|
80
|
+
* NOT wrapped in {connectionId, result}. The conductor expects the raw
|
|
81
|
+
* MCP response (e.g., InitializeResult, ToolsListResult) directly.
|
|
82
|
+
*/
|
|
83
|
+
async handleMessage(params) {
|
|
84
|
+
const connectionId = params.connectionId;
|
|
85
|
+
const method = params.method;
|
|
86
|
+
const mcpParams = params.params;
|
|
87
|
+
const connection = this._connections.get(connectionId);
|
|
88
|
+
if (!connection) {
|
|
89
|
+
// Return MCP-style error for unknown connection
|
|
90
|
+
throw new Error(`Unknown connection: ${connectionId}`);
|
|
91
|
+
}
|
|
92
|
+
// If this is a tools/list request, resolve any pending tools discovery promise
|
|
93
|
+
if (method === "tools/list") {
|
|
94
|
+
this._resolveToolsDiscovery(connection.sessionId);
|
|
95
|
+
}
|
|
96
|
+
const context = {
|
|
97
|
+
connectionId,
|
|
98
|
+
sessionId: connection.sessionId,
|
|
99
|
+
};
|
|
100
|
+
// Return the raw MCP result - the conductor will wrap it appropriately
|
|
101
|
+
return await connection.server.handleMethod(method, mcpParams, context);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wait for the agent to discover tools via tools/list.
|
|
105
|
+
*
|
|
106
|
+
* This is used to avoid a race condition where the client sends a prompt
|
|
107
|
+
* before the agent has finished MCP initialization. The promise resolves
|
|
108
|
+
* when tools/list is called or when the timeout expires.
|
|
109
|
+
*
|
|
110
|
+
* @param sessionId - The session to wait for
|
|
111
|
+
* @param timeout - Maximum time to wait in milliseconds (default: 2000ms)
|
|
112
|
+
*/
|
|
113
|
+
waitForToolsDiscovery(sessionId, timeout = 2000) {
|
|
114
|
+
// If there's already a pending promise for this session, return it
|
|
115
|
+
const existing = this._toolsDiscoveryBySession.get(sessionId);
|
|
116
|
+
if (existing) {
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
const originalResolve = existing.resolve;
|
|
119
|
+
existing.resolve = () => {
|
|
120
|
+
originalResolve();
|
|
121
|
+
resolve();
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const state = {
|
|
127
|
+
resolve: () => {
|
|
128
|
+
if (state.timeoutId) {
|
|
129
|
+
clearTimeout(state.timeoutId);
|
|
130
|
+
}
|
|
131
|
+
this._toolsDiscoveryBySession.delete(sessionId);
|
|
132
|
+
resolve();
|
|
133
|
+
},
|
|
134
|
+
timeoutId: setTimeout(() => {
|
|
135
|
+
state.timeoutId = null;
|
|
136
|
+
state.resolve();
|
|
137
|
+
}, timeout),
|
|
138
|
+
};
|
|
139
|
+
this._toolsDiscoveryBySession.set(sessionId, state);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Resolve the tools discovery promise for a session
|
|
144
|
+
*/
|
|
145
|
+
_resolveToolsDiscovery(sessionId) {
|
|
146
|
+
const state = this._toolsDiscoveryBySession.get(sessionId);
|
|
147
|
+
if (state) {
|
|
148
|
+
state.resolve();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Handle an incoming mcp/disconnect notification
|
|
153
|
+
*/
|
|
154
|
+
handleDisconnect(params) {
|
|
155
|
+
const connectionId = params.connectionId;
|
|
156
|
+
this._connections.delete(connectionId);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if this is an MCP-over-ACP request.
|
|
160
|
+
* Note: The ACP SDK strips the underscore prefix, so we check for "mcp/".
|
|
161
|
+
*/
|
|
162
|
+
isMcpRequest(method) {
|
|
163
|
+
return method.startsWith("mcp/");
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Route an MCP-over-ACP request to the appropriate handler.
|
|
167
|
+
* Note: Methods arrive without the underscore prefix (e.g., "mcp/connect" not "_mcp/connect").
|
|
168
|
+
*/
|
|
169
|
+
async routeRequest(method, params) {
|
|
170
|
+
switch (method) {
|
|
171
|
+
case "mcp/connect":
|
|
172
|
+
return this.handleConnect(params);
|
|
173
|
+
case "mcp/message":
|
|
174
|
+
return this.handleMessage(params);
|
|
175
|
+
case "mcp/disconnect":
|
|
176
|
+
this.handleDisconnect(params);
|
|
177
|
+
return undefined;
|
|
178
|
+
default:
|
|
179
|
+
throw new Error(`Unknown MCP-over-ACP method: ${method}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=mcp-over-acp-handler.js.map
|