@rekog/mcp-nest 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/decorators/tool.decorator.d.ts.map +1 -1
- package/dist/decorators/tool.decorator.js +0 -3
- package/dist/decorators/tool.decorator.js.map +1 -1
- package/dist/transport/sse.controller.factory.d.ts.map +1 -1
- package/dist/transport/sse.controller.factory.js +1 -1
- package/dist/transport/sse.controller.factory.js.map +1 -1
- package/dist/transport/streamable-http.controller.factory.js +2 -2
- package/dist/transport/streamable-http.controller.factory.js.map +1 -1
- package/package.json +1 -1
- package/src/decorators/tool.decorator.ts +1 -3
- package/src/transport/sse.controller.factory.ts +0 -1
- package/src/transport/streamable-http.controller.factory.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/tool.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAWD,eAAO,MAAM,IAAI,YAAa,WAAW,
|
|
1
|
+
{"version":3,"file":"tool.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/tool.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAWD,eAAO,MAAM,IAAI,YAAa,WAAW,qDAMxC,CAAC"}
|
|
@@ -8,9 +8,6 @@ const Tool = (options) => {
|
|
|
8
8
|
if (options.parameters === undefined) {
|
|
9
9
|
options.parameters = zod_1.z.object({});
|
|
10
10
|
}
|
|
11
|
-
if (options.outputSchema === undefined) {
|
|
12
|
-
options.outputSchema = zod_1.z.any();
|
|
13
|
-
}
|
|
14
11
|
return (0, common_1.SetMetadata)(constants_1.MCP_TOOL_METADATA_KEY, options);
|
|
15
12
|
};
|
|
16
13
|
exports.Tool = Tool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.decorator.js","sourceRoot":"","sources":["../../src/decorators/tool.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAC7C,2CAAoD;AACpD,6BAAwB;AAmCjB,MAAM,IAAI,GAAG,CAAC,OAAoB,EAAE,EAAE;IAC3C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"tool.decorator.js","sourceRoot":"","sources":["../../src/decorators/tool.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAC7C,2CAAoD;AACpD,6BAAwB;AAmCjB,MAAM,IAAI,GAAG,CAAC,OAAoB,EAAE,EAAE;IAC3C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,IAAA,oBAAW,EAAC,iCAAqB,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC,CAAC;AANW,QAAA,IAAI,QAMf","sourcesContent":["import { SetMetadata } from '@nestjs/common';\nimport { MCP_TOOL_METADATA_KEY } from './constants';\nimport { z } from 'zod';\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: ToolAnnotations;\n}\n\nexport interface ToolAnnotations {\n title?: string;\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n}\n\nexport interface ToolOptions {\n name?: string;\n description?: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: ToolAnnotations;\n}\n\n/**\n * Decorator that marks a controller method as an MCP tool.\n * @param {Object} options - The options for the decorator\n * @param {string} options.name - The name of the tool\n * @param {string} options.description - The description of the tool\n * @param {z.ZodTypeAny} [options.parameters] - The parameters of the tool\n * @param {z.ZodTypeAny} [options.outputSchema] - The output schema of the tool\n * @returns {MethodDecorator} - The decorator\n */\nexport const Tool = (options: ToolOptions) => {\n if (options.parameters === undefined) {\n options.parameters = z.object({});\n }\n\n return SetMetadata(MCP_TOOL_METADATA_KEY, options);\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.controller.factory.d.ts","sourceRoot":"","sources":["../../src/transport/sse.controller.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EAIX,MAAM,EAKN,IAAI,EAIL,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAM9D,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,MAAM,GAAE,IAAI,CAAC,WAAW,CAAC,EAAO,EAChC,UAAU,GAAE,cAAc,EAAO;kBAiBmB,UAAU,eACJ,MAAM,qBACzB,iBAAiB,aACzB,SAAS,gBACN,kBAAkB,eACK,cAAc;;;;0BALnB,UAAU;8BACJ,MAAM;oCACzB,iBAAiB;4BACzB,SAAS;+BACN,kBAAkB;8BACK,cAAc;;iBAmB/C,QAAQ;sBAiDhB,OAAO,OACP,QAAQ,QACN,OAAO;;
|
|
1
|
+
{"version":3,"file":"sse.controller.factory.d.ts","sourceRoot":"","sources":["../../src/transport/sse.controller.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EAIX,MAAM,EAKN,IAAI,EAIL,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAM9D,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,MAAM,GAAE,IAAI,CAAC,WAAW,CAAC,EAAO,EAChC,UAAU,GAAE,cAAc,EAAO;kBAiBmB,UAAU,eACJ,MAAM,qBACzB,iBAAiB,aACzB,SAAS,gBACN,kBAAkB,eACK,cAAc;;;;0BALnB,UAAU;8BACJ,MAAM;oCACzB,iBAAiB;4BACzB,SAAS;+BACN,kBAAkB;8BACK,cAAc;;iBAmB/C,QAAQ;sBAiDhB,OAAO,OACP,QAAQ,QACN,OAAO;;EA8B1B"}
|
|
@@ -72,7 +72,7 @@ function createSseController(sseEndpoint, messagesEndpoint, guards = [], decorat
|
|
|
72
72
|
return res.status(404).send('MCP server not found for session');
|
|
73
73
|
}
|
|
74
74
|
const contextId = core_1.ContextIdFactory.getByRequest(req);
|
|
75
|
-
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId
|
|
75
|
+
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId);
|
|
76
76
|
executor.registerRequestHandlers(mcpServer, req);
|
|
77
77
|
await transport.handlePostMessage(req, res, body);
|
|
78
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.controller.factory.js","sourceRoot":"","sources":["../../src/transport/sse.controller.factory.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AA+BA,
|
|
1
|
+
{"version":3,"file":"sse.controller.factory.js","sourceRoot":"","sources":["../../src/transport/sse.controller.factory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Body,\n CanActivate,\n Controller,\n Get,\n Inject,\n Logger,\n OnModuleInit,\n Post,\n Req,\n Res,\n Type,\n UseGuards,\n VERSION_NEUTRAL,\n applyDecorators,\n} from '@nestjs/common';\nimport { ApplicationConfig, ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport type { Request, Response } from 'express';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport { buildMcpCapabilities } from '../utils/capabilities-builder';\nimport { McpOptions } from '../interfaces';\nimport { McpExecutorService } from '../services/mcp-executor.service';\nimport { McpRegistryService } from '../services/mcp-registry.service';\nimport { SsePingService } from '../services/sse-ping.service';\nimport { normalizeEndpoint } from '../utils/normalize-endpoint';\n\n/**\n * Creates a controller for handling SSE connections and tool executions\n */\nexport function createSseController(\n sseEndpoint: string,\n messagesEndpoint: string,\n guards: Type<CanActivate>[] = [],\n decorators: ClassDecorator[] = [],\n) {\n @Controller({\n version: VERSION_NEUTRAL,\n })\n @applyDecorators(...decorators)\n class SseController implements OnModuleInit {\n readonly logger = new Logger(SseController.name);\n // Note: Currently, storing transports and servers in memory makes this not viable for scaling out.\n // Redis can be used for this purpose, but considering that HTTP Streamable succeeds SSE then we can drop keeping this in memory.\n\n // Map to store active transports by session ID\n public transports = new Map<string, SSEServerTransport>();\n // Map to store MCP server instances by session ID\n public mcpServers = new Map<string, McpServer>();\n\n constructor(\n @Inject('MCP_OPTIONS') public readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') public readonly mcpModuleId: string,\n public readonly applicationConfig: ApplicationConfig,\n public readonly moduleRef: ModuleRef,\n public readonly toolRegistry: McpRegistryService,\n @Inject(SsePingService) public readonly pingService: SsePingService,\n ) {}\n\n /**\n * Initialize the controller and configure ping service\n */\n onModuleInit() {\n // Configure ping service with options\n this.pingService.configure({\n pingEnabled: this.options.sse?.pingEnabled, // Enable by default\n pingIntervalMs: this.options.sse?.pingIntervalMs,\n });\n }\n\n /**\n * SSE connection endpoint\n */\n @Get(sseEndpoint)\n @UseGuards(...guards)\n async sse(@Res() res: Response) {\n const transport = new SSEServerTransport(\n normalizeEndpoint(\n `${this.applicationConfig.getGlobalPrefix()}/${messagesEndpoint}`,\n ),\n res,\n );\n const sessionId = transport.sessionId;\n\n // Create a new MCP server instance with dynamic capabilities\n const capabilities = buildMcpCapabilities(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n );\n this.logger.debug('Built MCP capabilities:', capabilities);\n\n // Create a new MCP server for this session with dynamic capabilities\n const mcpServer = new McpServer(\n { name: this.options.name, version: this.options.version },\n {\n capabilities,\n instructions: this.options.instructions || '',\n },\n );\n\n // Store the transport and server for this session\n this.transports.set(sessionId, transport);\n this.mcpServers.set(sessionId, mcpServer);\n\n // Register the connection with the ping service\n this.pingService.registerConnection(sessionId, transport, res);\n\n transport.onclose = () => {\n // Clean up when the connection closes\n this.transports.delete(sessionId);\n this.mcpServers.delete(sessionId);\n this.pingService.removeConnection(sessionId);\n };\n\n await mcpServer.connect(transport);\n }\n\n /**\n * Tool execution endpoint - protected by the provided guards\n */\n @Post(messagesEndpoint)\n @UseGuards(...guards)\n async messages(\n @Req() req: Request,\n @Res() res: Response,\n @Body() body: unknown,\n ) {\n const sessionId = req.query.sessionId as string;\n const transport = this.transports.get(sessionId);\n\n if (!transport) {\n return res.status(404).send('Session not found');\n }\n\n const mcpServer = this.mcpServers.get(sessionId);\n if (!mcpServer) {\n return res.status(404).send('MCP server not found for session');\n }\n\n // Resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n );\n\n // Register request handlers with the user context from this specific request\n executor.registerRequestHandlers(mcpServer, req);\n\n // Process the message\n await transport.handlePostMessage(req, res, body);\n }\n }\n\n return SseController;\n}\n"]}
|
|
@@ -48,7 +48,7 @@ function createStreamableHttpController(endpoint, guards = [], decorators = [])
|
|
|
48
48
|
});
|
|
49
49
|
await server.connect(transport);
|
|
50
50
|
const contextId = core_1.ContextIdFactory.getByRequest(req);
|
|
51
|
-
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId, { strict:
|
|
51
|
+
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId, { strict: true });
|
|
52
52
|
this.logger.debug('Registering request handlers for stateless MCP server');
|
|
53
53
|
executor.registerRequestHandlers(server, req);
|
|
54
54
|
return { server, transport };
|
|
@@ -154,7 +154,7 @@ function createStreamableHttpController(endpoint, guards = [], decorators = [])
|
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
156
|
const contextId = core_1.ContextIdFactory.getByRequest(req);
|
|
157
|
-
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId, { strict:
|
|
157
|
+
const executor = await this.moduleRef.resolve(mcp_executor_service_1.McpExecutorService, contextId, { strict: true });
|
|
158
158
|
executor.registerRequestHandlers(mcpServer, req);
|
|
159
159
|
await transport.handleRequest(req, res, body);
|
|
160
160
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamable-http.controller.factory.js","sourceRoot":"","sources":["../../src/transport/streamable-http.controller.factory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Body,\n CanActivate,\n Controller,\n Delete,\n Get,\n Inject,\n Logger,\n Post,\n Req,\n Res,\n Type,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { randomUUID } from 'crypto';\nimport type { Request, Response } from 'express';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { McpOptions } from '../interfaces';\nimport { McpExecutorService } from '../services/mcp-executor.service';\nimport { McpRegistryService } from '../services/mcp-registry.service';\nimport { buildMcpCapabilities } from '../utils/capabilities-builder';\n\n/**\n * Creates a controller for handling Streamable HTTP connections and tool executions\n */\nexport function createStreamableHttpController(\n endpoint: string,\n guards: Type<CanActivate>[] = [],\n decorators: ClassDecorator[] = [],\n) {\n @Controller()\n @applyDecorators(...decorators)\n class StreamableHttpController {\n public readonly logger = new Logger(StreamableHttpController.name);\n public transports: { [sessionId: string]: StreamableHTTPServerTransport } =\n {};\n public mcpServers: { [sessionId: string]: McpServer } = {};\n\n public isStatelessMode: boolean = false;\n\n constructor(\n @Inject('MCP_OPTIONS') public readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') public readonly mcpModuleId: string,\n public readonly moduleRef: ModuleRef,\n public readonly toolRegistry: McpRegistryService,\n ) {\n // Determine if we're in stateless mode\n this.isStatelessMode = !!options.streamableHttp?.statelessMode;\n }\n\n /**\n * Create a new MCP server instance for stateless requests\n */\n async createStatelessServer(req: Request): Promise<{\n server: McpServer;\n transport: StreamableHTTPServerTransport;\n }> {\n // Create a new transport for this request\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n // Create a new MCP server instance with dynamic capabilities\n const capabilities = buildMcpCapabilities(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n );\n this.logger.debug('Built MCP capabilities:', capabilities);\n\n const server = new McpServer(\n { name: this.options.name, version: this.options.version },\n {\n capabilities: capabilities,\n instructions: this.options.instructions || '',\n },\n );\n\n // Connect the transport to the MCP server first\n await server.connect(transport);\n\n // Now resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: false },\n );\n\n // Register request handlers after connection\n this.logger.debug(\n 'Registering request handlers for stateless MCP server',\n );\n executor.registerRequestHandlers(server, req);\n\n return { server, transport };\n }\n\n /**\n * Main HTTP endpoint for both initialization and subsequent requests\n */\n @Post(endpoint)\n @UseGuards(...guards)\n async handlePostRequest(\n @Req() req: Request,\n @Res() res: Response,\n @Body() body: unknown,\n ) {\n this.logger.debug('Received MCP request:', body);\n\n try {\n if (this.isStatelessMode) {\n return this.handleStatelessRequest(req, res, body);\n } else {\n return this.handleStatefulRequest(req, res, body);\n }\n } catch (error) {\n this.logger.error('Error handling MCP request:', error);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n }\n }\n\n /**\n * Handle requests in stateless mode\n */\n public async handleStatelessRequest(\n req: Request,\n res: Response,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `Handling stateless MCP request at ${req.url} with body: ${JSON.stringify(\n body,\n )}`,\n );\n\n let server: McpServer | null = null;\n let transport: StreamableHTTPServerTransport | null = null;\n\n try {\n // Create a new server and transport for each request\n const stateless = await this.createStatelessServer(req);\n server = stateless.server;\n transport = stateless.transport;\n\n // Handle the request\n await transport.handleRequest(req, res, body);\n\n // Clean up when the response closes\n res.on('close', () => {\n this.logger.debug('Stateless request closed, cleaning up');\n transport?.close();\n server?.close();\n });\n } catch (error) {\n this.logger.error('Error in stateless request handling:', error);\n // Clean up on error\n transport?.close();\n server?.close();\n throw error;\n }\n }\n\n /**\n * Handle requests in stateful mode\n */\n public async handleStatefulRequest(\n req: Request,\n res: Response,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `Handling stateful MCP request at ${req.url} with body: ${JSON.stringify(\n body,\n )}`,\n );\n // Check for existing session ID\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.transports[sessionId]) {\n // Reuse existing transport\n transport = this.transports[sessionId];\n } else if (!sessionId && this.isInitializeRequest(body)) {\n // New initialization request\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator:\n this.options.streamableHttp?.sessionIdGenerator ||\n (() => randomUUID()),\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n // Create a new MCP server for this session with dynamic capabilities\n const capabilities = buildMcpCapabilities(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n );\n this.logger.debug('Built MCP capabilities:', capabilities);\n\n const mcpServer = new McpServer(\n { name: this.options.name, version: this.options.version },\n {\n capabilities,\n instructions: this.options.instructions || '',\n },\n );\n\n // Connect the transport to the MCP server BEFORE handling the request\n await mcpServer.connect(transport);\n\n // Handle the initialization request\n await transport.handleRequest(req, res, body);\n\n // Store the transport and server by session ID for future requests\n if (transport.sessionId) {\n this.transports[transport.sessionId] = transport;\n this.mcpServers[transport.sessionId] = mcpServer;\n\n // Set up cleanup when connection closes\n transport.onclose = () => {\n this.cleanupSession(transport.sessionId!);\n };\n }\n\n this.logger.log(\n `Initialized new session with ID: ${transport.sessionId}`,\n );\n return;\n } else {\n // Invalid request - no session ID or not initialization request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // For subsequent requests to an existing session\n const mcpServer = this.mcpServers[sessionId];\n if (!mcpServer) {\n res.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n // Resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: false },\n );\n\n // Register request handlers with the user context from this specific request\n executor.registerRequestHandlers(mcpServer, req);\n\n // Handle the request with existing transport\n await transport.handleRequest(req, res, body);\n }\n\n /**\n * GET endpoint for SSE streams - not supported in stateless mode\n */\n @Get(endpoint)\n @UseGuards(...guards)\n async handleGetRequest(@Req() req: Request, @Res() res: Response) {\n if (this.isStatelessMode) {\n res.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n this.logger.debug(`Establishing SSE stream for session ${sessionId}`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(req, res);\n }\n\n /**\n * DELETE endpoint for terminating sessions - not supported in stateless mode\n */\n @Delete(endpoint)\n @UseGuards(...guards)\n async handleDeleteRequest(@Req() req: Request, @Res() res: Response) {\n if (this.isStatelessMode) {\n res.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n this.logger.debug(`Terminating session ${sessionId}`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(req, res);\n this.cleanupSession(sessionId);\n }\n\n // Helper function to detect initialize requests\n public isInitializeRequest(body: unknown): boolean {\n if (Array.isArray(body)) {\n return body.some(\n (msg) =>\n typeof msg === 'object' &&\n msg !== null &&\n 'method' in msg &&\n msg.method === 'initialize',\n );\n }\n return (\n typeof body === 'object' &&\n body !== null &&\n 'method' in body &&\n body.method === 'initialize'\n );\n }\n\n // Clean up session resources\n public cleanupSession(sessionId: string): void {\n if (sessionId) {\n this.logger.debug(`Cleaning up session: ${sessionId}`);\n delete this.transports[sessionId];\n delete this.mcpServers[sessionId];\n }\n }\n }\n\n return StreamableHttpController;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"streamable-http.controller.factory.js","sourceRoot":"","sources":["../../src/transport/streamable-http.controller.factory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Body,\n CanActivate,\n Controller,\n Delete,\n Get,\n Inject,\n Logger,\n Post,\n Req,\n Res,\n Type,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { randomUUID } from 'crypto';\nimport type { Request, Response } from 'express';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { McpOptions } from '../interfaces';\nimport { McpExecutorService } from '../services/mcp-executor.service';\nimport { McpRegistryService } from '../services/mcp-registry.service';\nimport { buildMcpCapabilities } from '../utils/capabilities-builder';\n\n/**\n * Creates a controller for handling Streamable HTTP connections and tool executions\n */\nexport function createStreamableHttpController(\n endpoint: string,\n guards: Type<CanActivate>[] = [],\n decorators: ClassDecorator[] = [],\n) {\n @Controller()\n @applyDecorators(...decorators)\n class StreamableHttpController {\n public readonly logger = new Logger(StreamableHttpController.name);\n public transports: { [sessionId: string]: StreamableHTTPServerTransport } =\n {};\n public mcpServers: { [sessionId: string]: McpServer } = {};\n\n public isStatelessMode: boolean = false;\n\n constructor(\n @Inject('MCP_OPTIONS') public readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') public readonly mcpModuleId: string,\n public readonly moduleRef: ModuleRef,\n public readonly toolRegistry: McpRegistryService,\n ) {\n // Determine if we're in stateless mode\n this.isStatelessMode = !!options.streamableHttp?.statelessMode;\n }\n\n /**\n * Create a new MCP server instance for stateless requests\n */\n async createStatelessServer(req: Request): Promise<{\n server: McpServer;\n transport: StreamableHTTPServerTransport;\n }> {\n // Create a new transport for this request\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n // Create a new MCP server instance with dynamic capabilities\n const capabilities = buildMcpCapabilities(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n );\n this.logger.debug('Built MCP capabilities:', capabilities);\n\n const server = new McpServer(\n { name: this.options.name, version: this.options.version },\n {\n capabilities: capabilities,\n instructions: this.options.instructions || '',\n },\n );\n\n // Connect the transport to the MCP server first\n await server.connect(transport);\n\n // Now resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n\n // Register request handlers after connection\n this.logger.debug(\n 'Registering request handlers for stateless MCP server',\n );\n executor.registerRequestHandlers(server, req);\n\n return { server, transport };\n }\n\n /**\n * Main HTTP endpoint for both initialization and subsequent requests\n */\n @Post(endpoint)\n @UseGuards(...guards)\n async handlePostRequest(\n @Req() req: Request,\n @Res() res: Response,\n @Body() body: unknown,\n ) {\n this.logger.debug('Received MCP request:', body);\n\n try {\n if (this.isStatelessMode) {\n return this.handleStatelessRequest(req, res, body);\n } else {\n return this.handleStatefulRequest(req, res, body);\n }\n } catch (error) {\n this.logger.error('Error handling MCP request:', error);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n }\n }\n\n /**\n * Handle requests in stateless mode\n */\n public async handleStatelessRequest(\n req: Request,\n res: Response,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `Handling stateless MCP request at ${req.url} with body: ${JSON.stringify(\n body,\n )}`,\n );\n\n let server: McpServer | null = null;\n let transport: StreamableHTTPServerTransport | null = null;\n\n try {\n // Create a new server and transport for each request\n const stateless = await this.createStatelessServer(req);\n server = stateless.server;\n transport = stateless.transport;\n\n // Handle the request\n await transport.handleRequest(req, res, body);\n\n // Clean up when the response closes\n res.on('close', () => {\n this.logger.debug('Stateless request closed, cleaning up');\n transport?.close();\n server?.close();\n });\n } catch (error) {\n this.logger.error('Error in stateless request handling:', error);\n // Clean up on error\n transport?.close();\n server?.close();\n throw error;\n }\n }\n\n /**\n * Handle requests in stateful mode\n */\n public async handleStatefulRequest(\n req: Request,\n res: Response,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `Handling stateful MCP request at ${req.url} with body: ${JSON.stringify(\n body,\n )}`,\n );\n // Check for existing session ID\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.transports[sessionId]) {\n // Reuse existing transport\n transport = this.transports[sessionId];\n } else if (!sessionId && this.isInitializeRequest(body)) {\n // New initialization request\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator:\n this.options.streamableHttp?.sessionIdGenerator ||\n (() => randomUUID()),\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n // Create a new MCP server for this session with dynamic capabilities\n const capabilities = buildMcpCapabilities(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n );\n this.logger.debug('Built MCP capabilities:', capabilities);\n\n const mcpServer = new McpServer(\n { name: this.options.name, version: this.options.version },\n {\n capabilities,\n instructions: this.options.instructions || '',\n },\n );\n\n // Connect the transport to the MCP server BEFORE handling the request\n await mcpServer.connect(transport);\n\n // Handle the initialization request\n await transport.handleRequest(req, res, body);\n\n // Store the transport and server by session ID for future requests\n if (transport.sessionId) {\n this.transports[transport.sessionId] = transport;\n this.mcpServers[transport.sessionId] = mcpServer;\n\n // Set up cleanup when connection closes\n transport.onclose = () => {\n this.cleanupSession(transport.sessionId!);\n };\n }\n\n this.logger.log(\n `Initialized new session with ID: ${transport.sessionId}`,\n );\n return;\n } else {\n // Invalid request - no session ID or not initialization request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // For subsequent requests to an existing session\n const mcpServer = this.mcpServers[sessionId];\n if (!mcpServer) {\n res.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n // Resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n\n // Register request handlers with the user context from this specific request\n executor.registerRequestHandlers(mcpServer, req);\n\n // Handle the request with existing transport\n await transport.handleRequest(req, res, body);\n }\n\n /**\n * GET endpoint for SSE streams - not supported in stateless mode\n */\n @Get(endpoint)\n @UseGuards(...guards)\n async handleGetRequest(@Req() req: Request, @Res() res: Response) {\n if (this.isStatelessMode) {\n res.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n this.logger.debug(`Establishing SSE stream for session ${sessionId}`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(req, res);\n }\n\n /**\n * DELETE endpoint for terminating sessions - not supported in stateless mode\n */\n @Delete(endpoint)\n @UseGuards(...guards)\n async handleDeleteRequest(@Req() req: Request, @Res() res: Response) {\n if (this.isStatelessMode) {\n res.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n this.logger.debug(`Terminating session ${sessionId}`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(req, res);\n this.cleanupSession(sessionId);\n }\n\n // Helper function to detect initialize requests\n public isInitializeRequest(body: unknown): boolean {\n if (Array.isArray(body)) {\n return body.some(\n (msg) =>\n typeof msg === 'object' &&\n msg !== null &&\n 'method' in msg &&\n msg.method === 'initialize',\n );\n }\n return (\n typeof body === 'object' &&\n body !== null &&\n 'method' in body &&\n body.method === 'initialize'\n );\n }\n\n // Clean up session resources\n public cleanupSession(sessionId: string): void {\n if (sessionId) {\n this.logger.debug(`Cleaning up session: ${sessionId}`);\n delete this.transports[sessionId];\n delete this.mcpServers[sessionId];\n }\n }\n }\n\n return StreamableHttpController;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -39,8 +39,6 @@ export const Tool = (options: ToolOptions) => {
|
|
|
39
39
|
if (options.parameters === undefined) {
|
|
40
40
|
options.parameters = z.object({});
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
options.outputSchema = z.any();
|
|
44
|
-
}
|
|
42
|
+
|
|
45
43
|
return SetMetadata(MCP_TOOL_METADATA_KEY, options);
|
|
46
44
|
};
|
|
@@ -90,7 +90,7 @@ export function createStreamableHttpController(
|
|
|
90
90
|
const executor = await this.moduleRef.resolve(
|
|
91
91
|
McpExecutorService,
|
|
92
92
|
contextId,
|
|
93
|
-
{ strict:
|
|
93
|
+
{ strict: true },
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
// Register request handlers after connection
|
|
@@ -275,7 +275,7 @@ export function createStreamableHttpController(
|
|
|
275
275
|
const executor = await this.moduleRef.resolve(
|
|
276
276
|
McpExecutorService,
|
|
277
277
|
contextId,
|
|
278
|
-
{ strict:
|
|
278
|
+
{ strict: true },
|
|
279
279
|
);
|
|
280
280
|
|
|
281
281
|
// Register request handlers with the user context from this specific request
|