@moleculer/mcp-server
v0.2.0
Published
Moleculer MCP Server
Downloads
115
Readme

@moleculer/mcp-server
MCP server mixin for Moleculer API Gateway (moleculer-web).
Expose your Moleculer microservices to AI assistants (Claude, GPT, etc.) via the Model Context Protocol.
Features
- Auto-discovery — Moleculer actions are automatically registered as MCP tools with proper schemas
- Schema conversion — fastest-validator params are converted to Zod schemas automatically
- Tool annotations — REST method-based auto-detection of
readOnlyHint,destructiveHint,idempotentHint - Service filtering — Whitelist services or exclude specific actions via glob patterns
- MCP metadata — Fine-grained control over tool name, description, and annotations per action
- Built-in broker tools — List nodes, services, actions, events + generic call/emit tools
- Streamable HTTP transport — Standards-compliant MCP server with session management and resumability
Install
npm i @moleculer/mcp-server moleculer-webQuick Start
Add the MCP server mixin to your API Gateway service:
import ApiGateway from "moleculer-web";
import { McpServerMixin } from "@moleculer/mcp-server";
// api.service.js
export default {
name: "api",
mixins: [ApiGateway, McpServerMixin()],
settings: {
port: 3000
}
};That's it! The MCP server is available at http://localhost:3000/mcp. All your Moleculer actions are automatically exposed as MCP tools.
Auto-discovery
When the broker starts, the mixin scans the service registry and registers each action as an individual MCP tool. This means AI assistants get dedicated, well-typed tools instead of a single generic "call action" tool.
Example: A weather.forecast action becomes a weather_forecast MCP tool with its own input schema and description.
// weather.service.js
export default {
name: "weather",
actions: {
forecast: {
description: "Get weather forecast for a city",
params: {
city: "string",
days: { type: "number", default: 3 }
},
rest: "GET /forecast",
handler(ctx) {
return { city: ctx.params.city, forecast: "sunny" };
}
}
}
};This automatically generates an MCP tool:
- Name:
weather_forecast - Description:
"Get weather forecast for a city" - Input schema:
{ city: z.string(), days: z.number().default(3) } - Annotations:
{ readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false }(auto-detected fromGET)
Tool names are derived from the action name by replacing dots with underscores: service.action → service_action.
Internal actions (prefixed with $) are always excluded.
Configuration Options
McpServerMixin({
// Route options for moleculer-web (default path: /mcp)
routeOptions: {
path: "/mcp"
},
// Whitelist specific services (array or glob pattern string)
services: ["weather", "products"],
// OR: services: "tools.*",
// Exclude specific actions by glob pattern
excludeActions: ["$node.*", "api.*"],
// Show/hide built-in broker tools (list_nodes, list_services, etc.)
// Default: true
exposeBrokerTools: true,
// Prefix for auto-generated tool names
// Default: "" (no prefix)
// Example: "mol_" → tool name: "mol_weather_forecast"
toolNamePrefix: ""
})| Option | Type | Default | Description |
|---|---|---|---|
| routeOptions | ApiRouteSchema | { path: "/mcp" } | moleculer-web route configuration |
| services | string \| string[] | undefined | Whitelist of service names or glob pattern |
| excludeActions | string[] | undefined | Action name patterns to exclude |
| exposeBrokerTools | boolean | true | Whether to register built-in broker tools |
| toolNamePrefix | string | "" | Prefix for auto-generated tool names |
Action Description & MCP Metadata
Description field
Add a description field to your actions. It will be used as the MCP tool description, helping AI assistants understand what the tool does:
actions: {
forecast: {
description: "Get weather forecast for a city by name",
params: { city: "string" },
handler(ctx) { /* ... */ }
}
}If no description is provided, a default is generated: "Call the weather.forecast Moleculer action".
MCP metadata
Use the mcp field in your action schema for fine-grained control:
actions: {
forecast: {
params: { city: "string" },
mcp: {
// Override the tool description
description: "Get detailed weather forecast",
// Override the tool name (default: service_action)
name: "get_forecast",
// Explicit annotations (overrides auto-detection)
annotations: { readOnlyHint: true },
// Enable/disable this tool (default: true)
enabled: true
},
handler(ctx) { /* ... */ }
}
}You can also use shorthand:
mcp: true— register with default settings (same as omitting the field)mcp: false— exclude this action from MCP tools
Tool Annotations
MCP tool annotations help AI assistants understand the behavior of each tool. The mixin auto-detects annotations based on the action's REST method:
| REST Method | readOnlyHint | destructiveHint | idempotentHint | openWorldHint |
|---|---|---|---|---|
| GET | true | false | true | false |
| PUT | false | false | true | false |
| DELETE | false | true | true | false |
| POST (or no REST) | false | false | false | false |
You can override auto-detected annotations using the mcp.annotations field:
actions: {
dangerousReset: {
rest: "POST /reset",
mcp: {
annotations: {
destructiveHint: true,
idempotentHint: false
}
},
handler(ctx) { /* ... */ }
}
}Schema Conversion
The mixin automatically converts fastest-validator parameter schemas to Zod schemas for MCP tool input validation. The following mappings are supported:
| fastest-validator | Zod equivalent |
|---|---|
| "string" | z.string() |
| "number" | z.number() |
| "boolean" | z.boolean() |
| "date" | z.string() |
| "email" | z.string().email() |
| "url" | z.string().url() |
| "any" | z.any() |
| { type: "string", min: 3, max: 100 } | z.string().min(3).max(100) |
| { type: "string", pattern: "^[a-z]+$" } | z.string().regex(...) |
| { type: "string", enum: ["a", "b"] } | z.enum(["a", "b"]) |
| { type: "number", positive: true, integer: true } | z.number().positive().int() |
| { type: "number", min: 0, max: 100 } | z.number().min(0).max(100) |
| { type: "array", items: "string" } | z.array(z.string()) |
| { type: "object", props: { name: "string" } } | z.object({ name: z.string() }) |
| { type: "enum", values: ["a", "b"] } | z.enum(["a", "b"]) |
| { type: "string", optional: true } | z.string().optional() |
| { type: "number", default: 5 } | z.number().default(5) |
| "number\|integer\|positive" (pipe syntax) | z.number().int().positive() |
| Unknown/custom type | z.any() |
Built-in Tools
When exposeBrokerTools is true (default), the following broker-level tools are registered:
| Tool | Description |
|---|---|
| moleculer_list_nodes | List all Moleculer nodes in the cluster |
| moleculer_list_services | List all registered services |
| moleculer_list_actions | List all available actions |
| moleculer_list_events | List all event listeners |
These tools are always registered regardless of exposeBrokerTools:
| Tool | Description |
|---|---|
| moleculer_call_action | Call any Moleculer action by name (generic fallback) |
| moleculer_emit_event | Emit or broadcast a Moleculer event |
MCP Client Setup
Claude Code
claude mcp add --transport http moleculer http://127.0.0.1:3000/mcpClaude Desktop
Add to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"moleculer": {
"url": "http://localhost:3000/mcp",
"type": "http"
}
}
}Generic MCP Client
{
"servers": {
"moleculer": {
"url": "http://localhost:3000/mcp",
"type": "http"
}
}
}License
The project is available under the MIT license.
Contact
Copyright (c) 2026 MoleculerJS
