Skip to content

platform-mcp

A reusable NestJS module for building Model Context Protocol (MCP) servers. Provides decorators, automatic handler discovery, and transport management — all wired through NestJS dependency injection.

Package: @breadstone/archipel-platform-mcp

Installation

typescript
import {
  McpModule,
  McpTool,
  McpResource,
  McpPrompt,
  McpServerService,
  McpRegistryService,
} from '@breadstone/archipel-platform-mcp';

Peer Dependencies

The host application must provide @nestjs/common, @nestjs/core, @modelcontextprotocol/sdk, zod, reflect-metadata, and rxjs.


Features

FeatureDescription
Decorator-drivenAnnotate methods with @McpTool, @McpResource, or @McpPrompt to register MCP handlers.
Auto-discoveryHandlers are discovered at module init via NestJS DiscoveryService.
Dynamic ModuleConfigure via McpModule.register() (sync) or McpModule.registerAsync() (factory/class).
Transport-agnosticBuilt-in Stdio, Streamable HTTP, and SSE transports. Plug in any custom Transport.
Zod schemasDefine input/output schemas for tools and argument schemas for prompts using Zod.
NPM-publishableStandalone library for reuse across multiple NestJS projects.

Module Configuration

Synchronous

typescript
import { Module } from '@nestjs/common';
import { McpModule } from '@breadstone/archipel-platform-mcp';

@Module({
  imports: [
    McpModule.register({
      name: 'my-mcp-server',
      version: '1.0.0',
      capabilities: {
        tools: { listChanged: true },
        resources: { subscribe: false, listChanged: true },
        prompts: { listChanged: true },
      },
      instructions: 'Optional instructions for MCP clients.',
    }),
  ],
})
export class AppModule {}

Asynchronous (factory)

typescript
McpModule.registerAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    name: config.get('MCP_SERVER_NAME'),
    version: config.get('MCP_SERVER_VERSION'),
  }),
});

Asynchronous (class / existing)

typescript
// useExisting — reuse an already-registered provider
McpModule.registerAsync({ useExisting: McpConfigService });

// useClass — instantiate a new provider
McpModule.registerAsync({ useClass: McpConfigService });

The provider must implement IMcpModuleOptionsFactory.


Decorators

All decorators are placed on methods of NestJS @Injectable() providers.

@McpTool

typescript
import { McpTool } from '@breadstone/archipel-platform-mcp';
import { z } from 'zod';

@Injectable()
class CalculatorTools {
  @McpTool({
    name: 'add',
    description: 'Adds two numbers',
    inputSchema: { a: z.number(), b: z.number() },
    annotations: { readOnlyHint: true },
  })
  public add(input: { a: number; b: number }) {
    return {
      content: [{ type: 'text' as const, text: String(input.a + input.b) }],
    };
  }
}
OptionTypeRequiredDescription
namestringYesUnique tool name
titlestringNoHuman-readable title
descriptionstringNoWhat the tool does
inputSchemaZodRawShapeNoZod schema for input parameters
outputSchemaZodRawShapeNoZod schema for structured output
annotationsRecord<string, unknown>NoHints (readOnlyHint, destructiveHint)

@McpResource

Supports both static URIs and URI templates (containing {…} placeholders).

typescript
@Injectable()
class ConfigResources {
  @McpResource({
    name: 'app-config',
    uri: 'config://application',
    mimeType: 'application/json',
  })
  public getAppConfig() {
    return {
      contents: [{ uri: 'config://application', text: '{"debug": false}' }],
    };
  }

  @McpResource({
    name: 'user-profile',
    uri: 'users://{userId}/profile',
    mimeType: 'application/json',
  })
  public getUserProfile(uri: URL, params: { userId: string }) {
    return {
      contents: [{ uri: uri.href, text: JSON.stringify({ userId: params.userId }) }],
    };
  }
}
OptionTypeRequiredDescription
namestringYesUnique resource name
uristringYesStatic URI or template
titlestringNoHuman-readable title
descriptionstringNoResource description
mimeTypestringNoMIME type of the content

@McpPrompt

typescript
@Injectable()
class PromptProviders {
  @McpPrompt({
    name: 'code-review',
    description: 'Generates a code review prompt.',
    argsSchema: { code: z.string(), language: z.string().optional() },
  })
  public codeReview(args: { code: string; language?: string }) {
    return {
      messages: [
        {
          role: 'user' as const,
          content: { type: 'text' as const, text: `Review:\n${args.code}` },
        },
      ],
    };
  }
}
OptionTypeRequiredDescription
namestringYesUnique prompt name
titlestringNoHuman-readable title
descriptionstringNoPrompt description
argsSchemaZodRawShapeNoZod schema for prompt args

Transports

Stdio

Best for CLI tools and process-spawned integrations.

typescript
const app = await NestFactory.createApplicationContext(AppModule);
const mcpServer = app.get(McpServerService);
await mcpServer.connectStdio();

Streamable HTTP

Recommended for HTTP-based deployments.

typescript
const transport = await mcpServer.createStreamableHttpTransport({
  sessionIdGenerator: () => crypto.randomUUID(),
});

const httpApp = express();
httpApp.post('/mcp', (req, res) => transport.handleRequest(req, res));
httpApp.get('/mcp', (req, res) => transport.handleRequest(req, res));
httpApp.delete('/mcp', (req, res) => transport.handleRequest(req, res));
httpApp.listen(3000);

Server-Sent Events (SSE)

Legacy transport for older clients.

typescript
httpApp.get('/sse', async (req, res) => {
  await mcpServer.createSseTransport({ endpoint: '/messages' }, res);
});

Custom Transport

typescript
await mcpServer.connectTransport(myCustomTransport);

Services

McpServerService

Main entry point for controlling the MCP server at runtime.

MethodDescription
server: McpServerThe underlying McpServer instance (readonly)
connectStdio()Connects a stdio transport
createStreamableHttpTransport(options?)Creates Streamable HTTP transport
createSseTransport(options, response)Creates SSE transport
connectTransport(transport)Connects any custom Transport

McpRegistryService

Central registry for all discovered handlers. Also supports manual registration.

Property / MethodDescription
tools: Map<string, …>All registered tools
resources: Map<string, …>All registered resources
prompts: Map<string, …>All registered prompts
registerTool(metadata, handler)Manually register a tool
registerResource(…)Manually register a resource
registerPrompt(…)Manually register a prompt

Architecture

  1. McpModule bootstraps the module with sync or async configuration.
  2. McpDiscoveryService runs onModuleInit, iterates all providers, reads decorator metadata, and populates the registry.
  3. McpServerService runs onModuleInit, creates the McpServer instance, and registers all handlers from the registry.
  4. The application connects a transport (stdio, HTTP, SSE, or custom) to start serving MCP requests.

Released under the MIT License.