UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

376 lines 11.4 kB
/** * MCP Elicitation Protocol * * Protocol-level interface for the MCP elicitation system that enables * tools to request interactive user input during execution. * * This module provides: * - Protocol message types for MCP elicitation requests/responses * - Protocol-level handlers for different transport types * - Utility functions for protocol message construction * - Integration with the ElicitationManager * * Implements MCP 2024-11-05 elicitation specification. * * @module mcp/elicitationProtocol * @since 8.39.0 */ import { randomUUID } from "crypto"; import { logger } from "../utils/logger.js"; import { withTimeout } from "../utils/async/withTimeout.js"; import { ElicitationManager, globalElicitationManager, } from "./elicitation/elicitationManager.js"; /** * Create an elicitation request protocol message */ export function createElicitationRequest(params) { return { jsonrpc: "2.0", id: randomUUID(), method: "elicitation/request", params: { type: params.type, message: params.message, toolName: params.toolName, serverId: params.serverId, timeout: params.timeout, optional: params.optional, defaultValue: params.defaultValue, options: params.options ?? {}, }, }; } /** * Create an elicitation response protocol message */ export function createElicitationResponse(requestId, response) { return { jsonrpc: "2.0", id: randomUUID(), method: "elicitation/response", params: { requestId, responded: response.responded, value: response.value, cancelled: response.cancelled, timedOut: response.timedOut, error: response.error, }, }; } /** * Create an elicitation cancel protocol message */ export function createElicitationCancel(requestId, reason) { return { jsonrpc: "2.0", id: randomUUID(), method: "elicitation/cancel", params: { requestId, reason, }, }; } /** * Check if a message is an elicitation protocol message */ export function isElicitationProtocolMessage(message) { if (typeof message !== "object" || message === null) { return false; } const msg = message; return (msg.jsonrpc === "2.0" && typeof msg.id === "string" && (msg.method === "elicitation/request" || msg.method === "elicitation/response" || msg.method === "elicitation/cancel") && typeof msg.params === "object" && msg.params !== null); } /** * Convert protocol message to Elicitation type */ export function protocolMessageToElicitation(message) { const { type, message: displayMessage, toolName, serverId, timeout, optional, defaultValue, options, } = message.params; const base = { id: message.id, type, message: displayMessage, toolName, serverId, timeout, optional, defaultValue, }; // Add type-specific options switch (type) { case "confirmation": return { ...base, type: "confirmation", confirmLabel: options?.confirmLabel, cancelLabel: options?.cancelLabel, }; case "text": return { ...base, type: "text", placeholder: options?.placeholder, minLength: options?.minLength, maxLength: options?.maxLength, pattern: options?.pattern, multiline: options?.multiline, }; case "select": return { ...base, type: "select", options: options?.options ?? [], }; case "multiselect": return { ...base, type: "multiselect", options: options?.options ?? [], minSelections: options?.minSelections, maxSelections: options?.maxSelections, }; case "form": return { ...base, type: "form", fields: options?.fields ?? [], submitLabel: options?.submitLabel, }; case "file": return { ...base, type: "file", accept: options?.accept, multiple: options?.multiple, maxSize: options?.maxSize, }; case "secret": return { ...base, type: "secret", hint: options?.hint, }; default: return base; } } /** * Convert ElicitationResponse to protocol message */ export function elicitationResponseToProtocol(response) { return createElicitationResponse(response.requestId, { responded: response.responded, value: response.value, cancelled: response.cancelled, timedOut: response.timedOut, error: response.error, }); } /** * Elicitation Protocol Adapter * * Bridges protocol-level messages with the ElicitationManager */ export class ElicitationProtocolAdapter { manager; config; constructor(config = {}) { this.manager = config.manager ?? globalElicitationManager; this.config = { manager: this.manager, defaultTimeout: config.defaultTimeout ?? 60000, enableLogging: config.enableLogging ?? false, customHandler: config.customHandler, }; } /** * Handle incoming protocol message */ async handleMessage(message) { if (this.config.enableLogging) { logger.info("[ElicitationProtocol] Received:", message.method); } // Use custom handler if provided if (this.config.customHandler) { return await withTimeout(this.config.customHandler(message), this.config.defaultTimeout, `[ElicitationProtocol] Custom handler timed out after ${this.config.defaultTimeout}ms`); } switch (message.method) { case "elicitation/request": return this.handleRequest(message); case "elicitation/response": return this.handleResponse(message); case "elicitation/cancel": return this.handleCancel(message); default: if (this.config.enableLogging) { logger.warn(`[ElicitationProtocol] Unhandled method: ${message.method}`); } return; } } /** * Handle elicitation request */ async handleRequest(message) { const elicitation = protocolMessageToElicitation(message); // Use the manager to process the request const response = await this.manager.request({ ...elicitation, timeout: elicitation.timeout ?? this.config.defaultTimeout, }); return elicitationResponseToProtocol(response); } /** * Handle elicitation response (for external responses) */ async handleResponse(_message) { // Responses are typically handled by the manager internally // This is for external systems sending responses if (this.config.enableLogging) { logger.info("[ElicitationProtocol] Response received (no action needed)"); } } /** * Handle elicitation cancel */ async handleCancel(message) { this.manager.cancel(message.params.requestId, message.params.reason); } /** * Send an elicitation request through the protocol */ async requestElicitation(params) { const message = createElicitationRequest(params); const elicitation = protocolMessageToElicitation(message); return this.manager.request({ ...elicitation, timeout: elicitation.timeout ?? this.config.defaultTimeout, }); } /** * Cancel a pending elicitation */ cancelElicitation(requestId, reason) { this.manager.cancel(requestId, reason); } /** * Get the underlying manager */ getManager() { return this.manager; } /** * Set protocol handler for the manager */ setHandler(handler) { this.manager.setHandler(handler); } /** * Enable/disable the protocol */ setEnabled(enabled) { this.manager.setEnabled(enabled); } /** * Check if protocol is enabled */ isEnabled() { return this.manager.isEnabled(); } } /** * Create protocol-compliant confirmation request */ export function createConfirmationRequest(message, options) { const requestOptions = {}; if (options.confirmLabel !== undefined) { requestOptions.confirmLabel = options.confirmLabel; } if (options.cancelLabel !== undefined) { requestOptions.cancelLabel = options.cancelLabel; } return createElicitationRequest({ type: "confirmation", message, toolName: options.toolName, serverId: options.serverId, timeout: options.timeout, options: requestOptions, }); } /** * Create protocol-compliant text input request */ export function createTextInputRequest(message, options) { const requestOptions = {}; if (options.placeholder !== undefined) { requestOptions.placeholder = options.placeholder; } if (options.minLength !== undefined) { requestOptions.minLength = options.minLength; } if (options.maxLength !== undefined) { requestOptions.maxLength = options.maxLength; } if (options.pattern !== undefined) { requestOptions.pattern = options.pattern; } if (options.multiline !== undefined) { requestOptions.multiline = options.multiline; } return createElicitationRequest({ type: "text", message, toolName: options.toolName, serverId: options.serverId, defaultValue: options.defaultValue, timeout: options.timeout, options: requestOptions, }); } /** * Create protocol-compliant select request */ export function createSelectRequest(message, selectOptions, options) { return createElicitationRequest({ type: "select", message, toolName: options.toolName, serverId: options.serverId, defaultValue: options.defaultValue, timeout: options.timeout, options: { options: selectOptions, }, }); } /** * Create protocol-compliant form request */ export function createFormRequest(message, fields, options) { const requestOptions = { fields: fields, }; if (options.submitLabel !== undefined) { requestOptions.submitLabel = options.submitLabel; } return createElicitationRequest({ type: "form", message, toolName: options.toolName, serverId: options.serverId, timeout: options.timeout, options: requestOptions, }); } /** * Global protocol adapter instance */ export const globalElicitationProtocol = new ElicitationProtocolAdapter(); //# sourceMappingURL=elicitationProtocol.js.map