@thinkwell/conductor 0.2.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/README.md +140 -0
- package/dist/conductor.d.ts +219 -0
- package/dist/conductor.d.ts.map +1 -0
- package/dist/conductor.js +960 -0
- package/dist/conductor.js.map +1 -0
- package/dist/connectors/channel.d.ts +60 -0
- package/dist/connectors/channel.d.ts.map +1 -0
- package/dist/connectors/channel.js +155 -0
- package/dist/connectors/channel.js.map +1 -0
- package/dist/connectors/index.d.ts +6 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +6 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/stdio.d.ts +36 -0
- package/dist/connectors/stdio.d.ts.map +1 -0
- package/dist/connectors/stdio.js +198 -0
- package/dist/connectors/stdio.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/instantiators.d.ts +129 -0
- package/dist/instantiators.d.ts.map +1 -0
- package/dist/instantiators.js +183 -0
- package/dist/instantiators.js.map +1 -0
- package/dist/logger.d.ts +121 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +162 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp-bridge/http-listener.d.ts +35 -0
- package/dist/mcp-bridge/http-listener.d.ts.map +1 -0
- package/dist/mcp-bridge/http-listener.js +204 -0
- package/dist/mcp-bridge/http-listener.js.map +1 -0
- package/dist/mcp-bridge/index.d.ts +9 -0
- package/dist/mcp-bridge/index.d.ts.map +1 -0
- package/dist/mcp-bridge/index.js +8 -0
- package/dist/mcp-bridge/index.js.map +1 -0
- package/dist/mcp-bridge/mcp-bridge.d.ts +80 -0
- package/dist/mcp-bridge/mcp-bridge.d.ts.map +1 -0
- package/dist/mcp-bridge/mcp-bridge.js +170 -0
- package/dist/mcp-bridge/mcp-bridge.js.map +1 -0
- package/dist/mcp-bridge/types.d.ts +69 -0
- package/dist/mcp-bridge/types.d.ts.map +1 -0
- package/dist/mcp-bridge/types.js +8 -0
- package/dist/mcp-bridge/types.js.map +1 -0
- package/dist/message-queue.d.ts +46 -0
- package/dist/message-queue.d.ts.map +1 -0
- package/dist/message-queue.js +90 -0
- package/dist/message-queue.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Listener for MCP Bridge
|
|
3
|
+
*
|
|
4
|
+
* Listens on an ephemeral HTTP port for MCP connections from agents
|
|
5
|
+
* and bridges them to ACP `_mcp/*` messages.
|
|
6
|
+
*
|
|
7
|
+
* The listener handles the Streamable HTTP transport for MCP:
|
|
8
|
+
* - POST requests for JSON-RPC messages
|
|
9
|
+
* - SSE responses for streaming
|
|
10
|
+
*/
|
|
11
|
+
import type { McpBridgeMessage } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Options for creating an HTTP listener
|
|
14
|
+
*/
|
|
15
|
+
export interface HttpListenerOptions {
|
|
16
|
+
acpUrl: string;
|
|
17
|
+
onMessage: (message: McpBridgeMessage) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* An active HTTP listener for a specific `acp:` URL
|
|
21
|
+
*/
|
|
22
|
+
export interface HttpListener {
|
|
23
|
+
readonly acpUrl: string;
|
|
24
|
+
readonly port: number;
|
|
25
|
+
setSessionId(sessionId: string): void;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create an HTTP listener for MCP connections
|
|
30
|
+
*
|
|
31
|
+
* The listener waits for the session ID before accepting connections,
|
|
32
|
+
* ensuring we can always correlate connections with sessions.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createHttpListener(options: HttpListenerOptions): Promise<HttpListener>;
|
|
35
|
+
//# sourceMappingURL=http-listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-listener.d.ts","sourceRoot":"","sources":["../../src/mcp-bridge/http-listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAeH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAWD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAyH5F"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Listener for MCP Bridge
|
|
3
|
+
*
|
|
4
|
+
* Listens on an ephemeral HTTP port for MCP connections from agents
|
|
5
|
+
* and bridges them to ACP `_mcp/*` messages.
|
|
6
|
+
*
|
|
7
|
+
* The listener handles the Streamable HTTP transport for MCP:
|
|
8
|
+
* - POST requests for JSON-RPC messages
|
|
9
|
+
* - SSE responses for streaming
|
|
10
|
+
*/
|
|
11
|
+
import { createServer } from "node:http";
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
import { isJsonRpcRequest, isJsonRpcNotification, createResponder, } from "@thinkwell/protocol";
|
|
14
|
+
/**
|
|
15
|
+
* Create an HTTP listener for MCP connections
|
|
16
|
+
*
|
|
17
|
+
* The listener waits for the session ID before accepting connections,
|
|
18
|
+
* ensuring we can always correlate connections with sessions.
|
|
19
|
+
*/
|
|
20
|
+
export async function createHttpListener(options) {
|
|
21
|
+
const { acpUrl, onMessage } = options;
|
|
22
|
+
let sessionId = null;
|
|
23
|
+
const connections = new Map();
|
|
24
|
+
let server = null;
|
|
25
|
+
// Create HTTP server
|
|
26
|
+
server = createServer(async (req, res) => {
|
|
27
|
+
// CORS headers for local development
|
|
28
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
29
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS, GET");
|
|
30
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id");
|
|
31
|
+
if (req.method === "OPTIONS") {
|
|
32
|
+
res.writeHead(204);
|
|
33
|
+
res.end();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (req.method === "GET") {
|
|
37
|
+
// GET request is for SSE stream - return 204 to acknowledge
|
|
38
|
+
res.writeHead(204);
|
|
39
|
+
res.end();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (req.method !== "POST") {
|
|
43
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
44
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
// Parse the request body first - don't wait for session ID
|
|
49
|
+
const body = await readBody(req);
|
|
50
|
+
const message = JSON.parse(body);
|
|
51
|
+
// Get or create connection for this request
|
|
52
|
+
// For simplicity, we use a single connection per listener
|
|
53
|
+
// (in practice, MCP typically uses one connection per session)
|
|
54
|
+
let connection = connections.get("default");
|
|
55
|
+
if (!connection) {
|
|
56
|
+
const connectionId = randomUUID();
|
|
57
|
+
// Use a placeholder session ID - it will be set later
|
|
58
|
+
// The session ID is not needed for MCP protocol messages
|
|
59
|
+
connection = {
|
|
60
|
+
connectionId,
|
|
61
|
+
sessionId: sessionId ?? "pending",
|
|
62
|
+
pendingResponses: new Map(),
|
|
63
|
+
};
|
|
64
|
+
connections.set("default", connection);
|
|
65
|
+
// Notify conductor of new connection
|
|
66
|
+
onMessage({
|
|
67
|
+
type: "connection-received",
|
|
68
|
+
acpUrl,
|
|
69
|
+
sessionId: connection.sessionId,
|
|
70
|
+
connectionId,
|
|
71
|
+
send: (responseData) => {
|
|
72
|
+
// This is called when conductor sends a response back
|
|
73
|
+
// We need to route it to the right pending response
|
|
74
|
+
},
|
|
75
|
+
close: () => {
|
|
76
|
+
connections.delete("default");
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Handle the message
|
|
81
|
+
await handleMessage(connection, message, res, onMessage);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error("MCP bridge HTTP error:", error);
|
|
85
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
86
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Listen on an ephemeral port
|
|
90
|
+
await new Promise((resolve, reject) => {
|
|
91
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
92
|
+
server.on("error", reject);
|
|
93
|
+
});
|
|
94
|
+
const address = server.address();
|
|
95
|
+
if (!address || typeof address === "string") {
|
|
96
|
+
throw new Error("Failed to get server address");
|
|
97
|
+
}
|
|
98
|
+
const port = address.port;
|
|
99
|
+
return {
|
|
100
|
+
acpUrl,
|
|
101
|
+
port,
|
|
102
|
+
setSessionId(id) {
|
|
103
|
+
sessionId = id;
|
|
104
|
+
// Update any existing connections with the real session ID
|
|
105
|
+
for (const conn of connections.values()) {
|
|
106
|
+
conn.sessionId = id;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
async close() {
|
|
110
|
+
// Close all connections
|
|
111
|
+
for (const [key, conn] of connections) {
|
|
112
|
+
onMessage({
|
|
113
|
+
type: "connection-closed",
|
|
114
|
+
connectionId: conn.connectionId,
|
|
115
|
+
});
|
|
116
|
+
connections.delete(key);
|
|
117
|
+
}
|
|
118
|
+
// Close the server
|
|
119
|
+
if (server) {
|
|
120
|
+
await new Promise((resolve) => {
|
|
121
|
+
server.close(() => resolve());
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Handle an incoming MCP message
|
|
129
|
+
*/
|
|
130
|
+
async function handleMessage(connection, message, res, onMessage) {
|
|
131
|
+
if (isJsonRpcRequest(message)) {
|
|
132
|
+
// Use string key for the pending responses map
|
|
133
|
+
const idKey = String(message.id);
|
|
134
|
+
// Create a promise to wait for the response
|
|
135
|
+
const responsePromise = new Promise((resolve) => {
|
|
136
|
+
connection.pendingResponses.set(idKey, resolve);
|
|
137
|
+
});
|
|
138
|
+
// Create a dispatch with a responder that sends the response to HTTP
|
|
139
|
+
const dispatch = {
|
|
140
|
+
type: "request",
|
|
141
|
+
id: message.id,
|
|
142
|
+
method: message.method,
|
|
143
|
+
params: message.params,
|
|
144
|
+
responder: createResponder((result) => {
|
|
145
|
+
const resolver = connection.pendingResponses.get(idKey);
|
|
146
|
+
connection.pendingResponses.delete(idKey);
|
|
147
|
+
resolver?.({ jsonrpc: "2.0", id: message.id, result });
|
|
148
|
+
}, (error) => {
|
|
149
|
+
const resolver = connection.pendingResponses.get(idKey);
|
|
150
|
+
connection.pendingResponses.delete(idKey);
|
|
151
|
+
resolver?.({ jsonrpc: "2.0", id: message.id, error });
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
// Notify conductor of the request
|
|
155
|
+
onMessage({
|
|
156
|
+
type: "client-message",
|
|
157
|
+
connectionId: connection.connectionId,
|
|
158
|
+
dispatch,
|
|
159
|
+
});
|
|
160
|
+
// Wait for the response
|
|
161
|
+
const response = await responsePromise;
|
|
162
|
+
// Return JSON-RPC response directly
|
|
163
|
+
// Include Mcp-Session-Id header per MCP Streamable HTTP spec
|
|
164
|
+
res.writeHead(200, {
|
|
165
|
+
"Content-Type": "application/json",
|
|
166
|
+
"Mcp-Session-Id": connection.connectionId,
|
|
167
|
+
});
|
|
168
|
+
res.end(JSON.stringify(response));
|
|
169
|
+
}
|
|
170
|
+
else if (isJsonRpcNotification(message)) {
|
|
171
|
+
// Create a notification dispatch
|
|
172
|
+
const dispatch = {
|
|
173
|
+
type: "notification",
|
|
174
|
+
method: message.method,
|
|
175
|
+
params: message.params,
|
|
176
|
+
};
|
|
177
|
+
// Notify conductor
|
|
178
|
+
onMessage({
|
|
179
|
+
type: "client-message",
|
|
180
|
+
connectionId: connection.connectionId,
|
|
181
|
+
dispatch,
|
|
182
|
+
});
|
|
183
|
+
// Notifications don't have responses
|
|
184
|
+
res.writeHead(202);
|
|
185
|
+
res.end();
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Unknown message type
|
|
189
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
190
|
+
res.end(JSON.stringify({ error: "Invalid message type" }));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Read the request body as a string
|
|
195
|
+
*/
|
|
196
|
+
function readBody(req) {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const chunks = [];
|
|
199
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
200
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
201
|
+
req.on("error", reject);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=http-listener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-listener.js","sourceRoot":"","sources":["../../src/mcp-bridge/http-listener.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Bridge module
|
|
3
|
+
*
|
|
4
|
+
* Provides bridging between MCP-over-ACP and traditional HTTP-based MCP.
|
|
5
|
+
*/
|
|
6
|
+
export { McpBridge, type McpBridgeOptions } from "./mcp-bridge.js";
|
|
7
|
+
export { createHttpListener, type HttpListener, type HttpListenerOptions } from "./http-listener.js";
|
|
8
|
+
export type { McpServerSpec, TransformedMcpServer, PendingSession, McpBridgeListener, McpBridgeConnection, McpBridgeMessage, } from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp-bridge/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACrG,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp-bridge/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAA+C,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Bridge
|
|
3
|
+
*
|
|
4
|
+
* The MCP bridge manages HTTP listeners for `acp:` URLs and coordinates
|
|
5
|
+
* the transformation of MCP server configurations during session creation.
|
|
6
|
+
*
|
|
7
|
+
* Key responsibilities:
|
|
8
|
+
* 1. Transform `acp:$UUID` URLs to `http://localhost:$PORT` during session/new
|
|
9
|
+
* 2. Spawn HTTP listeners for each `acp:` URL
|
|
10
|
+
* 3. Route MCP messages between agents and proxies via `_mcp/*` messages
|
|
11
|
+
* 4. Manage connection lifecycle
|
|
12
|
+
*/
|
|
13
|
+
import type { Responder } from "@thinkwell/protocol";
|
|
14
|
+
import type { McpServerSpec } from "./types.js";
|
|
15
|
+
import type { MessageQueue } from "../message-queue.js";
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating an MCP bridge
|
|
18
|
+
*/
|
|
19
|
+
export interface McpBridgeOptions {
|
|
20
|
+
messageQueue: MessageQueue;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Active connection through the MCP bridge
|
|
24
|
+
*/
|
|
25
|
+
interface ActiveMcpConnection {
|
|
26
|
+
connectionId: string;
|
|
27
|
+
acpUrl: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
responder: Responder | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* MCP Bridge manager
|
|
33
|
+
*
|
|
34
|
+
* Manages HTTP listeners and connection lifecycle for MCP-over-ACP bridging.
|
|
35
|
+
*/
|
|
36
|
+
export declare class McpBridge {
|
|
37
|
+
private readonly messageQueue;
|
|
38
|
+
private listeners;
|
|
39
|
+
private connections;
|
|
40
|
+
private pendingSessions;
|
|
41
|
+
constructor(options: McpBridgeOptions);
|
|
42
|
+
/**
|
|
43
|
+
* Transform MCP servers in a session/new request
|
|
44
|
+
*
|
|
45
|
+
* For each `acp:` URL:
|
|
46
|
+
* 1. Spawn an HTTP listener on an ephemeral port
|
|
47
|
+
* 2. Replace the URL with `http://localhost:$PORT`
|
|
48
|
+
*
|
|
49
|
+
* Returns the transformed server list and a session key for later correlation.
|
|
50
|
+
*/
|
|
51
|
+
transformMcpServers(servers: McpServerSpec[] | undefined, sessionKey: string): Promise<{
|
|
52
|
+
transformedServers: McpServerSpec[] | undefined;
|
|
53
|
+
hasAcpServers: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Complete session creation after receiving the session ID from the agent
|
|
57
|
+
*
|
|
58
|
+
* This delivers the session ID to all pending listeners so they can
|
|
59
|
+
* correlate connections with the session.
|
|
60
|
+
*/
|
|
61
|
+
completeSession(sessionKey: string, sessionId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Cancel a pending session (e.g., on error)
|
|
64
|
+
*/
|
|
65
|
+
cancelSession(sessionKey: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Handle a message from an HTTP listener
|
|
68
|
+
*/
|
|
69
|
+
private handleBridgeMessage;
|
|
70
|
+
/**
|
|
71
|
+
* Get connection info by connection ID
|
|
72
|
+
*/
|
|
73
|
+
getConnection(connectionId: string): ActiveMcpConnection | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Close all listeners and connections
|
|
76
|
+
*/
|
|
77
|
+
close(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=mcp-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-bridge.d.ts","sourceRoot":"","sources":["../../src/mcp-bridge/mcp-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,KAAK,EAAE,aAAa,EAA0C,MAAM,YAAY,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,YAAY,CAAC;CAC5B;AAUD;;GAEG;AACH,UAAU,mBAAmB;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAG5C,OAAO,CAAC,SAAS,CAAmC;IAGpD,OAAO,CAAC,WAAW,CAA0C;IAG7D,OAAO,CAAC,eAAe,CAAyC;gBAEpD,OAAO,EAAE,gBAAgB;IAIrC;;;;;;;;OAQG;IACG,mBAAmB,CACvB,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EACpC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;QACT,kBAAkB,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC;QAChD,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IA8CF;;;;;OAKG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAW5D;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4C3B;;OAEG;IACH,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIpE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAe7B"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Bridge
|
|
3
|
+
*
|
|
4
|
+
* The MCP bridge manages HTTP listeners for `acp:` URLs and coordinates
|
|
5
|
+
* the transformation of MCP server configurations during session creation.
|
|
6
|
+
*
|
|
7
|
+
* Key responsibilities:
|
|
8
|
+
* 1. Transform `acp:$UUID` URLs to `http://localhost:$PORT` during session/new
|
|
9
|
+
* 2. Spawn HTTP listeners for each `acp:` URL
|
|
10
|
+
* 3. Route MCP messages between agents and proxies via `_mcp/*` messages
|
|
11
|
+
* 4. Manage connection lifecycle
|
|
12
|
+
*/
|
|
13
|
+
import { createHttpListener } from "./http-listener.js";
|
|
14
|
+
/**
|
|
15
|
+
* MCP Bridge manager
|
|
16
|
+
*
|
|
17
|
+
* Manages HTTP listeners and connection lifecycle for MCP-over-ACP bridging.
|
|
18
|
+
*/
|
|
19
|
+
export class McpBridge {
|
|
20
|
+
messageQueue;
|
|
21
|
+
// Maps acp:uuid → HttpListener
|
|
22
|
+
listeners = new Map();
|
|
23
|
+
// Maps connectionId → ActiveMcpConnection
|
|
24
|
+
connections = new Map();
|
|
25
|
+
// Pending sessions waiting for session ID
|
|
26
|
+
pendingSessions = new Map();
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.messageQueue = options.messageQueue;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Transform MCP servers in a session/new request
|
|
32
|
+
*
|
|
33
|
+
* For each `acp:` URL:
|
|
34
|
+
* 1. Spawn an HTTP listener on an ephemeral port
|
|
35
|
+
* 2. Replace the URL with `http://localhost:$PORT`
|
|
36
|
+
*
|
|
37
|
+
* Returns the transformed server list and a session key for later correlation.
|
|
38
|
+
*/
|
|
39
|
+
async transformMcpServers(servers, sessionKey) {
|
|
40
|
+
if (!servers || servers.length === 0) {
|
|
41
|
+
return { transformedServers: servers, hasAcpServers: false };
|
|
42
|
+
}
|
|
43
|
+
const pendingSession = {
|
|
44
|
+
listeners: [],
|
|
45
|
+
urlMap: new Map(),
|
|
46
|
+
};
|
|
47
|
+
const transformedServers = [];
|
|
48
|
+
let hasAcpServers = false;
|
|
49
|
+
for (const server of servers) {
|
|
50
|
+
if (server.url.startsWith("acp:")) {
|
|
51
|
+
hasAcpServers = true;
|
|
52
|
+
// Spawn HTTP listener for this acp: URL
|
|
53
|
+
const listener = await createHttpListener({
|
|
54
|
+
acpUrl: server.url,
|
|
55
|
+
onMessage: (msg) => this.handleBridgeMessage(msg),
|
|
56
|
+
});
|
|
57
|
+
this.listeners.set(server.url, listener);
|
|
58
|
+
pendingSession.listeners.push(listener);
|
|
59
|
+
pendingSession.urlMap.set(server.url, `http://127.0.0.1:${listener.port}`);
|
|
60
|
+
// Transform to HTTP URL
|
|
61
|
+
transformedServers.push({
|
|
62
|
+
...server,
|
|
63
|
+
url: `http://127.0.0.1:${listener.port}`,
|
|
64
|
+
type: "http",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Pass through non-acp servers unchanged
|
|
69
|
+
transformedServers.push(server);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (hasAcpServers) {
|
|
73
|
+
this.pendingSessions.set(sessionKey, pendingSession);
|
|
74
|
+
}
|
|
75
|
+
return { transformedServers, hasAcpServers };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Complete session creation after receiving the session ID from the agent
|
|
79
|
+
*
|
|
80
|
+
* This delivers the session ID to all pending listeners so they can
|
|
81
|
+
* correlate connections with the session.
|
|
82
|
+
*/
|
|
83
|
+
completeSession(sessionKey, sessionId) {
|
|
84
|
+
const pending = this.pendingSessions.get(sessionKey);
|
|
85
|
+
if (!pending)
|
|
86
|
+
return;
|
|
87
|
+
for (const listener of pending.listeners) {
|
|
88
|
+
listener.setSessionId(sessionId);
|
|
89
|
+
}
|
|
90
|
+
this.pendingSessions.delete(sessionKey);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Cancel a pending session (e.g., on error)
|
|
94
|
+
*/
|
|
95
|
+
async cancelSession(sessionKey) {
|
|
96
|
+
const pending = this.pendingSessions.get(sessionKey);
|
|
97
|
+
if (!pending)
|
|
98
|
+
return;
|
|
99
|
+
for (const listener of pending.listeners) {
|
|
100
|
+
await listener.close();
|
|
101
|
+
this.listeners.delete(listener.acpUrl);
|
|
102
|
+
}
|
|
103
|
+
this.pendingSessions.delete(sessionKey);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Handle a message from an HTTP listener
|
|
107
|
+
*/
|
|
108
|
+
handleBridgeMessage(msg) {
|
|
109
|
+
switch (msg.type) {
|
|
110
|
+
case "connection-received": {
|
|
111
|
+
// Track the new connection
|
|
112
|
+
this.connections.set(msg.connectionId, {
|
|
113
|
+
connectionId: msg.connectionId,
|
|
114
|
+
acpUrl: msg.acpUrl,
|
|
115
|
+
sessionId: msg.sessionId,
|
|
116
|
+
responder: null,
|
|
117
|
+
});
|
|
118
|
+
// Queue the connection notification for the conductor
|
|
119
|
+
this.messageQueue.push({
|
|
120
|
+
type: "mcp-connection-received",
|
|
121
|
+
acpUrl: msg.acpUrl,
|
|
122
|
+
connectionId: msg.connectionId,
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case "client-message": {
|
|
127
|
+
// Route MCP message to conductor
|
|
128
|
+
this.messageQueue.push({
|
|
129
|
+
type: "mcp-client-to-server",
|
|
130
|
+
connectionId: msg.connectionId,
|
|
131
|
+
dispatch: msg.dispatch,
|
|
132
|
+
});
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "connection-closed": {
|
|
136
|
+
// Clean up connection
|
|
137
|
+
this.connections.delete(msg.connectionId);
|
|
138
|
+
// Queue disconnect notification
|
|
139
|
+
this.messageQueue.push({
|
|
140
|
+
type: "mcp-connection-disconnected",
|
|
141
|
+
connectionId: msg.connectionId,
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get connection info by connection ID
|
|
149
|
+
*/
|
|
150
|
+
getConnection(connectionId) {
|
|
151
|
+
return this.connections.get(connectionId);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Close all listeners and connections
|
|
155
|
+
*/
|
|
156
|
+
async close() {
|
|
157
|
+
// Close all connections
|
|
158
|
+
this.connections.clear();
|
|
159
|
+
// Close all listeners
|
|
160
|
+
for (const listener of this.listeners.values()) {
|
|
161
|
+
await listener.close();
|
|
162
|
+
}
|
|
163
|
+
this.listeners.clear();
|
|
164
|
+
// Cancel pending sessions
|
|
165
|
+
for (const key of this.pendingSessions.keys()) {
|
|
166
|
+
await this.cancelSession(key);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=mcp-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-bridge.js","sourceRoot":"","sources":["../../src/mcp-bridge/mcp-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the MCP Bridge
|
|
3
|
+
*
|
|
4
|
+
* The MCP bridge enables agents without native MCP-over-ACP support
|
|
5
|
+
* to work with proxy components that provide MCP servers using ACP transport.
|
|
6
|
+
*/
|
|
7
|
+
import type { Dispatch } from "@thinkwell/protocol";
|
|
8
|
+
/**
|
|
9
|
+
* MCP server configuration that may use ACP transport
|
|
10
|
+
*/
|
|
11
|
+
export interface McpServerSpec {
|
|
12
|
+
name: string;
|
|
13
|
+
url: string;
|
|
14
|
+
type?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Transformed MCP server configuration with stdio transport
|
|
18
|
+
*/
|
|
19
|
+
export interface TransformedMcpServer {
|
|
20
|
+
name: string;
|
|
21
|
+
command: string;
|
|
22
|
+
args: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Pending session that's waiting for the session ID from the agent's response
|
|
26
|
+
*/
|
|
27
|
+
export interface PendingSession {
|
|
28
|
+
acpUrl: string;
|
|
29
|
+
sessionIdResolver: (sessionId: string) => void;
|
|
30
|
+
sessionIdRejector: (error: Error) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Active MCP bridge listener
|
|
34
|
+
*/
|
|
35
|
+
export interface McpBridgeListener {
|
|
36
|
+
acpUrl: string;
|
|
37
|
+
port: number;
|
|
38
|
+
sessionId: string | null;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Active MCP connection through the bridge
|
|
43
|
+
*/
|
|
44
|
+
export interface McpBridgeConnection {
|
|
45
|
+
connectionId: string;
|
|
46
|
+
acpUrl: string;
|
|
47
|
+
sessionId: string;
|
|
48
|
+
send(message: unknown): void;
|
|
49
|
+
close(): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Messages from the MCP bridge to the conductor
|
|
53
|
+
*/
|
|
54
|
+
export type McpBridgeMessage = {
|
|
55
|
+
type: "connection-received";
|
|
56
|
+
acpUrl: string;
|
|
57
|
+
sessionId: string;
|
|
58
|
+
connectionId: string;
|
|
59
|
+
send: (message: unknown) => void;
|
|
60
|
+
close: () => void;
|
|
61
|
+
} | {
|
|
62
|
+
type: "client-message";
|
|
63
|
+
connectionId: string;
|
|
64
|
+
dispatch: Dispatch;
|
|
65
|
+
} | {
|
|
66
|
+
type: "connection-closed";
|
|
67
|
+
connectionId: string;
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/mcp-bridge/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,iBAAiB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;CACpB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/mcp-bridge/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|