关于 NestJS MCP Module
NestJS MCP Module 是一个 NestJS 模块,用于通过装饰器以 NestJS 的方式实现 MCP 服务。适合已在使用 NestJS 的开发者,支持 STDIO 与 HTTP/SSE 两种传输方式。
主要功能
- 01使用 @McpTool、@McpResource、@McpPrompt 装饰器
- 02自动发现装饰的方法并注册到 MCP 服务
- 03支持 STDIO 与 HTTP/SSE 传输
- 04使用 Zod 定义工具参数与提示词参数
- 05提供 forRoot 与 forRootAsync 配置方式
适用场景
- 01在现有 NestJS 应用中暴露工具与资源给 MCP 客户端
- 02为命令行工具构建基于 STDIO 的 MCP 服务
- 03通过 HTTP/SSE 端点提供远程 MCP 服务
MCP 相关
该模块用于实现 MCP 服务,将被装饰的方法以工具、资源和提示词的形式暴露给 MCP 客户端,底层基于 @modelcontextprotocol/sdk。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
NestJS MCP 模块
这是一个 NestJS 模块,可无缝集成用于实现 Model Context Protocol (MCP) 服务。该模块使用熟悉的 NestJS 方法装饰器(@McpResource、@McpTool、@McpPrompt)和模式,简化了向 MCP 客户端公开资源、工具和提示词的过程。它支持多种传输层,包括 STDIO 和 HTTP/SSE。
目录
✨ 功能
- 无缝集成 MCP: 使用
@modelcontextprotocol/sdk,轻松在 NestJS 应用程序中构建符合 MCP 规范的服务。 - 基于方法装饰器: 在 NestJS Provider 中使用装饰器定义 MCP 资源、工具和提示词(
@McpResource、@McpTool、@McpPrompt)。 - 自动发现: 使用 NestJS 发现机制(
@nestjs/core/discovery)查找带装饰器的方法。 - 传输处理:
- 内置支持 STDIO 传输(适用于 CLI 工具)。
- 内置支持 HTTP/SSE 传输,通过专用控制器(
McpHttpController,路径为/api/mcp)实现。 - 可配置传输方式。
- Zod Schema: 使用 Zod schema 定义工具参数和提示词参数,以进行验证并确保类型安全。
- 原生 NestJS: 使用标准 NestJS 模块、Provider 和依赖注入构建。
- 强类型: 利用 TypeScript 实现稳健开发。
- 可配置: 支持通过标准
forRoot和forRootAsync模式进行配置。 - Stderr 日志记录: 提供
StderrLogger,确保应用程序日志不会干扰 STDIO 传输。
📦 安装
首先,请确保已安装所需的对等依赖。然后安装该模块:
# Using npm
npm install @bamada/nestjs-mcp
# Using yarn
yarn add @bamada/nestjs-mcp
⚠️ 对等依赖
此模块依赖 NestJS 核心包、MCP SDK 和其他需要在宿主应用中安装的库。请确保 package.json 包含兼容版本:
@modelcontextprotocol/sdk:^1.10.2@nestjs/platform-express:^10.0.0或^11.0.0(如果不使用 Express,也可以选择其他平台)zod:^3.0.0
未安装这些对等依赖会导致运行时错误。
🚀 快速开始
-
导入
McpModule: 将McpModule导入根AppModule(或功能模块),并使用.forRoot()或.forRootAsync()进行配置。提供必需的serverInfo。// src/app.module.ts import { Module } from '@nestjs/common'; import { McpModule, TransportType } from '@bamada/nestjs-mcp'; import { MyMcpProvider } from './my-mcp.provider'; @Module({ imports: [ McpModule.forRoot({ serverInfo: { name: 'my-awesome-mcp-app', version: '1.0.0', }, serverOptions: {}, transport: TransportType.SSE, // Use HTTP/SSE controller }), ], providers: [MyMcpProvider], }) export class AppModule {}
以下是使用 @McpTool 装饰器定义 MCP 工具的方法:
MCP 工具示例
import {
McpTool,
RequestHandlerExtra,
CallToolResult,
} from '@bamada/nestjs-mcp';
import { Injectable, Logger } from '@nestjs/common';
import { z } from 'zod';
import { Request, Notification } from '@modelcontextprotocol/sdk/types';
@Injectable()
export class MyMcpProvider {
private readonly logger = new Logger(MyMcpProvider.name);
@McpTool({
name: 'add',
description: 'Adds two numbers together.',
paramsSchema: {
a: z.number().describe('The first number to add'),
b: z.number().describe('The second number to add'),
optionalMessage: z
.string()
.nullable()
.optional()
.describe('An optional message'),
},
})
addTool(
params: { a: number; b: number; optionalMessage?: string },
extra: RequestHandlerExtra<Request, Notification>,
): CallToolResult {
const clientId =
extra.authInfo?.clientId ?? extra.sessionId ?? 'Unknown Client';
this.logger.log(
`Tool 'add' called by client ${clientId} with params: ${JSON.stringify(params)}`,
);
const sum = params.a + params.b;
let resultText = `The sum is ${sum}.`;
if (params.optionalMessage) {
resultText += ` Your message: ${params.optionalMessage}`;
}
return {
content: [{ type: 'text', text: resultText }],
};
}
}
使用 @McpResource 装饰器定义固定 MCP 资源和模板 MCP 资源:
MCP 资源示例
import {
RequestHandlerExtra,
McpResource,
ReadResourceResult,
Variables,
} from '@bamada/nestjs-mcp';
import { Injectable, Logger } from '@nestjs/common';
import { Request, Notification } from '@modelcontextprotocol/sdk/types';
@Injectable()
export class MyMcpProvider {
private readonly logger = new Logger(MyMcpProvider.name);
@McpResource({
name: 'static-config',
uri: 'mcp://my-app/config/settings.json',
description: 'Provides static configuration settings.',
})
getStaticConfig(
uri: string,
extra: RequestHandlerExtra<Request, Notification>,
): ReadResourceResult {
this.logger.log(`Resource 'static-config' requested for URI: ${uri}`);
const clientId =
extra.authInfo?.clientId ?? extra.sessionId ?? 'Unknown Client';
return {
contents: [
{
type: 'application/json',
text: JSON.stringify({
theme: 'dark',
featureFlags: ['newUI'],
clientId,
}),
uri: uri.toString(),
},
],
};
}
// Template Resource
@McpResource({
name: 'user-profile',
uriTemplate: 'mcp://my-app/users/{userId}/profile',
description: 'Provides user profile data based on userId.',
})
getUserProfile(
uri: string,
variables: Variables,
extra: RequestHandlerExtra<Request, Notification>,
): ReadResourceResult {
const clientId =
extra.authInfo?.clientId ?? extra.sessionId ?? 'Unknown Client';
const userId = Array.isArray(variables.userId)
? variables.userId.join(', ')
: String(variables.userId);
this.logger.log(`Resource 'user-profile' requested for userId: ${userId}`);
// Fetch user data based on userId...
const userProfile = {
id: userId,
name: `User ${userId}`,
email: `${userId}@example.com`,
clientId,
};
return {
contents: [
{
type: 'application/json',
text: JSON.stringify(userProfile),
uri: uri.toString(),
},
],
};
}
}
使用 @McpPrompt 装饰器创建 MCP 提示词处理程序:
MCP 提示词示例
import { McpPrompt, RequestHandlerExtra } from '@bamada/nestjs-mcp';
import { Injectable, Logger } from '@nestjs/common';
import {
Request,
Notification,
GetPromptResult,
} from '@modelcontextprotocol/sdk/types';
@Injectable()
export class MyMcpProvider {
private readonly logger = new Logger(AppProvider.name);
@McpPrompt({
name: 'greetingPrompt',
description: 'Generates a personalized greeting message',
arguments: [
{
name: 'userName',
description: 'The name of the person to greet',
required: true,
},
{
name: 'style',
description: 'Greeting style (e.g., formal, casual)',
required: false,
},
],
})
generateGreeting(
params: { userName: string; style?: string },
extra: RequestHandlerExtra<Request, Notification>,
): GetPromptResult {
this.logger.log(
`Prompt 'greetingPrompt' called with params: ${JSON.stringify(params)}`,
);
const clientId =
extra.authInfo?.clientId ?? extra.sessionId ?? 'Unknown Client';
const greeting = params.style === 'formal' ? 'Greetings' : 'Hello';
return {
messages: [
{
role: 'assistant',
content: {
type: 'text',
text: `${greeting}, ${params.userName}! Welcome. How can I assist you today? clientId: ${clientId}`,
},
},
],
// Optional: context, toolCalls, toolResult etc.
};
}
}
-
使用
StderrLogger(适用于 STDIO Transport): 如果使用TransportType.STDIO,请确保 NestJS 日志输出到stderr,以免干扰通过stdout进行的 MCP 通信。import 'reflect-metadata'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { StderrLogger } from '@bamada/nestjs-mcp'; async function bootstrap() { const app = await NestFactory.create(AppModule, { // Use the StderrLogger IF using STDIO transport logger: new StderrLogger(), }); await app.listen(3000); // Or app.init() for non-HTTP apps console.error('NestJS application started...'); // Log to stderr } bootstrap(); -
运行应用程序: 模块会发现带有装饰器的提供程序方法,并将其注册到底层 MCP 服务。根据配置的传输方式,它会通过 STDIO 监听,或通过
McpHttpController暴露端点(默认:/api/mcp/sse和/api/mcp/messages)。
⚙️ 配置
使用 McpModule.forRoot(options) 或 McpModule.forRootAsync(options) 配置模块。
McpModuleOptions:
import { Implementation } from '@modelcontextprotocol/sdk/types';
import { ServerOptions } from '@modelcontextprotocol/sdk/server';
export enum TransportType {
STDIO = 'stdio',
SSE = 'sse',
NONE = 'none',
}
export interface McpModuleOptions {
/**
* Required. Server implementation information.
*/
serverInfo: Implementation;
/**
* Optional. Server configuration options passed to the MCP SDK's McpServer.
* @see https://github.com/modelcontextprotocol/tsp-sdk/blob/main/server/src/server_options.ts
*/
serverOptions?: ServerOptions;
/**
* Optional. Selects the transport mechanism.
* - `STDIO`: For CLI tools, uses stdin/stdout for MCP, logs to stderr (use StderrLogger).
* - `SSE`: Assumes HTTP/SSE transport via McpHttpController (default path /api/mcp).
* - `NONE`: No transport is automatically managed by this module (e.g., for custom transport).
*/
transport?: TransportType;
}
同步配置(.forRoot)
import { McpModule, TransportType } from '@bamada/nestjs-mcp';
@Module({
imports: [
McpModule.forRoot({
serverInfo: { name: 'my-mcp-server', version: '0.1.0' },
transport: TransportType.SSE,
serverOptions: { maxConnections: 10 },
}),
],
})
export class AppModule {}
异步配置(.forRootAsync)
适用于注入 ConfigService 或使用工厂函数。
import { McpModule, TransportType } from '@bamada/nestjs-mcp';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
McpModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
serverInfo: {
name: configService.get<string>(
'MCP_SERVER_NAME',
'default-mcp-server',
),
version: configService.get<string>('APP_VERSION', '0.0.1'),
},
transport: configService.get<TransportType>(
'MCP_TRANSPORT',
TransportType.SSE,
),
serverOptions: {
// Example: get options from config
maxConnections: configService.get<number>('MCP_MAX_CONNECTIONS'),
},
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
📜 API 参考(装饰器)
这些装饰器应用于 NestJS 提供程序(服务)中的方法。
@McpResource(options: ResourceOptions)
将方法标记为 MCP 资源处理程序。该方法会接收请求的 uri、提取出的 variables(用于模板)和 extra 上下文,并应返回 ReadResourceResult。
options:符合ResourceOptions的对象(可以是FixedResourceOptions或TemplateResourceOptions):name:(string)必需。资源的唯一标识符。description?:(string)可选描述。uri:(string)固定资源必需。资源的确切 URI。uriTemplate:(string | ResourceTemplate)模板资源必需。URI 模式(例如,/users/{id})。metadata?:(ResourceMetadata)可选元数据,例如contentType、schema等。
@McpTool(options: ToolOptions)
将方法标记为 MCP 工具。该方法会接收经过验证的 params 对象(符合 paramsSchema)和 extra 上下文,并应返回 CallToolResult。
options:符合ToolOptions的对象:name:(string)必需。工具的唯一标识符。description?:(string)对工具功能的可选描述。paramsSchema?:(ZodRawShape)可选。定义工具输入参数的 Zod 原始形状对象。键为参数名称,值为 Zod schema(例如,z.string()、z.number().optional())。在 Zod schema 上使用.describe(),可为 MCP 客户端提供描述。
@McpPrompt(options: PromptType)
将方法装饰为 MCP 提示词处理程序。该方法会接收经过验证的 params 对象(与 arguments 定义相匹配)和 extra 上下文,并应返回一个 GetPromptResult。
options:符合@modelcontextprotocol/sdk/types.js中Prompt类型的对象:name:(string)必填。提示词的唯一标识符。description?:(string)可选描述。arguments?:(Array)可选数组,用于定义提示词的输入参数:name:(string)必填。参数名称(必须与处理程序params对象中的键相匹配)。description?:(string)可选参数描述。required?:(boolean)参数是否必填(默认值:false)。
input?:(PromptInputDefinition)定义预期输入格式(例如文本、图像)。output?:(PromptOutputDefinition)定义预期输出格式。
🛠️ 开发与贡献
欢迎贡献!请按照以下步骤操作:
-
**Fork 并克隆:**Fork 此仓库并将其克隆到本地。
git clone https://github.com/YOUR_USERNAME/nestjs-mcp.git cd nestjs-mcp -
安装依赖项:
npm install # or yarn install -
**开发:**在
src目录中进行修改。使用npm link或 Yarn/PNPM 工作区,在使用该项目的应用中进行本地开发。 -
**Lint 与格式化:**确保代码质量和一致性。
npm run lint npm run format -
**构建:**将 TypeScript 编译为 JavaScript。
npm run build -
**测试:**运行测试套件。
npm test # For coverage: npm run test:cov -
**提交并推送:**提交修改,并附上清晰的说明。
-
**创建 Pull Request:**针对原始仓库的
main分支提交 PR。
请通过 GitHub Issues 页面报告错误或建议新功能。
📜 许可证
本项目采用 MIT 许可证授权;详情请参阅 LICENSE 文件。
🔗 链接
- 作者:bamada
- 仓库:https://github.com/bamada/nestjs-mcp
- 问题反馈:https://github.com/bamada/nestjs-mcp/issues
- Model Context Protocol:https://modelcontextprotocol.org/
- MCP TypeScript SDK:https://github.com/modelcontextprotocol/tsp-sdk
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。