Skip to content

Commit 3de00cc

Browse files
committed
Add multi-auth support to SSE server (Bearer, X-API-Key, query param)
1 parent 073a0ab commit 3de00cc

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

mcp-sse-server/server.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -246,38 +246,47 @@ app.get('/health', (_req, res) => res.json({ status: 'healthy', mcp: 'sse', time
246246
// In-memory session store: sessionId -> { transport, server, res, heartbeat }
247247
const sessions = new Map();
248248

249-
// Helper: validate and extract Bearer token
250-
function getBearerToken(req) {
249+
// Helper: validate and extract token from multiple sources
250+
function getAuthToken(req) {
251251
const auth = req.headers['authorization'] || '';
252252
const m = auth.match(/^Bearer\s+(.+)$/i);
253-
return m ? m[1] : undefined;
253+
return (
254+
(m ? m[1] : undefined) ||
255+
req.headers['x-api-key'] ||
256+
req.query.api_key ||
257+
process.env.AUTOMEM_API_TOKEN
258+
);
254259
}
255260

256261
// SSE endpoint
257262
app.get('/mcp/sse', async (req, res) => {
258263
try {
259264
const endpoint = process.env.AUTOMEM_ENDPOINT || 'http://127.0.0.1:8001';
260-
const token = getBearerToken(req);
265+
const token = getAuthToken(req);
261266
if (!endpoint) return res.status(500).json({ error: 'AUTOMEM_ENDPOINT not configured' });
262-
if (!token) return res.status(401).json({ error: 'Missing Authorization Bearer token' });
267+
if (!token) return res.status(401).json({ error: 'Missing API token (use Authorization: Bearer, X-API-Key, or ?api_key=)' });
263268

264269
const client = new AutoMemClient({ endpoint, apiKey: token });
265270
const server = buildMcpServer(client);
271+
// Help with proxy buffering before SSE headers are written
272+
res.set('X-Accel-Buffering', 'no');
273+
res.set('Cache-Control', 'no-cache, no-transform');
266274
const transport = new SSEServerTransport('/mcp/messages', res);
267275
await server.connect(transport);
268-
await transport.start();
269276

270-
// Heartbeat to keep proxies from closing idle streams
277+
// Prepare session and lifecycle BEFORE sending the endpoint event to avoid race
271278
const heartbeat = setInterval(() => {
272279
try { res.write(': ping\n\n'); } catch (_) { /* ignore */ }
273280
}, 20000);
274-
275281
res.on('close', () => {
276282
clearInterval(heartbeat);
277283
sessions.delete(transport.sessionId);
278284
});
279-
280285
sessions.set(transport.sessionId, { transport, server, res, heartbeat });
286+
console.log(`[MCP] New SSE session established: ${transport.sessionId}`);
287+
288+
// Now start SSE (writes event: endpoint)
289+
await transport.start();
281290
} catch (e) {
282291
try { res.status(500).json({ error: String(e) }); } catch (_) { /* ignore */ }
283292
}
@@ -288,10 +297,14 @@ app.post('/mcp/messages', async (req, res) => {
288297
const sessionId = req.query.sessionId;
289298
if (!sessionId || typeof sessionId !== 'string') return res.status(400).send('Missing sessionId');
290299
const s = sessions.get(sessionId);
291-
if (!s) return res.status(404).send('Session not found');
300+
if (!s) {
301+
console.warn(`[MCP] POST for unknown session: ${sessionId}`);
302+
return res.status(404).send('Session not found');
303+
}
292304
try {
293305
await s.transport.handlePostMessage(req, res, req.body);
294306
} catch (e) {
307+
console.error(`[MCP] Error handling message for session ${sessionId}:`, e);
295308
try { res.status(400).send(String(e)); } catch (_) { /* ignore */ }
296309
}
297310
});
@@ -300,4 +313,3 @@ const port = process.env.PORT || 8080;
300313
app.listen(port, () => {
301314
console.log(`AutoMem MCP SSE server listening on :${port}`);
302315
});
303-

0 commit comments

Comments
 (0)