nestjs-mcp-decorators
v1.0.0
Published
NestJS decorators for automatic MCP tool exposure over HTTP with streamable responses
Maintainers
Readme
NestJS MCP Decorators
A NestJS package that automatically exposes controller methods as MCP (Model Context Protocol) tools over HTTP with streamable responses.
Features
- 🎯 Automatic MCP Tool Registration: Decorate controller methods with
@McpTool()to expose them as MCP tools - 🔄 Streamable HTTP: Built-in support for NDJSON streaming responses
- 🧠 Auto Schema Inference: Automatically generates Zod schemas from TypeScript parameter types
- 🚀 Zero Configuration: Works out of the box with any NestJS application
- 🔌 AI Client Ready: Compatible with Cursor, Claude Desktop, and other MCP clients
Installation
npm install nestjs-mcp-decoratorsQuick Start
- Import the controller in your app module:
import { Module } from '@nestjs/common';
import { McpHttpController } from 'nestjs-mcp-decorators';
@Module({
controllers: [McpHttpController],
// ... other imports
})
export class AppModule {}- Decorate your controller methods:
import { Controller, Get, Query } from '@nestjs/common';
import { McpTool, Streamable } from 'nestjs-mcp-decorators';
@Controller('calculator')
export class CalculatorController {
@Get('add')
@Streamable()
@McpTool()
add(@Query('a') a: number, @Query('b') b: number) {
return a + b;
}
@Get('multiply')
@Streamable()
@McpTool()
multiply(@Query('a') a: number, @Query('b') b: number) {
return a * b;
}
}- Configure your AI client:
For Cursor, add to ~/.cursor/mcp.json:
{
"mcpServers": {
"my-service": {
"url": "http://localhost:3000/mcp",
"transport": "streamable"
}
}
}API Reference
Decorators
@McpTool()
Registers a controller method as an MCP tool. The tool name will be the method name.
@McpTool()
add(@Query('a') a: number, @Query('b') b: number) {
return a + b;
}@Streamable()
Applies NDJSON headers and enables chunked transfer encoding for streaming responses.
@Streamable()
@McpTool()
getData() {
return { result: 'data' };
}Controller
McpHttpController
Automatically exposes all @McpTool() decorated methods at the /mcp endpoint.
GET /mcp- SSE endpoint for MCP clientsPOST /mcp- RPC endpoint for tool calls
Registry
McpToolRegistry
Static registry that stores tool metadata.
import { McpToolRegistry } from 'nestjs-mcp-decorators';
// Get all registered tools
const tools = McpToolRegistry.getAll();
// Clear registry (useful for testing)
McpToolRegistry.clear();Supported Parameter Types
The package automatically infers Zod schemas for these TypeScript types:
number→z.number()string→z.string()boolean→z.boolean()any→z.any()(fallback)
Example Usage
Calculator Service
import { Controller, Get, Query } from '@nestjs/common';
import { McpTool, Streamable } from 'nestjs-mcp-decorators';
@Controller('calculator')
export class CalculatorController {
@Get('add')
@Streamable()
@McpTool()
add(@Query('a') a: number, @Query('b') b: number) {
return a + b;
}
@Get('subtract')
@Streamable()
@McpTool()
subtract(@Query('a') a: number, @Query('b') b: number) {
return a - b;
}
@Get('multiply')
@Streamable()
@McpTool()
multiply(@Query('a') a: number, @Query('b') b: number) {
return a * b;
}
@Get('divide')
@Streamable()
@McpTool()
divide(@Query('a') a: number, @Query('b') b: number) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
}Weather Service
import { Controller, Get, Query } from '@nestjs/common';
import { McpTool, Streamable } from 'nestjs-mcp-decorators';
@Controller('weather')
export class WeatherController {
@Get('temperature')
@Streamable()
@McpTool()
getTemperature(@Query('city') city: string) {
// Simulate weather API call
return { city, temperature: 22, unit: 'celsius' };
}
@Get('forecast')
@Streamable()
@McpTool()
getForecast(@Query('city') city: string, @Query('days') days: number) {
return { city, forecast: 'sunny', days };
}
}Testing
import { Test, TestingModule } from '@nestjs/testing';
import { McpHttpController } from 'nestjs-mcp-decorators';
describe('MCP Integration', () => {
let controller: McpHttpController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [McpHttpController],
}).compile();
controller = module.get<McpHttpController>(McpHttpController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});Troubleshooting
Tool Not Found
Ensure your controller is properly registered in a module that's imported by your AppModule.
Schema Inference Issues
For complex types, the package falls back to z.any(). Consider using explicit Zod schemas for better type safety.
Cursor Configuration
Make sure your ~/.cursor/mcp.json points to the correct URL and uses "transport": "streamable".
License
MIT
