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
xmcpServiceandxmcpController
Installation
xmcp can work on top of your existing NestJS project. To get started, run the following command in your project directory:
npx init-xmcp@latestOn 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:
src/xmcp/
├── xmcp.filter.ts # Exception filter for JSON-RPC errors
├── xmcp.controller.ts # Controller with configurable route
└── xmcp.module.ts # NestJS module configurationAfter setting up the project, update your package.json scripts:
{
"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:
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.jsonGenerated 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:
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:
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:
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:
@Controller("api/v1/mcp") // Now accessible at /api/v1/mcp
export class McpController extends xmcpController {}Module
The module configures the controller and providers:
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:
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 initializedwhen the module initializes - Shutdown: Logs
[xmcpService] XMCP service shutting downwhen 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:
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:
npm install jsonwebtoken
npm install -D @types/jsonwebtokenCreate an auth guard configuration file:
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:
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:
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
| Option | Type | Default | Description |
|---|---|---|---|
verifyToken | (token: string) => Promise<AuthInfo> | AuthInfo | Required | Verify the token and return auth info |
required | boolean | false | If true, requests without tokens are rejected |
The verifyToken function receives the Bearer token (without the "Bearer " prefix) and should return:
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:
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
# 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:
-
Ensure
tsconfig.jsonhas the path mapping:Code { "compilerOptions": { "paths": { "@xmcp/*": ["./.xmcp/*"] } } } -
Ensure
.xmcpis included in theincludearray:Code { "include": ["src/**/*", "xmcp-env.d.ts", ".xmcp/**/*"] }