inspector
inspector copied to clipboard
working bearer tokens can still break or silently fail over SSE
I have written an MCP server that works locally and correctly with STDIO, but when trying to use MCP with a bearer token, the token will work, but the app will silently fail.
Query parameters: { transportType: 'sse', url: 'http://localhost:3001/sse' }
SSE transport: url=http://localhost:3001/sse, headers=Accept,authorization
New SSE connection
Query parameters: { transportType: 'sse', url: 'http://localhost:3001/sse' }
SSE transport: url=http://localhost:3001/sse, headers=Accept,authorization
New SSE connection
Query parameters: { transportType: 'sse', url: 'http://localhost:3001/sse' }
SSE transport: url=http://localhost:3001/sse, headers=Accept,authorization
I could click connect 10 times and not see an outout on this.
relevant example of my code;
const server = new McpServer({
name: "HorseRacing",
version: "0.1.0"
});
// --- Register Resources, Tools, and Prompts ---
registerResources(server, db, debugLog, errorLogger);
registerToolsAndPrompts(server, db, debugLog, errorLogger);
// --- Server Setup (Conditional) ---
let httpServer: http.Server | null = null;
async function runServer() {
if (transportMode === 'sse') {
// --- Start Server with Express and SSE ---
infoLogger("Starting HorseRacing MCP server via HTTP/SSE..."); // Use infoLogger
const app = express();
const port = 3001;
httpServer = http.createServer(app);
const transports: { [sessionId: string]: SSEServerTransport } = {};
app.get("/sse", async (req: Request, res: Response) => {
// --- Authentication Check (Keep for SSE mode) ---
const authHeader = req.headers.authorization;
let isAuthenticated = false;
if (authHeader && authHeader.startsWith("Bearer ")) {
const token = authHeader.substring(7);
if (token === EXPECTED_BEARER_TOKEN) {
isAuthenticated = true;
}
}
if (!isAuthenticated) {
debugLog(`Authentication failed for ${req.ip}. Invalid or missing token.`); // Use debugLog
res.writeHead(401, { 'Content-Type': 'text/plain' });
res.end('Unauthorized');
return;
}
// --- End Authentication Check ---
debugLog(`Authenticated SSE connection request received from ${req.ip}`); // Use debugLog
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
debugLog(`Transport created with session ID: ${transport.sessionId}`); // Use debugLog
res.on("close", () => {
debugLog(`SSE connection closed for session ID: ${transport.sessionId}`); // Use debugLog
delete transports[transport.sessionId];
});
try {
await server.connect(transport);
debugLog(`MCP Server connected to transport ${transport.sessionId}`); // Use debugLog
} catch (error) {
errorLogger(`Failed to connect MCP server to transport ${transport.sessionId}:`, error);
if (!res.writableEnded) {
res.status(500).send("Failed to establish MCP connection");
}
delete transports[transport.sessionId];
}
});
app.post("/messages", express.json(), async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
debugLog(`>>> [POST /messages] Received request for session ID: ${sessionId}`); // Use debugLog
if (transport) {
try {
const requestBody = JSON.stringify(req.body, null, 2);
debugLog(`... [POST /messages] Request Body for session ${sessionId}: ${requestBody}`); // Use debugLog
debugLog(`... [POST /messages] Calling transport.handlePostMessage for session ${sessionId}...`); // Use debugLog
await transport.handlePostMessage(req, res);
debugLog(`<<< [POST /messages] Successfully handled POST for session ID: ${sessionId}`); // Use debugLog
} catch (error) {
errorLogger(`!!! [POST /messages] Error handling POST for session ID ${sessionId}:`, error);
if (!res.headersSent) {
res.status(500).send('Error processing message');
}
}
} else {
debugLog(`No active transport found for session ID: ${sessionId}`); // Use debugLog (was warn)
res.status(400).send('No transport found for sessionId');
}
});
httpServer.listen(port, () => {
infoLogger(`HorseRacing MCP server running via HTTP/SSE`); // Use infoLogger
infoLogger(` -> SSE Endpoint: http://localhost:${port}/sse`); // Use infoLogger
infoLogger(` -> MSG Endpoint: http://localhost:${port}/messages`); // Use infoLogger
});
} else {
// --- Start Server with Stdio (Default) ---
infoLogger("Starting HorseRacing MCP server via stdio..."); // Use infoLogger (goes to stderr in stdio mode)
const transport = new StdioServerTransport();
await server.connect(transport);
infoLogger("HorseRacing MCP server connected via stdio."); // Use infoLogger (goes to stderr in stdio mode)
}
}
// --- Graceful Shutdown Handling ---
let isShuttingDown = false;
const shutdown = (signal: string) => {
if (isShuttingDown) return;
isShuttingDown = true;
infoLogger(`\nReceived ${signal}. Shutting down server...`); // Use infoLogger
const closeHttpServer = (callback: () => void) => {
if (httpServer) {
infoLogger("Closing HTTP server..."); // Use infoLogger
httpServer.close(() => {
infoLogger("HTTP server closed."); // Use infoLogger
callback();
});
setTimeout(() => {
infoLogger("Could not close HTTP connections gracefully, forcefully shutting down"); // Use infoLogger
callback();
}, 5000);
} else {
callback();
}
};
closeHttpServer(() => {
try {
db.close();
infoLogger("Database connection closed."); // Use infoLogger
} catch (error) {
errorLogger("Error closing database during shutdown:", error);
}
process.exit(0);
});
};
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('exit', (code) => {
infoLogger(`Server process exited with code ${code}`); // Use infoLogger
try {
if (typeof db?.close === 'function') {
db.close();
infoLogger("Database connection closed on process exit."); // Use infoLogger
}
} catch (error) {
// Ignore
}
});
// --- Run the Server ---
runServer().catch(error => {
errorLogger("Failed to start server:", error);
try {
if (typeof db?.close === 'function') {
db.close();
}
} catch (dbError) {
errorLogger("Error closing database after startup error:", dbError);
}
process.exit(1);
});
Does your MCP server log any errors in this scenario, that might not be showing up in Inspector itself?
Closing this but feel free to re-open if its still a problem, I think we might need a little more info to try and repro this.