NestJS MCP Module について
NestJS MCP Module は、デコレーターを使って MCP サーバーを構築する NestJS モジュールです。すでに NestJS を使っている開発者向けで、STDIO と HTTP/SSE の両方のトランスポートに対応しています。
主な機能
- 01ツール、リソース、プロンプト用のデコレーター
- 02デコレーターを付けたメソッドの自動検出
- 03STDIO と HTTP/SSE のトランスポート対応
- 04ツールとプロンプト引数の Zod スキーマ
- 05forRoot と forRootAsync による設定
活用シーン
- 01既存の NestJS アプリケーションからツールとリソースを公開する
- 02コマンドライン向けに STDIO ベースの MCP サーバーを構築する
- 03HTTP/SSE エンドポイントで MCP を提供する
MCP 対応
このモジュールはデコレーターを付けたメソッドをツール、リソース、プロンプトとして MCP クライアントに公開し、@modelcontextprotocol/sdk 上に実装されています。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
NestJS MCP モジュール
Model Context Protocol (MCP) サーバーを実装するための、シームレスな統合を提供する NestJS モジュールです。このモジュールでは、使い慣れた NestJS のメソッドデコレーター(@McpResource、@McpTool、@McpPrompt)とパターンを使用して、リソース、ツール、プロンプトを MCP クライアントに公開するプロセスを簡素化します。STDIO や HTTP/SSE など、複数のトランスポート層をサポートしています。
目次
✨ 機能
- シームレスな MCP 統合:
@modelcontextprotocol/sdkを使用して、NestJS アプリケーション内で MCP に準拠したサーバーを簡単に構築できます。 - メソッドデコレーター方式: NestJS プロバイダー内のメソッドにデコレーター(
@McpResource、@McpTool、@McpPrompt)を付けて、MCP リソース、ツール、プロンプトを定義できます。 - 自動検出: NestJS の検出メカニズム(
@nestjs/core/discovery)を使用して、デコレーターが付いたメソッドを検索します。 - トランスポート処理:
- STDIO トランスポートを標準でサポートします(CLI ツールに最適です)。
- 専用コントローラー(
/api/mcpのMcpHttpController)を介した HTTP/SSE トランスポートを標準でサポートします。 - トランスポートを設定で選択できます。
- Zod スキーマ: 検証と型安全性のために、Zod スキーマを使用してツールのパラメーターとプロンプトの引数を定義できます。
- NestJS ネイティブ: 標準的な NestJS のモジュール、プロバイダー、依存性注入を使用して構築されています。
- 強い型付け: 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 トランスポートの場合):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 の raw shape オブジェクトです。キーはパラメーター名、値は Zod スキーマ(例:z.string()、z.number().optional())です。Zod スキーマで.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)想定される出力形式を定義します。
🛠️ 開発と貢献
貢献を歓迎します。以下の手順に従ってください。
-
フォークとクローン: リポジトリをフォークし、ローカルにクローンします。
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 -
コミットとプッシュ: 内容が明確なメッセージを付けて変更をコミットします。
-
プルリクエストの作成: 元のリポジトリの
mainブランチに対して PR を作成します。
バグの報告や機能の提案は、GitHub Issues ページからお寄せください。
📜 ライセンス
このプロジェクトは MIT License の下でライセンスされています。詳細は 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 サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。