@ateam-ai/mcp 0.3.16 → 0.3.17

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/http.js +61 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "mcpName": "io.github.ariekogan/ateam-mcp",
5
5
  "description": "A-Team MCP Server — build, validate, and deploy multi-agent solutions from any AI environment",
6
6
  "type": "module",
package/src/http.js CHANGED
@@ -205,11 +205,16 @@ export function startHttpServer(port = 3100) {
205
205
  // Reuse existing session — seed credentials if Bearer token present
206
206
  transport = transports[sessionId];
207
207
  seedCredentials(req, sessionId);
208
- } else if (isInitializeRequest(req.body)) {
209
- // New session (or stale session after server restart) create fresh session.
210
- // Accept initialize requests even if they carry a stale mcp-session-id.
211
- if (sessionId) {
212
- console.log(`[HTTP] Stale session ${sessionId} creating new session (server was restarted)`);
208
+ } else if (isInitializeRequest(req.body) || (sessionId && !transports[sessionId])) {
209
+ // New session, OR stale session with any request type (server restart recovery).
210
+ // Many MCP clients (Claude mobile, Claude Code) cache the session ID and fail to
211
+ // re-initialize on 400. To survive container restarts transparently, we synthesize
212
+ // a fresh initialize under the hood whenever we see a stale session.
213
+ const isStaleRecovery = sessionId && !transports[sessionId] && !isInitializeRequest(req.body);
214
+ if (sessionId && isInitializeRequest(req.body)) {
215
+ console.log(`[HTTP] Stale session ${sessionId} — client re-initialized`);
216
+ } else if (isStaleRecovery) {
217
+ console.log(`[HTTP] Stale session ${sessionId} — auto-reinitializing transparently (${req.body?.method || "unknown"})`);
213
218
  }
214
219
 
215
220
  const newSessionId = randomUUID();
@@ -235,18 +240,59 @@ export function startHttpServer(port = 3100) {
235
240
 
236
241
  const server = createServer(newSessionId);
237
242
  await server.connect(transport);
243
+
244
+ if (isStaleRecovery) {
245
+ // Synthesize an initialize handshake so the SDK transport is in "ready" state
246
+ // before we dispatch the real request. We call handleRequest twice: once with a
247
+ // fake initialize (response is discarded), then with the real payload.
248
+ const fakeInit = {
249
+ jsonrpc: "2.0",
250
+ id: `__auto_init_${newSessionId}`,
251
+ method: "initialize",
252
+ params: {
253
+ protocolVersion: "2025-03-26",
254
+ capabilities: {},
255
+ clientInfo: { name: "auto-reinit", version: "1.0" },
256
+ },
257
+ };
258
+ // Mock response object that swallows the init response
259
+ const mockRes = {
260
+ _h: {},
261
+ statusCode: 200,
262
+ headersSent: false,
263
+ setHeader(k, v) { this._h[k.toLowerCase()] = v; },
264
+ getHeader(k) { return this._h[k.toLowerCase()]; },
265
+ removeHeader(k) { delete this._h[k.toLowerCase()]; },
266
+ writeHead() { return this; },
267
+ write() { return true; },
268
+ end() { this.headersSent = true; return this; },
269
+ json() { return this; },
270
+ status() { return this; },
271
+ on() {},
272
+ once() {},
273
+ emit() {},
274
+ };
275
+ // Strip the stale session-id header so the SDK treats this as a new init
276
+ const initReq = { ...req, headers: { ...req.headers } };
277
+ delete initReq.headers["mcp-session-id"];
278
+ try {
279
+ await transport.handleRequest(initReq, mockRes, fakeInit);
280
+ } catch (e) {
281
+ console.error("[HTTP] Auto-reinit synthetic initialize failed:", e.message);
282
+ }
283
+ // Ensure the transport is registered under the new id and update the real request
284
+ transports[newSessionId] = transport;
285
+ // Overwrite the client-supplied (stale) session-id with the new one so the SDK
286
+ // routes the real request to the freshly-initialized transport.
287
+ req.headers["mcp-session-id"] = newSessionId;
288
+ // Tell the client about the new session id so future requests use it.
289
+ res.setHeader("mcp-session-id", newSessionId);
290
+ await transport.handleRequest(req, res, req.body);
291
+ return;
292
+ }
293
+
238
294
  await transport.handleRequest(req, res, req.body);
239
295
  return;
240
- } else if (sessionId && !transports[sessionId]) {
241
- // Stale session (non-initialize request) — tell client to re-initialize.
242
- // This happens after server restarts when the client still has the old session ID.
243
- console.log(`[HTTP] Stale session ${sessionId} — returning 400 to trigger re-init`);
244
- res.status(400).json({
245
- jsonrpc: "2.0",
246
- error: { code: -32600, message: "Session expired. Please re-initialize." },
247
- id: req.body?.id || null,
248
- });
249
- return;
250
296
  } else {
251
297
  res.status(400).json({
252
298
  jsonrpc: "2.0",