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.

1,565 lines (1,551 loc) 220 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/react/rpc-logger.ts var rpc_logger_exports = {}; __export(rpc_logger_exports, { clearRpcLogs: () => clearRpcLogs, getAllRpcLogs: () => getAllRpcLogs, getRpcLogs: () => getRpcLogs, subscribeToRpcLogs: () => subscribeToRpcLogs, wrapTransportForLogging: () => wrapTransportForLogging }); function getRpcLogs(serverId) { return rpcLogStore.getLogsForServer(serverId); } function getAllRpcLogs() { return rpcLogStore.getAllLogs(); } function subscribeToRpcLogs(listener) { return rpcLogStore.subscribe(listener); } function clearRpcLogs(serverId) { rpcLogStore.clear(serverId); } function wrapTransportForLogging(transport, serverId) { class LoggingTransport { constructor(inner) { this.inner = inner; this.inner.onmessage = (message, extra) => { rpcLogStore.publish({ serverId, direction: "receive", timestamp: (/* @__PURE__ */ new Date()).toISOString(), message }); this.onmessage?.(message, extra); }; this.inner.onclose = () => { this.onclose?.(); }; this.inner.onerror = (error) => { this.onerror?.(error); }; } static { __name(this, "LoggingTransport"); } onclose; onerror; onmessage; async start() { if (typeof this.inner.start === "function") { await this.inner.start(); } } async send(message, options) { rpcLogStore.publish({ serverId, direction: "send", timestamp: (/* @__PURE__ */ new Date()).toISOString(), message }); await this.inner.send(message, options); } async close() { await this.inner.close(); } get sessionId() { return this.inner.sessionId; } setProtocolVersion(version) { if (typeof this.inner.setProtocolVersion === "function") { this.inner.setProtocolVersion(version); } } } return new LoggingTransport(transport); } var RpcLogStore, rpcLogStore; var init_rpc_logger = __esm({ "src/react/rpc-logger.ts"() { "use strict"; RpcLogStore = class { static { __name(this, "RpcLogStore"); } logs = []; listeners = /* @__PURE__ */ new Set(); maxLogs = 1e3; publish(entry) { console.log( "[RPC Logger] Publishing log:", entry.direction, entry.serverId, entry.message?.method ); this.logs.push(entry); if (this.logs.length > this.maxLogs) { this.logs = this.logs.slice(-this.maxLogs); } console.log( "[RPC Logger] Total logs:", this.logs.length, "Listeners:", this.listeners.size ); this.listeners.forEach((listener) => { try { listener(entry); } catch (err) { console.error("[RPC Logger] Listener error:", err); } }); } subscribe(listener) { this.listeners.add(listener); return () => this.listeners.delete(listener); } getLogsForServer(serverId) { return this.logs.filter((log) => log.serverId === serverId); } getAllLogs() { return [...this.logs]; } clear(serverId) { if (serverId) { this.logs = this.logs.filter((log) => log.serverId !== serverId); } else { this.logs = []; } } }; rpcLogStore = new RpcLogStore(); __name(getRpcLogs, "getRpcLogs"); __name(getAllRpcLogs, "getAllRpcLogs"); __name(subscribeToRpcLogs, "subscribeToRpcLogs"); __name(clearRpcLogs, "clearRpcLogs"); __name(wrapTransportForLogging, "wrapTransportForLogging"); } }); // src/react/index.ts var react_exports = {}; __export(react_exports, { BrowserTelemetry: () => Tel, ErrorBoundary: () => ErrorBoundary, Image: () => Image, LocalStorageProvider: () => LocalStorageProvider, McpClientProvider: () => McpClientProvider, McpUseProvider: () => McpUseProvider, MemoryStorageProvider: () => MemoryStorageProvider, Tel: () => Tel, Telemetry: () => Telemetry, ThemeProvider: () => ThemeProvider, WidgetControls: () => WidgetControls, clearRpcLogs: () => clearRpcLogs, getAllRpcLogs: () => getAllRpcLogs, getRpcLogs: () => getRpcLogs, onMcpAuthorization: () => onMcpAuthorization, setBrowserTelemetrySource: () => setTelemetrySource, setTelemetrySource: () => setTelemetrySource, subscribeToRpcLogs: () => subscribeToRpcLogs, useMcp: () => useMcp, useMcpClient: () => useMcpClient, useMcpServer: () => useMcpServer, useWidget: () => useWidget, useWidgetProps: () => useWidgetProps, useWidgetState: () => useWidgetState, useWidgetTheme: () => useWidgetTheme }); module.exports = __toCommonJS(react_exports); // src/react/useMcp.ts var import_react = require("react"); // src/utils/url-sanitize.ts function sanitizeUrl(raw) { const abort = /* @__PURE__ */ __name(() => { throw new Error(`Invalid url to pass to open(): ${raw}`); }, "abort"); let url; try { url = new URL(raw); } catch (_) { abort(); } if (url.protocol !== "https:" && url.protocol !== "http:") abort(); if (url.hostname !== encodeURIComponent(url.hostname)) abort(); if (url.username) url.username = encodeURIComponent(url.username); if (url.password) url.password = encodeURIComponent(url.password); url.pathname = url.pathname.slice(0, 1) + encodeURIComponent(url.pathname.slice(1)).replace(/%2f/gi, "/"); url.search = url.search.slice(0, 1) + Array.from(url.searchParams.entries()).map(sanitizeParam).join("&"); url.hash = url.hash.slice(0, 1) + encodeURIComponent(url.hash.slice(1)); return url.href; } __name(sanitizeUrl, "sanitizeUrl"); function sanitizeParam([k, v]) { return `${encodeURIComponent(k)}${v.length > 0 ? `=${encodeURIComponent(v)}` : ""}`; } __name(sanitizeParam, "sanitizeParam"); // src/auth/browser-provider.ts async function serializeBody(body) { if (typeof body === "string") return body; if (body instanceof URLSearchParams || body instanceof FormData) { return Object.fromEntries(body.entries()); } if (body instanceof Blob) return await body.text(); return body; } __name(serializeBody, "serializeBody"); var BrowserOAuthClientProvider = class { static { __name(this, "BrowserOAuthClientProvider"); } serverUrl; storageKeyPrefix; serverUrlHash; clientName; clientUri; logoUri; callbackUrl; preventAutoAuth; useRedirectFlow; oauthProxyUrl; connectionUrl; // MCP proxy URL that client connected to originalFetch; onPopupWindow; constructor(serverUrl, options = {}) { this.serverUrl = serverUrl; this.storageKeyPrefix = options.storageKeyPrefix || "mcp:auth"; this.serverUrlHash = this.hashString(serverUrl); this.clientName = options.clientName || "mcp-use"; this.clientUri = options.clientUri || (typeof window !== "undefined" ? window.location.origin : ""); this.logoUri = options.logoUri || "https://mcp-use.com/logo.png"; this.callbackUrl = sanitizeUrl( options.callbackUrl || (typeof window !== "undefined" ? new URL("/oauth/callback", window.location.origin).toString() : "/oauth/callback") ); this.preventAutoAuth = options.preventAutoAuth; this.useRedirectFlow = options.useRedirectFlow; this.oauthProxyUrl = options.oauthProxyUrl; this.connectionUrl = options.connectionUrl; this.onPopupWindow = options.onPopupWindow; } /** * Install fetch interceptor to proxy OAuth requests through the backend */ installFetchInterceptor() { if (!this.oauthProxyUrl) { console.warn( "[BrowserOAuthProvider] No OAuth proxy URL configured, skipping fetch interceptor installation" ); return; } if (!this.originalFetch) { this.originalFetch = window.fetch; } else { console.warn( "[BrowserOAuthProvider] Fetch interceptor already installed" ); return; } const oauthProxyUrl = this.oauthProxyUrl; const connectionUrl = this.connectionUrl; const originalFetch = this.originalFetch; console.log( `[BrowserOAuthProvider] Installing fetch interceptor with proxy: ${oauthProxyUrl}` ); window.fetch = /* @__PURE__ */ __name(async function interceptedFetch(input, init) { const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url; const isOAuthRequest = url.includes("/.well-known/") || url.match(/\/(register|token|authorize)$/); if (!isOAuthRequest) { return await originalFetch(input, init); } try { const urlObj = new URL(url); const proxyUrlObj = new URL(oauthProxyUrl); if (urlObj.origin === proxyUrlObj.origin && (urlObj.pathname.startsWith(proxyUrlObj.pathname) || url.includes("/inspector/api/oauth"))) { return await originalFetch(input, init); } } catch { } try { const isMetadata = url.includes("/.well-known/"); const proxyEndpoint = isMetadata ? `${oauthProxyUrl}/metadata?url=${encodeURIComponent(url)}` : `${oauthProxyUrl}/proxy`; console.log( `[OAuth Proxy] Routing ${isMetadata ? "metadata" : "request"} through: ${proxyEndpoint}` ); if (isMetadata) { const headers = { ...init?.headers ? Object.fromEntries(new Headers(init.headers)) : {} }; if (connectionUrl) { headers["X-Connection-URL"] = connectionUrl; } return await originalFetch(proxyEndpoint, { ...init, method: "GET", headers }); } const body = init?.body ? await serializeBody(init.body) : void 0; const response = await originalFetch(proxyEndpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url, method: init?.method || "POST", headers: init?.headers ? Object.fromEntries(new Headers(init.headers)) : {}, body }) }); const data = await response.json(); return new Response(JSON.stringify(data.body), { status: data.status, statusText: data.statusText, headers: new Headers(data.headers) }); } catch (error) { console.error( "[OAuth Proxy] Request failed, falling back to direct fetch:", error ); return await originalFetch(input, init); } }, "interceptedFetch"); } /** * Restore original fetch after OAuth flow completes */ restoreFetch() { if (this.originalFetch) { console.log("[BrowserOAuthProvider] Restoring original fetch"); window.fetch = this.originalFetch; this.originalFetch = void 0; } } // --- SDK Interface Methods --- get redirectUrl() { return sanitizeUrl(this.callbackUrl); } get clientMetadata() { return { redirect_uris: [this.redirectUrl], token_endpoint_auth_method: "none", // Public client grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], client_name: this.clientName, client_uri: this.clientUri, logo_uri: this.logoUri // scope: 'openid profile email mcp', // Example scopes, adjust as needed }; } async clientInformation() { const key = this.getKey("client_info"); const data = localStorage.getItem(key); if (!data) return void 0; try { return JSON.parse(data); } catch (e) { console.warn( `[${this.storageKeyPrefix}] Failed to parse client information:`, e ); localStorage.removeItem(key); return void 0; } } // NOTE: The SDK's auth() function uses this if dynamic registration is needed. // Ensure your OAuthClientInformationFull matches the expected structure if DCR is used. async saveClientInformation(clientInformation) { const key = this.getKey("client_info"); localStorage.setItem(key, JSON.stringify(clientInformation)); } async tokens() { const key = this.getKey("tokens"); const data = localStorage.getItem(key); if (!data) return void 0; try { return JSON.parse(data); } catch (e) { console.warn(`[${this.storageKeyPrefix}] Failed to parse tokens:`, e); localStorage.removeItem(key); return void 0; } } async saveTokens(tokens) { const key = this.getKey("tokens"); localStorage.setItem(key, JSON.stringify(tokens)); localStorage.removeItem(this.getKey("code_verifier")); localStorage.removeItem(this.getKey("last_auth_url")); } async saveCodeVerifier(codeVerifier) { const key = this.getKey("code_verifier"); localStorage.setItem(key, codeVerifier); } async codeVerifier() { const key = this.getKey("code_verifier"); const verifier = localStorage.getItem(key); if (!verifier) { throw new Error( `[${this.storageKeyPrefix}] Code verifier not found in storage for key ${key}. Auth flow likely corrupted or timed out.` ); } return verifier; } /** * Generates and stores the authorization URL with state, without opening a popup. * Used when preventAutoAuth is enabled to provide the URL for manual navigation. * @param authorizationUrl The fully constructed authorization URL from the SDK. * @returns The full authorization URL with state parameter. */ async prepareAuthorizationUrl(authorizationUrl) { const state = globalThis.crypto.randomUUID(); const stateKey = `${this.storageKeyPrefix}:state_${state}`; const stateData = { serverUrlHash: this.serverUrlHash, expiry: Date.now() + 1e3 * 60 * 10, // State expires in 10 minutes // Store provider options needed to reconstruct on callback providerOptions: { serverUrl: this.serverUrl, storageKeyPrefix: this.storageKeyPrefix, clientName: this.clientName, clientUri: this.clientUri, callbackUrl: this.callbackUrl }, // Store flow type so callback knows how to handle the response flowType: this.useRedirectFlow ? "redirect" : "popup", // Always store current URL so we can return to it after auth // This is critical for popup flow when popup is blocked and user clicks link manually returnUrl: typeof window !== "undefined" ? window.location.href : void 0 }; console.log(`[OAuth] Storing state key: ${stateKey}`); localStorage.setItem(stateKey, JSON.stringify(stateData)); const verified = localStorage.getItem(stateKey); console.log(`[OAuth] State stored successfully: ${!!verified}`); authorizationUrl.searchParams.set("state", state); const authUrlString = authorizationUrl.toString(); const sanitizedAuthUrl = sanitizeUrl(authUrlString); localStorage.setItem(this.getKey("last_auth_url"), sanitizedAuthUrl); return sanitizedAuthUrl; } /** * Redirects the user agent to the authorization URL, storing necessary state. * This now adheres to the SDK's void return type expectation for the interface. * @param authorizationUrl The fully constructed authorization URL from the SDK. */ async redirectToAuthorization(authorizationUrl) { const sanitizedAuthUrl = await this.prepareAuthorizationUrl(authorizationUrl); if (this.preventAutoAuth) { console.info( `[${this.storageKeyPrefix}] Auto-auth prevented. Authorization URL stored for manual trigger.` ); return; } if (this.useRedirectFlow) { console.info( `[${this.storageKeyPrefix}] Redirecting to authorization URL (full-page redirect).` ); window.location.href = sanitizedAuthUrl; return; } const popupFeatures = "width=600,height=700,resizable=yes,scrollbars=yes,status=yes"; try { const popup = window.open( sanitizedAuthUrl, `mcp_auth_${this.serverUrlHash}`, popupFeatures ); if (this.onPopupWindow) { this.onPopupWindow(sanitizedAuthUrl, popupFeatures, popup); } if (!popup || popup.closed || typeof popup.closed === "undefined") { console.warn( `[${this.storageKeyPrefix}] Popup likely blocked by browser. Manual navigation might be required using the stored URL.` ); } else { popup.focus(); console.info( `[${this.storageKeyPrefix}] Redirecting to authorization URL in popup.` ); } } catch (e) { console.error( `[${this.storageKeyPrefix}] Error opening popup window:`, e ); } } // --- Helper Methods --- /** * Retrieves the last URL passed to `redirectToAuthorization`. Useful for manual fallback. */ getLastAttemptedAuthUrl() { const storedUrl = localStorage.getItem(this.getKey("last_auth_url")); return storedUrl ? sanitizeUrl(storedUrl) : null; } clearStorage() { const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`; const statePattern = `${this.storageKeyPrefix}:state_`; const keysToRemove = []; let count = 0; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (!key) continue; if (key.startsWith(prefixPattern)) { keysToRemove.push(key); } else if (key.startsWith(statePattern)) { try { const item = localStorage.getItem(key); if (item) { const state = JSON.parse(item); if (state.serverUrlHash === this.serverUrlHash) { keysToRemove.push(key); } } } catch (e) { console.warn( `[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`, e ); } } } const uniqueKeysToRemove = [...new Set(keysToRemove)]; uniqueKeysToRemove.forEach((key) => { localStorage.removeItem(key); count++; }); return count; } hashString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return Math.abs(hash).toString(16); } getKey(keySuffix) { return `${this.storageKeyPrefix}_${this.serverUrlHash}_${keySuffix}`; } }; // src/connectors/http.ts var import_client = require("@modelcontextprotocol/sdk/client/index.js"); var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js"); // src/logging.ts var DEFAULT_LOGGER_NAME = "mcp-use"; function resolveLevel(env) { const envValue = typeof process !== "undefined" && process.env ? env : void 0; switch (envValue?.trim()) { case "2": return "debug"; case "1": return "info"; default: return "info"; } } __name(resolveLevel, "resolveLevel"); function formatArgs(args) { if (args.length === 0) return ""; return args.map((arg) => { if (typeof arg === "string") return arg; try { return JSON.stringify(arg); } catch { return String(arg); } }).join(" "); } __name(formatArgs, "formatArgs"); var SimpleConsoleLogger = class { static { __name(this, "SimpleConsoleLogger"); } _level; name; format; constructor(name = DEFAULT_LOGGER_NAME, level = "info", format = "minimal") { this.name = name; this._level = level; this.format = format; } shouldLog(level) { const levels = [ "error", "warn", "info", "http", "verbose", "debug", "silly" ]; const currentIndex = levels.indexOf(this._level); const messageIndex = levels.indexOf(level); return messageIndex <= currentIndex; } formatMessage(level, message, args) { const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }); const extraArgs = formatArgs(args); const fullMessage = extraArgs ? `${message} ${extraArgs}` : message; switch (this.format) { case "detailed": return `${timestamp} [${this.name}] ${level.toUpperCase()}: ${fullMessage}`; case "emoji": { const emojiMap = { error: "\u274C", warn: "\u26A0\uFE0F", info: "\u2139\uFE0F", http: "\u{1F310}", verbose: "\u{1F4DD}", debug: "\u{1F50D}", silly: "\u{1F92A}" }; return `${timestamp} [${this.name}] ${emojiMap[level] || ""} ${level.toUpperCase()}: ${fullMessage}`; } case "minimal": default: return `${timestamp} [${this.name}] ${level}: ${fullMessage}`; } } error(message, ...args) { if (this.shouldLog("error")) { console.error(this.formatMessage("error", message, args)); } } warn(message, ...args) { if (this.shouldLog("warn")) { console.warn(this.formatMessage("warn", message, args)); } } info(message, ...args) { if (this.shouldLog("info")) { console.info(this.formatMessage("info", message, args)); } } debug(message, ...args) { if (this.shouldLog("debug")) { console.debug(this.formatMessage("debug", message, args)); } } http(message, ...args) { if (this.shouldLog("http")) { console.log(this.formatMessage("http", message, args)); } } verbose(message, ...args) { if (this.shouldLog("verbose")) { console.log(this.formatMessage("verbose", message, args)); } } silly(message, ...args) { if (this.shouldLog("silly")) { console.log(this.formatMessage("silly", message, args)); } } get level() { return this._level; } set level(newLevel) { this._level = newLevel; } setFormat(format) { this.format = format; } }; var Logger = class { static { __name(this, "Logger"); } static instances = {}; static currentFormat = "minimal"; static get(name = DEFAULT_LOGGER_NAME) { if (!this.instances[name]) { const debugEnv = typeof process !== "undefined" && process.env?.DEBUG || void 0; this.instances[name] = new SimpleConsoleLogger( name, resolveLevel(debugEnv), this.currentFormat ); } return this.instances[name]; } static configure(options = {}) { const { level, format = "minimal" } = options; const debugEnv = typeof process !== "undefined" && process.env?.DEBUG || void 0; const resolvedLevel = level ?? resolveLevel(debugEnv); this.currentFormat = format; Object.values(this.instances).forEach((logger2) => { logger2.level = resolvedLevel; logger2.setFormat(format); }); } static setDebug(enabled) { let level; if (enabled === 2 || enabled === true) level = "debug"; else if (enabled === 1) level = "info"; else level = "info"; Object.values(this.instances).forEach((logger2) => { logger2.level = level; }); if (typeof process !== "undefined" && process.env) { process.env.DEBUG = enabled ? enabled === true ? "2" : String(enabled) : "0"; } } static setFormat(format) { this.currentFormat = format; this.configure({ format }); } }; var logger = Logger.get(); // src/task_managers/sse.ts var import_sse = require("@modelcontextprotocol/sdk/client/sse.js"); // src/task_managers/base.ts var ConnectionManager = class { static { __name(this, "ConnectionManager"); } _readyPromise; _readyResolver; _donePromise; _doneResolver; _exception = null; _connection = null; _task = null; _abortController = null; constructor() { this.reset(); } /** * Start the connection manager and establish a connection. * * @returns The established connection. * @throws If the connection cannot be established. */ async start() { this.reset(); logger.debug(`Starting ${this.constructor.name}`); this._task = this.connectionTask(); await this._readyPromise; if (this._exception) { throw this._exception; } if (this._connection === null) { throw new Error("Connection was not established"); } return this._connection; } /** * Stop the connection manager and close the connection. */ async stop() { if (this._task && this._abortController) { logger.debug(`Cancelling ${this.constructor.name} task`); this._abortController.abort(); try { await this._task; } catch (e) { if (e instanceof Error && e.name === "AbortError") { logger.debug(`${this.constructor.name} task aborted successfully`); } else { logger.warn(`Error stopping ${this.constructor.name} task: ${e}`); } } } await this._donePromise; logger.debug(`${this.constructor.name} task completed`); } /** * Reset all internal state. */ reset() { this._readyPromise = new Promise((res) => this._readyResolver = res); this._donePromise = new Promise((res) => this._doneResolver = res); this._exception = null; this._connection = null; this._task = null; this._abortController = new AbortController(); } /** * The background task responsible for establishing and maintaining the * connection until it is cancelled. */ async connectionTask() { logger.debug(`Running ${this.constructor.name} task`); try { this._connection = await this.establishConnection(); logger.debug(`${this.constructor.name} connected successfully`); this._readyResolver(); await this.waitForAbort(); } catch (err) { this._exception = err; logger.error(`Error in ${this.constructor.name} task: ${err}`); this._readyResolver(); } finally { if (this._connection !== null) { try { await this.closeConnection(this._connection); } catch (closeErr) { logger.warn( `Error closing connection in ${this.constructor.name}: ${closeErr}` ); } this._connection = null; } this._doneResolver(); } } /** * Helper that returns a promise which resolves when the abort signal fires. */ async waitForAbort() { return new Promise((_resolve, _reject) => { if (!this._abortController) { return; } const signal = this._abortController.signal; if (signal.aborted) { _resolve(); return; } const onAbort = /* @__PURE__ */ __name(() => { signal.removeEventListener("abort", onAbort); _resolve(); }, "onAbort"); signal.addEventListener("abort", onAbort); }); } }; // src/task_managers/sse.ts var SseConnectionManager = class extends ConnectionManager { static { __name(this, "SseConnectionManager"); } url; opts; _transport = null; reinitializing = false; /** * Create an SSE connection manager. * * @param url The SSE endpoint URL. * @param opts Optional transport options (auth, headers, etc.). */ constructor(url, opts) { super(); this.url = typeof url === "string" ? new URL(url) : url; this.opts = opts; } /** * Spawn a new `SSEClientTransport` and wrap it with 404 handling. * Per MCP spec, clients MUST re-initialize when receiving 404 for stale sessions. */ async establishConnection() { const transport = new import_sse.SSEClientTransport(this.url, this.opts); const originalSend = transport.send.bind(transport); transport.send = async (message) => { const sendMessage = /* @__PURE__ */ __name(async (msg) => { if (Array.isArray(msg)) { for (const singleMsg of msg) { await originalSend(singleMsg); } } else { await originalSend(msg); } }, "sendMessage"); try { await sendMessage(message); } catch (error) { if (error?.code === 404 && transport.sessionId && !this.reinitializing) { logger.warn( `[SSE] Session not found (404), re-initializing per MCP spec...` ); this.reinitializing = true; try { transport.sessionId = void 0; await this.reinitialize(transport); logger.info(`[SSE] Re-initialization successful, retrying request`); await sendMessage(message); } finally { this.reinitializing = false; } } else { throw error; } } }; this._transport = transport; logger.debug(`${this.constructor.name} connected successfully`); return transport; } /** * Re-initialize the transport with a new session * This is called when the server returns 404 for a stale session */ async reinitialize(transport) { logger.debug(`[SSE] Re-initialization triggered`); } /** * Close the underlying transport and clean up resources. */ async closeConnection(_connection) { if (this._transport) { try { await this._transport.close(); } catch (e) { logger.warn(`Error closing SSE transport: ${e}`); } finally { this._transport = null; } } } }; // src/connectors/base.ts var import_types = require("@modelcontextprotocol/sdk/types.js"); // src/telemetry/events.ts var BaseTelemetryEvent = class { static { __name(this, "BaseTelemetryEvent"); } }; var MCPAgentExecutionEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "MCPAgentExecutionEvent"); } get name() { return "mcp_agent_execution"; } get properties() { return { // Core execution info execution_method: this.data.executionMethod, query: this.data.query, query_length: this.data.query.length, success: this.data.success, // Agent configuration model_provider: this.data.modelProvider, model_name: this.data.modelName, server_count: this.data.serverCount, server_identifiers: this.data.serverIdentifiers, total_tools_available: this.data.totalToolsAvailable, tools_available_names: this.data.toolsAvailableNames, max_steps_configured: this.data.maxStepsConfigured, memory_enabled: this.data.memoryEnabled, use_server_manager: this.data.useServerManager, // Execution parameters (always include, even if null) max_steps_used: this.data.maxStepsUsed, manage_connector: this.data.manageConnector, external_history_used: this.data.externalHistoryUsed, // Execution results (always include, even if null) steps_taken: this.data.stepsTaken ?? null, tools_used_count: this.data.toolsUsedCount ?? null, tools_used_names: this.data.toolsUsedNames ?? null, response: this.data.response ?? null, response_length: this.data.response ? this.data.response.length : null, execution_time_ms: this.data.executionTimeMs ?? null, error_type: this.data.errorType ?? null, conversation_history_length: this.data.conversationHistoryLength ?? null }; } }; function createServerRunEventData(server, transport) { const toolRegistrations = Array.from(server.registrations.tools.values()); const promptRegistrations = Array.from(server.registrations.prompts.values()); const resourceRegistrations = Array.from( server.registrations.resources.values() ); const templateRegistrations = Array.from( server.registrations.resourceTemplates.values() ); const allResources = resourceRegistrations.map((r) => ({ name: r.config.name, title: r.config.title ?? null, description: r.config.description ?? null, uri: r.config.uri ?? null, mime_type: r.config.mimeType ?? null })); const appsSdkResources = allResources.filter( (r) => r.mime_type === "text/html+skybridge" ); const mcpUiResources = allResources.filter( (r) => r.mime_type === "text/uri-list" || r.mime_type === "text/html" ); const mcpAppsResources = allResources.filter( (r) => r.mime_type === "text/html+mcp" ); return { transport, toolsNumber: server.registeredTools.length, resourcesNumber: server.registeredResources.length, promptsNumber: server.registeredPrompts.length, auth: !!server.oauthProvider, name: server.config.name, description: server.config.description ?? null, baseUrl: server.serverBaseUrl ?? null, toolNames: server.registeredTools.length > 0 ? server.registeredTools : null, resourceNames: server.registeredResources.length > 0 ? server.registeredResources : null, promptNames: server.registeredPrompts.length > 0 ? server.registeredPrompts : null, tools: toolRegistrations.length > 0 ? toolRegistrations.map((r) => ({ name: r.config.name, title: r.config.title ?? null, description: r.config.description ?? null, input_schema: r.config.schema ? JSON.stringify(r.config.schema) : null, output_schema: r.config.outputSchema ? JSON.stringify(r.config.outputSchema) : null })) : null, resources: allResources.length > 0 ? allResources : null, prompts: promptRegistrations.length > 0 ? promptRegistrations.map((r) => ({ name: r.config.name, title: r.config.title ?? null, description: r.config.description ?? null, args: r.config.args ? JSON.stringify(r.config.args) : null })) : null, templates: templateRegistrations.length > 0 ? templateRegistrations.map((r) => ({ name: r.config.name, title: r.config.title ?? null, description: r.config.description ?? null })) : null, capabilities: { logging: true, resources: { subscribe: true, listChanged: true } }, appsSdkResources: appsSdkResources.length > 0 ? appsSdkResources : null, appsSdkResourcesNumber: appsSdkResources.length, mcpUiResources: mcpUiResources.length > 0 ? mcpUiResources : null, mcpUiResourcesNumber: mcpUiResources.length, mcpAppsResources: mcpAppsResources.length > 0 ? mcpAppsResources : null, mcpAppsResourcesNumber: mcpAppsResources.length }; } __name(createServerRunEventData, "createServerRunEventData"); var ServerRunEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerRunEvent"); } get name() { return "server_run"; } get properties() { return { transport: this.data.transport, tools_number: this.data.toolsNumber, resources_number: this.data.resourcesNumber, prompts_number: this.data.promptsNumber, auth: this.data.auth, name: this.data.name, description: this.data.description ?? null, base_url: this.data.baseUrl ?? null, tool_names: this.data.toolNames ?? null, resource_names: this.data.resourceNames ?? null, prompt_names: this.data.promptNames ?? null, tools: this.data.tools ?? null, resources: this.data.resources ?? null, prompts: this.data.prompts ?? null, templates: this.data.templates ?? null, capabilities: this.data.capabilities ? JSON.stringify(this.data.capabilities) : null, apps_sdk_resources: this.data.appsSdkResources ? JSON.stringify(this.data.appsSdkResources) : null, apps_sdk_resources_number: this.data.appsSdkResourcesNumber ?? 0, mcp_ui_resources: this.data.mcpUiResources ? JSON.stringify(this.data.mcpUiResources) : null, mcp_ui_resources_number: this.data.mcpUiResourcesNumber ?? 0, mcp_apps_resources: this.data.mcpAppsResources ? JSON.stringify(this.data.mcpAppsResources) : null, mcp_apps_resources_number: this.data.mcpAppsResourcesNumber ?? 0 }; } }; var ServerInitializeEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerInitializeEvent"); } get name() { return "server_initialize_call"; } get properties() { return { protocol_version: this.data.protocolVersion, client_info: JSON.stringify(this.data.clientInfo), client_capabilities: JSON.stringify(this.data.clientCapabilities), session_id: this.data.sessionId ?? null }; } }; var ServerToolCallEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerToolCallEvent"); } get name() { return "server_tool_call"; } get properties() { return { tool_name: this.data.toolName, length_input_argument: this.data.lengthInputArgument, success: this.data.success, error_type: this.data.errorType ?? null, execution_time_ms: this.data.executionTimeMs ?? null }; } }; var ServerResourceCallEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerResourceCallEvent"); } get name() { return "server_resource_call"; } get properties() { return { name: this.data.name, description: this.data.description, contents: this.data.contents, success: this.data.success, error_type: this.data.errorType ?? null }; } }; var ServerPromptCallEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerPromptCallEvent"); } get name() { return "server_prompt_call"; } get properties() { return { name: this.data.name, description: this.data.description, success: this.data.success, error_type: this.data.errorType ?? null }; } }; var ServerContextEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ServerContextEvent"); } get name() { return `server_context_${this.data.contextType}`; } get properties() { return { context_type: this.data.contextType, notification_type: this.data.notificationType ?? null }; } }; var MCPClientInitEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "MCPClientInitEvent"); } get name() { return "mcpclient_init"; } get properties() { return { code_mode: this.data.codeMode, sandbox: this.data.sandbox, all_callbacks: this.data.allCallbacks, verify: this.data.verify, servers: this.data.servers, num_servers: this.data.numServers, is_browser: this.data.isBrowser }; } }; var ConnectorInitEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ConnectorInitEvent"); } get name() { return "connector_init"; } get properties() { return { connector_type: this.data.connectorType, server_command: this.data.serverCommand ?? null, server_args: this.data.serverArgs ?? null, server_url: this.data.serverUrl ?? null, public_identifier: this.data.publicIdentifier ?? null }; } }; var ClientAddServerEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ClientAddServerEvent"); } get name() { return "client_add_server"; } get properties() { const { serverName, serverConfig } = this.data; const url = serverConfig.url; return { server_name: serverName, server_url_domain: url ? this._extractHostname(url) : null, transport: serverConfig.transport ?? null, has_auth: !!(serverConfig.authToken || serverConfig.authProvider) }; } _extractHostname(url) { try { return new URL(url).hostname; } catch { return null; } } }; var ClientRemoveServerEvent = class extends BaseTelemetryEvent { constructor(data) { super(); this.data = data; } static { __name(this, "ClientRemoveServerEvent"); } get name() { return "client_remove_server"; } get properties() { return { server_name: this.data.serverName }; } }; // src/version.ts var VERSION = "1.12.4"; function getPackageVersion() { return VERSION; } __name(getPackageVersion, "getPackageVersion"); // src/telemetry/telemetry-browser.ts function generateUUID() { if (typeof globalThis !== "undefined" && globalThis.crypto && typeof globalThis.crypto.randomUUID === "function") { return globalThis.crypto.randomUUID(); } if (typeof globalThis !== "undefined" && globalThis.crypto && typeof globalThis.crypto.getRandomValues === "function") { const array = new Uint8Array(16); globalThis.crypto.getRandomValues(array); const hex = Array.from(array, (v) => v.toString(16).padStart(2, "0")).join( "" ); return `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`; } return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}-${Math.random().toString(36).substring(2, 15)}`; } __name(generateUUID, "generateUUID"); function secureRandomString() { if (typeof window !== "undefined" && window.crypto && typeof window.crypto.getRandomValues === "function") { const array = new Uint8Array(8); window.crypto.getRandomValues(array); return Array.from(array, (v) => v.toString(16).padStart(2, "0")).join(""); } if (typeof globalThis !== "undefined" && globalThis.crypto && typeof globalThis.crypto.getRandomValues === "function") { const array = new Uint8Array(8); globalThis.crypto.getRandomValues(array); return Array.from(array, (v) => v.toString(16).padStart(2, "0")).join(""); } return Math.random().toString(36).substring(2, 15); } __name(secureRandomString, "secureRandomString"); var USER_ID_STORAGE_KEY = "mcp_use_user_id"; function detectRuntimeEnvironment() { try { if (typeof window !== "undefined" && typeof document !== "undefined") { return "browser"; } return "unknown"; } catch { return "unknown"; } } __name(detectRuntimeEnvironment, "detectRuntimeEnvironment"); function getStorageCapability(env) { if (env === "browser") { try { if (typeof localStorage !== "undefined") { localStorage.setItem("__mcp_use_test__", "1"); localStorage.removeItem("__mcp_use_test__"); return "localStorage"; } } catch { } } return "session-only"; } __name(getStorageCapability, "getStorageCapability"); var cachedEnvironment = null; function getRuntimeEnvironment() { if (cachedEnvironment === null) { cachedEnvironment = detectRuntimeEnvironment(); } return cachedEnvironment; } __name(getRuntimeEnvironment, "getRuntimeEnvironment"); var Telemetry = class _Telemetry { static { __name(this, "Telemetry"); } static instance = null; PROJECT_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI"; HOST = "https://eu.i.posthog.com"; UNKNOWN_USER_ID = "UNKNOWN_USER_ID"; _currUserId = null; _posthogBrowserClient = null; _posthogLoading = null; _runtimeEnvironment; _storageCapability; _source; constructor() { this._runtimeEnvironment = getRuntimeEnvironment(); this._storageCapability = getStorageCapability(this._runtimeEnvironment); this._source = this._getSourceFromLocalStorage() || this._runtimeEnvironment; const telemetryDisabled = this._checkTelemetryDisabled(); const canSupportTelemetry = this._runtimeEnvironment !== "unknown"; if (telemetryDisabled) { this._posthogBrowserClient = null; logger.debug("Telemetry disabled via localStorage"); } else if (!canSupportTelemetry) { this._posthogBrowserClient = null; logger.debug( `Telemetry disabled - unknown environment: ${this._runtimeEnvironment}` ); } else { logger.info( "Anonymized telemetry enabled. Set MCP_USE_ANONYMIZED_TELEMETRY=false in localStorage to disable." ); this._posthogLoading = this._initPostHogBrowser(); } } _getSourceFromLocalStorage() { try { if (typeof localStorage !== "undefined") { return localStorage.getItem("MCP_USE_TELEMETRY_SOURCE"); } } catch { } return null; } _checkTelemetryDisabled() { if (typeof localStorage !== "undefined" && localStorage.getItem("MCP_USE_ANONYMIZED_TELEMETRY") === "false") { return true; } return false; } async _initPostHogBrowser() { try { const posthogModule = await import("posthog-js"); const posthogModuleTyped = posthogModule; const posthog = posthogModuleTyped.default || posthogModuleTyped.posthog; if (!posthog || typeof posthog.init !== "function") { throw new Error("posthog-js module did not export expected interface"); } posthog.init(this.PROJECT_API_KEY, { api_host: this.HOST, persistence: "localStorage", autocapture: false, // We only want explicit captures capture_pageview: false, // We don't want automatic pageview tracking disable_session_recording: true, // No session recording loaded: /* @__PURE__ */ __name(() => { logger.debug("PostHog browser client initialized"); }, "loaded") }); this._posthogBrowserClient = posthog; } catch (e) { logger.warn(`Failed to initialize PostHog browser telemetry: ${e}`); this._posthogBrowserClient = null; } } /** * Get the detected runtime environment */ get runtimeEnvironment() { return this._runtimeEnvironment; } /** * Get the storage capability for this environment */ get storageCapability() { return this._storageCapability; } static getInstance() { if (!_Telemetry.instance) { _Telemetry.instance = new _Telemetry(); } return _Telemetry.instance; } /** * Set the source identifier for telemetry events. * This allows tracking usage from different applications. * @param source - The source identifier (e.g., "my-app", "cli", "vs-code-extension") */ setSource(source) { this._source = source; try { if (typeof localStorage !== "undefined") { localStorage.setItem("MCP_USE_TELEMETRY_SOURCE", source); } } catch { } logger.debug(`Telemetry source set to: ${source}`); } /** * Get the current source identifier. */ getSource() { return this._source; } /** * Check if telemetry is enabled. */ get isEnabled() { return this._posthogBrowserClient !== null; } get userId() { if (this._currUserId) { return this._currUserId; } try { switch (this._storageCapability) { case "localStorage": this._currUserId = this._getUserIdFromLocalStorage(); break; case "session-only": default: try { this._currUserId = `session-${generateUUID()}`; } catch (uuidError) { this._currUserId = `session-${Date.now()}-${secureRandomString()}`; } break; } } catch (e) { this._currUserId = this.UNKNOWN_USER_ID; } return this._currUserId; } /** * Get or create user ID from localStorage (Browser) */ _getUserIdFromLocalStorage() { try { if (typeof localStorage === "undefined") { throw new Error("localStorage is not available"); } try { localStorage.setItem("__mcp_use_test__", "1"); localStorage.removeItem("__mcp_use_test__"); } catch (testError) { throw new Error(`localStorage is not writable: ${testError}`); } let userId = localStorage.getItem(USER_ID_STORAGE_KEY); if (!userId) { try { userId = generateUUID(); } catch (uuidError) { userId = `${Date.now()}-${secureRandomString()}`; } localStorage.setItem(USER_ID_STORAGE_KEY, userId); } return userId; } catch (e) { logger.debug(`Failed to access localStorage for user ID: ${e}`); let sessionId; try { sessionId = `session-${generateUUID()}`; } catch (uuidError) { sessionId = `session-${Date.now()}-${secureRandomString()}`; } return sessionId; } } async capture(event) { if (this._posthogLoading) { await this._posthogLoading; } if (!this._posthogBrowserClient) { return; } const currentUserId = this.userId; const properties = { ...event.properties };