@chrysb/alphaclaw 0.9.0-beta.6 → 0.9.0-beta.7
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/lib/server/routes/mcp.js +72 -32
- package/package.json +1 -1
package/lib/server/routes/mcp.js
CHANGED
|
@@ -26,28 +26,57 @@ const resolveGatewayWsUrl = ({ openclawDir, gatewayPort }) => {
|
|
|
26
26
|
return `${scheme}://127.0.0.1:${gatewayPort}`;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
const sessions = new Map();
|
|
30
30
|
let activeTransport = null;
|
|
31
31
|
const kSseKeepAliveMs = 15_000;
|
|
32
|
+
const kMaxSessions = 8;
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const id = activeSessionId;
|
|
36
|
-
const t = activeTransport;
|
|
37
|
-
activeSessionId = null;
|
|
38
|
-
activeTransport = null;
|
|
39
|
-
t.close().catch(() => {});
|
|
40
|
-
if (id) console.log(`[mcp] Closed session: ${id}`);
|
|
41
|
-
};
|
|
34
|
+
let nextBridgeId = 1;
|
|
35
|
+
const pendingRequests = new Map();
|
|
42
36
|
|
|
43
37
|
const adoptSession = (sessionId, transport) => {
|
|
44
|
-
|
|
45
|
-
const prevId = activeSessionId;
|
|
46
|
-
activeTransport.close().catch(() => {});
|
|
47
|
-
console.log(`[mcp] Replaced session ${prevId} → ${sessionId}`);
|
|
48
|
-
}
|
|
49
|
-
activeSessionId = sessionId;
|
|
38
|
+
sessions.set(sessionId, transport);
|
|
50
39
|
activeTransport = transport;
|
|
40
|
+
|
|
41
|
+
if (sessions.size > kMaxSessions) {
|
|
42
|
+
const oldestId = sessions.keys().next().value;
|
|
43
|
+
if (oldestId !== sessionId) {
|
|
44
|
+
const old = sessions.get(oldestId);
|
|
45
|
+
sessions.delete(oldestId);
|
|
46
|
+
old.close().catch(() => {});
|
|
47
|
+
console.log(`[mcp] Evicted oldest session: ${oldestId}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const forwardToBridge = (message, transport) => {
|
|
53
|
+
if (message.id != null) {
|
|
54
|
+
const bridgeId = nextBridgeId++;
|
|
55
|
+
pendingRequests.set(bridgeId, { originalId: message.id, transport });
|
|
56
|
+
writeToMcpBridge({ ...message, id: bridgeId });
|
|
57
|
+
} else {
|
|
58
|
+
writeToMcpBridge(message);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const cleanupTransport = (transport) => {
|
|
63
|
+
for (const [id, t] of sessions) {
|
|
64
|
+
if (t === transport) {
|
|
65
|
+
sessions.delete(id);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const [bridgeId, pending] of pendingRequests) {
|
|
70
|
+
if (pending.transport === transport) pendingRequests.delete(bridgeId);
|
|
71
|
+
}
|
|
72
|
+
if (activeTransport === transport) activeTransport = null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const closeAllSessions = () => {
|
|
76
|
+
for (const [, t] of sessions) t.close().catch(() => {});
|
|
77
|
+
sessions.clear();
|
|
78
|
+
pendingRequests.clear();
|
|
79
|
+
activeTransport = null;
|
|
51
80
|
};
|
|
52
81
|
|
|
53
82
|
const registerMcpRoutes = ({
|
|
@@ -58,10 +87,21 @@ const registerMcpRoutes = ({
|
|
|
58
87
|
openclawDir,
|
|
59
88
|
}) => {
|
|
60
89
|
setOnMcpMessage((message) => {
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
90
|
+
if (message.id != null) {
|
|
91
|
+
const pending = pendingRequests.get(message.id);
|
|
92
|
+
if (pending) {
|
|
93
|
+
pendingRequests.delete(message.id);
|
|
94
|
+
pending.transport
|
|
95
|
+
.send({ ...message, id: pending.originalId })
|
|
96
|
+
.catch((err) => {
|
|
97
|
+
console.error("[mcp] Failed to forward response:", err?.message);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (activeTransport) {
|
|
103
|
+
activeTransport.send(message).catch(() => {});
|
|
104
|
+
}
|
|
65
105
|
});
|
|
66
106
|
|
|
67
107
|
// ── Internal API (session auth) ────────────────────────────────
|
|
@@ -96,7 +136,7 @@ const registerMcpRoutes = ({
|
|
|
96
136
|
});
|
|
97
137
|
|
|
98
138
|
app.post("/api/mcp/stop", requireAuth, async (_req, res) => {
|
|
99
|
-
|
|
139
|
+
closeAllSessions();
|
|
100
140
|
const result = stopMcpBridge();
|
|
101
141
|
res.json(result);
|
|
102
142
|
});
|
|
@@ -146,12 +186,13 @@ const registerMcpRoutes = ({
|
|
|
146
186
|
|
|
147
187
|
// ── Existing session ───────────────────────────────────────
|
|
148
188
|
if (sessionId) {
|
|
149
|
-
|
|
189
|
+
const transport = sessions.get(sessionId);
|
|
190
|
+
if (transport) {
|
|
150
191
|
console.log(
|
|
151
|
-
`[mcp] ${req.method} sessionId=${sessionId} →
|
|
192
|
+
`[mcp] ${req.method} sessionId=${sessionId} → routed (sessions=${sessions.size})`,
|
|
152
193
|
);
|
|
153
194
|
try {
|
|
154
|
-
await
|
|
195
|
+
await transport.handleRequest(req, res, req.body);
|
|
155
196
|
} catch (err) {
|
|
156
197
|
console.error(
|
|
157
198
|
"[mcp] handleRequest error (existing session):",
|
|
@@ -163,7 +204,7 @@ const registerMcpRoutes = ({
|
|
|
163
204
|
}
|
|
164
205
|
} else {
|
|
165
206
|
console.log(
|
|
166
|
-
`[mcp] ${req.method} sessionId=${sessionId} → NOT FOUND (
|
|
207
|
+
`[mcp] ${req.method} sessionId=${sessionId} → NOT FOUND (sessions=${sessions.size})`,
|
|
167
208
|
);
|
|
168
209
|
res.status(404).json({
|
|
169
210
|
jsonrpc: "2.0",
|
|
@@ -184,20 +225,19 @@ const registerMcpRoutes = ({
|
|
|
184
225
|
enableJsonResponse: true,
|
|
185
226
|
onsessioninitialized: (newSessionId) => {
|
|
186
227
|
adoptSession(newSessionId, transport);
|
|
187
|
-
console.log(
|
|
228
|
+
console.log(
|
|
229
|
+
`[mcp] Session adopted: ${newSessionId} (sessions=${sessions.size})`,
|
|
230
|
+
);
|
|
188
231
|
},
|
|
189
232
|
});
|
|
190
233
|
|
|
191
234
|
transport.onmessage = (message) => {
|
|
192
|
-
|
|
235
|
+
forwardToBridge(message, transport);
|
|
193
236
|
};
|
|
194
237
|
|
|
195
238
|
transport.onclose = () => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
activeTransport = null;
|
|
199
|
-
}
|
|
200
|
-
console.log(`[mcp] Transport closed`);
|
|
239
|
+
cleanupTransport(transport);
|
|
240
|
+
console.log(`[mcp] Transport closed (sessions=${sessions.size})`);
|
|
201
241
|
};
|
|
202
242
|
|
|
203
243
|
transport.onerror = (err) => {
|