@chrysb/alphaclaw 0.9.0-beta.0 → 0.9.0-beta.1
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 +60 -50
- package/package.json +1 -1
package/lib/server/routes/mcp.js
CHANGED
|
@@ -26,17 +26,20 @@ const resolveGatewayWsUrl = ({ openclawDir, gatewayPort }) => {
|
|
|
26
26
|
return `${scheme}://127.0.0.1:${gatewayPort}`;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
const sessions = new Map();
|
|
29
30
|
let activeTransport = null;
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const closeSession = (sessionId) => {
|
|
33
|
+
const t = sessions.get(sessionId);
|
|
34
|
+
if (!t) return;
|
|
35
|
+
sessions.delete(sessionId);
|
|
36
|
+
if (activeTransport === t) activeTransport = null;
|
|
37
|
+
t.close().catch(() => {});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const closeAllSessions = () => {
|
|
41
|
+
for (const [id] of sessions) closeSession(id);
|
|
34
42
|
activeTransport = null;
|
|
35
|
-
try {
|
|
36
|
-
await t.close();
|
|
37
|
-
} catch {
|
|
38
|
-
/* already closed */
|
|
39
|
-
}
|
|
40
43
|
};
|
|
41
44
|
|
|
42
45
|
const registerMcpRoutes = ({
|
|
@@ -46,7 +49,6 @@ const registerMcpRoutes = ({
|
|
|
46
49
|
gatewayEnv,
|
|
47
50
|
openclawDir,
|
|
48
51
|
}) => {
|
|
49
|
-
// Wire bridge stdout messages → active transport
|
|
50
52
|
setOnMcpMessage((message) => {
|
|
51
53
|
if (!activeTransport) return;
|
|
52
54
|
activeTransport.send(message).catch((err) => {
|
|
@@ -86,7 +88,7 @@ const registerMcpRoutes = ({
|
|
|
86
88
|
});
|
|
87
89
|
|
|
88
90
|
app.post("/api/mcp/stop", requireAuth, async (_req, res) => {
|
|
89
|
-
|
|
91
|
+
closeAllSessions();
|
|
90
92
|
const result = stopMcpBridge();
|
|
91
93
|
res.json(result);
|
|
92
94
|
});
|
|
@@ -125,38 +127,52 @@ const registerMcpRoutes = ({
|
|
|
125
127
|
const sessionId = req.headers["mcp-session-id"];
|
|
126
128
|
|
|
127
129
|
// ── Existing session ───────────────────────────────────────
|
|
128
|
-
if (sessionId
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
if (sessionId) {
|
|
131
|
+
const transport = sessions.get(sessionId);
|
|
132
|
+
if (transport) {
|
|
133
|
+
console.log(
|
|
134
|
+
`[mcp] ${req.method} sessionId=${sessionId} → routed to transport (sessions=${sessions.size})`,
|
|
135
|
+
);
|
|
136
|
+
try {
|
|
137
|
+
await transport.handleRequest(req, res, req.body);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error(
|
|
140
|
+
"[mcp] handleRequest error (existing session):",
|
|
141
|
+
err?.message,
|
|
142
|
+
);
|
|
143
|
+
if (!res.headersSent) {
|
|
144
|
+
res.status(500).json({ error: "Internal transport error" });
|
|
145
|
+
}
|
|
135
146
|
}
|
|
147
|
+
} else {
|
|
148
|
+
console.log(
|
|
149
|
+
`[mcp] ${req.method} sessionId=${sessionId} → NOT FOUND (known=[${[...sessions.keys()].join(", ")}])`,
|
|
150
|
+
);
|
|
151
|
+
res.status(404).json({
|
|
152
|
+
jsonrpc: "2.0",
|
|
153
|
+
error: {
|
|
154
|
+
code: -32001,
|
|
155
|
+
message: "Session not found. The server may have been restarted.",
|
|
156
|
+
},
|
|
157
|
+
id: null,
|
|
158
|
+
});
|
|
136
159
|
}
|
|
137
160
|
return;
|
|
138
161
|
}
|
|
139
162
|
|
|
140
|
-
// ── Stale / unknown session ────────────────────────────────
|
|
141
|
-
if (sessionId && !activeTransport) {
|
|
142
|
-
res.status(404).json({
|
|
143
|
-
jsonrpc: "2.0",
|
|
144
|
-
error: {
|
|
145
|
-
code: -32000,
|
|
146
|
-
message: "Session not found. The server may have been restarted.",
|
|
147
|
-
},
|
|
148
|
-
id: null,
|
|
149
|
-
});
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
163
|
// ── New session (POST without session ID) ────────────────
|
|
154
|
-
|
|
155
|
-
// non-initialize requests and return the proper JSON-RPC error.
|
|
156
|
-
if (!sessionId && req.method === "POST") {
|
|
164
|
+
if (req.method === "POST") {
|
|
157
165
|
const transport = new StreamableHTTPServerTransport({
|
|
158
166
|
sessionIdGenerator: () => randomUUID(),
|
|
159
167
|
enableJsonResponse: true,
|
|
168
|
+
onsessioninitialized: (newSessionId) => {
|
|
169
|
+
closeAllSessions();
|
|
170
|
+
sessions.set(newSessionId, transport);
|
|
171
|
+
activeTransport = transport;
|
|
172
|
+
console.log(
|
|
173
|
+
`[mcp] Session registered: ${newSessionId} (sessions=${sessions.size})`,
|
|
174
|
+
);
|
|
175
|
+
},
|
|
160
176
|
});
|
|
161
177
|
|
|
162
178
|
transport.onmessage = (message) => {
|
|
@@ -164,10 +180,14 @@ const registerMcpRoutes = ({
|
|
|
164
180
|
};
|
|
165
181
|
|
|
166
182
|
transport.onclose = () => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
for (const [id, t] of sessions) {
|
|
184
|
+
if (t === transport) {
|
|
185
|
+
sessions.delete(id);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
170
188
|
}
|
|
189
|
+
if (activeTransport === transport) activeTransport = null;
|
|
190
|
+
console.log(`[mcp] Transport closed (sessions=${sessions.size})`);
|
|
171
191
|
};
|
|
172
192
|
|
|
173
193
|
transport.onerror = (err) => {
|
|
@@ -176,27 +196,17 @@ const registerMcpRoutes = ({
|
|
|
176
196
|
|
|
177
197
|
await transport.start();
|
|
178
198
|
|
|
179
|
-
// Set activeTransport BEFORE handleRequest so the onMcpMessage
|
|
180
|
-
// callback can forward the child's response to this transport.
|
|
181
|
-
const prev = activeTransport;
|
|
182
|
-
activeTransport = transport;
|
|
183
|
-
|
|
184
199
|
try {
|
|
185
200
|
await transport.handleRequest(req, res, req.body);
|
|
186
201
|
} catch (err) {
|
|
187
|
-
console.error(
|
|
202
|
+
console.error(
|
|
203
|
+
"[mcp] handleRequest error (new session):",
|
|
204
|
+
err?.message,
|
|
205
|
+
);
|
|
188
206
|
if (!res.headersSent) {
|
|
189
207
|
res.status(500).json({ error: "Failed to initialize MCP session" });
|
|
190
208
|
}
|
|
191
209
|
}
|
|
192
|
-
|
|
193
|
-
if (transport.sessionId) {
|
|
194
|
-
if (prev) prev.close().catch(() => {});
|
|
195
|
-
console.log(`[mcp] Session established: ${transport.sessionId}`);
|
|
196
|
-
} else {
|
|
197
|
-
// Session was not established — roll back
|
|
198
|
-
if (activeTransport === transport) activeTransport = prev;
|
|
199
|
-
}
|
|
200
210
|
return;
|
|
201
211
|
}
|
|
202
212
|
|