example-remote-server/src/index.ts
Jerome 8579d446aa
Add comprehensive logging to debug Redis transport issues
- Add detailed logging to Redis transport lifecycle
- Log session validation and message flow
- Track MCP server request handling
- Add diagnostics for streamable HTTP transport connections

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-30 20:29:41 +01:00

117 lines
4.1 KiB
TypeScript

import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
import { AuthRouterOptions, mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
import cors from "cors";
import express from "express";
import { EverythingAuthProvider } from "./auth/provider.js";
import { BASE_URI, PORT } from "./config.js";
import { authContext } from "./handlers/common.js";
import { handleFakeAuthorize, handleFakeAuthorizeRedirect } from "./handlers/fakeauth.js";
import { handleStreamableHTTP } from "./handlers/shttp.js";
import { handleMessage, handleSSEConnection } from "./handlers/sse.js";
import { redisClient } from "./redis.js";
const app = express();
// Base security middleware - applied to all routes
const baseSecurityHeaders = (req: express.Request, res: express.Response, next: express.NextFunction) => {
// Basic security headers
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
// Content Security Policy
const csp = [
"default-src 'self'",
"object-src 'none'", // Disable plugins
"frame-ancestors 'none'", // No embedding
"form-action 'self'", // Only allow forms to submit to our domain
"base-uri 'self'", // Restrict base tag
"upgrade-insecure-requests",
"block-all-mixed-content"
].join('; ');
res.setHeader('Content-Security-Policy', csp);
next();
};
// simple logging middleware
const logger = (req: express.Request, res: express.Response, next: express.NextFunction) => {
console.log(`${req.method} ${req.url}`);
next();
// Log the response status code
res.on('finish', () => {
console.log(`Response status code: ${res.statusCode}`);
});
};
// Sensitive data middleware - for routes with sensitive data
const sensitiveDataHeaders = (req: express.Request, res: express.Response, next: express.NextFunction) => {
res.setHeader('Cache-Control', 'no-store, max-age=0');
next();
};
// SSE middleware - specific for SSE endpoint
const sseHeaders = (req: express.Request, res: express.Response, next: express.NextFunction) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-store, max-age=0');
res.setHeader('Connection', 'keep-alive');
next();
};
// Configure CORS to allow any origin since this is a public API service
const corsOptions = {
origin: true, // Allow any origin
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization', "Mcp-Protocol-Version", "Mcp-Protocol-Id"],
exposedHeaders: ["Mcp-Protocol-Version", "Mcp-Protocol-Id"],
credentials: true
};
app.use(express.json());
app.use(logger);
// Apply base security headers to all routes
app.use(baseSecurityHeaders);
// Enable CORS pre-flight requests
app.options('*', cors(corsOptions));
// Auth configuration
const options: AuthRouterOptions = {
provider: new EverythingAuthProvider(),
issuerUrl: new URL(BASE_URI),
tokenOptions: {
rateLimit: {
windowMs: 5 * 1000,
limit: 100,
}
}
};
app.use(mcpAuthRouter(options));
const bearerAuth = requireBearerAuth(options);
// MCP routes (legacy SSE transport)
app.get("/sse", cors(corsOptions), bearerAuth, authContext, sseHeaders, handleSSEConnection);
app.post("/message", cors(corsOptions), bearerAuth, authContext, sensitiveDataHeaders, handleMessage);
// MCP routes (new streamable HTTP transport)
app.get("/mcp", cors(corsOptions), bearerAuth, authContext, handleStreamableHTTP);
app.post("/mcp", cors(corsOptions), bearerAuth, authContext, handleStreamableHTTP);
app.delete("/mcp", cors(corsOptions), bearerAuth, authContext, handleStreamableHTTP);
// Upstream auth routes
app.get("/fakeupstreamauth/authorize", cors(corsOptions), handleFakeAuthorize);
app.get("/fakeupstreamauth/callback", cors(corsOptions), handleFakeAuthorizeRedirect);
try {
await redisClient.connect();
} catch (error) {
console.error("Could not connect to Redis:", error);
process.exit(1);
}
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});