# MetaMCP Middleware System for Request/Response Processing: Architecture and Implementation Guide

> Explore the MetaMCP middleware system architecture. Learn how Next.js edge functions and an Express backend with a functional core enable composable request/response processing for JSON-RPC handlers.

- Repository: [metatool-ai/metamcp](https://github.com/metatool-ai/metamcp)
- Tags: architecture
- Published: 2026-03-07

---

**MetaMCP uses a layered middleware pipeline that combines Next.js edge functions for frontend routing with an Express-based backend system enhanced by a functional middleware core, enabling composable request/response transformations for MCP JSON-RPC handlers.**

MetaMCP implements a sophisticated **middleware architecture** to normalize, authenticate, rate-limit, and enrich every request before it reaches core handlers like List Tools and Call Tool. The system separates cross-cutting concerns from business logic through two distinct layers: a frontend edge middleware stack for internationalization and session validation, and a backend pipeline combining Express middleware with a functional composition framework. This design enables type-safe, reusable request/response transformers that maintain clean separation between transport concerns and MCP protocol handling.

## Next.js Edge Middleware: Frontend Request Processing

The frontend layer runs on Vercel Edge Runtime to handle **locale routing** and **session validation** before requests reach the backend API.

### Locale Detection and Routing

In [`apps/frontend/middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/frontend/middleware.ts), the system examines the pathname, `preferred-language` cookies, and the `Accept-Language` header to determine the user's locale via the `getLocale()` function. Static assets and API routes bypass processing via `NextResponse.next()`, while missing locale prefixes trigger redirects to `/<locale>/<original-path>` using `NextResponse.redirect()`.

### Session Validation

After locale resolution, the middleware validates user sessions by calling the internal `/api/auth/get-session` endpoint with original request cookies using `betterFetch`. Failed authentication redirects users to `/<locale>/login`, ensuring only authenticated traffic reaches the MetaMCP proxy endpoints.

## Backend Express Middleware Pipeline

The backend handles the **MetaMCP API** and **MCP proxy endpoints** (`/mcp-proxy/*`) through a sequential Express middleware chain defined in the router configuration.

### Endpoint Resolution and Authentication

The pipeline begins with three critical middlewares that attach security context to requests:

- **[`lookup-endpoint.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/lookup-endpoint.middleware.ts)** – Resolves the `:endpoint_name` URL parameter to a database record, attaching `namespaceUuid`, `endpointName`, and the full `endpoint` object to the request object for downstream handlers.

- **[`api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/api-key-oauth.middleware.ts)** – Extracts API keys or OAuth bearer tokens from request headers, validates credentials against the database, enforces per-endpoint access rules, and tracks failed authentication attempts for rate-limiting purposes.

- **[`better-auth-mcp.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/better-auth-mcp.middleware.ts)** – For internal proxy routes, forwards request cookies to the Better-Auth session endpoint, aborting with HTTP 401 or 500 status codes on validation failures.

### Rate Limiting and Access Control

The **[`rate-limit.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/rate-limit.middleware.ts)** implements dual limiting strategies based on endpoint configuration:

1. **Sliding-Window Limiter** – Returns HTTP 429 when `enable_client_max_rate` is exceeded
2. **Token-Bucket Limiter** – Returns HTTP 503 when `enable_max_rate` thresholds are breached

These middlewares execute in strict order before reaching the RPC handler:

```typescript
// Execution order in router configuration
lookupEndpoint → apiKeyOAuth → betterAuthMcp → rateLimitMiddleware → RPC handler

```

## Functional Middleware Core for Business Logic

Beyond Express middleware, MetaMCP implements a **functional middleware framework** in [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts) that wraps business-logic handlers with composable request/response transformers.

### Composable Handler Architecture

The core defines generic types (`ListToolsHandler`, `CallToolHandler`, `ListToolsMiddleware`, `CallToolMiddleware`) and two essential utilities:

- **`createFunctionalMiddleware()`** – Builds wrappers that can transform requests before the handler and/or responses after execution
- **`compose()`** – Chains multiple middleware wrappers into a single executable function

This approach keeps MCP protocol handlers as pure async functions accepting `(request, MetaMCPHandlerContext)` while allowing orthogonal concerns like caching, filtering, and transformations to wrap them cleanly.

### Creating Custom Middleware

Developers implement functional middleware by defining transformation functions:

```typescript
import { createFunctionalMiddleware } from "@/lib/metamcp/metamcp-middleware/functional-middleware";

export function createAuditLogMiddleware() {
  return createFunctionalMiddleware({
    transformRequest: async (req, ctx) => {
      console.log(`MetaMCP ${ctx.namespaceUuid} calling ${req.method} ${req.params.name}`);
      return req; // Pass through unchanged
    },
  });
}

```

Compose multiple middlewares using the `compose()` utility:

```typescript
import { compose } from "@/lib/metamcp/metamcp-middleware/functional-middleware";
import { createFilterListToolsMiddleware } from "@/lib/metamcp/metamcp-middleware/filter-tools.functional";
import { createToolOverridesListToolsMiddleware } from "@/lib/metamcp/metamcp-middleware/tool-overrides.functional";

// Pipeline: filter inactive tools, then apply namespace overrides
const listToolsPipeline = compose(
  createFilterListToolsMiddleware({ cacheEnabled: true }),
  createToolOverridesListToolsMiddleware({ cacheEnabled: true }),
);

export const listToolsHandler = listToolsPipeline(coreListToolsHandler);

```

## Domain-Specific Middleware Implementations

MetaMCP ships with specialized functional middlewares for tool management and access control.

### Tool Filtering and Access Control

The **[`filter-tools.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/filter-tools.functional.ts)** middleware removes tools marked **INACTIVE** in a namespace and blocks execution attempts against disabled tools. It utilizes a short-lived cache for tool-status lookups to minimize database queries during high-volume List Tools operations.

### Tool Overrides and Transformations

The **[`tool-overrides.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/tool-overrides.functional.ts)** middleware enables namespace-level customization of tool metadata:

- Renaming tools via string mapping
- Modifying titles and descriptions
- Injecting custom annotations
- Reverse-mapping overridden names back to original identifiers for Call Tool requests

This middleware optionally persists cache entries when List Tools requests serve as the source of truth, ensuring consistent behavior across distributed MCP clients.

## End-to-End Request Flow

A complete request traverses both middleware layers sequentially:

1. **Next.js Edge** – Locale detection and session validation
2. **Express Router** – Endpoint lookup (`lookupEndpoint`)
3. **Authentication** – API key/OAuth validation and Better-Auth session checks
4. **Rate Limiting** – Sliding-window and token-bucket enforcement
5. **Functional Pipeline** – `compose(filterTools, toolOverrides)` applied to core handler
6. **Response** – JSON-RPC response returned to client

This architecture ensures that cross-cutting concerns like authentication and rate limiting execute before business logic, while functional middleware provides granular control over MCP protocol data transformations.

## Extending the Middleware System

To add new Express middleware for custom routes, register handlers in the router configuration following the established pattern:

```typescript
// apps/backend/src/routers/custom-router.ts
router.use(
  "/mcp-proxy/:endpoint_name",
  lookupEndpoint,
  apiKeyOAuth,
  betterAuthMcp,
  rateLimitMiddleware,
  async (req, res) => {
    const { method, params } = req.body;
    const ctx = { 
      namespaceUuid: (req as any).namespaceUuid, 
      sessionId: req.session.id 
    };
    
    const result = await dispatchMethod(method, params, ctx);
    res.json(result);
  },
);

```

## Summary

- **MetaMCP's middleware system** combines Next.js edge functions for frontend concerns with Express middleware for backend API processing.
- **Express pipeline** executes in fixed order: endpoint lookup → API key/OAuth auth → Better-Auth session validation → rate limiting (sliding-window and token-bucket).
- **Functional middleware core** provides `createFunctionalMiddleware()` and `compose()` utilities for building reusable request/response transformers around pure MCP handlers.
- **Domain middlewares** like [`filter-tools.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/filter-tools.functional.ts) and [`tool-overrides.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/tool-overrides.functional.ts) enable namespace-specific tool filtering and metadata customization without modifying core handler logic.
- **Type-safe composition** allows developers to chain multiple concerns (logging, caching, filtering) while maintaining clean separation between transport and business logic.

## Frequently Asked Questions

### How does MetaMCP handle authentication in the middleware pipeline?

MetaMCP implements dual authentication strategies in the backend pipeline. The [`api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/api-key-oauth.middleware.ts) validates API keys and OAuth tokens for external clients, while [`better-auth-mcp.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/better-auth-mcp.middleware.ts) handles session cookies for internal proxy routes. Both middlewares attach authentication context to the request object before rate limiting and endpoint resolution occur.

### What is the difference between Express middleware and functional middleware in MetaMCP?

**Express middleware** handles transport-layer concerns like HTTP headers, cookies, rate limiting, and endpoint resolution in the `apps/backend/src/middleware/` directory. **Functional middleware** operates on MCP protocol data after Express processing, using [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts) utilities to transform JSON-RPC requests and responses around core business logic handlers like List Tools and Call Tool.

### How can I add custom request logging to MetaMCP handlers?

Create a functional middleware using `createFunctionalMiddleware()` with a `transformRequest` function that logs the `namespaceUuid` and request parameters, then compose it with existing middlewares using the `compose()` utility from [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts). This approach logs requests without modifying the core handler implementation or Express middleware stack.

### Where does rate limiting occur in the MetaMCP request lifecycle?

Rate limiting executes in [`rate-limit.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/rate-limit.middleware.ts) after authentication but before functional middleware processing. The system applies both sliding-window limits (HTTP 429) and token-bucket limits (HTTP 503) based on endpoint configuration flags `enable_client_max_rate` and `enable_max_rate`, protecting downstream MCP handlers from overload.