| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.ResourceTemplate = exports.McpServer = void 0;
- const index_js_1 = require("./index.js");
- const zod_compat_js_1 = require("./zod-compat.js");
- const zod_json_schema_compat_js_1 = require("./zod-json-schema-compat.js");
- const types_js_1 = require("../types.js");
- const completable_js_1 = require("./completable.js");
- const uriTemplate_js_1 = require("../shared/uriTemplate.js");
- const toolNameValidation_js_1 = require("../shared/toolNameValidation.js");
- const mcp_server_js_1 = require("../experimental/tasks/mcp-server.js");
- const zod_1 = require("zod");
- /**
- * High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
- * For advanced usage (like sending notifications or setting custom request handlers), use the underlying
- * Server instance available via the `server` property.
- */
- 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);
- }
- /**
- * Access experimental features.
- *
- * WARNING: These APIs are experimental and may change without notice.
- *
- * @experimental
- */
- get experimental() {
- if (!this._experimental) {
- this._experimental = {
- tasks: new mcp_server_js_1.ExperimentalMcpServerTasks(this)
- };
- }
- return this._experimental;
- }
- /**
- * Attaches to the given transport, starts it, and starts listening for messages.
- *
- * The `server` object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
- */
- async connect(transport) {
- return await this.server.connect(transport);
- }
- /**
- * Closes the connection.
- */
- async close() {
- await this.server.close();
- }
- setToolRequestHandlers() {
- if (this._toolHandlersInitialized) {
- return;
- }
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.ListToolsRequestSchema));
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.CallToolRequestSchema));
- 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]) => {
- const toolDefinition = {
- name,
- title: tool.title,
- description: tool.description,
- inputSchema: (() => {
- const obj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.inputSchema);
- return obj
- ? (0, zod_json_schema_compat_js_1.toJsonSchemaCompat)(obj, {
- strictUnions: true,
- pipeStrategy: 'input'
- })
- : EMPTY_OBJECT_JSON_SCHEMA;
- })(),
- annotations: tool.annotations,
- execution: tool.execution,
- _meta: tool._meta
- };
- if (tool.outputSchema) {
- const obj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.outputSchema);
- if (obj) {
- toolDefinition.outputSchema = (0, zod_json_schema_compat_js_1.toJsonSchemaCompat)(obj, {
- strictUnions: true,
- pipeStrategy: 'output'
- });
- }
- }
- return toolDefinition;
- })
- }));
- this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => {
- try {
- 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`);
- }
- const isTaskRequest = !!request.params.task;
- const taskSupport = tool.execution?.taskSupport;
- const isTaskHandler = 'createTask' in tool.handler;
- // Validate task hint configuration
- if ((taskSupport === 'required' || taskSupport === 'optional') && !isTaskHandler) {
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Tool ${request.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`);
- }
- // Handle taskSupport 'required' without task augmentation
- if (taskSupport === 'required' && !isTaskRequest) {
- throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Tool ${request.params.name} requires task augmentation (taskSupport: 'required')`);
- }
- // Handle taskSupport 'optional' without task augmentation - automatic polling
- if (taskSupport === 'optional' && !isTaskRequest && isTaskHandler) {
- return await this.handleAutomaticTaskPolling(tool, request, extra);
- }
- // Normal execution path
- const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
- const result = await this.executeToolHandler(tool, args, extra);
- // Return CreateTaskResult immediately for task requests
- if (isTaskRequest) {
- return result;
- }
- // Validate output schema for non-task requests
- await this.validateToolOutput(tool, result, request.params.name);
- return result;
- }
- catch (error) {
- if (error instanceof types_js_1.McpError) {
- if (error.code === types_js_1.ErrorCode.UrlElicitationRequired) {
- throw error; // Return the error to the caller without wrapping in CallToolResult
- }
- }
- return this.createToolError(error instanceof Error ? error.message : String(error));
- }
- });
- this._toolHandlersInitialized = true;
- }
- /**
- * Creates a tool error result.
- *
- * @param errorMessage - The error message.
- * @returns The tool error result.
- */
- createToolError(errorMessage) {
- return {
- content: [
- {
- type: 'text',
- text: errorMessage
- }
- ],
- isError: true
- };
- }
- /**
- * Validates tool input arguments against the tool's input schema.
- */
- async validateToolInput(tool, args, toolName) {
- if (!tool.inputSchema) {
- return undefined;
- }
- // Try to normalize to object schema first (for raw shapes and object schemas)
- // If that fails, use the schema directly (for union/intersection/etc)
- const inputObj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.inputSchema);
- const schemaToParse = inputObj ?? tool.inputSchema;
- const parseResult = await (0, zod_compat_js_1.safeParseAsync)(schemaToParse, args);
- if (!parseResult.success) {
- const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
- const errorMessage = (0, zod_compat_js_1.getParseErrorMessage)(error);
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
- }
- return parseResult.data;
- }
- /**
- * Validates tool output against the tool's output schema.
- */
- async validateToolOutput(tool, result, toolName) {
- if (!tool.outputSchema) {
- return;
- }
- // Only validate CallToolResult, not CreateTaskResult
- if (!('content' in result)) {
- return;
- }
- if (result.isError) {
- return;
- }
- if (!result.structuredContent) {
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
- }
- // if the tool has an output schema, validate structured content
- const outputObj = (0, zod_compat_js_1.normalizeObjectSchema)(tool.outputSchema);
- const parseResult = await (0, zod_compat_js_1.safeParseAsync)(outputObj, result.structuredContent);
- if (!parseResult.success) {
- const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
- const errorMessage = (0, zod_compat_js_1.getParseErrorMessage)(error);
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
- }
- }
- /**
- * Executes a tool handler (either regular or task-based).
- */
- async executeToolHandler(tool, args, extra) {
- const handler = tool.handler;
- const isTaskHandler = 'createTask' in handler;
- if (isTaskHandler) {
- if (!extra.taskStore) {
- throw new Error('No task store provided.');
- }
- const taskExtra = { ...extra, taskStore: extra.taskStore };
- if (tool.inputSchema) {
- const typedHandler = handler;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return await Promise.resolve(typedHandler.createTask(args, taskExtra));
- }
- else {
- const typedHandler = handler;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return await Promise.resolve(typedHandler.createTask(taskExtra));
- }
- }
- if (tool.inputSchema) {
- const typedHandler = handler;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return await Promise.resolve(typedHandler(args, extra));
- }
- else {
- const typedHandler = handler;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return await Promise.resolve(typedHandler(extra));
- }
- }
- /**
- * Handles automatic task polling for tools with taskSupport 'optional'.
- */
- async handleAutomaticTaskPolling(tool, request, extra) {
- if (!extra.taskStore) {
- throw new Error('No task store provided for task-capable tool.');
- }
- // Validate input and create task
- const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
- const handler = tool.handler;
- const taskExtra = { ...extra, taskStore: extra.taskStore };
- const createTaskResult = args // undefined only if tool.inputSchema is undefined
- ? await Promise.resolve(handler.createTask(args, taskExtra))
- : // eslint-disable-next-line @typescript-eslint/no-explicit-any
- await Promise.resolve(handler.createTask(taskExtra));
- // Poll until completion
- const taskId = createTaskResult.task.taskId;
- let task = createTaskResult.task;
- const pollInterval = task.pollInterval ?? 5000;
- while (task.status !== 'completed' && task.status !== 'failed' && task.status !== 'cancelled') {
- await new Promise(resolve => setTimeout(resolve, pollInterval));
- const updatedTask = await extra.taskStore.getTask(taskId);
- if (!updatedTask) {
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Task ${taskId} not found during polling`);
- }
- task = updatedTask;
- }
- // Return the final result
- return (await extra.taskStore.getTaskResult(taskId));
- }
- setCompletionRequestHandler() {
- if (this._completionHandlerInitialized) {
- return;
- }
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.CompleteRequestSchema));
- this.server.registerCapabilities({
- completions: {}
- });
- this.server.setRequestHandler(types_js_1.CompleteRequestSchema, async (request) => {
- switch (request.params.ref.type) {
- case 'ref/prompt':
- (0, types_js_1.assertCompleteRequestPrompt)(request);
- return this.handlePromptCompletion(request, request.params.ref);
- case 'ref/resource':
- (0, types_js_1.assertCompleteRequestResourceTemplate)(request);
- 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 promptShape = (0, zod_compat_js_1.getObjectShape)(prompt.argsSchema);
- const field = promptShape?.[request.params.argument.name];
- if (!(0, completable_js_1.isCompletable)(field)) {
- return EMPTY_COMPLETION_RESULT;
- }
- const completer = (0, completable_js_1.getCompleter)(field);
- if (!completer) {
- return EMPTY_COMPLETION_RESULT;
- }
- const suggestions = await completer(request.params.argument.value, request.params.context);
- 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]) {
- // Attempting to autocomplete a fixed resource URI is not an error in the spec (but probably should be).
- 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, request.params.context);
- return createCompletionResult(suggestions);
- }
- setResourceRequestHandlers() {
- if (this._resourceHandlersInitialized) {
- return;
- }
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.ListResourcesRequestSchema));
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.ListResourceTemplatesRequestSchema));
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.ReadResourceRequestSchema));
- 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({
- ...template.metadata,
- // the defined resource metadata should override the template metadata if present
- ...resource
- });
- }
- }
- 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);
- // First check for exact resource match
- 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);
- }
- // Then check templates
- 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._resourceHandlersInitialized = true;
- }
- setPromptRequestHandlers() {
- if (this._promptHandlersInitialized) {
- return;
- }
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.ListPromptsRequestSchema));
- this.server.assertCanSetRequestHandler(getMethodValue(types_js_1.GetPromptRequestSchema));
- 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,
- title: prompt.title,
- 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 argsObj = (0, zod_compat_js_1.normalizeObjectSchema)(prompt.argsSchema);
- const parseResult = await (0, zod_compat_js_1.safeParseAsync)(argsObj, request.params.arguments);
- if (!parseResult.success) {
- const error = 'error' in parseResult ? parseResult.error : 'Unknown error';
- const errorMessage = (0, zod_compat_js_1.getParseErrorMessage)(error);
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
- }
- const args = parseResult.data;
- const cb = prompt.callback;
- return await Promise.resolve(cb(args, extra));
- }
- else {
- const cb = prompt.callback;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return await Promise.resolve(cb(extra));
- }
- });
- 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 = this._createRegisteredResource(name, undefined, uriOrTemplate, metadata, readCallback);
- this.setResourceRequestHandlers();
- this.sendResourceListChanged();
- return registeredResource;
- }
- else {
- if (this._registeredResourceTemplates[name]) {
- throw new Error(`Resource template ${name} is already registered`);
- }
- const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, undefined, uriOrTemplate, metadata, readCallback);
- this.setResourceRequestHandlers();
- this.sendResourceListChanged();
- return registeredResourceTemplate;
- }
- }
- registerResource(name, uriOrTemplate, config, readCallback) {
- if (typeof uriOrTemplate === 'string') {
- if (this._registeredResources[uriOrTemplate]) {
- throw new Error(`Resource ${uriOrTemplate} is already registered`);
- }
- const registeredResource = this._createRegisteredResource(name, config.title, uriOrTemplate, config, readCallback);
- this.setResourceRequestHandlers();
- this.sendResourceListChanged();
- return registeredResource;
- }
- else {
- if (this._registeredResourceTemplates[name]) {
- throw new Error(`Resource template ${name} is already registered`);
- }
- const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config.title, uriOrTemplate, config, readCallback);
- this.setResourceRequestHandlers();
- this.sendResourceListChanged();
- return registeredResourceTemplate;
- }
- }
- _createRegisteredResource(name, title, uri, metadata, readCallback) {
- const registeredResource = {
- name,
- title,
- metadata,
- 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 !== uri) {
- delete this._registeredResources[uri];
- if (updates.uri)
- this._registeredResources[updates.uri] = registeredResource;
- }
- if (typeof updates.name !== 'undefined')
- registeredResource.name = updates.name;
- if (typeof updates.title !== 'undefined')
- registeredResource.title = updates.title;
- 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[uri] = registeredResource;
- return registeredResource;
- }
- _createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
- const registeredResourceTemplate = {
- resourceTemplate: template,
- title,
- metadata,
- 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.title !== 'undefined')
- registeredResourceTemplate.title = updates.title;
- 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;
- // If the resource template has any completion callbacks, enable completions capability
- const variableNames = template.uriTemplate.variableNames;
- const hasCompleter = Array.isArray(variableNames) && variableNames.some(v => !!template.completeCallback(v));
- if (hasCompleter) {
- this.setCompletionRequestHandler();
- }
- return registeredResourceTemplate;
- }
- _createRegisteredPrompt(name, title, description, argsSchema, callback) {
- const registeredPrompt = {
- title,
- description,
- argsSchema: argsSchema === undefined ? undefined : (0, zod_compat_js_1.objectFromShape)(argsSchema),
- callback,
- 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.title !== 'undefined')
- registeredPrompt.title = updates.title;
- if (typeof updates.description !== 'undefined')
- registeredPrompt.description = updates.description;
- if (typeof updates.argsSchema !== 'undefined')
- registeredPrompt.argsSchema = (0, zod_compat_js_1.objectFromShape)(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;
- // If any argument uses a Completable schema, enable completions capability
- if (argsSchema) {
- const hasCompletable = Object.values(argsSchema).some(field => {
- const inner = field instanceof zod_1.ZodOptional ? field._def?.innerType : field;
- return (0, completable_js_1.isCompletable)(inner);
- });
- if (hasCompletable) {
- this.setCompletionRequestHandler();
- }
- }
- return registeredPrompt;
- }
- _createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
- // Validate tool name according to SEP specification
- (0, toolNameValidation_js_1.validateAndWarnToolName)(name);
- const registeredTool = {
- title,
- description,
- inputSchema: getZodSchemaObject(inputSchema),
- outputSchema: getZodSchemaObject(outputSchema),
- annotations,
- execution,
- _meta,
- handler: handler,
- 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) {
- if (typeof updates.name === 'string') {
- (0, toolNameValidation_js_1.validateAndWarnToolName)(updates.name);
- }
- delete this._registeredTools[name];
- if (updates.name)
- this._registeredTools[updates.name] = registeredTool;
- }
- if (typeof updates.title !== 'undefined')
- registeredTool.title = updates.title;
- if (typeof updates.description !== 'undefined')
- registeredTool.description = updates.description;
- if (typeof updates.paramsSchema !== 'undefined')
- registeredTool.inputSchema = (0, zod_compat_js_1.objectFromShape)(updates.paramsSchema);
- if (typeof updates.outputSchema !== 'undefined')
- registeredTool.outputSchema = (0, zod_compat_js_1.objectFromShape)(updates.outputSchema);
- if (typeof updates.callback !== 'undefined')
- registeredTool.handler = updates.callback;
- if (typeof updates.annotations !== 'undefined')
- registeredTool.annotations = updates.annotations;
- if (typeof updates._meta !== 'undefined')
- registeredTool._meta = updates._meta;
- if (typeof updates.enabled !== 'undefined')
- registeredTool.enabled = updates.enabled;
- this.sendToolListChanged();
- }
- };
- this._registeredTools[name] = registeredTool;
- this.setToolRequestHandlers();
- this.sendToolListChanged();
- return registeredTool;
- }
- /**
- * tool() implementation. Parses arguments passed to overrides defined above.
- */
- tool(name, ...rest) {
- if (this._registeredTools[name]) {
- throw new Error(`Tool ${name} is already registered`);
- }
- let description;
- let inputSchema;
- let outputSchema;
- let annotations;
- // Tool properties are passed as separate arguments, with omissions allowed.
- // Support for this style is frozen as of protocol version 2025-03-26. Future additions
- // to tool definition should *NOT* be added.
- if (typeof rest[0] === 'string') {
- description = rest.shift();
- }
- // Handle the different overload combinations
- if (rest.length > 1) {
- // We have at least one more arg before the callback
- const firstArg = rest[0];
- if (isZodRawShapeCompat(firstArg)) {
- // We have a params schema as the first arg
- inputSchema = rest.shift();
- // Check if the next arg is potentially annotations
- if (rest.length > 1 && typeof rest[0] === 'object' && rest[0] !== null && !isZodRawShapeCompat(rest[0])) {
- // Case: tool(name, paramsSchema, annotations, cb)
- // Or: tool(name, description, paramsSchema, annotations, cb)
- annotations = rest.shift();
- }
- }
- else if (typeof firstArg === 'object' && firstArg !== null) {
- // ToolAnnotations values are primitives. Nested objects indicate a misplaced schema
- if (Object.values(firstArg).some(v => typeof v === 'object' && v !== null)) {
- throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
- }
- annotations = rest.shift();
- }
- }
- const callback = rest[0];
- return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, { taskSupport: 'forbidden' }, undefined, callback);
- }
- /**
- * Registers a tool with a config object and callback.
- */
- registerTool(name, config, cb) {
- if (this._registeredTools[name]) {
- throw new Error(`Tool ${name} is already registered`);
- }
- const { title, description, inputSchema, outputSchema, annotations, _meta } = config;
- return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: 'forbidden' }, _meta, cb);
- }
- 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 = this._createRegisteredPrompt(name, undefined, description, argsSchema, cb);
- this.setPromptRequestHandlers();
- this.sendPromptListChanged();
- return registeredPrompt;
- }
- /**
- * Registers a prompt with a config object and callback.
- */
- registerPrompt(name, config, cb) {
- if (this._registeredPrompts[name]) {
- throw new Error(`Prompt ${name} is already registered`);
- }
- const { title, description, argsSchema } = config;
- const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb);
- this.setPromptRequestHandlers();
- this.sendPromptListChanged();
- return registeredPrompt;
- }
- /**
- * Checks if the server is connected to a transport.
- * @returns True if the server is connected
- */
- isConnected() {
- return this.server.transport !== undefined;
- }
- /**
- * Sends a logging message to the client, if connected.
- * Note: You only need to send the parameters object, not the entire JSON RPC message
- * @see LoggingMessageNotification
- * @param params
- * @param sessionId optional for stateless and backward compatibility
- */
- async sendLoggingMessage(params, sessionId) {
- return this.server.sendLoggingMessage(params, sessionId);
- }
- /**
- * Sends a resource list changed event to the client, if connected.
- */
- sendResourceListChanged() {
- if (this.isConnected()) {
- this.server.sendResourceListChanged();
- }
- }
- /**
- * Sends a tool list changed event to the client, if connected.
- */
- sendToolListChanged() {
- if (this.isConnected()) {
- this.server.sendToolListChanged();
- }
- }
- /**
- * Sends a prompt list changed event to the client, if connected.
- */
- sendPromptListChanged() {
- if (this.isConnected()) {
- this.server.sendPromptListChanged();
- }
- }
- }
- exports.McpServer = McpServer;
- /**
- * A resource template combines a URI pattern with optional functionality to enumerate
- * all resources matching that pattern.
- */
- class ResourceTemplate {
- constructor(uriTemplate, _callbacks) {
- this._callbacks = _callbacks;
- this._uriTemplate = typeof uriTemplate === 'string' ? new uriTemplate_js_1.UriTemplate(uriTemplate) : uriTemplate;
- }
- /**
- * Gets the URI template pattern.
- */
- get uriTemplate() {
- return this._uriTemplate;
- }
- /**
- * Gets the list callback, if one was provided.
- */
- get listCallback() {
- return this._callbacks.list;
- }
- /**
- * Gets the callback for completing a specific URI template variable, if one was provided.
- */
- completeCallback(variable) {
- return this._callbacks.complete?.[variable];
- }
- }
- exports.ResourceTemplate = ResourceTemplate;
- const EMPTY_OBJECT_JSON_SCHEMA = {
- type: 'object',
- properties: {}
- };
- /**
- * Checks if a value looks like a Zod schema by checking for parse/safeParse methods.
- */
- function isZodTypeLike(value) {
- return (value !== null &&
- typeof value === 'object' &&
- 'parse' in value &&
- typeof value.parse === 'function' &&
- 'safeParse' in value &&
- typeof value.safeParse === 'function');
- }
- /**
- * Checks if an object is a Zod schema instance (v3 or v4).
- *
- * Zod schemas have internal markers:
- * - v3: `_def` property
- * - v4: `_zod` property
- *
- * This includes transformed schemas like z.preprocess(), z.transform(), z.pipe().
- */
- function isZodSchemaInstance(obj) {
- return '_def' in obj || '_zod' in obj || isZodTypeLike(obj);
- }
- /**
- * Checks if an object is a "raw shape" - a plain object where values are Zod schemas.
- *
- * Raw shapes are used as shorthand: `{ name: z.string() }` instead of `z.object({ name: z.string() })`.
- *
- * IMPORTANT: This must NOT match actual Zod schema instances (like z.preprocess, z.pipe),
- * which have internal properties that could be mistaken for schema values.
- */
- function isZodRawShapeCompat(obj) {
- if (typeof obj !== 'object' || obj === null) {
- return false;
- }
- // If it's already a Zod schema instance, it's NOT a raw shape
- if (isZodSchemaInstance(obj)) {
- return false;
- }
- // Empty objects are valid raw shapes (tools with no parameters)
- if (Object.keys(obj).length === 0) {
- return true;
- }
- // A raw shape has at least one property that is a Zod schema
- return Object.values(obj).some(isZodTypeLike);
- }
- /**
- * Converts a provided Zod schema to a Zod object if it is a ZodRawShapeCompat,
- * otherwise returns the schema as is. Throws if the value is not a valid Zod schema.
- */
- function getZodSchemaObject(schema) {
- if (!schema) {
- return undefined;
- }
- if (isZodRawShapeCompat(schema)) {
- return (0, zod_compat_js_1.objectFromShape)(schema);
- }
- if (!isZodSchemaInstance(schema)) {
- throw new Error('inputSchema must be a Zod schema or raw shape, received an unrecognized object');
- }
- return schema;
- }
- function promptArgumentsFromSchema(schema) {
- const shape = (0, zod_compat_js_1.getObjectShape)(schema);
- if (!shape)
- return [];
- return Object.entries(shape).map(([name, field]) => {
- // Get description - works for both v3 and v4
- const description = (0, zod_compat_js_1.getSchemaDescription)(field);
- // Check if optional - works for both v3 and v4
- const isOptional = (0, zod_compat_js_1.isSchemaOptional)(field);
- return {
- name,
- description,
- required: !isOptional
- };
- });
- }
- function getMethodValue(schema) {
- const shape = (0, zod_compat_js_1.getObjectShape)(schema);
- const methodSchema = shape?.method;
- if (!methodSchema) {
- throw new Error('Schema is missing a method literal');
- }
- // Extract literal value - works for both v3 and v4
- const value = (0, zod_compat_js_1.getLiteralValue)(methodSchema);
- if (typeof value === 'string') {
- return value;
- }
- throw new Error('Schema method literal must be a string');
- }
- 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
- }
- };
- //# sourceMappingURL=mcp.js.map
|