NestJS

Plug xmcp into your existing NestJS application with automatic tool discovery and customizable module configuration.

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

Overview

The NestJS adapter allows you to integrate xmcp into your existing NestJS application. It provides:

  • Automatic tool discovery from your src/tools/ directory
  • Scaffolded module with customizable controller, filter, and route configuration
  • NestJS integration with xmcpService and xmcpController

Installation

xmcp can work on top of your existing NestJS project. To get started, run the following command in your project directory:

Code
npx init-xmcp@latest

On initialization, you'll see the following prompts:

? Tools directory path: (tools)

The package manager and framework will be detected automatically.

After initialization, xmcp generates a src/xmcp/ folder with customizable module files:

Code
src/xmcp/
├── xmcp.filter.ts      # Exception filter for JSON-RPC errors
├── xmcp.controller.ts  # Controller with configurable route
└── xmcp.module.ts      # NestJS module configuration

After setting up the project, update your package.json scripts:

package.json
{
  "scripts": {
    "dev": "xmcp dev & nest start --watch",
    "build": "xmcp build && nest build",
    "start": "node dist/main.js"
  }
}

Project Structure

After initialization, your project structure will look like this:

Code
my-nestjs-app/
├── src/
│   ├── tools/              # Tool files are auto-discovered here
│   │   └── greet.ts
│   ├── xmcp/               # Generated xmcp module (customizable)
│   │   ├── xmcp.filter.ts
│   │   ├── xmcp.controller.ts
│   │   └── xmcp.module.ts
│   ├── app.module.ts       # Import XmcpModule here
│   └── main.ts
├── .xmcp/                  # Generated by xmcp build (gitignored)
├── xmcp.config.ts          # xmcp configuration
├── xmcp-env.d.ts           # Type declarations
├── package.json
└── tsconfig.json

Generated Files

src/xmcp/ - Contains the scaffolded module with controller, filter, and module configuration. These files are yours to customize - change the route path, add middleware, modify error handling, or extend functionality as needed.

.xmcp/ - Contains the compiled adapter and auto-generated TypeScript definitions. This directory is created by xmcp build and should be added to .gitignore. It includes xmcpService, xmcpController, and all type definitions needed to integrate with NestJS.

xmcp-env.d.ts - Provides TypeScript type declarations for xmcp imports like @xmcp/adapter. This file is auto-generated and should not be edited manually. It ensures TypeScript can resolve the path alias configured in tsconfig.json.

Basic Usage

Import and add the XmcpModule to your application module:

src/app.module.ts
import { Module } from "@nestjs/common";
import { XmcpModule } from "./xmcp/xmcp.module";

@Module({
  imports: [XmcpModule],
})
export class AppModule {}

This registers a /mcp endpoint that handles MCP requests via POST.

Generated Module Files

Exception Filter

The exception filter provides JSON-RPC error handling for MCP endpoints:

src/xmcp/xmcp.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, Logger } from "@nestjs/common";
import { Response } from "express";

@Catch()
export class McpExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(McpExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    this.logger.error(
      "MCP request failed",
      exception instanceof Error ? exception.stack : String(exception)
    );

    if (!response.headersSent) {
      response.status(500).json({
        jsonrpc: "2.0",
        error: {
          code: -32603,
          message: "Internal server error",
        },
        id: null,
      });
    }
  }
}

This filter is scaffolded into your project, so you can customize it to handle specific error types or modify the error response format.

Controller

The controller extends xmcpController and uses NestJS decorators:

src/xmcp/xmcp.controller.ts
import { Controller, UseFilters } from "@nestjs/common";
import { xmcpController } from "@xmcp/adapter";
import { McpExceptionFilter } from "./xmcp.filter";

@Controller("mcp")
@UseFilters(McpExceptionFilter)
export class McpController extends xmcpController {}

To change the route, simply modify the @Controller argument:

Code
@Controller("api/v1/mcp") // Now accessible at /api/v1/mcp
export class McpController extends xmcpController {}

Module

The module configures the controller and providers:

src/xmcp/xmcp.module.ts
import { Module } from "@nestjs/common";
import { xmcpService } from "@xmcp/adapter";
import { McpController } from "./xmcp.controller";
import { McpExceptionFilter } from "./xmcp.filter";

@Module({
  controllers: [McpController],
  providers: [xmcpService, McpExceptionFilter],
  exports: [xmcpService],
})
export class XmcpModule {}

Configuration

Configure the NestJS adapter in your xmcp.config.ts:

xmcp.config.ts
import { type XmcpConfig } from "xmcp";

const config: XmcpConfig = {
  http: true,
  experimental: {
    adapter: "nestjs",
  },
};

export default config;

The NestJS adapter uses HTTP transport and integrates with NestJS's module system, allowing you to use the xmcpService in your custom controllers.

NestJS Integration Features

The adapter provides full NestJS integration with proper lifecycle management and error handling:

Lifecycle Hooks

xmcpService implements OnModuleInit and OnModuleDestroy for proper initialization and shutdown logging:

  • Startup: Logs [xmcpService] XMCP service initialized when the module initializes
  • Shutdown: Logs [xmcpService] XMCP service shutting down when the application stops

Structured Logging

All xmcp internal logs use the NestJS Logger class, automatically inheriting your application's logging configuration.

Adding Tools

Tools are automatically discovered from your src/tools/ directory. Create a new file and export a default handler function:

src/tools/greet.ts
import { z } from "zod";
import { type InferSchema } from "xmcp";

export const schema = {
  name: z.string().describe("The name of the user to greet"),
};

export const metadata = {
  name: "greet",
  description: "Greet the user by name",
};

export default async function greet({ name }: InferSchema<typeof schema>) {
  return `Hello, ${name}!`;
}

When you run xmcp dev or xmcp build, xmcp automatically discovers this file and registers it as an MCP tool. No additional configuration needed.

Authentication

The NestJS adapter provides a createMcpAuthGuard factory function for JWT authentication. You provide the verification logic, and the adapter handles token extraction, error responses, and attaching auth info to requests.

Setup

First, install the JWT library:

Code
npm install jsonwebtoken
npm install -D @types/jsonwebtoken

Create an auth guard configuration file:

src/xmcp/xmcp.auth.ts
import { createMcpAuthGuard } from "@xmcp/adapter";
import * as jwt from "jsonwebtoken";

export const McpAuthGuard = createMcpAuthGuard({
  verifyToken: async (token) => {
    const decoded = jwt.verify(
      token,
      process.env.JWT_SECRET!
    ) as jwt.JwtPayload;

    return {
      clientId: decoded.sub || "unknown",
      scopes: decoded.scope?.split(" ") || [],
      expiresAt: decoded.exp,
    };
  },
  required: false, // Set to true to require authentication
});

Enable Authentication

To enable authentication, add the guard to your controller:

src/xmcp/xmcp.controller.ts
import { Controller, UseFilters, UseGuards } from "@nestjs/common";
import { xmcpController } from "@xmcp/adapter";
import { McpExceptionFilter } from "./xmcp.filter";
import { McpAuthGuard } from "./xmcp.auth";

@Controller("mcp")
@UseFilters(McpExceptionFilter)
@UseGuards(McpAuthGuard)
export class McpController extends xmcpController {}

And add it to your module providers:

src/xmcp/xmcp.module.ts
import { Module } from "@nestjs/common";
import { xmcpService } from "@xmcp/adapter";
import { McpController } from "./xmcp.controller";
import { McpExceptionFilter } from "./xmcp.filter";
import { McpAuthGuard } from "./xmcp.auth";

@Module({
  controllers: [McpController],
  providers: [xmcpService, McpExceptionFilter, McpAuthGuard],
  exports: [xmcpService],
})
export class XmcpModule {}

Configuration Options

OptionTypeDefaultDescription
verifyToken(token: string) => Promise<AuthInfo> | AuthInfoRequiredVerify the token and return auth info
requiredbooleanfalseIf true, requests without tokens are rejected

The verifyToken function receives the Bearer token (without the "Bearer " prefix) and should return:

Code
interface AuthInfo {
  clientId: string; // User/client identifier
  scopes: string[]; // Permissions/scopes
  expiresAt?: number; // Token expiration (Unix timestamp)
  extra?: Record<string, unknown>; // Additional custom data
}

If verification fails, throw an error with a descriptive message.

Accessing Auth Info in Tools

Auth info is available in tools via the extra argument:

src/tools/whoami.ts
import { type ToolMetadata, type ToolExtraArguments } from "xmcp";

export const schema = {};

export const metadata: ToolMetadata = {
  name: "whoami",
  description: "Returns information about the authenticated user",
};

export default async function whoami(
  _args: unknown,
  extra: ToolExtraArguments
) {
  const authInfo = extra.authInfo;
  const clientInfo = extra.clientInfo;

  if (!authInfo) {
    return "Not authenticated";
  }

  return JSON.stringify(
    {
      clientId: authInfo.clientId,
      scopes: authInfo.scopes,
      clientName: clientInfo?.name,
      clientVersion: clientInfo?.version,
    },
    null,
    2
  );
}

Testing with curl

Code
# Without authentication (if required: false)
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

# With authentication
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-jwt-token>" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Troubleshooting

Cannot find module '@xmcp/adapter'

This error occurs when the .xmcp directory hasn't been generated yet.

Solution: Run npx xmcp build before starting your NestJS application.

TypeScript path resolution errors

If TypeScript can't resolve @xmcp/* imports:

  1. Ensure tsconfig.json has the path mapping:

    Code
    {
      "compilerOptions": {
        "paths": {
          "@xmcp/*": ["./.xmcp/*"]
        }
      }
    }
  2. Ensure .xmcp is included in the include array:

    Code
    {
      "include": ["src/**/*", "xmcp-env.d.ts", ".xmcp/**/*"]
    }

Next Steps