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
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
| Feature | Description |
|---|---|
| Decorator-driven | Annotate methods with @McpTool, @McpResource, or @McpPrompt to register MCP handlers. |
| Auto-discovery | Handlers are discovered at module init via NestJS DiscoveryService. |
| Dynamic Module | Configure via McpModule.register() (sync) or McpModule.registerAsync() (factory/class). |
| Transport-agnostic | Built-in Stdio, Streamable HTTP, and SSE transports. Plug in any custom Transport. |
| Zod schemas | Define input/output schemas for tools and argument schemas for prompts using Zod. |
| NPM-publishable | Standalone library for reuse across multiple NestJS projects. |
Module Configuration
Synchronous
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)
McpModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
name: config.get('MCP_SERVER_NAME'),
version: config.get('MCP_SERVER_VERSION'),
}),
});Asynchronous (class / existing)
// 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
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) }],
};
}
}| Option | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique tool name |
title | string | No | Human-readable title |
description | string | No | What the tool does |
inputSchema | ZodRawShape | No | Zod schema for input parameters |
outputSchema | ZodRawShape | No | Zod schema for structured output |
annotations | Record<string, unknown> | No | Hints (readOnlyHint, destructiveHint) |
@McpResource
Supports both static URIs and URI templates (containing {…} placeholders).
@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 }) }],
};
}
}| Option | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique resource name |
uri | string | Yes | Static URI or template |
title | string | No | Human-readable title |
description | string | No | Resource description |
mimeType | string | No | MIME type of the content |
@McpPrompt
@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}` },
},
],
};
}
}| Option | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique prompt name |
title | string | No | Human-readable title |
description | string | No | Prompt description |
argsSchema | ZodRawShape | No | Zod schema for prompt args |
Transports
Stdio
Best for CLI tools and process-spawned integrations.
const app = await NestFactory.createApplicationContext(AppModule);
const mcpServer = app.get(McpServerService);
await mcpServer.connectStdio();Streamable HTTP
Recommended for HTTP-based deployments.
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.
httpApp.get('/sse', async (req, res) => {
await mcpServer.createSseTransport({ endpoint: '/messages' }, res);
});Custom Transport
await mcpServer.connectTransport(myCustomTransport);Services
McpServerService
Main entry point for controlling the MCP server at runtime.
| Method | Description |
|---|---|
server: McpServer | The 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 / Method | Description |
|---|---|
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
- McpModule bootstraps the module with sync or async configuration.
- McpDiscoveryService runs
onModuleInit, iterates all providers, reads decorator metadata, and populates the registry. - McpServerService runs
onModuleInit, creates theMcpServerinstance, and registers all handlers from the registry. - The application connects a transport (stdio, HTTP, SSE, or custom) to start serving MCP requests.