CommunityCodierung & Entwicklunggithub.com

ruwanb/nestjs-mcp-server

Verwandelt eine NestJS 12-Anwendung in einen Model Context Protocol Server mit @nestjs/mcp — McpModule.forRoot/forRootAsync, @Tool mit Zod- oder Standard-Schema-inputSchema/outputSchema und Annotationen, @Resource und @ResourceTemplate, @Prompt, McpContext (authInfo, signal, reportProgress, inputRequired), Guards, Interceptors, Pipes und Filter mit McpExecutionContext, Tool-Fehler vs. JSON-RPC-Fehler, OAuth 2.1 Resource-Server-Authentifizierung mit McpTokenVerifier, Scopes und @nestjs/authentication-Integration.

Was ist nestjs-mcp-server?

Dieses Skill verwandelt eine NestJS 12-Anwendung in einen vollwertigen Model Context Protocol (MCP)-Server mithilfe des @nestjs/mcp-Pakets. Es ermöglicht Entwicklern, Tools, Ressourcen und Prompts als Decorators auf NestJS-Providern zu definieren, wobei Zod 4.2+ oder andere Schema-Bibliotheken für die Eingabe- und Ausgabevalidierung verwendet werden. Der Server unterstützt sowohl Streamable-HTTP als auch stdio-Transports und bietet leistungsstarke Funktionen wie OAuth 2.1-Ressourcenserver-Authentifizierung mit McpTokenVerifier, detaillierte Autorisierung über Scopes, und nahtlose Integration mit NestJS-Pipes, -Guards, -Interceptors und -Filtern. Dank McpContext können Tools auf Authentifizierungsinformationen, Abbruchsignale und Fortschrittsmeldungen zugreifen und Benutzereingaben anfordern, während die Fehlerbehandlung zwischen Tool-Fehlern (isError: true) und JSON-RPC-Fehlern unterscheidet.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ruwanb/nestjs-boost/tree/HEAD/skills/nestjs-mcp-server

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

NestJS MCP Server

npm i @nestjs/mcp @modelcontextprotocol/server zod
npm i -D @modelcontextprotocol/client          # In-Memory-Test-Client

Zod 4.2+ und ArkType funktionieren direkt; Valibot über toStandardJsonSchema(); ein vorhandenes JSON-Schema über fromJsonSchema(). Zod 3-Schemas werden beim Start abgelehnt.

McpModule.forRoot({
  name: 'cat-store', version: '1.0.0',
  instructions: 'Look orders up with find-order before changing them.',   // teilt dem Modell mit, wie der Server zu verwenden ist
}),

app.listen(3000 stellt den Endpunkt unter /mcp bereit. Untersuchen Sie ihn mit npx @modelcontextprotocol/inspector. Beim Start scannt das Modul die Provider (alle Module oder nur include) nach dekorierten Methoden. Es schlägt bei doppelten Namen, ungültigen Schemas oder deklarierten scopes ohne auth fehl.

Primitive: Methoden auf Providern

PrimitiveDecoratorWer verwendet esAufgerufen mit
Tool (eine Aktion)@Tool({ name, description, inputSchema, outputSchema?, annotations?, scopes? })das Modellvalidierte Eingabe, ctx
Ressource (Daten)@Resource({ uri, mimeType }) / @ResourceTemplate({ uriTemplate: 'orders://{orderId}' })der Client, oft vom Benutzer angehängt{} oder die Template-Variablen, ctx (ctx.uri)
Prompt (eine Vorlage)@Prompt({ name, description, argsSchema })der Benutzer wählt eine ausvalidierte Argumente, ctx
@Injectable()                                          // ein einfacher Provider, der in den providers eines Moduls aufgeführt ist
export class OrdersTools {
  constructor(private readonly orders: OrdersService) {}

  @Tool({
    name: 'find-order',
    description: 'Find an order by its id, or the latest orders of a customer by email',
    inputSchema: z.object({
      orderId: z.string().optional().describe('The order id, such as ord_123'),
      email: z.email().optional().describe("The customer's email address"),
    }),
    annotations: { readOnlyHint: true },               // Client kann es ohne Rückfrage beim Benutzer ausführen
  })
  async find({ orderId, email }: { orderId?: string; email?: string }) {
    const found = orderId ? [await this.orders.findById(orderId)] : await this.orders.latestFor(email!);
    if (!found[0]) throw new NotFoundException('No such order');   // → Tool-Ergebnis mit isError: true
    return found;
  }

  @Tool({
    name: 'change-address',
    description: 'Change the delivery address of an order that has not shipped yet',
    inputSchema: z.object({ orderId: z.string(), address: z.string().min(10) }),
    outputSchema: z.object({ orderId: z.string(), address: z.string(), status: z.string() }),
    scopes: ['orders:write'],
  })
  async changeAddress({ orderId, address }: { orderId: string; address: string }, ctx: McpContext) {
    const o = await this.orders.changeAddress(orderId, address, ctx.authInfo?.clientId);
    return { orderId: o.id, address: o.address, status: o.status };   // → structuredContent + JSON-Text
  }
}

Rückgabewerte: Ein String eines Tools wird zu einem Textblock. Ein Objekt wird zu structuredContent, wenn ein outputSchema vorhanden ist, andernfalls zu JSON-Text. Geben Sie für Bilder, Audio oder eingebettete Ressourcen selbst ein CallToolResult zurück; undefined ergibt ein leeres Ergebnis. Ein String einer Ressource wird als Text gesendet, ein Buffer/Uint8Array als Blob, ein Objekt als JSON. Ein String eines Prompts wird zu einer Benutzernachricht; ein Array gibt PromptMessage[] zurück. Toolnamen sind 1–128 Zeichen aus A-Z a-z 0-9 _ - . und standardmäßig der Methodenname.

Schreiben Sie Namen, Beschreibungen und .describe()-Texte für das Modell. Das ist alles, was es weiß: Sagen Sie, was das Tool tut, wann es verwendet werden soll, ob es etwas ändert, und geben Sie einen Beispielwert für jedes Argument. Verwenden Sie Ressourcen für Inhalte, die unverändert geladen werden; verwenden Sie Tools für Suchvorgänge, die Argumente benötigen.

Aufrufkontext

ctx: McpContext hat diese Eigenschaften:

  • kind, name, uri
  • authInfo (clientId, scopes, expiresAt, extra)
  • request (die Plattformanfrage; undefined über stdio)
  • requestInfo
  • transport ('http' | 'stdio')
  • era ('modern' 2026-07-28 / 'legacy' 2025)
  • signal: übergeben Sie es an fetch- und DB-Aufrufe; rufen Sie throwIfAborted() in Schleifen auf
  • reportProgress(progress, total?, message?)
  • server (SDK-Kontext)

Um den Benutzer zu fragen (Bestätigungen, fehlende Werte), geben Sie inputRequired({ inputRequests: { confirm: inputRequired.elicit({ message, requestedSchema }) } }) zurück. Beim erneuten Versuch lesen Sie die Antwort mit acceptedContent(ctx.server.mcpReq.inputResponses, 'confirm', Schema). Über HTTP funktioniert dies nur für Clients von 2026-07-28, da der Endpunkt zustandslos ist. Über stdio funktioniert es für beide Epochen.

Provider bleiben Singletons. Lesen Sie aufrufspezifische Daten aus ctx, nicht aus anforderungsbezogenen Providern.

Enhancer und Fehler

  • Guards, Interceptors, Pipes und Filter funktionieren wie bei Routen, mit context.getType() === 'mcp'. McpExecutionContext.create(context) bietet getInput(), getContext(), getKind(), getName(), getRequest() und getAuthInfo().
  • Globale Enhancer werden bei MCP-Aufrufen und der dazugehörigen HTTP-Anfrage ausgeführt. Nur-HTTP-Enhancer müssen vor dem Aufruf von switchToHttp() context.getType() === 'http' prüfen.
  • Ein globaler ValidationPipe({ transform: true }) validiert DTO-typisierte Eingaben. Benutzerdefinierte Parameter-Decoratoren folgen auf (input, ctx, @CurrentUser() user).
  • Von Tools ausgelöste Fehler werden zu isError: true-Ergebnissen, von denen sich das Modell erholen kann. Ein HttpException behält seine Nachricht; jeder andere Fehler wird zu Internal server error und wird protokolliert.
  • Ressourcen und Prompts antworten mit JSON-RPC-Fehlern: NotFoundException → Ressource nicht gefunden, BadRequestException → ungültige Parameter.
  • Filter: Prüfen Sie host.getType<McpContextType>() === 'mcp'. Was auch immer der Filter zurückgibt, wird zum Ergebnis; um einen JSON-RPC-Fehler zu senden, werfen Sie einen ProtocolError.

Autorisierung (netzwerkerreichbare Server)

McpModule.forRootAsync({
  imports: [ConfigModule, AuthModule],
  inject: [ConfigService, IdentityProviderTokenVerifier],
  useFactory: (config: ConfigService, verifier: IdentityProviderTokenVerifier) => ({
    name: 'cat-store', version: config.get('VERSION'),
    auth: {
      verifier,                                               // implementiert McpTokenVerifier.verifyAccessToken(token) → AuthInfo | null
      resource: config.get('MCP_RESOURCE'),                   // kanonische öffentliche URL = Token-Audience
      authorizationServers: [config.get('IDENTITY_PROVIDER_URL')],
      scopesSupported: ['orders:read', 'orders:write'],
    },
  }),
}),
  • Der Server ist ein OAuth 2.1 Ressourcenserver: Er verifiziert Tokens und gibt sie niemals aus.
  • Er stellt RFC-9728-Metadaten unter /.well-known/oauth-protected-resource/mcp bereit. Anfragen ohne Token erhalten einen 401-Status mit einer WWW-Authenticate: Bearer-Aufforderung, die das Anmeldeverfahren des Clients startet.
  • Verifier:
    • JWTs: Verwenden Sie JwtVerifier von @nestjs/authentication (prüft Signatur, Aussteller, Audience und Ablauf).
    • Opaque-Tokens: Rufen Sie den RFC-7662-Introspection-Endpunkt auf.
    • Das zurückgegebene AuthInfo muss resource === auth.resource und ein zukünftiges expiresAt enthalten, andernfalls wird das Token mit 401 abgelehnt.
    • Werfen Sie einen 401-Fehler, um ein Token abzulehnen; jeder andere Fehler wird zu einem 500er.
  • Fehlende scopes bei einem Primitiv → 403 mit Step-Up-Challenge. auth.requiredScopes gilt für jede Anfrage.
  • Mit @nestjs/authentication ≥ 0.1.3 und @nestjs/authorization ≥ 0.1.1:
    • Deren globale Guards authentifizieren jeden Aufruf, und @CurrentUser() und @Can(Policy, 'action') funktionieren in Tools.
    • Setzen Sie decorators: [Public()] auf den Endpunkt, damit auth die Challenge bearbeitet.
    • Ein Tool ohne @Can() wird verweigert.
  • Nur-interne Server können OAuth überspringen und decorators: [UseGuards(ApiKeyGuard)] auf den Endpunkt setzen.

Transporte

  • Streamable HTTP wird standardmäßig bereitgestellt; path: false deaktiviert es. Der Endpunkt ist ein normaler Controller, daher gelten globales Präfix, Versionierung (endpointVersion), Middleware und CORS. Er ist zustandslos: Es werden keine Sticky Sessions benötigt. Beide Protokoll-Epochen werden bedient (http.legacy: 'reject' bedient nur 2026-07-28). http.responseMode kann 'auto', 'json' oder 'sse' sein.
  • stdio, für Assistenten, die einen lokalen Prozess starten: Erstellen Sie stdio.ts mit NestFactory.createApplicationContext(AppModule, { logger: new McpStdioLogger() }), dann await app.get(McpStdioServer).listen(); await app.close();. Alles andere, was auf stdout geschrieben wird, unterbricht das Protokoll. Anmeldeinformationen stammen aus process.env; Scopes werden nicht geprüft, aber Guards werden ausgeführt.

Siehe references/mcp-reference.md für die vollständige Optionstabelle, Produktionseinstellungen, die stdio-Client-Konfiguration, Testmuster und die Migrationskarte.

Arbeitsablauf

  • 1. Installieren; auf Zod 4.2+ prüfen; McpModule.forRoot mit name, version, instructions.
  • 2. Tools, Ressourcen und Prompts auf Providern hinzufügen, mit modellorientierten Beschreibungen, .describe() bei jedem Argument, readOnlyHint bei schreibgeschützten Tools und outputSchema, wenn das Ergebnis strukturiert ist.
  • 3. Sicherstellen, dass nur-HTTP-globale Enhancer getType() prüfen; MCP-Guards und -Filter nach Bedarf hinzufügen.
  • 4. Netzwerkerreichbare Server: auth (Verifier, Ressource, Autorisierungsserver, Scopes), allowedHosts/allowedOrigins, CORS-Header und das Body-Limit hinzufügen. Lokale Server: stdio.ts mit McpStdioLogger hinzufügen.
  • 5. Tests: Methoden direkt unit-testen; e2e mit connectMcpClient(moduleRef, { token }) gegen einen Test-Verifier ausführen; die HTTP-Route über einen Socket testen. Bauen, linten, Tests ausführen, die App starten und Tools mit dem Inspector auflisten.

Referenzdateien

  • references/mcp-reference.md: Moduloptionen, Decorator-Optionen, HTTP-Härtung (Origin, Host, CORS-Header, Body-Größe), die stdio-Client-Konfiguration, NestJS Observe-Namensgebung, die Produktions-Checkliste, Tests (Tokens und Scopes, Protokoll-Epochen, McpRegistry-Prüfungen, HTTP- und stdio-Tests) und Migration von @rekog/mcp-nest 1.x/2.x. Lesen Sie diese Datei beim Konfigurieren der Produktion, Schreiben von Tests oder bei der Migration.

Quellen: Übersicht · Primitive · Transporte · Testen · Migration

Verwandte Skills