UNPKG

mcp-use

Version:

Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents, Clients and Servers with support for ChatGPT Apps, Code Mode, OAuth, Notifications, Sampling, Observability and more.

524 lines (519 loc) 15 kB
import { ElicitationValidationError } from "./chunk-KUEVOU4M.js"; import { Telemetry } from "./chunk-QQO5WIVJ.js"; import { __name } from "./chunk-3GQAWCBQ.js"; // src/server/tools/tool-execution-helpers.ts import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js"; // src/server/utils/runtime.ts var isDeno = typeof globalThis.Deno !== "undefined"; function getEnv(key) { if (isDeno) { return globalThis.Deno.env.get(key); } return process.env[key]; } __name(getEnv, "getEnv"); function getCwd() { if (isDeno) { return globalThis.Deno.cwd(); } return process.cwd(); } __name(getCwd, "getCwd"); var fsHelpers = { async readFileSync(path, encoding = "utf8") { if (isDeno) { return await globalThis.Deno.readTextFile(path); } const { readFileSync } = await import("fs"); const result = readFileSync(path, encoding); return typeof result === "string" ? result : result.toString(encoding); }, async readFile(path) { if (isDeno) { const data = await globalThis.Deno.readFile(path); return data.buffer; } const { readFileSync } = await import("fs"); const buffer = readFileSync(path); return buffer.buffer.slice( buffer.byteOffset, buffer.byteOffset + buffer.byteLength ); }, async existsSync(path) { if (isDeno) { try { await globalThis.Deno.stat(path); return true; } catch { return false; } } const { existsSync } = await import("fs"); return existsSync(path); }, async readdirSync(path) { if (isDeno) { const entries = []; for await (const entry of globalThis.Deno.readDir(path)) { entries.push(entry.name); } return entries; } const { readdirSync } = await import("fs"); return readdirSync(path); } }; var pathHelpers = { join(...paths) { if (isDeno) { return paths.join("/").replace(/\/+/g, "/"); } return paths.join("/").replace(/\/+/g, "/"); }, relative(from, to) { const fromParts = from.split("/").filter((p) => p); const toParts = to.split("/").filter((p) => p); let i = 0; while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) { i++; } const upCount = fromParts.length - i; const relativeParts = [...Array(upCount).fill(".."), ...toParts.slice(i)]; return relativeParts.join("/"); } }; function generateUUID() { return globalThis.crypto.randomUUID(); } __name(generateUUID, "generateUUID"); // src/server/tools/tool-execution-helpers.ts function findSessionContext(sessions, initialRequestContext, extraProgressToken, extraSendNotification) { let requestContext = initialRequestContext; let session; let progressToken = extraProgressToken; let sendNotification = extraSendNotification; if (!requestContext) { for (const [, s] of sessions.entries()) { if (s.context) { requestContext = s.context; break; } } } if (!progressToken || !sendNotification) { if (requestContext) { for (const [, s] of sessions.entries()) { if (s.context === requestContext) { session = s; break; } } } else { const firstSession = sessions.values().next().value; if (firstSession) { session = firstSession; } } if (session) { if (!progressToken && session.progressToken) { progressToken = session.progressToken; } if (!sendNotification && session.sendNotification) { sendNotification = session.sendNotification; } } } return { requestContext, session, progressToken, sendNotification }; } __name(findSessionContext, "findSessionContext"); async function sendProgressNotification(sendNotification, progressToken, progress, total, message) { if (sendNotification && progressToken !== void 0) { try { await sendNotification({ method: "notifications/progress", params: { progressToken, progress, total, message } }); } catch { } } } __name(sendProgressNotification, "sendProgressNotification"); async function withTimeout(promise, timeout, errorMessage) { if (timeout && timeout !== Infinity) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(errorMessage)), timeout); }); return await Promise.race([promise, timeoutPromise]); } return await promise; } __name(withTimeout, "withTimeout"); function parseElicitParams(messageOrParams, schemaOrUrlOrOptions, maybeOptions) { let sdkParams; let zodSchema = null; let options; if (typeof messageOrParams === "string") { const message = messageOrParams; if (typeof schemaOrUrlOrOptions === "string") { options = maybeOptions; const elicitationId = `elicit-${generateUUID()}`; sdkParams = { mode: "url", message, url: schemaOrUrlOrOptions, elicitationId }; } else if (schemaOrUrlOrOptions && typeof schemaOrUrlOrOptions === "object" && "_def" in schemaOrUrlOrOptions) { options = maybeOptions; zodSchema = schemaOrUrlOrOptions; const jsonSchema = toJsonSchemaCompat(schemaOrUrlOrOptions); sdkParams = { mode: "form", message, requestedSchema: jsonSchema }; } else { throw new Error( "Invalid elicit signature: second parameter must be a Zod schema or URL string" ); } } else { options = schemaOrUrlOrOptions; const params = messageOrParams; if (params.mode === "url") { const elicitationId = `elicit-${generateUUID()}`; sdkParams = { mode: "url", message: params.message, url: params.url, elicitationId }; } else { sdkParams = { mode: "form", message: params.message, requestedSchema: params.requestedSchema }; } } return { sdkParams, zodSchema, options }; } __name(parseElicitParams, "parseElicitParams"); function createSampleMethod(createMessage, progressToken, sendNotification) { return async (promptOrParams, options) => { let sampleParams; if (typeof promptOrParams === "string") { sampleParams = { messages: [ { role: "user", content: { type: "text", text: promptOrParams } } ], maxTokens: options?.maxTokens || 1e3, ...options?.modelPreferences && { modelPreferences: options.modelPreferences }, ...options?.systemPrompt && { systemPrompt: options.systemPrompt }, ...options?.temperature !== void 0 && { temperature: options.temperature }, ...options?.stopSequences && { stopSequences: options.stopSequences }, ...options?.metadata && { metadata: options.metadata } }; } else { sampleParams = promptOrParams; } const { timeout, progressIntervalMs = 5e3, onProgress } = options ?? {}; let progressCount = 0; let completed = false; let progressInterval = null; if (progressToken !== void 0 && sendNotification) { progressInterval = setInterval(async () => { if (completed) return; progressCount++; const progressData = { progress: progressCount, total: void 0, message: `Waiting for LLM response... (${progressCount * Math.round(progressIntervalMs / 1e3)}s elapsed)` }; if (onProgress) { try { onProgress(progressData); } catch { } } await sendProgressNotification( sendNotification, progressToken, progressData.progress, progressData.total, progressData.message ); }, progressIntervalMs); } try { console.log("[SAMPLING DEBUG] Calling createMessage..."); const sdkTimeout = timeout && timeout !== Infinity ? timeout : 2147483647; const samplePromise = createMessage(sampleParams, { timeout: sdkTimeout }); console.log("[SAMPLING DEBUG] Waiting for response..."); const result = await withTimeout( samplePromise, timeout, `Sampling timed out after ${timeout}ms` ); console.log("[SAMPLING DEBUG] Got result:", result); Telemetry.getInstance().trackServerContext({ contextType: "sample" }).catch((e) => console.debug(`Failed to track sample context: ${e}`)); return result; } catch (error) { console.error("[SAMPLING DEBUG] Error during sampling:", error); throw error; } finally { completed = true; if (progressInterval) { clearInterval(progressInterval); } } }; } __name(createSampleMethod, "createSampleMethod"); function createElicitMethod(elicitInput) { return async (messageOrParams, schemaOrUrlOrOptions, maybeOptions) => { const { sdkParams, zodSchema, options } = parseElicitParams( messageOrParams, schemaOrUrlOrOptions, maybeOptions ); const { timeout } = options ?? {}; const sdkTimeout = timeout && timeout !== Infinity ? timeout : 2147483647; const result = await elicitInput(sdkParams, { timeout: sdkTimeout }); Telemetry.getInstance().trackServerContext({ contextType: "elicit" }).catch((e) => console.debug(`Failed to track elicit context: ${e}`)); if (zodSchema && result.action === "accept" && result.data) { try { const validatedData = zodSchema.parse(result.data); return { ...result, data: validatedData }; } catch (error) { const err = error; throw new ElicitationValidationError( `Elicitation data validation failed: ${err.message}`, err ); } } return result; }; } __name(createElicitMethod, "createElicitMethod"); function createReportProgressMethod(progressToken, sendNotification) { if (progressToken !== void 0 && sendNotification) { return async (progress, total, message) => { await sendProgressNotification( sendNotification, progressToken, progress, total, message ); }; } return void 0; } __name(createReportProgressMethod, "createReportProgressMethod"); var LOG_LEVELS = { debug: 0, info: 1, notice: 2, warning: 3, error: 4, critical: 5, alert: 6, emergency: 7 }; var VALID_LOG_LEVELS = [ "debug", "info", "notice", "warning", "error", "critical", "alert", "emergency" ]; function isValidLogLevel(level) { return VALID_LOG_LEVELS.includes(level); } __name(isValidLogLevel, "isValidLogLevel"); function shouldLogMessage(messageLevel, minLevel) { if (!minLevel) { return true; } if (!isValidLogLevel(messageLevel) || !isValidLogLevel(minLevel)) { return true; } return LOG_LEVELS[messageLevel] >= LOG_LEVELS[minLevel]; } __name(shouldLogMessage, "shouldLogMessage"); function createLogMethod(sendNotification, minLogLevel) { if (!sendNotification) { return void 0; } return async (level, message, logger) => { if (!shouldLogMessage(level, minLogLevel)) { return; } await sendNotification({ method: "notifications/message", params: { level, data: message, logger: logger || "tool" } }); Telemetry.getInstance().trackServerContext({ contextType: "notification", notificationType: "message" }).catch( (e) => console.debug(`Failed to track notification context: ${e}`) ); }; } __name(createLogMethod, "createLogMethod"); function createClientCapabilityChecker(clientCapabilities) { const caps = clientCapabilities || {}; return { can(capability) { return capability in caps; }, capabilities() { return { ...caps }; } }; } __name(createClientCapabilityChecker, "createClientCapabilityChecker"); function createSendNotificationMethod(sessionId, sessions) { if (!sessionId || !sessions) { return void 0; } return async (method, params) => { const session = sessions.get(sessionId); if (!session?.sendNotification) { console.warn( `[MCP] Cannot send notification to session ${sessionId} - no sendNotification function` ); return; } try { await session.sendNotification({ method, params: params || {} }); } catch (error) { console.error( `[MCP] Error sending notification to session ${sessionId}:`, error ); } }; } __name(createSendNotificationMethod, "createSendNotificationMethod"); function createSendNotificationToSessionMethod(sessions) { if (!sessions) { return void 0; } return async (sessionId, method, params) => { const session = sessions.get(sessionId); if (!session?.sendNotification) { return false; } try { await session.sendNotification({ method, params: params || {} }); return true; } catch (error) { console.error( `[MCP] Error sending notification to session ${sessionId}:`, error ); return false; } }; } __name(createSendNotificationToSessionMethod, "createSendNotificationToSessionMethod"); function createEnhancedContext(baseContext, createMessage, elicitInput, progressToken, sendNotification, minLogLevel, clientCapabilities, sessionId, sessions) { const enhancedContext = baseContext ? Object.create(baseContext) : {}; enhancedContext.sample = createSampleMethod( createMessage, progressToken, sendNotification ); enhancedContext.elicit = createElicitMethod(elicitInput); enhancedContext.reportProgress = createReportProgressMethod( progressToken, sendNotification ); enhancedContext.log = createLogMethod(sendNotification, minLogLevel); enhancedContext.client = createClientCapabilityChecker(clientCapabilities); if (sessionId) { enhancedContext.session = { sessionId }; } const sendNotificationMethod = createSendNotificationMethod( sessionId, sessions ); if (sendNotificationMethod) { enhancedContext.sendNotification = sendNotificationMethod; } const sendNotificationToSessionMethod = createSendNotificationToSessionMethod(sessions); if (sendNotificationToSessionMethod) { enhancedContext.sendNotificationToSession = sendNotificationToSessionMethod; } return enhancedContext; } __name(createEnhancedContext, "createEnhancedContext"); export { isDeno, getEnv, getCwd, fsHelpers, pathHelpers, generateUUID, findSessionContext, sendProgressNotification, withTimeout, parseElicitParams, createSampleMethod, createElicitMethod, createReportProgressMethod, VALID_LOG_LEVELS, isValidLogLevel, shouldLogMessage, createEnhancedContext };