NestJS MCP Server Module

Um módulo NestJS para construir servidores MCP que expõem ferramentas e recursos para IA, com suporte a múltiplos tipos de transporte.

Documentação

Módulo de Servidor MCP para NestJS

CI Code Coverage NPM Version NPM Downloads NPM License

Um módulo NestJS para expor facilmente ferramentas, recursos e prompts para IA, a partir de suas aplicações NestJS usando o Model Context Protocol (MCP).

Com o @rekog/mcp-nest você define ferramentas, recursos e prompts de uma forma familiar no NestJS e aproveita todo o poder da injeção de dependência para utilizar sua base de código existente na construção de servidores MCP prontos para ambientes empresariais complexos.

Recursos

  • 🚀 Suporte para todos os Tipos de Transporte:
    • HTTP Streamable
    • HTTP+SSE
    • STDIO
  • 🔍 Descoberta e registro automáticos de tool, resource e prompt
  • 💯 Validação de chamadas de ferramentas baseada em Zod
  • 📊 Notificações de progresso
  • 🔒 Autenticação baseada em guards
  • 🌐 Acesso às informações da Requisição HTTP dentro dos Recursos MCP (Ferramentas, Recursos, Prompts)

Instalação

npm install @rekog/mcp-nest @modelcontextprotocol/sdk zod

Início Rápido

1. Importe o Módulo

// app.module.ts
import { Module } from '@nestjs/common';
import { McpModule } from '@rekog/mcp-nest';
import { GreetingTool } from './greeting.tool';

@Module({
  imports: [
    McpModule.forRoot({
      name: 'my-mcp-server',
      version: '1.0.0',
    }),
  ],
  providers: [GreetingTool],
})
export class AppModule {}

2. Defina Ferramentas e Recursos

// greeting.tool.ts
import type { Request } from 'express';
import { Injectable } from '@nestjs/common';
import { Tool, Resource, Context } from '@rekog/mcp-nest';
import { z } from 'zod';
import { Progress } from '@modelcontextprotocol/sdk/types';

@Injectable()
export class GreetingTool {
  constructor() {}

  @Tool({
    name: 'hello-world',
    description:
      'Returns a greeting and simulates a long operation with progress updates',
    parameters: z.object({
      name: z.string().default('World'),
    }),
  })
  async sayHello({ name }, context: Context, request: Request) {
    const userAgent = request.get('user-agent') || 'Unknown';
    const greeting = `Hello, ${name}! Your user agent is: ${userAgent}`;
    const totalSteps = 5;
    for (let i = 0; i < totalSteps; i++) {
      await new Promise((resolve) => setTimeout(resolve, 100));

      // Send a progress update.
      await context.reportProgress({
        progress: (i + 1) * 20,
        total: 100,
      } as Progress);
    }

    return {
      content: [{ type: 'text', text: greeting }],
    };
  }

  @Resource({
    uri: 'mcp://hello-world/{userName}',
    name: 'Hello World',
    description: 'A simple greeting resource',
    mimeType: 'text/plain',
  })
  // Different from the SDK, we put the parameters and URI in the same object.
  async getCurrentSchema({ uri, userName }) {
    return {
      content: [
        {
          uri,
          text: `User is ${userName}`,
          mimeType: 'text/plain',
        },
      ],
    };
  }
}

Pronto!

[!TIP] O exemplo acima mostra como os cabeçalhos HTTP Request são acessados dentro das Ferramentas MCP. Isso é útil para identificar usuários, adicionar lógica específica do cliente e muitos outros casos de uso. Para mais exemplos, consulte os Testes de Autenticação.

Início Rápido para STDIO

A principal diferença é que você precisa fornecer a opção transport ao importar o módulo.

McpModule.forRoot({
  name: 'playground-stdio-server',
  version: '0.0.1',
  transport: McpTransportType.STDIO,
});

O restante é igual; você pode definir ferramentas, recursos e prompts normalmente. Um exemplo de uma aplicação NestJS autônoma usando o transporte STDIO é o seguinte:

async function bootstrap() {
  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: false,
  });
  return app.close();
}

void bootstrap();

Em seguida, você pode usar o servidor MCP com um Cliente MCP Stdio (veja o exemplo), ou após compilar seu projeto, você pode usá-lo com a seguinte configuração de Cliente MCP:

{
  "mcpServers": {
    "greeting": {
      "command": "node",
      "args": [
        "<path to dist js file>",
      ]
    }
  }
}

Endpoints da API

O transporte HTTP+SSE expõe dois endpoints:

  • GET /sse: Endpoint de conexão SSE (Protegido por guards se configurado)
  • POST /messages: Endpoint de execução de ferramentas (Protegido por guards se configurado)

O transporte HTTP Streamable expõe os seguintes endpoints:

  • POST /mcp: Endpoint principal para todas as operações MCP (execução de ferramentas, acesso a recursos, etc.). No modo com estado, isso cria e mantém sessões.
  • GET /mcp: Estabelece fluxos Server-Sent Events (SSE) para atualizações em tempo real e notificações de progresso. Disponível apenas no modo com estado.
  • DELETE /mcp: Encerra sessões MCP. Disponível apenas no modo com estado.

Dicas

É possível usar o módulo com prefixo global, mas a forma recomendada é excluir esses endpoints com:

app.setGlobalPrefix('/api', { exclude: ['sse', 'messages', 'mcp'] });

Autenticação

Você pode proteger seus endpoints MCP usando Guards padrão do NestJS.

1. Crie um Guard

Implemente a interface CanActivate. O guard deve lidar com a validação da requisição (por exemplo, verificação de JWTs, chaves de API) e, opcionalmente, anexar informações do usuário ao objeto de requisição.

Nada de especial; consulte a documentação do NestJS para mais detalhes.

2. Aplique o Guard

Passe seu(s) guard(s) para a configuração McpModule.forRoot. O(s) guard(s) será(ão) aplicado(s) tanto aos endpoints /sse quanto /messages.

// app.module.ts
import { Module } from '@nestjs/common';
import { McpModule } from '@rekog/mcp-nest';
import { GreetingTool } from './greeting.tool';
import { AuthGuard } from './auth.guard';

@Module({
  imports: [
    McpModule.forRoot({
      name: 'my-mcp-server',
      version: '1.0.0',
      guards: [AuthGuard], // Apply the guard here
    }),
  ],
  providers: [GreetingTool, AuthGuard], // Ensure the Guard is also provided
})
export class AppModule {}

É isso! O restante é igual aos Guards do NestJS.

Playground

O diretório playground contém exemplos para testar rapidamente os recursos do MCP e do @rekog/mcp-nest. Consulte o playground/README.md para mais detalhes.

Configuração

O método McpModule.forRoot() aceita um objeto McpOptions para configurar o servidor. Aqui estão as opções disponíveis:

OpçãoDescriçãoPadrão
nameObrigatório. O nome do seu servidor MCP.-
versionObrigatório. A versão do seu servidor MCP.-
capabilitiesCapacidades opcionais do servidor MCP a serem anunciadas. Consulte @modelcontextprotocol/sdk.undefined
instructionsInstruções opcionais para o cliente sobre como interagir com o servidor.undefined
transportEspecifica o(s) tipo(s) de transporte a serem habilitados.[McpTransportType.SSE, McpTransportType.STREAMABLE_HTTP, McpTransportType.STDIO]
sseEndpointO caminho do endpoint para a conexão SSE (usado com o transporte SSE).'sse'
messagesEndpointO caminho do endpoint para envio de mensagens (usado com o transporte SSE).'messages'
mcpEndpointO caminho base do endpoint para operações MCP (usado com o transporte STREAMABLE_HTTP).'mcp'
guardsUm array de Guards do NestJS para aplicar aos endpoints MCP para autenticação/autorização.[]
decoratorsUm array de Decorators de Classe do NestJS para aplicar aos controladores MCP gerados.[]
sseConfiguração específica para o transporte SSE.{ pingEnabled: true, pingIntervalMs: 30000 }
sse.pingEnabledSe deve habilitar mensagens periódicas de ping SSE para manter a conexão ativa.true
sse.pingIntervalMsO intervalo (em milissegundos) para envio de mensagens de ping SSE.30000
streamableHttpConfiguração específica para o transporte STREAMABLE_HTTP.{ enableJsonResponse: true, sessionIdGenerator: undefined, statelessMode: true }
streamableHttp.enableJsonResponseSe true, permite que o endpoint /mcp retorne respostas JSON para requisições sem streaming (como listTools).true
streamableHttp.sessionIdGeneratorUma função para gerar IDs de sessão únicos ao executar no modo com estado. Obrigatório se statelessMode for false.undefined
streamableHttp.statelessModeSe true, o transporte STREAMABLE_HTTP opera sem estado (sem sessões). Se false, opera com estado, exigindo um sessionIdGenerator.true