@rekog/mcp-nest 1.9.6 → 1.9.7
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/mcp/interfaces/mcp-options.interface.d.ts +6 -1
- package/dist/mcp/interfaces/mcp-options.interface.d.ts.map +1 -1
- package/dist/mcp/interfaces/mcp-options.interface.js.map +1 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.js +1 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.js.map +1 -1
- package/dist/mcp/utils/mcp-server.factory.d.ts.map +1 -1
- package/dist/mcp/utils/mcp-server.factory.js +8 -1
- package/dist/mcp/utils/mcp-server.factory.js.map +1 -1
- package/package.json +1 -1
- package/src/mcp/interfaces/mcp-options.interface.ts +11 -1
- package/src/mcp/services/handlers/mcp-tools.handler.spec.ts +41 -0
- package/src/mcp/services/handlers/mcp-tools.handler.ts +1 -1
- package/src/mcp/utils/mcp-server.factory.ts +8 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
1
|
+
import { Icon, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { CanActivate, ModuleMetadata, Type } from '@nestjs/common';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
export type { Icon };
|
|
4
5
|
export declare enum McpTransportType {
|
|
5
6
|
SSE = "sse",
|
|
6
7
|
STREAMABLE_HTTP = "streamable-http",
|
|
@@ -8,7 +9,11 @@ export declare enum McpTransportType {
|
|
|
8
9
|
}
|
|
9
10
|
export interface McpOptions {
|
|
10
11
|
name: string;
|
|
12
|
+
title?: string;
|
|
11
13
|
version: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
websiteUrl?: string;
|
|
16
|
+
icons?: Icon[];
|
|
12
17
|
capabilities?: ServerCapabilities;
|
|
13
18
|
instructions?: string;
|
|
14
19
|
transport?: McpTransportType | McpTransportType[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-options.interface.d.ts","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-options.interface.d.ts","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,YAAY,EAAE,IAAI,EAAE,CAAC;AAErB,oBAAY,gBAAgB;IAC1B,GAAG,QAAQ;IACX,eAAe,oBAAoB;IACnC,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,UAAU;IAGzB,IAAI,EAAE,MAAM,CAAC;IAEb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAEhB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,SAAS,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAClD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IAgB7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;QAIlC,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;IAOF,OAAO,CAAC,EACJ,KAAK,GACL;QACE,KAAK,EAAE,CAAC,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC;KAC3D,CAAC;CACP;AAGD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;CAChE;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;IAC5E,WAAW,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAC5E,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC;CACxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-options.interface.js","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"mcp-options.interface.js","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":";;;AAMA,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,+BAAW,CAAA;IACX,uDAAmC,CAAA;IACnC,mCAAe,CAAA;AACjB,CAAC,EAJW,gBAAgB,gCAAhB,gBAAgB,QAI3B","sourcesContent":["import { Icon, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport { CanActivate, ModuleMetadata, Type } from '@nestjs/common';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport type { Icon };\n\nexport enum McpTransportType {\n SSE = 'sse',\n STREAMABLE_HTTP = 'streamable-http',\n STDIO = 'stdio',\n}\n\nexport interface McpOptions {\n // When and if, additional properties are introduced in ServerOptions or ServerInfo,\n // consider deprecating these fields in favor of using ServerOptions and ServerInfo directly.\n name: string;\n /** Human-readable display name for the server (passed to MCP Implementation). */\n title?: string;\n version: string;\n /** Short description of what this MCP server does. */\n description?: string;\n /** URL of the website associated with this server. */\n websiteUrl?: string;\n /** Icons representing this server. Use `Icon` exported from `@rekog/mcp-nest`. */\n icons?: Icon[];\n capabilities?: ServerCapabilities;\n instructions?: string;\n\n transport?: McpTransportType | McpTransportType[];\n serverMutator?: (server: McpServer) => McpServer;\n sseEndpoint?: string;\n messagesEndpoint?: string;\n mcpEndpoint?: string;\n /**\n * @deprecated Use `app.setGlobalPrefix()` for global api prefix. Use apiPrefix to attach a prefix to the handshake.\n */\n globalApiPrefix?: never;\n apiPrefix?: string;\n guards?: Type<CanActivate>[];\n /**\n * Allow unauthenticated sessions to connect and access @PublicTool() tools.\n *\n * When true (freemium mode):\n * - Unauthenticated requests are allowed through guards\n * - Can access tools marked with @PublicTool()\n * - Must authenticate to access protected tools (based on scopes/roles)\n *\n * When false or undefined (standard OAuth flow - default):\n * - Unauthenticated requests receive 401 response\n * - Triggers MCP OAuth authorization flow\n * - All tools require authentication\n *\n * @default false\n */\n allowUnauthenticatedAccess?: boolean;\n decorators?: ClassDecorator[];\n sse?: {\n pingEnabled?: boolean;\n pingIntervalMs?: number;\n };\n streamableHttp?: {\n enableJsonResponse?: boolean;\n sessionIdGenerator?: () => string;\n /**\n * @experimental: The current implementation does not fully comply with the MCP Specification.\n */\n statelessMode?: boolean;\n };\n /**\n * Configure logging for the MCP module.\n * - `false` to disable all MCP logging\n * - `{ level: LogLevel[] }` to specify which log levels to show\n * - `undefined` (default) to use standard NestJS logging\n */\n logging?:\n | false\n | {\n level: ('log' | 'error' | 'warn' | 'debug' | 'verbose')[];\n };\n}\n\n// Async variant omits transport since controllers are not auto-registered in forRootAsync\nexport type McpAsyncOptions = Omit<McpOptions, 'transport'>;\n\nexport interface McpOptionsFactory {\n createMcpOptions(): Promise<McpAsyncOptions> | McpAsyncOptions;\n}\n\nexport interface McpModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n useExisting?: Type<McpOptionsFactory>;\n useClass?: Type<McpOptionsFactory>;\n useFactory?: (...args: any[]) => Promise<McpAsyncOptions> | McpAsyncOptions;\n inject?: any[];\n extraProviders?: any[]; // allow user to provide additional providers in async mode\n}\n"]}
|
|
@@ -50,7 +50,7 @@ let McpToolsHandler = McpToolsHandler_1 = class McpToolsHandler extends mcp_hand
|
|
|
50
50
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Tool result does not match outputSchema: ${validation.error.message}`);
|
|
51
51
|
}
|
|
52
52
|
return {
|
|
53
|
-
structuredContent:
|
|
53
|
+
structuredContent: validation.data,
|
|
54
54
|
content: this.buildDefaultContentBlock(result),
|
|
55
55
|
};
|
|
56
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-tools.handler.js","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n CallToolRequestSchema,\n CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n CanActivate,\n ExecutionContext,\n Inject,\n Injectable,\n Scope,\n Type,\n} from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef, Reflector } from '@nestjs/core';\nimport {\n DiscoveredCapability,\n McpRegistryDiscoveryService,\n} from '../mcp-registry-discovery.service';\nimport { ToolGuardExecutionContext, ToolMetadata } from '../../decorators';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodType } from 'zod';\nimport { HttpRequest } from '../../interfaces/http-adapter.interface';\nimport { McpRequestWithUser } from 'src/authz';\nimport { ToolAuthorizationService } from '../tool-authorization.service';\nimport { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';\nimport { normalizeObjectSchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';\nimport type { McpOptions } from '../../interfaces/mcp-options.interface';\nimport {\n McpRegistryService,\n DYNAMIC_TOOL_HANDLER_TOKEN,\n} from '../mcp-dynamic-registry.service';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class McpToolsHandler extends McpHandlerBase {\n private readonly moduleHasGuards: boolean;\n\n constructor(\n moduleRef: ModuleRef,\n registry: McpRegistryDiscoveryService,\n reflector: Reflector,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n private readonly authService: ToolAuthorizationService,\n ) {\n super(moduleRef, registry, reflector, McpToolsHandler.name, options);\n this.moduleHasGuards =\n this.options.guards !== undefined && this.options.guards.length > 0;\n }\n\n private buildDefaultContentBlock(result: any) {\n return [\n {\n type: 'text',\n text: JSON.stringify(result),\n },\n ];\n }\n\n private formatToolResult(result: any, outputSchema?: ZodType): any {\n if (result && typeof result === 'object' && Array.isArray(result.content)) {\n return result;\n }\n\n if (outputSchema) {\n const validation = outputSchema.safeParse(result);\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InternalError,\n `Tool result does not match outputSchema: ${validation.error.message}`,\n );\n }\n return {\n structuredContent: result,\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n return {\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n protected override createErrorResponse(\n errorText: string,\n ): CallToolResult | never {\n return {\n content: [{ type: 'text', text: errorText }],\n isError: true,\n };\n }\n\n /**\n * Creates an ExecutionContext for @ToolGuards() evaluation.\n *\n * Only the fields documented in ToolGuardExecutionContext are available.\n * Invalid fields throw with a descriptive message rather than silently\n * returning garbage.\n */\n private createToolGuardExecutionContext(\n httpRequest: HttpRequest,\n tool: DiscoveredCapability<ToolMetadata>,\n ): ToolGuardExecutionContext & ExecutionContext {\n const providerClass = tool.providerClass as Type;\n const methodHandler =\n providerClass.prototype?.[tool.methodName] ?? (() => {});\n\n const unavailable = (method: string): never => {\n throw new Error(\n `${method} is not available in @ToolGuards() context. ` +\n `MCP tools share a single HTTP endpoint, so only a limited API is available.` +\n `See ToolGuardExecutionContext for the supported API.`,\n );\n };\n\n return {\n switchToHttp: () => ({\n getRequest: <T = unknown>() => httpRequest.raw as T,\n getResponse: () => unavailable('switchToHttp().getResponse()'),\n getNext: () => unavailable('switchToHttp().getNext()'),\n }),\n getClass: <T = unknown>() => providerClass as Type<T>,\n getHandler: () => methodHandler as () => void,\n getArgs: () => unavailable('getArgs()'),\n getArgByIndex: () => unavailable('getArgByIndex()'),\n getType: <TContext extends string = string>() => 'http' as TContext,\n switchToRpc: () => unavailable('switchToRpc()'),\n switchToWs: () => unavailable('switchToWs()'),\n };\n }\n\n /**\n * Evaluates all @ToolGuards() for a tool.\n * Returns true if the tool has no guards or all guards pass.\n */\n private async checkToolGuards(\n tool: DiscoveredCapability<ToolMetadata>,\n httpRequest: HttpRequest,\n ): Promise<boolean> {\n const guards = tool.metadata.guards;\n if (!guards || guards.length === 0) {\n return true;\n }\n\n // Guards require HTTP context - not available on STDIO\n if (!httpRequest.raw) {\n this.logger.warn(\n `@ToolGuards() on tool '${tool.metadata.name}' cannot be evaluated without HTTP context (STDIO transport). ` +\n `The tool will be hidden. Use HTTP transport to support guarded tools.`,\n );\n return false;\n }\n\n const context = this.createToolGuardExecutionContext(httpRequest, tool);\n\n for (const GuardClass of guards) {\n try {\n const guard = this.moduleRef.get<CanActivate>(GuardClass, {\n strict: false,\n });\n const result = guard.canActivate(context);\n const canActivate = result instanceof Promise ? await result : result;\n if (!canActivate) {\n return false;\n }\n } catch (error) {\n this.logger.warn(\n `@ToolGuards() guard ${GuardClass.name} threw on tool '${tool.metadata.name}': ${error.message}. ` +\n `The tool will be hidden. If this is unexpected, ensure the guard only uses ` +\n `the API available in ToolGuardExecutionContext.`,\n );\n return false;\n }\n }\n\n return true;\n }\n registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {\n if (this.registry.getTools(this.mcpModuleId).length === 0) {\n this.logger.debug('No tools registered, skipping tool handlers');\n return;\n }\n\n mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Extract user from request (may be undefined if not authenticated or STDIO)\n // For STDIO transport, httpRequest.raw is undefined, so bypass auth entirely\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n\n // Get all tools and filter based on user permissions\n // STDIO: If no httpRequest.raw, disable guards (local dev mode)\n const allTools = this.registry.getTools(this.mcpModuleId);\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n const allowUnauthenticatedAccess =\n this.options.allowUnauthenticatedAccess ?? false;\n\n // Filter by JWT-based authorization (scopes, roles, public)\n const jwtAuthorizedTools = allTools.filter((tool) =>\n this.authService.canAccessTool(\n user,\n tool,\n effectiveModuleHasGuards,\n allowUnauthenticatedAccess,\n ),\n );\n\n // Filter by @ToolGuards() - evaluate each tool's guards\n const authorizedTools: typeof jwtAuthorizedTools = [];\n for (const tool of jwtAuthorizedTools) {\n if (await this.checkToolGuards(tool, httpRequest)) {\n authorizedTools.push(tool);\n }\n }\n\n const tools = authorizedTools.map((tool) => {\n // Create base schema\n const toolSchema = {\n name: tool.metadata.name,\n description: tool.metadata.description,\n annotations: tool.metadata.annotations,\n _meta: tool.metadata._meta,\n };\n\n // Add security schemes\n const securitySchemes = this.authService.generateSecuritySchemes(\n tool,\n effectiveModuleHasGuards,\n );\n if (securitySchemes.length > 0) {\n toolSchema['securitySchemes'] = securitySchemes;\n // Note: Currently securitySchemes are not supported in MCP sdk, adding to _meta as workaround\n // (see https://developers.openai.com/apps-sdk/reference/)\n toolSchema._meta = {\n ...toolSchema._meta,\n securitySchemes,\n };\n }\n\n // Add input schema if defined\n const normalizedInputParameters = normalizeObjectSchema(\n tool.metadata.parameters,\n );\n if (normalizedInputParameters) {\n toolSchema['inputSchema'] = toJsonSchemaCompat(\n normalizedInputParameters,\n );\n }\n\n // Add output schema if defined, ensuring it has type: 'object'\n const normalizedOutputSchema = normalizeObjectSchema(\n tool.metadata.outputSchema,\n );\n if (normalizedOutputSchema) {\n const outputSchema = toJsonSchemaCompat(normalizedOutputSchema);\n\n // Create a new object that explicitly includes type: 'object'\n const jsonSchema = {\n ...outputSchema,\n type: 'object',\n };\n\n toolSchema['outputSchema'] = jsonSchema;\n }\n\n return toolSchema;\n });\n\n return {\n tools,\n };\n });\n\n mcpServer.server.setRequestHandler(\n CallToolRequestSchema,\n async (request) => {\n this.logger.debug('CallToolRequestSchema is being called');\n\n const toolInfo = this.registry.findTool(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!toolInfo) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n // Validate authorization before execution\n // For STDIO transport, bypass auth entirely (local dev mode)\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n const allowUnauthenticatedAccess =\n this.options.allowUnauthenticatedAccess ?? false;\n this.authService.validateToolAccess(\n user,\n toolInfo,\n effectiveModuleHasGuards,\n allowUnauthenticatedAccess,\n );\n\n // Validate @ToolGuards()\n const guardsPassed = await this.checkToolGuards(toolInfo, httpRequest);\n if (!guardsPassed) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Access denied: insufficient permissions for tool '${request.params.name}'`,\n );\n }\n\n try {\n // Validate input parameters against the tool's schema\n if (toolInfo.metadata.parameters) {\n const validation = toolInfo.metadata.parameters.safeParse(\n request.params.arguments || {},\n );\n if (!validation.success) {\n const issues = validation.error.issues\n .map((issue) => {\n const path =\n issue.path.length > 0 ? issue.path.join('.') : '';\n const location = path ? `[${path}]: ` : '';\n return `${location}${issue.message}`;\n })\n .join('; ');\n return {\n content: [\n {\n type: 'text',\n text: `Invalid parameters: ${issues}`,\n },\n ],\n isError: true,\n };\n }\n // Use validated arguments to ensure defaults and transformations are applied\n request.params.arguments = validation.data as Record<\n string,\n unknown\n >;\n }\n\n const contextId = ContextIdFactory.getByRequest(httpRequest);\n this.moduleRef.registerRequestByContextId(httpRequest, contextId);\n\n const context = this.createContext(mcpServer, request);\n let result: any;\n\n // Check if this is a dynamic tool (registered via McpRegistryService)\n if (toolInfo.providerClass === DYNAMIC_TOOL_HANDLER_TOKEN) {\n // Dynamic tool - get handler using static method with the correct moduleId\n const handler = McpRegistryService.getHandlerByModuleId(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!handler) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Handler not found for dynamic tool: ${request.params.name}`,\n );\n }\n\n result = await handler(\n request.params.arguments || {},\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n } else {\n // Decorator-based tool - resolve provider instance and call method\n const toolInstance = await this.moduleRef.resolve(\n toolInfo.providerClass,\n contextId,\n { strict: false },\n );\n\n if (!toolInstance) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n result = await toolInstance[toolInfo.methodName].call(\n toolInstance,\n request.params.arguments,\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n }\n\n const transformedResult = this.formatToolResult(\n result,\n toolInfo.metadata.outputSchema,\n );\n\n this.logger.debug('CallToolRequestSchema result', transformedResult);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformedResult;\n } catch (error) {\n // We are assuming error as at least a message property\n return this.handleError(error as Error, toolInfo, httpRequest);\n }\n },\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-tools.handler.js","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n CallToolRequestSchema,\n CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n CanActivate,\n ExecutionContext,\n Inject,\n Injectable,\n Scope,\n Type,\n} from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef, Reflector } from '@nestjs/core';\nimport {\n DiscoveredCapability,\n McpRegistryDiscoveryService,\n} from '../mcp-registry-discovery.service';\nimport { ToolGuardExecutionContext, ToolMetadata } from '../../decorators';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodType } from 'zod';\nimport { HttpRequest } from '../../interfaces/http-adapter.interface';\nimport { McpRequestWithUser } from 'src/authz';\nimport { ToolAuthorizationService } from '../tool-authorization.service';\nimport { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';\nimport { normalizeObjectSchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';\nimport type { McpOptions } from '../../interfaces/mcp-options.interface';\nimport {\n McpRegistryService,\n DYNAMIC_TOOL_HANDLER_TOKEN,\n} from '../mcp-dynamic-registry.service';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class McpToolsHandler extends McpHandlerBase {\n private readonly moduleHasGuards: boolean;\n\n constructor(\n moduleRef: ModuleRef,\n registry: McpRegistryDiscoveryService,\n reflector: Reflector,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n private readonly authService: ToolAuthorizationService,\n ) {\n super(moduleRef, registry, reflector, McpToolsHandler.name, options);\n this.moduleHasGuards =\n this.options.guards !== undefined && this.options.guards.length > 0;\n }\n\n private buildDefaultContentBlock(result: any) {\n return [\n {\n type: 'text',\n text: JSON.stringify(result),\n },\n ];\n }\n\n private formatToolResult(result: any, outputSchema?: ZodType): any {\n if (result && typeof result === 'object' && Array.isArray(result.content)) {\n return result;\n }\n\n if (outputSchema) {\n const validation = outputSchema.safeParse(result);\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InternalError,\n `Tool result does not match outputSchema: ${validation.error.message}`,\n );\n }\n return {\n structuredContent: validation.data,\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n return {\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n protected override createErrorResponse(\n errorText: string,\n ): CallToolResult | never {\n return {\n content: [{ type: 'text', text: errorText }],\n isError: true,\n };\n }\n\n /**\n * Creates an ExecutionContext for @ToolGuards() evaluation.\n *\n * Only the fields documented in ToolGuardExecutionContext are available.\n * Invalid fields throw with a descriptive message rather than silently\n * returning garbage.\n */\n private createToolGuardExecutionContext(\n httpRequest: HttpRequest,\n tool: DiscoveredCapability<ToolMetadata>,\n ): ToolGuardExecutionContext & ExecutionContext {\n const providerClass = tool.providerClass as Type;\n const methodHandler =\n providerClass.prototype?.[tool.methodName] ?? (() => {});\n\n const unavailable = (method: string): never => {\n throw new Error(\n `${method} is not available in @ToolGuards() context. ` +\n `MCP tools share a single HTTP endpoint, so only a limited API is available.` +\n `See ToolGuardExecutionContext for the supported API.`,\n );\n };\n\n return {\n switchToHttp: () => ({\n getRequest: <T = unknown>() => httpRequest.raw as T,\n getResponse: () => unavailable('switchToHttp().getResponse()'),\n getNext: () => unavailable('switchToHttp().getNext()'),\n }),\n getClass: <T = unknown>() => providerClass as Type<T>,\n getHandler: () => methodHandler as () => void,\n getArgs: () => unavailable('getArgs()'),\n getArgByIndex: () => unavailable('getArgByIndex()'),\n getType: <TContext extends string = string>() => 'http' as TContext,\n switchToRpc: () => unavailable('switchToRpc()'),\n switchToWs: () => unavailable('switchToWs()'),\n };\n }\n\n /**\n * Evaluates all @ToolGuards() for a tool.\n * Returns true if the tool has no guards or all guards pass.\n */\n private async checkToolGuards(\n tool: DiscoveredCapability<ToolMetadata>,\n httpRequest: HttpRequest,\n ): Promise<boolean> {\n const guards = tool.metadata.guards;\n if (!guards || guards.length === 0) {\n return true;\n }\n\n // Guards require HTTP context - not available on STDIO\n if (!httpRequest.raw) {\n this.logger.warn(\n `@ToolGuards() on tool '${tool.metadata.name}' cannot be evaluated without HTTP context (STDIO transport). ` +\n `The tool will be hidden. Use HTTP transport to support guarded tools.`,\n );\n return false;\n }\n\n const context = this.createToolGuardExecutionContext(httpRequest, tool);\n\n for (const GuardClass of guards) {\n try {\n const guard = this.moduleRef.get<CanActivate>(GuardClass, {\n strict: false,\n });\n const result = guard.canActivate(context);\n const canActivate = result instanceof Promise ? await result : result;\n if (!canActivate) {\n return false;\n }\n } catch (error) {\n this.logger.warn(\n `@ToolGuards() guard ${GuardClass.name} threw on tool '${tool.metadata.name}': ${error.message}. ` +\n `The tool will be hidden. If this is unexpected, ensure the guard only uses ` +\n `the API available in ToolGuardExecutionContext.`,\n );\n return false;\n }\n }\n\n return true;\n }\n registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {\n if (this.registry.getTools(this.mcpModuleId).length === 0) {\n this.logger.debug('No tools registered, skipping tool handlers');\n return;\n }\n\n mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Extract user from request (may be undefined if not authenticated or STDIO)\n // For STDIO transport, httpRequest.raw is undefined, so bypass auth entirely\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n\n // Get all tools and filter based on user permissions\n // STDIO: If no httpRequest.raw, disable guards (local dev mode)\n const allTools = this.registry.getTools(this.mcpModuleId);\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n const allowUnauthenticatedAccess =\n this.options.allowUnauthenticatedAccess ?? false;\n\n // Filter by JWT-based authorization (scopes, roles, public)\n const jwtAuthorizedTools = allTools.filter((tool) =>\n this.authService.canAccessTool(\n user,\n tool,\n effectiveModuleHasGuards,\n allowUnauthenticatedAccess,\n ),\n );\n\n // Filter by @ToolGuards() - evaluate each tool's guards\n const authorizedTools: typeof jwtAuthorizedTools = [];\n for (const tool of jwtAuthorizedTools) {\n if (await this.checkToolGuards(tool, httpRequest)) {\n authorizedTools.push(tool);\n }\n }\n\n const tools = authorizedTools.map((tool) => {\n // Create base schema\n const toolSchema = {\n name: tool.metadata.name,\n description: tool.metadata.description,\n annotations: tool.metadata.annotations,\n _meta: tool.metadata._meta,\n };\n\n // Add security schemes\n const securitySchemes = this.authService.generateSecuritySchemes(\n tool,\n effectiveModuleHasGuards,\n );\n if (securitySchemes.length > 0) {\n toolSchema['securitySchemes'] = securitySchemes;\n // Note: Currently securitySchemes are not supported in MCP sdk, adding to _meta as workaround\n // (see https://developers.openai.com/apps-sdk/reference/)\n toolSchema._meta = {\n ...toolSchema._meta,\n securitySchemes,\n };\n }\n\n // Add input schema if defined\n const normalizedInputParameters = normalizeObjectSchema(\n tool.metadata.parameters,\n );\n if (normalizedInputParameters) {\n toolSchema['inputSchema'] = toJsonSchemaCompat(\n normalizedInputParameters,\n );\n }\n\n // Add output schema if defined, ensuring it has type: 'object'\n const normalizedOutputSchema = normalizeObjectSchema(\n tool.metadata.outputSchema,\n );\n if (normalizedOutputSchema) {\n const outputSchema = toJsonSchemaCompat(normalizedOutputSchema);\n\n // Create a new object that explicitly includes type: 'object'\n const jsonSchema = {\n ...outputSchema,\n type: 'object',\n };\n\n toolSchema['outputSchema'] = jsonSchema;\n }\n\n return toolSchema;\n });\n\n return {\n tools,\n };\n });\n\n mcpServer.server.setRequestHandler(\n CallToolRequestSchema,\n async (request) => {\n this.logger.debug('CallToolRequestSchema is being called');\n\n const toolInfo = this.registry.findTool(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!toolInfo) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n // Validate authorization before execution\n // For STDIO transport, bypass auth entirely (local dev mode)\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n const allowUnauthenticatedAccess =\n this.options.allowUnauthenticatedAccess ?? false;\n this.authService.validateToolAccess(\n user,\n toolInfo,\n effectiveModuleHasGuards,\n allowUnauthenticatedAccess,\n );\n\n // Validate @ToolGuards()\n const guardsPassed = await this.checkToolGuards(toolInfo, httpRequest);\n if (!guardsPassed) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Access denied: insufficient permissions for tool '${request.params.name}'`,\n );\n }\n\n try {\n // Validate input parameters against the tool's schema\n if (toolInfo.metadata.parameters) {\n const validation = toolInfo.metadata.parameters.safeParse(\n request.params.arguments || {},\n );\n if (!validation.success) {\n const issues = validation.error.issues\n .map((issue) => {\n const path =\n issue.path.length > 0 ? issue.path.join('.') : '';\n const location = path ? `[${path}]: ` : '';\n return `${location}${issue.message}`;\n })\n .join('; ');\n return {\n content: [\n {\n type: 'text',\n text: `Invalid parameters: ${issues}`,\n },\n ],\n isError: true,\n };\n }\n // Use validated arguments to ensure defaults and transformations are applied\n request.params.arguments = validation.data as Record<\n string,\n unknown\n >;\n }\n\n const contextId = ContextIdFactory.getByRequest(httpRequest);\n this.moduleRef.registerRequestByContextId(httpRequest, contextId);\n\n const context = this.createContext(mcpServer, request);\n let result: any;\n\n // Check if this is a dynamic tool (registered via McpRegistryService)\n if (toolInfo.providerClass === DYNAMIC_TOOL_HANDLER_TOKEN) {\n // Dynamic tool - get handler using static method with the correct moduleId\n const handler = McpRegistryService.getHandlerByModuleId(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!handler) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Handler not found for dynamic tool: ${request.params.name}`,\n );\n }\n\n result = await handler(\n request.params.arguments || {},\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n } else {\n // Decorator-based tool - resolve provider instance and call method\n const toolInstance = await this.moduleRef.resolve(\n toolInfo.providerClass,\n contextId,\n { strict: false },\n );\n\n if (!toolInstance) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n result = await toolInstance[toolInfo.methodName].call(\n toolInstance,\n request.params.arguments,\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n }\n\n const transformedResult = this.formatToolResult(\n result,\n toolInfo.metadata.outputSchema,\n );\n\n this.logger.debug('CallToolRequestSchema result', transformedResult);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformedResult;\n } catch (error) {\n // We are assuming error as at least a message property\n return this.handleError(error as Error, toolInfo, httpRequest);\n }\n },\n );\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.factory.d.ts","sourceRoot":"","sources":["../../../src/mcp/utils/mcp-server.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,2BAA2B,EACrC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,SAAS,
|
|
1
|
+
{"version":3,"file":"mcp-server.factory.d.ts","sourceRoot":"","sources":["../../../src/mcp/utils/mcp-server.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,2BAA2B,EACrC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,SAAS,CAqBX"}
|
|
@@ -6,7 +6,14 @@ const capabilities_builder_1 = require("./capabilities-builder");
|
|
|
6
6
|
function createMcpServer(mcpModuleId, registry, options, logger) {
|
|
7
7
|
const capabilities = (0, capabilities_builder_1.buildMcpCapabilities)(mcpModuleId, registry, options);
|
|
8
8
|
logger.debug('Built MCP capabilities:', capabilities);
|
|
9
|
-
const mcpServer = new mcp_js_1.McpServer({
|
|
9
|
+
const mcpServer = new mcp_js_1.McpServer({
|
|
10
|
+
name: options.name,
|
|
11
|
+
version: options.version,
|
|
12
|
+
...(options.title && { title: options.title }),
|
|
13
|
+
...(options.description && { description: options.description }),
|
|
14
|
+
...(options.websiteUrl && { websiteUrl: options.websiteUrl }),
|
|
15
|
+
...(options.icons && { icons: options.icons }),
|
|
16
|
+
}, {
|
|
10
17
|
capabilities,
|
|
11
18
|
instructions: options.instructions || '',
|
|
12
19
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.factory.js","sourceRoot":"","sources":["../../../src/mcp/utils/mcp-server.factory.ts"],"names":[],"mappings":";;AAMA,
|
|
1
|
+
{"version":3,"file":"mcp-server.factory.js","sourceRoot":"","sources":["../../../src/mcp/utils/mcp-server.factory.ts"],"names":[],"mappings":";;AAMA,0CA0BC;AA/BD,oEAAoE;AACpE,iEAA8D;AAI9D,SAAgB,eAAe,CAC7B,WAAmB,EACnB,QAAqC,EACrC,OAAmB,EACnB,MAAc;IAEd,MAAM,YAAY,GAAG,IAAA,2CAAoB,EAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE1E,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,YAAY,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,IAAI,kBAAS,CAC7B;QACE,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC9C,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;QAC7D,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;KAC/C,EACD;QACE,YAAY;QACZ,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,EAAE;KACzC,CACF,CAAC;IAEF,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC","sourcesContent":["import { McpOptions } from '../interfaces';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { buildMcpCapabilities } from './capabilities-builder';\nimport { McpRegistryDiscoveryService } from '../services/mcp-registry-discovery.service';\nimport { Logger } from '@nestjs/common';\n\nexport function createMcpServer(\n mcpModuleId: string,\n registry: McpRegistryDiscoveryService,\n options: McpOptions,\n logger: Logger,\n): McpServer {\n const capabilities = buildMcpCapabilities(mcpModuleId, registry, options);\n\n logger.debug('Built MCP capabilities:', capabilities);\n\n const mcpServer = new McpServer(\n {\n name: options.name,\n version: options.version,\n ...(options.title && { title: options.title }),\n ...(options.description && { description: options.description }),\n ...(options.websiteUrl && { websiteUrl: options.websiteUrl }),\n ...(options.icons && { icons: options.icons }),\n },\n {\n capabilities,\n instructions: options.instructions || '',\n },\n );\n\n return options.serverMutator ? options.serverMutator(mcpServer) : mcpServer;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
1
|
+
import { Icon, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { CanActivate, ModuleMetadata, Type } from '@nestjs/common';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
|
|
5
|
+
export type { Icon };
|
|
6
|
+
|
|
5
7
|
export enum McpTransportType {
|
|
6
8
|
SSE = 'sse',
|
|
7
9
|
STREAMABLE_HTTP = 'streamable-http',
|
|
@@ -12,7 +14,15 @@ export interface McpOptions {
|
|
|
12
14
|
// When and if, additional properties are introduced in ServerOptions or ServerInfo,
|
|
13
15
|
// consider deprecating these fields in favor of using ServerOptions and ServerInfo directly.
|
|
14
16
|
name: string;
|
|
17
|
+
/** Human-readable display name for the server (passed to MCP Implementation). */
|
|
18
|
+
title?: string;
|
|
15
19
|
version: string;
|
|
20
|
+
/** Short description of what this MCP server does. */
|
|
21
|
+
description?: string;
|
|
22
|
+
/** URL of the website associated with this server. */
|
|
23
|
+
websiteUrl?: string;
|
|
24
|
+
/** Icons representing this server. Use `Icon` exported from `@rekog/mcp-nest`. */
|
|
25
|
+
icons?: Icon[];
|
|
16
26
|
capabilities?: ServerCapabilities;
|
|
17
27
|
instructions?: string;
|
|
18
28
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { McpToolsHandler } from './mcp-tools.handler';
|
|
4
|
+
|
|
5
|
+
const formatToolResult = McpToolsHandler.prototype['formatToolResult'].bind({
|
|
6
|
+
buildDefaultContentBlock: McpToolsHandler.prototype['buildDefaultContentBlock'],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const schema = z.object({ name: z.string(), age: z.number() });
|
|
10
|
+
|
|
11
|
+
describe('formatToolResult', () => {
|
|
12
|
+
it('strips extra keys from structuredContent when outputSchema is set', () => {
|
|
13
|
+
const result = formatToolResult({ name: 'Alice', age: 30, extra: true }, schema);
|
|
14
|
+
|
|
15
|
+
expect(result.structuredContent).toEqual({ name: 'Alice', age: 30 });
|
|
16
|
+
expect(result.structuredContent).not.toHaveProperty('extra');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('keeps the original result (with extra keys) in the content text block', () => {
|
|
20
|
+
const result = formatToolResult({ name: 'Alice', age: 30, extra: true }, schema);
|
|
21
|
+
const text = JSON.parse(result.content[0].text);
|
|
22
|
+
|
|
23
|
+
expect(text).toHaveProperty('extra', true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('throws McpError when result does not match outputSchema', () => {
|
|
27
|
+
expect(() => formatToolResult({ name: 'Alice' }, schema)).toThrow(McpError);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('wraps result in content block when no outputSchema is provided', () => {
|
|
31
|
+
const result = formatToolResult({ anything: 42 });
|
|
32
|
+
|
|
33
|
+
expect(result.structuredContent).toBeUndefined();
|
|
34
|
+
expect(result.content[0].text).toBe('{"anything":42}');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns as-is when result already has a content array', () => {
|
|
38
|
+
const existing = { content: [{ type: 'text', text: 'hi' }] };
|
|
39
|
+
expect(formatToolResult(existing, schema)).toBe(existing);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -15,7 +15,14 @@ export function createMcpServer(
|
|
|
15
15
|
logger.debug('Built MCP capabilities:', capabilities);
|
|
16
16
|
|
|
17
17
|
const mcpServer = new McpServer(
|
|
18
|
-
{
|
|
18
|
+
{
|
|
19
|
+
name: options.name,
|
|
20
|
+
version: options.version,
|
|
21
|
+
...(options.title && { title: options.title }),
|
|
22
|
+
...(options.description && { description: options.description }),
|
|
23
|
+
...(options.websiteUrl && { websiteUrl: options.websiteUrl }),
|
|
24
|
+
...(options.icons && { icons: options.icons }),
|
|
25
|
+
},
|
|
19
26
|
{
|
|
20
27
|
capabilities,
|
|
21
28
|
instructions: options.instructions || '',
|