| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.ResourceTemplate = exports.McpServer = void 0; |
| const index_js_1 = require("./index.js"); |
| const zod_to_json_schema_1 = require("zod-to-json-schema"); |
| const zod_1 = require("zod"); |
| const types_js_1 = require("../types.js"); |
| const completable_js_1 = require("./completable.js"); |
| const uriTemplate_js_1 = require("../shared/uriTemplate.js"); |
| |
| |
| |
| |
| |
| class McpServer { |
| constructor(serverInfo, options) { |
| this._registeredResources = {}; |
| this._registeredResourceTemplates = {}; |
| this._registeredTools = {}; |
| this._registeredPrompts = {}; |
| this._toolHandlersInitialized = false; |
| this._completionHandlerInitialized = false; |
| this._resourceHandlersInitialized = false; |
| this._promptHandlersInitialized = false; |
| this.server = new index_js_1.Server(serverInfo, options); |
| } |
| |
| |
| |
| |
| |
| async connect(transport) { |
| return await this.server.connect(transport); |
| } |
| |
| |
| |
| async close() { |
| await this.server.close(); |
| } |
| setToolRequestHandlers() { |
| if (this._toolHandlersInitialized) { |
| return; |
| } |
| this.server.assertCanSetRequestHandler(types_js_1.ListToolsRequestSchema.shape.method.value); |
| this.server.assertCanSetRequestHandler(types_js_1.CallToolRequestSchema.shape.method.value); |
| this.server.registerCapabilities({ |
| tools: { |
| listChanged: true |
| } |
| }); |
| this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => ({ |
| tools: Object.entries(this._registeredTools).filter(([, tool]) => tool.enabled).map(([name, tool]) => { |
| return { |
| name, |
| description: tool.description, |
| inputSchema: tool.inputSchema |
| ? (0, zod_to_json_schema_1.zodToJsonSchema)(tool.inputSchema, { |
| strictUnions: true, |
| }) |
| : EMPTY_OBJECT_JSON_SCHEMA, |
| annotations: tool.annotations, |
| }; |
| }), |
| })); |
| this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => { |
| const tool = this._registeredTools[request.params.name]; |
| if (!tool) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Tool ${request.params.name} not found`); |
| } |
| if (!tool.enabled) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Tool ${request.params.name} disabled`); |
| } |
| if (tool.inputSchema) { |
| const parseResult = await tool.inputSchema.safeParseAsync(request.params.arguments); |
| if (!parseResult.success) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid arguments for tool ${request.params.name}: ${parseResult.error.message}`); |
| } |
| const args = parseResult.data; |
| const cb = tool.callback; |
| try { |
| return await Promise.resolve(cb(args, extra)); |
| } |
| catch (error) { |
| return { |
| content: [ |
| { |
| type: "text", |
| text: error instanceof Error ? error.message : String(error), |
| }, |
| ], |
| isError: true, |
| }; |
| } |
| } |
| else { |
| const cb = tool.callback; |
| try { |
| return await Promise.resolve(cb(extra)); |
| } |
| catch (error) { |
| return { |
| content: [ |
| { |
| type: "text", |
| text: error instanceof Error ? error.message : String(error), |
| }, |
| ], |
| isError: true, |
| }; |
| } |
| } |
| }); |
| this._toolHandlersInitialized = true; |
| } |
| setCompletionRequestHandler() { |
| if (this._completionHandlerInitialized) { |
| return; |
| } |
| this.server.assertCanSetRequestHandler(types_js_1.CompleteRequestSchema.shape.method.value); |
| this.server.setRequestHandler(types_js_1.CompleteRequestSchema, async (request) => { |
| switch (request.params.ref.type) { |
| case "ref/prompt": |
| return this.handlePromptCompletion(request, request.params.ref); |
| case "ref/resource": |
| return this.handleResourceCompletion(request, request.params.ref); |
| default: |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid completion reference: ${request.params.ref}`); |
| } |
| }); |
| this._completionHandlerInitialized = true; |
| } |
| async handlePromptCompletion(request, ref) { |
| const prompt = this._registeredPrompts[ref.name]; |
| if (!prompt) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Prompt ${ref.name} not found`); |
| } |
| if (!prompt.enabled) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Prompt ${ref.name} disabled`); |
| } |
| if (!prompt.argsSchema) { |
| return EMPTY_COMPLETION_RESULT; |
| } |
| const field = prompt.argsSchema.shape[request.params.argument.name]; |
| if (!(field instanceof completable_js_1.Completable)) { |
| return EMPTY_COMPLETION_RESULT; |
| } |
| const def = field._def; |
| const suggestions = await def.complete(request.params.argument.value); |
| return createCompletionResult(suggestions); |
| } |
| async handleResourceCompletion(request, ref) { |
| const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri); |
| if (!template) { |
| if (this._registeredResources[ref.uri]) { |
| |
| return EMPTY_COMPLETION_RESULT; |
| } |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`); |
| } |
| const completer = template.resourceTemplate.completeCallback(request.params.argument.name); |
| if (!completer) { |
| return EMPTY_COMPLETION_RESULT; |
| } |
| const suggestions = await completer(request.params.argument.value); |
| return createCompletionResult(suggestions); |
| } |
| setResourceRequestHandlers() { |
| if (this._resourceHandlersInitialized) { |
| return; |
| } |
| this.server.assertCanSetRequestHandler(types_js_1.ListResourcesRequestSchema.shape.method.value); |
| this.server.assertCanSetRequestHandler(types_js_1.ListResourceTemplatesRequestSchema.shape.method.value); |
| this.server.assertCanSetRequestHandler(types_js_1.ReadResourceRequestSchema.shape.method.value); |
| this.server.registerCapabilities({ |
| resources: { |
| listChanged: true |
| } |
| }); |
| this.server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async (request, extra) => { |
| const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({ |
| uri, |
| name: resource.name, |
| ...resource.metadata, |
| })); |
| const templateResources = []; |
| for (const template of Object.values(this._registeredResourceTemplates)) { |
| if (!template.resourceTemplate.listCallback) { |
| continue; |
| } |
| const result = await template.resourceTemplate.listCallback(extra); |
| for (const resource of result.resources) { |
| templateResources.push({ |
| ...resource, |
| ...template.metadata, |
| }); |
| } |
| } |
| return { resources: [...resources, ...templateResources] }; |
| }); |
| this.server.setRequestHandler(types_js_1.ListResourceTemplatesRequestSchema, async () => { |
| const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({ |
| name, |
| uriTemplate: template.resourceTemplate.uriTemplate.toString(), |
| ...template.metadata, |
| })); |
| return { resourceTemplates }; |
| }); |
| this.server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request, extra) => { |
| const uri = new URL(request.params.uri); |
| |
| const resource = this._registeredResources[uri.toString()]; |
| if (resource) { |
| if (!resource.enabled) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Resource ${uri} disabled`); |
| } |
| return resource.readCallback(uri, extra); |
| } |
| |
| for (const template of Object.values(this._registeredResourceTemplates)) { |
| const variables = template.resourceTemplate.uriTemplate.match(uri.toString()); |
| if (variables) { |
| return template.readCallback(uri, variables, extra); |
| } |
| } |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Resource ${uri} not found`); |
| }); |
| this.setCompletionRequestHandler(); |
| this._resourceHandlersInitialized = true; |
| } |
| setPromptRequestHandlers() { |
| if (this._promptHandlersInitialized) { |
| return; |
| } |
| this.server.assertCanSetRequestHandler(types_js_1.ListPromptsRequestSchema.shape.method.value); |
| this.server.assertCanSetRequestHandler(types_js_1.GetPromptRequestSchema.shape.method.value); |
| this.server.registerCapabilities({ |
| prompts: { |
| listChanged: true |
| } |
| }); |
| this.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, () => ({ |
| prompts: Object.entries(this._registeredPrompts).filter(([, prompt]) => prompt.enabled).map(([name, prompt]) => { |
| return { |
| name, |
| description: prompt.description, |
| arguments: prompt.argsSchema |
| ? promptArgumentsFromSchema(prompt.argsSchema) |
| : undefined, |
| }; |
| }), |
| })); |
| this.server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request, extra) => { |
| const prompt = this._registeredPrompts[request.params.name]; |
| if (!prompt) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Prompt ${request.params.name} not found`); |
| } |
| if (!prompt.enabled) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Prompt ${request.params.name} disabled`); |
| } |
| if (prompt.argsSchema) { |
| const parseResult = await prompt.argsSchema.safeParseAsync(request.params.arguments); |
| if (!parseResult.success) { |
| throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${parseResult.error.message}`); |
| } |
| const args = parseResult.data; |
| const cb = prompt.callback; |
| return await Promise.resolve(cb(args, extra)); |
| } |
| else { |
| const cb = prompt.callback; |
| return await Promise.resolve(cb(extra)); |
| } |
| }); |
| this.setCompletionRequestHandler(); |
| this._promptHandlersInitialized = true; |
| } |
| resource(name, uriOrTemplate, ...rest) { |
| let metadata; |
| if (typeof rest[0] === "object") { |
| metadata = rest.shift(); |
| } |
| const readCallback = rest[0]; |
| if (typeof uriOrTemplate === "string") { |
| if (this._registeredResources[uriOrTemplate]) { |
| throw new Error(`Resource ${uriOrTemplate} is already registered`); |
| } |
| const registeredResource = { |
| name, |
| metadata, |
| readCallback: readCallback, |
| enabled: true, |
| disable: () => registeredResource.update({ enabled: false }), |
| enable: () => registeredResource.update({ enabled: true }), |
| remove: () => registeredResource.update({ uri: null }), |
| update: (updates) => { |
| if (typeof updates.uri !== "undefined" && updates.uri !== uriOrTemplate) { |
| delete this._registeredResources[uriOrTemplate]; |
| if (updates.uri) |
| this._registeredResources[updates.uri] = registeredResource; |
| } |
| if (typeof updates.name !== "undefined") |
| registeredResource.name = updates.name; |
| if (typeof updates.metadata !== "undefined") |
| registeredResource.metadata = updates.metadata; |
| if (typeof updates.callback !== "undefined") |
| registeredResource.readCallback = updates.callback; |
| if (typeof updates.enabled !== "undefined") |
| registeredResource.enabled = updates.enabled; |
| this.sendResourceListChanged(); |
| }, |
| }; |
| this._registeredResources[uriOrTemplate] = registeredResource; |
| this.setResourceRequestHandlers(); |
| this.sendResourceListChanged(); |
| return registeredResource; |
| } |
| else { |
| if (this._registeredResourceTemplates[name]) { |
| throw new Error(`Resource template ${name} is already registered`); |
| } |
| const registeredResourceTemplate = { |
| resourceTemplate: uriOrTemplate, |
| metadata, |
| readCallback: readCallback, |
| enabled: true, |
| disable: () => registeredResourceTemplate.update({ enabled: false }), |
| enable: () => registeredResourceTemplate.update({ enabled: true }), |
| remove: () => registeredResourceTemplate.update({ name: null }), |
| update: (updates) => { |
| if (typeof updates.name !== "undefined" && updates.name !== name) { |
| delete this._registeredResourceTemplates[name]; |
| if (updates.name) |
| this._registeredResourceTemplates[updates.name] = registeredResourceTemplate; |
| } |
| if (typeof updates.template !== "undefined") |
| registeredResourceTemplate.resourceTemplate = updates.template; |
| if (typeof updates.metadata !== "undefined") |
| registeredResourceTemplate.metadata = updates.metadata; |
| if (typeof updates.callback !== "undefined") |
| registeredResourceTemplate.readCallback = updates.callback; |
| if (typeof updates.enabled !== "undefined") |
| registeredResourceTemplate.enabled = updates.enabled; |
| this.sendResourceListChanged(); |
| }, |
| }; |
| this._registeredResourceTemplates[name] = registeredResourceTemplate; |
| this.setResourceRequestHandlers(); |
| this.sendResourceListChanged(); |
| return registeredResourceTemplate; |
| } |
| } |
| tool(name, ...rest) { |
| if (this._registeredTools[name]) { |
| throw new Error(`Tool ${name} is already registered`); |
| } |
| |
| const isZodRawShape = (obj) => { |
| if (typeof obj !== "object" || obj === null) |
| return false; |
| const isEmptyObject = zod_1.z.object({}).strict().safeParse(obj).success; |
| |
| return isEmptyObject || Object.values(obj).some(v => v instanceof zod_1.ZodType); |
| }; |
| let description; |
| if (typeof rest[0] === "string") { |
| description = rest.shift(); |
| } |
| let paramsSchema; |
| let annotations; |
| |
| if (rest.length > 1) { |
| |
| const firstArg = rest[0]; |
| if (isZodRawShape(firstArg)) { |
| |
| paramsSchema = rest.shift(); |
| |
| if (rest.length > 1 && typeof rest[0] === "object" && rest[0] !== null && !(isZodRawShape(rest[0]))) { |
| |
| |
| annotations = rest.shift(); |
| } |
| } |
| else if (typeof firstArg === "object" && firstArg !== null) { |
| |
| |
| |
| annotations = rest.shift(); |
| } |
| } |
| const cb = rest[0]; |
| const registeredTool = { |
| description, |
| inputSchema: paramsSchema === undefined ? undefined : zod_1.z.object(paramsSchema), |
| annotations, |
| callback: cb, |
| enabled: true, |
| disable: () => registeredTool.update({ enabled: false }), |
| enable: () => registeredTool.update({ enabled: true }), |
| remove: () => registeredTool.update({ name: null }), |
| update: (updates) => { |
| if (typeof updates.name !== "undefined" && updates.name !== name) { |
| delete this._registeredTools[name]; |
| if (updates.name) |
| this._registeredTools[updates.name] = registeredTool; |
| } |
| if (typeof updates.description !== "undefined") |
| registeredTool.description = updates.description; |
| if (typeof updates.paramsSchema !== "undefined") |
| registeredTool.inputSchema = zod_1.z.object(updates.paramsSchema); |
| if (typeof updates.callback !== "undefined") |
| registeredTool.callback = updates.callback; |
| if (typeof updates.annotations !== "undefined") |
| registeredTool.annotations = updates.annotations; |
| if (typeof updates.enabled !== "undefined") |
| registeredTool.enabled = updates.enabled; |
| this.sendToolListChanged(); |
| }, |
| }; |
| this._registeredTools[name] = registeredTool; |
| this.setToolRequestHandlers(); |
| this.sendToolListChanged(); |
| return registeredTool; |
| } |
| prompt(name, ...rest) { |
| if (this._registeredPrompts[name]) { |
| throw new Error(`Prompt ${name} is already registered`); |
| } |
| let description; |
| if (typeof rest[0] === "string") { |
| description = rest.shift(); |
| } |
| let argsSchema; |
| if (rest.length > 1) { |
| argsSchema = rest.shift(); |
| } |
| const cb = rest[0]; |
| const registeredPrompt = { |
| description, |
| argsSchema: argsSchema === undefined ? undefined : zod_1.z.object(argsSchema), |
| callback: cb, |
| enabled: true, |
| disable: () => registeredPrompt.update({ enabled: false }), |
| enable: () => registeredPrompt.update({ enabled: true }), |
| remove: () => registeredPrompt.update({ name: null }), |
| update: (updates) => { |
| if (typeof updates.name !== "undefined" && updates.name !== name) { |
| delete this._registeredPrompts[name]; |
| if (updates.name) |
| this._registeredPrompts[updates.name] = registeredPrompt; |
| } |
| if (typeof updates.description !== "undefined") |
| registeredPrompt.description = updates.description; |
| if (typeof updates.argsSchema !== "undefined") |
| registeredPrompt.argsSchema = zod_1.z.object(updates.argsSchema); |
| if (typeof updates.callback !== "undefined") |
| registeredPrompt.callback = updates.callback; |
| if (typeof updates.enabled !== "undefined") |
| registeredPrompt.enabled = updates.enabled; |
| this.sendPromptListChanged(); |
| }, |
| }; |
| this._registeredPrompts[name] = registeredPrompt; |
| this.setPromptRequestHandlers(); |
| this.sendPromptListChanged(); |
| return registeredPrompt; |
| } |
| |
| |
| |
| |
| isConnected() { |
| return this.server.transport !== undefined; |
| } |
| |
| |
| |
| sendResourceListChanged() { |
| if (this.isConnected()) { |
| this.server.sendResourceListChanged(); |
| } |
| } |
| |
| |
| |
| sendToolListChanged() { |
| if (this.isConnected()) { |
| this.server.sendToolListChanged(); |
| } |
| } |
| |
| |
| |
| sendPromptListChanged() { |
| if (this.isConnected()) { |
| this.server.sendPromptListChanged(); |
| } |
| } |
| } |
| exports.McpServer = McpServer; |
| |
| |
| |
| |
| class ResourceTemplate { |
| constructor(uriTemplate, _callbacks) { |
| this._callbacks = _callbacks; |
| this._uriTemplate = |
| typeof uriTemplate === "string" |
| ? new uriTemplate_js_1.UriTemplate(uriTemplate) |
| : uriTemplate; |
| } |
| |
| |
| |
| get uriTemplate() { |
| return this._uriTemplate; |
| } |
| |
| |
| |
| get listCallback() { |
| return this._callbacks.list; |
| } |
| |
| |
| |
| completeCallback(variable) { |
| var _a; |
| return (_a = this._callbacks.complete) === null || _a === void 0 ? void 0 : _a[variable]; |
| } |
| } |
| exports.ResourceTemplate = ResourceTemplate; |
| const EMPTY_OBJECT_JSON_SCHEMA = { |
| type: "object", |
| }; |
| function promptArgumentsFromSchema(schema) { |
| return Object.entries(schema.shape).map(([name, field]) => ({ |
| name, |
| description: field.description, |
| required: !field.isOptional(), |
| })); |
| } |
| function createCompletionResult(suggestions) { |
| return { |
| completion: { |
| values: suggestions.slice(0, 100), |
| total: suggestions.length, |
| hasMore: suggestions.length > 100, |
| }, |
| }; |
| } |
| const EMPTY_COMPLETION_RESULT = { |
| completion: { |
| values: [], |
| hasMore: false, |
| }, |
| }; |
| |