# How to Create Custom Middleware in MetaMCP to Intercept and Transform MCP Requests

> Learn how to create custom middleware in MetaMCP. Intercept and transform MCP requests by implementing higher-order functions and composing them into your request pipeline. Enhance your tooling workflows.

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

---

**Developers create custom middleware in MetaMCP by implementing higher-order functions that wrap `ListToolsHandler` or `CallToolHandler`, then composing them into the request pipeline using the `compose` utility from [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts).**

The MetaMCP repository (`metatool-ai/metamcp`) provides a lightweight, functional middleware architecture that enables developers to intercept, inspect, and modify Model Context Protocol (MCP) requests and responses without altering core business logic. This system leverages higher-order functions to create composable transformation layers for both tool discovery and tool execution operations.

## Understanding the MetaMCP Middleware Architecture

MetaMCP processes **List‑Tools** and **Call‑Tool** operations through a functional pipeline built on two core abstractions:

- **`ListToolsMiddleware`** – A higher‑order function that receives a `ListToolsHandler` and returns a new `ListToolsHandler`.
- **`CallToolMiddleware`** – A higher‑order function that receives a `CallToolHandler` and returns a new `CallToolHandler`.

The **`compose`** helper, defined in [`apps/backend/src/lib/metamcp/metamcp-middleware/functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/metamcp/metamcp-middleware/functional-middleware.ts), reduces an array of middleware functions right‑to‑left, stitching them into a single wrapped handler. When a request arrives, MetaMCP creates the original handlers (`createOriginalListToolsHandler`, `createOriginalCallToolHandler`) and then applies the middleware stack in [`apps/backend/src/routers/public-metamcp/openapi/handlers.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/openapi/handlers.ts).

## Creating a Custom ListTools Middleware

To intercept tool discovery requests, implement the `ListToolsMiddleware` type and inject your logic into the handler chain.

### Implementing Request/Response Logging

The following example demonstrates a logging middleware that records incoming requests and outgoing responses for the List‑Tools operation:

```typescript
// apps/backend/src/lib/metamcp/metamcp-middleware/logging.middleware.ts
import {
  ListToolsHandler,
  ListToolsMiddleware,
} from "./functional-middleware";

export const createLoggingMiddleware = (): ListToolsMiddleware => {
  return (handler: ListToolsHandler) => {
    return async (request, context) => {
      console.log(
        `[MetaMCP] ListTools request → namespace=${context.namespaceUuid}`,
        request,
      );

      const response = await handler(request, context);

      console.log(
        `[MetaMCP] ListTools response ← tools=${response.tools?.length ?? 0}`,
        response,
      );

      return response;
    };
  };
};

```

### Registering Your Middleware in the Pipeline

Add your custom middleware to the `compose` chain in [`apps/backend/src/routers/public-metamcp/openapi/handlers.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/openapi/handlers.ts):

```typescript
import { createLoggingMiddleware } from "@/lib/metamcp/metamcp-middleware/logging.middleware";

const listToolsWithMiddleware = compose(
  createToolOverridesListToolsMiddleware({ cacheEnabled: true }),
  createFilterListToolsMiddleware({ cacheEnabled: true }),
  createLoggingMiddleware(),               // ← custom middleware
)(originalListToolsHandler);

```

## Creating a Custom CallTool Middleware

For tool execution operations, implement `CallToolMiddleware` to transform arguments, validate inputs, or modify responses before they reach the client.

### Transforming Tool Arguments and Responses

This example injects default arguments and annotates the response to indicate transformation occurred:

```typescript
// apps/backend/src/lib/metamcp/metamcp-middleware/arg-transform.middleware.ts
import {
  CallToolHandler,
  CallToolMiddleware,
} from "./functional-middleware";

export const createArgTransformMiddleware = (): CallToolMiddleware => {
  return (handler: CallToolHandler) => {
    return async (request, context) => {
      // Example: inject a default `language` argument if missing
      if (!request.params.arguments?.language) {
        request.params.arguments = {
          ...request.params.arguments,
          language: "en",
        };
      }

      const response = await handler(request, context);
      
      // Example: add a custom flag to the result annotations
      response.annotations = {
        ...response.annotations,
        transformed: true,
      };
      return response;
    };
  };
};

```

Register this middleware in the Call‑Tool pipeline:

```typescript
import { createArgTransformMiddleware } from "@/lib/metamcp/metamcp-middleware/arg-transform.middleware";

const callToolWithMiddleware = compose(
  createFilterCallToolMiddleware({
    cacheEnabled: true,
    customErrorMessage: (tool, reason) =>
      `Access denied to tool "${tool}": ${reason}`,
  }),
  createToolOverridesCallToolMiddleware({ cacheEnabled: true }),
  createArgTransformMiddleware(),          // ← custom middleware
)(originalCallToolHandler);

```

## Key Files for Middleware Development

Understanding the repository structure helps you locate the correct extension points when you create custom middleware in MetaMCP:

| File | Role |
|------|------|
| [`apps/backend/src/lib/metamcp/metamcp-middleware/functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/metamcp/metamcp-middleware/functional-middleware.ts) | Defines `MetaMCPHandlerContext`, middleware type signatures, `createFunctionalMiddleware`, and the `compose` helper used to chain middleware. |
| [`apps/backend/src/lib/metamcp/metamcp-middleware/filter-tools.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/metamcp/metamcp-middleware/filter-tools.functional.ts) | Reference implementation of List‑Tools and Call‑Tool middleware that filters inactive tools based on database state. |
| [`apps/backend/src/lib/metamcp/metamcp-middleware/tool-overrides.functional.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/metamcp/metamcp-middleware/tool-overrides.functional.ts) | Example middleware that rewrites tool names and descriptions based on namespace-specific overrides stored in the cache. |
| [`apps/backend/src/routers/public-metamcp/openapi/handlers.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/openapi/handlers.ts) | The integration point where middleware is composed with original handlers; modify this file to register your custom middleware in the request pipeline. |

## Summary

- **MetaMCP middleware** consists of higher-order functions that wrap `ListToolsHandler` or `CallToolHandler` to intercept requests and responses.
- The **`compose`** utility in [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts) chains middleware right-to-left, creating a single wrapped handler.
- Developers implement **`ListToolsMiddleware`** for tool discovery operations and **`CallToolMiddleware`** for tool execution operations.
- Custom middleware can inspect, transform, or block requests by modifying the `request` object before calling the wrapped `handler` and altering the `response` before returning.
- Register new middleware by importing it into [`apps/backend/src/routers/public-metamcp/openapi/handlers.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/openapi/handlers.ts) and adding it to the `compose` chain.

## Frequently Asked Questions

### What is the difference between ListToolsMiddleware and CallToolMiddleware?

**`ListToolsMiddleware`** intercepts operations that enumerate available tools, allowing you to filter, rename, or annotate the tool list before it reaches the client. **`CallToolMiddleware`** intercepts actual tool execution requests, enabling you to validate arguments, inject defaults, transform inputs, or modify the execution response. Both follow the same higher-order function pattern but operate on different handler types defined in [`functional-middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/functional-middleware.ts).

### How do I order multiple custom middleware in the compose chain?

The **`compose`** function applies middleware right-to-left, meaning the rightmost middleware executes first on the incoming request and last on the outgoing response. Place logging or validation middleware toward the right (outer) side of the chain, and transformation or filtering logic toward the left (inner) side to ensure they operate on already-validated or logged data.

### Can I modify the request context in custom middleware?

Yes, the `context` object passed to every handler contains the `namespaceUuid` and other metadata, and you can extend or modify it within your middleware. However, avoid mutating core properties that downstream middleware or the original handler depend on unless you intend to override framework behavior. Always return the modified `context` alongside the request when calling the wrapped handler.

### Where should I store my custom middleware files?

Store custom middleware in `apps/backend/src/lib/metamcp/metamcp-middleware/` alongside existing implementations 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). This location keeps your code co-located with the core middleware utilities, ensuring easy imports into [`apps/backend/src/routers/public-metamcp/openapi/handlers.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/openapi/handlers.ts) where the composition occurs.