Middleware
Add authentication, logging, and other cross-cutting concerns to your MCP servers
ModelFetch provides middleware support built on top of Hono's middleware system, allowing you to add authentication, logging, rate limiting, and other cross-cutting concerns to your MCP servers.
Basic Usage
Using Middleware Array
The simplest way to add middleware is using the middleware
array in your config:
import handle from "@modelfetch/node";
import { bearerAuth } from "hono/bearer-auth";
import { logger } from "hono/logger";
handle({
server: myMcpServer,
middleware: [logger(), bearerAuth({ token: "secret-token" })],
});
Using Pre/Post Hooks
For more control, use the synchronous pre
and post
hooks to configure the Hono app:
handle({
server: myMcpServer,
pre: (app) => {
// Configure middleware before MCP routes
app.use("*", logger());
app.use("/mcp/*", bearerAuth({ token: "secret" }));
// Add error handling
app.onError((err, c) => {
console.error("Error:", err);
return c.json({ error: "Internal Server Error" }, 500);
});
},
post: (app) => {
// Add additional routes after MCP routes
app.get("/health", (c) => c.json({ status: "ok" }));
app.get("/", (c) => c.text("MCP Server"));
},
});
Common Middleware Patterns
Authentication
import { createMiddleware } from "hono/factory";
const apiKeyAuth = createMiddleware(async (c, next) => {
const apiKey = c.req.header("X-API-Key");
if (!apiKey || apiKey !== process.env.API_KEY) {
return c.json({ error: "Invalid API key" }, 401);
}
await next();
});
handle({
server: myMcpServer,
middleware: [apiKeyAuth],
});
Request Logging
const requestLogger = createMiddleware(async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${c.req.method} ${c.req.path} - ${c.res.status} ${ms}ms`);
});
CORS Configuration
import { cors } from "hono/cors";
handle({
server: myMcpServer,
pre: (app) => {
app.use(
"/mcp/*",
cors({
origin: ["https://app.example.com"],
allowHeaders: ["X-API-Key", "Content-Type"],
allowMethods: ["POST", "GET"],
credentials: true,
}),
);
},
});
Rate Limiting
import { rateLimiter } from "hono/rate-limiter";
handle({
server: myMcpServer,
pre: (app) => {
app.use(
"/mcp/*",
rateLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // 100 requests per window
keyGenerator: (c) => c.req.header("X-API-Key") || c.ip,
}),
);
},
});
Advanced Usage
Middleware with External Services
Note: Since hooks are synchronous, initialize external services before creating the server:
// Initialize external services first
const db = await connectToDatabase();
// Then create the server with middleware
handle({
server: myMcpServer,
pre: (app) => {
// Add database to context
app.use("*", async (c, next) => {
c.set("db", db);
await next();
});
// Authentication with database lookup
app.use("/mcp/*", async (c, next) => {
const token = c.req.header("Authorization")?.replace("Bearer ", "");
if (!token) {
return c.json({ error: "Missing token" }, 401);
}
const user = await db.users.findByToken(token);
if (!user) {
return c.json({ error: "Invalid token" }, 401);
}
c.set("user", user);
await next();
});
},
});
Combining Multiple Middleware Strategies
handle({
server: myMcpServer,
pre: (app) => {
// Global middleware
app.use("*", logger());
app.use("*", compress());
// MCP-specific middleware
app.use("/mcp/*", cors());
app.use("/mcp/*", bearerAuth({ token: process.env.API_TOKEN }));
// Custom error handling
app.onError((err, c) => {
console.error(err);
return c.json({ error: "Internal Server Error" }, 500);
});
},
post: (app) => {
// Health and metrics endpoints
app.get("/health", (c) => c.json({ status: "ok" }));
app.get("/metrics", (c) => c.text(getPrometheusMetrics()));
// Documentation endpoint
app.get("/docs", (c) => c.html(getApiDocs()));
},
});
Runtime Support
The middleware API is supported across all runtime packages.
Important Notes
-
Middleware Order: Middleware is executed in the order it's added. Authentication should typically come before other middleware.
-
Path Specificity: Use
/mcp/*
to apply middleware only to MCP routes, or*
for all routes. -
Context Extension: Use
c.set()
andc.get()
to pass data between middleware and handlers. -
Error Handling: Always include error handling middleware to catch and properly format errors.
-
Hooks are Synchronous: The
pre
andpost
hooks are synchronous. If you need to perform async initialization (like connecting to a database), do it before creating the server instance.