Add multi-account support and CORS/logging middleware

- Add garfield, sales, leads, founder accounts to IMAP and SMTP configs
- Refactor fetcherpay config into shared helper functions
- Add CORS middleware with wildcard origin
- Add request logging middleware and MCP session lifecycle logs
- Include package-lock.json and add @types/cors dependency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
garfieldheron
2026-04-14 08:45:45 -04:00
parent 9ecb02785c
commit 166f5d55a6
6 changed files with 2670 additions and 66 deletions

View File

@@ -1,5 +1,6 @@
import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -11,8 +12,24 @@ import {
import { tools, handleToolCall } from './tools.js';
const app = express();
app.use(cors({
origin: '*',
methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'mcp-session-id', 'Accept'],
credentials: true
}));
app.use(express.json());
// Request logging middleware
app.use((req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.path} - ${req.headers['user-agent'] || 'no-ua'}`);
if (req.body && Object.keys(req.body).length > 0) {
console.log(` Body: ${JSON.stringify(req.body).substring(0, 500)}`);
}
next();
});
function createMcpServer() {
const server = new Server(
{ name: 'hermes', version: '1.0.0' },
@@ -33,11 +50,13 @@ const httpTransports = new Map<string, StreamableHTTPServerTransport>();
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
console.log(`[mcp] POST sessionId=${sessionId ?? 'none'}, isInit=${isInitializeRequest(req.body)}`);
let transport: StreamableHTTPServerTransport;
if (sessionId && httpTransports.has(sessionId)) {
// Known active session — reuse it
console.log(`[mcp] Reusing existing session ${sessionId}`);
transport = httpTransports.get(sessionId)!;
} else if (isInitializeRequest(req.body)) {
// Initialize request: create a new session.
@@ -46,15 +65,23 @@ app.post('/mcp', async (req, res) => {
if (sessionId) {
console.warn(`[mcp] Stale session ${sessionId} re-initializing — pod may have restarted`);
}
console.log(`[mcp] Creating new session`);
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
onsessioninitialized: (id) => { httpTransports.set(id, transport); },
onsessioninitialized: (id) => {
console.log(`[mcp] Session initialized: ${id}`);
httpTransports.set(id, transport);
},
});
transport.onclose = () => {
if (transport.sessionId) httpTransports.delete(transport.sessionId);
if (transport.sessionId) {
console.log(`[mcp] Session closed: ${transport.sessionId}`);
httpTransports.delete(transport.sessionId);
}
};
const server = createMcpServer();
await server.connect(transport);
console.log(`[mcp] Server connected to transport`);
} else {
// Unknown session + non-initialize request: session expired (e.g. pod restarted).
// Return 404 so MCP clients know to re-initialize rather than keep retrying.
@@ -63,7 +90,13 @@ app.post('/mcp', async (req, res) => {
return;
}
await transport.handleRequest(req, res, req.body);
try {
await transport.handleRequest(req, res, req.body);
console.log(`[mcp] Request handled successfully`);
} catch (err) {
console.error(`[mcp] Error handling request:`, err);
throw err;
}
});
app.get('/mcp', async (req, res) => {