UNPKG

mcpcat

Version:

Analytics tool for MCP (Model Context Protocol) servers - tracks tool usage patterns and provides insights

920 lines (903 loc) 29.9 kB
// src/modules/logging.ts import { writeFileSync, appendFileSync, existsSync } from "fs"; import { homedir } from "os"; import { join } from "path"; var LOG_FILE = join(homedir(), "mcpcat.log"); function writeToLog(message) { const timestamp = (/* @__PURE__ */ new Date()).toISOString(); const logEntry = `[${timestamp}] ${message} `; try { if (!existsSync(LOG_FILE)) { writeFileSync(LOG_FILE, logEntry); } else { appendFileSync(LOG_FILE, logEntry); } } catch { } } // src/modules/compatibility.ts function logCompatibilityWarning() { writeToLog( "MCPCat SDK Compatibility: This version supports MCP SDK versions v1.0 - v1.12" ); } function isCompatibleServerType(server) { if (!server || typeof server !== "object") { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server must be an object." ); } let targetServer = server; if (server.server && typeof server.server === "object") { targetServer = server.server; } if (typeof targetServer.setRequestHandler !== "function") { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server must have a setRequestHandler method." ); } if (!targetServer._requestHandlers || !(targetServer._requestHandlers instanceof Map)) { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server._requestHandlers is not accessible." ); } if (typeof targetServer._requestHandlers.get !== "function") { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server._requestHandlers must be a Map with a get method." ); } if (typeof targetServer.getClientVersion !== "function") { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server.getClientVersion must be a function." ); } if (typeof targetServer._serverInfo !== "object" || !targetServer._serverInfo.name) { logCompatibilityWarning(); throw new Error( "MCPCat SDK compatibility error: Server._serverInfo is not accessible or missing name." ); } return targetServer; } function getMCPCompatibleErrorMessage(error) { if (error instanceof Error) { try { return JSON.stringify(error, Object.getOwnPropertyNames(error)); } catch { return "Unknown error"; } } else if (typeof error === "string") { return error; } else if (typeof error === "object" && error !== null) { return JSON.stringify(error); } return "Unknown error"; } // src/modules/tools.ts import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; // src/modules/internal.ts var _serverTracking = /* @__PURE__ */ new WeakMap(); function getServerTrackingData(server) { return _serverTracking.get(server); } function setServerTrackingData(server, data) { _serverTracking.set(server, data); } // src/modules/context-parameters.ts function addContextParameterToTools(tools) { return tools.map((tool) => { if (!tool.inputSchema) { tool.inputSchema = { type: "object", properties: {}, required: [] }; } if (!tool.inputSchema.properties?.context) { tool.inputSchema.properties.context = { type: "string", description: "Describe why you are calling this tool and how it fits into your overall task" }; if (Array.isArray(tool.inputSchema.required) && !tool.inputSchema.required.includes("context")) { tool.inputSchema.required.push("context"); } else if (!tool.inputSchema.required) { tool.inputSchema.required = ["context"]; } } return tool; }); } // src/modules/eventQueue.ts import { Configuration, EventsApi } from "mcpcat-api"; // src/thirdparty/ksuid/index.js import { randomBytes } from "crypto"; import { inspect } from "util"; import { promisify } from "util"; // src/thirdparty/ksuid/base-convert-int-array.js var maxLength = (array, from, to) => Math.ceil(array.length * Math.log2(from) / Math.log2(to)); function baseConvertIntArray(array, { from, to, fixedLength = null }) { const length = fixedLength === null ? maxLength(array, from, to) : fixedLength; const result = new Array(length); let offset = length; let input = array; while (input.length > 0) { if (offset === 0) { throw new RangeError( `Fixed length of ${fixedLength} is too small, expected at least ${maxLength(array, from, to)}` ); } const quotients = []; let remainder = 0; for (const digit of input) { const acc = digit + remainder * from; const q = Math.floor(acc / to); remainder = acc % to; if (quotients.length > 0 || q > 0) { quotients.push(q); } } result[--offset] = remainder; input = quotients; } if (fixedLength === null) { return offset > 0 ? result.slice(offset) : result; } while (offset > 0) { result[--offset] = 0; } return result; } var base_convert_int_array_default = baseConvertIntArray; // src/thirdparty/ksuid/base62.js var CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; function encode(buffer, fixedLength) { return base_convert_int_array_default(buffer, { from: 256, to: 62, fixedLength }).map((value) => CHARS[value]).join(""); } function decode(string, fixedLength) { const input = Array.from(string, (char) => { const charCode = char.charCodeAt(0); if (charCode < 58) return charCode - 48; if (charCode < 91) return charCode - 55; return charCode - 61; }); return Buffer.from( base_convert_int_array_default(input, { from: 62, to: 256, fixedLength }) ); } // src/thirdparty/ksuid/index.js var customInspectSymbol = inspect.custom; var asyncRandomBytes = promisify(randomBytes); var EPOCH_IN_MS = 14e11; var MAX_TIME_IN_MS = 1e3 * (2 ** 32 - 1) + EPOCH_IN_MS; var TIMESTAMP_BYTE_LENGTH = 4; var PAYLOAD_BYTE_LENGTH = 16; var BYTE_LENGTH = TIMESTAMP_BYTE_LENGTH + PAYLOAD_BYTE_LENGTH; var STRING_ENCODED_LENGTH = 27; var TIME_IN_MS_ASSERTION = `Valid KSUID timestamps must be in milliseconds since ${(/* @__PURE__ */ new Date(0)).toISOString()}, no earlier than ${new Date(EPOCH_IN_MS).toISOString()} and no later than ${new Date(MAX_TIME_IN_MS).toISOString()} `.trim().replace(/(\n|\s)+/g, " ").replace(/\.000Z/g, "Z"); var VALID_ENCODING_ASSERTION = `Valid encoded KSUIDs are ${STRING_ENCODED_LENGTH} characters`; var VALID_BUFFER_ASSERTION = `Valid KSUID buffers are ${BYTE_LENGTH} bytes`; var VALID_PAYLOAD_ASSERTION = `Valid KSUID payloads are ${PAYLOAD_BYTE_LENGTH} bytes`; function fromParts(timeInMs, payload) { const timestamp = Math.floor((timeInMs - EPOCH_IN_MS) / 1e3); const timestampBuffer = Buffer.allocUnsafe(TIMESTAMP_BYTE_LENGTH); timestampBuffer.writeUInt32BE(timestamp, 0); return Buffer.concat([timestampBuffer, payload], BYTE_LENGTH); } var bufferLookup = /* @__PURE__ */ new WeakMap(); var KSUID = class _KSUID { constructor(buffer) { if (!_KSUID.isValid(buffer)) { throw new TypeError(VALID_BUFFER_ASSERTION); } bufferLookup.set(this, buffer); Object.defineProperty(this, "buffer", { enumerable: true, get() { return Buffer.from(buffer); } }); } get raw() { return Buffer.from(bufferLookup.get(this).slice(0)); } get date() { return new Date(1e3 * this.timestamp + EPOCH_IN_MS); } get timestamp() { return bufferLookup.get(this).readUInt32BE(0); } get payload() { const payload = bufferLookup.get(this).slice(TIMESTAMP_BYTE_LENGTH, BYTE_LENGTH); return Buffer.from(payload); } get string() { const encoded = encode( bufferLookup.get(this), STRING_ENCODED_LENGTH ); return encoded.padStart(STRING_ENCODED_LENGTH, "0"); } compare(other) { if (!bufferLookup.has(other)) { return 0; } return bufferLookup.get(this).compare(bufferLookup.get(other), 0, BYTE_LENGTH); } equals(other) { return this === other || bufferLookup.has(other) && this.compare(other) === 0; } toString() { return `${this[Symbol.toStringTag]} { ${this.string} }`; } toJSON() { return this.string; } [customInspectSymbol]() { return this.toString(); } static async random(time = Date.now()) { const payload = await asyncRandomBytes(PAYLOAD_BYTE_LENGTH); return new _KSUID(fromParts(Number(time), payload)); } static randomSync(time = Date.now()) { const payload = randomBytes(PAYLOAD_BYTE_LENGTH); return new _KSUID(fromParts(Number(time), payload)); } static fromParts(timeInMs, payload) { if (!Number.isInteger(timeInMs) || timeInMs < EPOCH_IN_MS || timeInMs > MAX_TIME_IN_MS) { throw new TypeError(TIME_IN_MS_ASSERTION); } if (!Buffer.isBuffer(payload) || payload.byteLength !== PAYLOAD_BYTE_LENGTH) { throw new TypeError(VALID_PAYLOAD_ASSERTION); } return new _KSUID(fromParts(timeInMs, payload)); } static isValid(buffer) { return Buffer.isBuffer(buffer) && buffer.byteLength === BYTE_LENGTH; } static parse(string) { if (string.length !== STRING_ENCODED_LENGTH) { throw new TypeError(VALID_ENCODING_ASSERTION); } const decoded = decode(string, BYTE_LENGTH); if (decoded.byteLength === BYTE_LENGTH) { return new _KSUID(decoded); } const buffer = Buffer.allocUnsafe(BYTE_LENGTH); const padEnd = BYTE_LENGTH - decoded.byteLength; buffer.fill(0, 0, padEnd); decoded.copy(buffer, padEnd); return new _KSUID(buffer); } }; Object.defineProperty(KSUID.prototype, Symbol.toStringTag, { value: "KSUID" }); Object.defineProperty(KSUID, "MAX_STRING_ENCODED", { value: "aWgEPTl1tmebfsQzFP4bxwgy80V" }); Object.defineProperty(KSUID, "MIN_STRING_ENCODED", { value: "000000000000000000000000000" }); KSUID.withPrefix = function(prefix) { return { random: async (time = Date.now()) => { const ksuid = await KSUID.random(time); return `${prefix}_${ksuid.string}`; }, randomSync: (time = Date.now()) => { const ksuid = KSUID.randomSync(time); return `${prefix}_${ksuid.string}`; }, fromParts: (timeInMs, payload) => { const ksuid = KSUID.fromParts(timeInMs, payload); return `${prefix}_${ksuid.string}`; } }; }; var ksuid_default = KSUID; // package.json var package_default = { name: "mcpcat", version: "0.1.0", description: "Analytics tool for MCP (Model Context Protocol) servers - tracks tool usage patterns and provides insights", type: "module", main: "dist/index.js", module: "dist/index.mjs", types: "dist/index.d.ts", exports: { ".": { types: "./dist/index.d.ts", import: "./dist/index.mjs", require: "./dist/index.js" } }, scripts: { build: "tsup", dev: "tsup --watch", test: "vitest", "test:compatibility": "vitest run src/tests/mcp-version-compatibility.test.ts", lint: "eslint src/", typecheck: "tsc --noEmit", prepare: "husky", prepublishOnly: "pnpm run build && pnpm run test && pnpm run lint && pnpm run typecheck" }, keywords: [ "ai", "authentication", "mcp", "observability", "ai-agents", "ai-platform", "ai-agent", "mcps", "aiagents", "ai-agent-tools", "mcp-servers", "mcp-server", "mcp-tools", "agent-runtime", "mcp-framework", "mcp-analytics" ], author: "MCPcat", license: "MIT", repository: { type: "git", url: "git+https://github.com/MCPCat/mcpcat-typescript-sdk.git" }, bugs: { url: "https://github.com/MCPCat/mcpcat-typescript-sdk/issues" }, homepage: "https://github.com/MCPCat/mcpcat-typescript-sdk#readme", packageManager: "pnpm@10.11.0", devDependencies: { "@changesets/cli": "^2.29.4", "@modelcontextprotocol/sdk": "1.3.0", "@types/node": "^22.15.21", "@typescript-eslint/eslint-plugin": "^8.32.1", "@typescript-eslint/parser": "^8.32.1", "@vitest/coverage-v8": "^3.1.4", "@vitest/ui": "^3.1.4", eslint: "^9.27.0", husky: "^9.1.7", "lint-staged": "^16.1.0", prettier: "^3.5.3", tsup: "^8.5.0", typescript: "^5.8.3", vitest: "^3.1.4" }, peerDependencies: { "@modelcontextprotocol/sdk": ">=1.0.0" }, dependencies: { "mcpcat-api": "0.1.3", "redact-pii": "3.4.0" }, "lint-staged": { "*.{ts,js}": [ "eslint --fix", "prettier --write" ], "*.{json,md,yml,yaml}": [ "prettier --write" ] } }; // src/modules/constants.ts var INACTIVITY_TIMEOUT_IN_MINUTES = 30; // src/modules/session.ts function newSessionId() { return ksuid_default.withPrefix("ses").randomSync(); } function getServerSessionId(server) { const data = getServerTrackingData(server); if (!data) { throw new Error("Server tracking data not found"); } const now = Date.now(); const timeoutMs = INACTIVITY_TIMEOUT_IN_MINUTES * 60 * 1e3; if (now - data.lastActivity.getTime() > timeoutMs) { data.sessionId = newSessionId(); setServerTrackingData(server, data); } setLastActivity(server); return data.sessionId; } function setLastActivity(server) { const data = getServerTrackingData(server); if (!data) { throw new Error("Server tracking data not found"); } data.lastActivity = /* @__PURE__ */ new Date(); setServerTrackingData(server, data); } function getSessionInfo(server, data) { let clientInfo = { name: void 0, version: void 0 }; if (!data?.sessionInfo.clientName) { clientInfo = server.getClientVersion(); } const actorInfo = data?.identifiedSessions.get(data.sessionId); const sessionInfo = { ipAddress: void 0, // grab from django sdkLanguage: "TypeScript", // hardcoded for now mcpcatVersion: package_default.version, serverName: server._serverInfo?.name, serverVersion: server._serverInfo?.version, clientName: clientInfo?.name, clientVersion: clientInfo?.version, identifyActorGivenId: actorInfo?.userId, identifyActorName: actorInfo?.userData?.name, identifyActorData: actorInfo?.userData || {} }; if (!data) { return sessionInfo; } data.sessionInfo = sessionInfo; setServerTrackingData(server, data); return data.sessionInfo; } // src/modules/redaction.ts var PROTECTED_FIELDS = /* @__PURE__ */ new Set([ "sessionId", "id", "projectId", "server", "identifyActorGivenId", "identifyActorName", "identifyData", "resourceName", "eventType", "actorId" ]); async function redactStringsInObject(obj, redactFn, path = "", isProtected = false) { if (obj === null || obj === void 0) { return obj; } if (typeof obj === "string") { if (isProtected) { return obj; } return await redactFn(obj); } if (Array.isArray(obj)) { return Promise.all( obj.map( (item, index) => redactStringsInObject(item, redactFn, `${path}[${index}]`, isProtected) ) ); } if (obj instanceof Date) { return obj; } if (typeof obj === "object") { const redactedObj = {}; for (const [key, value] of Object.entries(obj)) { if (typeof value === "function" || value === void 0) { continue; } const fieldPath = path ? `${path}.${key}` : key; const isFieldProtected = isProtected || path === "" && PROTECTED_FIELDS.has(key); redactedObj[key] = await redactStringsInObject( value, redactFn, fieldPath, isFieldProtected ); } return redactedObj; } return obj; } async function redactEvent(event, redactFn) { return redactStringsInObject(event, redactFn, "", false); } // src/modules/eventQueue.ts var EventQueue = class { constructor() { this.queue = []; this.processing = false; this.maxRetries = 3; this.maxQueueSize = 1e4; // Prevent unbounded growth this.concurrency = 5; // Max parallel requests this.activeRequests = 0; const config = new Configuration({ basePath: "https://api.mcpcat.io" }); this.apiClient = new EventsApi(config); } add(event) { if (this.queue.length >= this.maxQueueSize) { writeToLog("Event queue full, dropping oldest event"); this.queue.shift(); } this.queue.push(event); this.process(); } async process() { if (this.processing) return; this.processing = true; while (this.queue.length > 0 && this.activeRequests < this.concurrency) { const event = this.queue.shift(); if (event?.redactionFn) { try { const redactedEvent = await redactEvent(event, event.redactionFn); event.redactionFn = void 0; Object.assign(event, redactedEvent); } catch (error) { writeToLog(`Failed to redact event: ${error}`); continue; } } if (event) { event.id = event.id || await ksuid_default.withPrefix("evt").random(); this.activeRequests++; this.sendEvent(event).finally(() => { this.activeRequests--; this.process(); }); } } this.processing = false; } async sendEvent(event, retries = 0) { try { await this.apiClient.publishEvent({ publishEventRequest: event }); writeToLog( `Successfully sent event ${event.id} | ${event.eventType} | ${event.projectId} | ${event.duration} ms | ${event.identifyActorGivenId || "anonymous"}` ); writeToLog(`Event details: ${JSON.stringify(event)}`); } catch (error) { writeToLog( `Failed to send event ${event.id}, retrying... [Error: ${getMCPCompatibleErrorMessage(error)}]` ); if (retries < this.maxRetries) { await this.delay(Math.pow(2, retries) * 1e3); return this.sendEvent(event, retries + 1); } throw error; } } delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } // Get queue stats for monitoring getStats() { return { queueLength: this.queue.length, activeRequests: this.activeRequests, isProcessing: this.processing }; } // Graceful shutdown - wait for active requests async destroy() { this.add = () => { writeToLog("Queue is shutting down, event dropped"); }; const timeout = 5e3; const start = Date.now(); while ((this.queue.length > 0 || this.activeRequests > 0) && Date.now() - start < timeout) { await this.delay(100); } if (this.queue.length > 0) { writeToLog( `Shutting down with ${this.queue.length} events still in queue` ); } } }; var eventQueue = new EventQueue(); process.once("SIGINT", () => eventQueue.destroy()); process.once("SIGTERM", () => eventQueue.destroy()); process.once("beforeExit", () => eventQueue.destroy()); function publishEvent(server, event) { if (!event.duration) { event.duration = event.timestamp && (/* @__PURE__ */ new Date()).getTime() - event.timestamp.getTime() || void 0; } const data = getServerTrackingData(server); if (!data) { writeToLog( "Warning: Server tracking data not found. Event will not be published." ); return; } const sessionInfo = getSessionInfo(server, data); let fullEvent = { projectId: data.projectId, ...event, ...sessionInfo }; eventQueue.add(fullEvent); } // src/modules/tools.ts import { PublishEventRequestEventTypeEnum } from "mcpcat-api"; async function handleReportMissing(args) { writeToLog(`Missing tool reported: ${JSON.stringify(args)}`); return { content: [ { type: "text", text: `Unfortunately, we have shown you the full tool list. We have noted your feedback and will work to improve the tool list in the future.` } ] }; } function setupMCPCatTools(server) { const handlers = server._requestHandlers; const originalListToolsHandler = handlers.get("tools/list"); const originalCallToolHandler = handlers.get("tools/call"); if (!originalListToolsHandler || !originalCallToolHandler) { writeToLog( "Warning: Original tool handlers not found. Your tools may not be setup before MCPCat .track()." ); return; } try { server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => { let tools = []; const data = getServerTrackingData(server); let event = { sessionId: getServerSessionId(server), parameters: { request, extra }, eventType: PublishEventRequestEventTypeEnum.mcpToolsList, timestamp: /* @__PURE__ */ new Date(), redactionFn: data?.options.redactSensitiveInformation }; try { const originalResponse = await originalListToolsHandler( request, extra ); tools = originalResponse.tools || []; } catch (error) { writeToLog( `Warning: Original list tools handler failed, this suggests an error MCPCat did not cause - ${error}` ); event.error = { message: getMCPCompatibleErrorMessage(error) }; event.isError = true; event.duration = event.timestamp && (/* @__PURE__ */ new Date()).getTime() - event.timestamp.getTime() || 0; publishEvent(server, event); throw error; } if (!data) { writeToLog( "Warning: MCPCat is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls." ); return { tools }; } if (tools.length === 0) { writeToLog( "Warning: No tools found in the original list. This is likely due to the tools not being registered before MCPCat.track()." ); event.error = { message: "No tools were sent to MCP client." }; event.isError = true; event.duration = event.timestamp && (/* @__PURE__ */ new Date()).getTime() - event.timestamp.getTime() || 0; publishEvent(server, event); return { tools }; } if (data.options.enableToolCallContext) { tools = addContextParameterToTools(tools); } tools.push({ name: "get_more_tools", description: "Check for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.", inputSchema: { type: "object", properties: { context: { type: "string", description: "A description of your goal and what kind of tool would help accomplish it." } }, required: ["context"] } }); event.response = { tools }; event.isError = false; event.duration = event.timestamp && (/* @__PURE__ */ new Date()).getTime() - event.timestamp.getTime() || 0; publishEvent(server, event); return { tools }; }); } catch (error) { writeToLog(`Warning: Failed to override list tools handler - ${error}`); } } // src/modules/tracing.ts import { CallToolRequestSchema, InitializeRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { PublishEventRequestEventTypeEnum as PublishEventRequestEventTypeEnum2 } from "mcpcat-api"; function isToolResultError(result) { return result && typeof result === "object" && result.isError === true; } function setupToolCallTracing(server) { try { const handlers = server._requestHandlers; const originalCallToolHandler = handlers.get("tools/call"); const originalInitializeHandler = handlers.get("initialize"); if (originalInitializeHandler) { server.setRequestHandler( InitializeRequestSchema, async (request, extra) => { const data = getServerTrackingData(server); if (!data) { writeToLog( "Warning: MCPCat is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls." ); return await originalInitializeHandler(request, extra); } const sessionId = getServerSessionId(server); let event = { sessionId, resourceName: request.params?.name || "Unknown Tool Name", eventType: PublishEventRequestEventTypeEnum2.mcpInitialize, parameters: { request, extra }, timestamp: /* @__PURE__ */ new Date(), redactionFn: data.options.redactSensitiveInformation }; const result = await originalInitializeHandler(request, extra); event.response = result; publishEvent(server, event); return result; } ); } server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { const data = getServerTrackingData(server); if (!data) { writeToLog( "Warning: MCPCat is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls." ); return await originalCallToolHandler?.(request, extra); } const sessionId = getServerSessionId(server); let event = { sessionId, resourceName: request.params?.name || "Unknown Tool Name", parameters: { request, extra }, eventType: PublishEventRequestEventTypeEnum2.mcpToolsCall, timestamp: /* @__PURE__ */ new Date(), redactionFn: data.options.redactSensitiveInformation }; try { if (data.options.identify && data.identifiedSessions.get(sessionId) === void 0) { let identifyEvent = { ...event, eventType: PublishEventRequestEventTypeEnum2.mcpcatIdentify }; try { const identityResult = await data.options.identify(request, extra); if (identityResult) { writeToLog( `Identified session ${sessionId} with identity: ${JSON.stringify(identityResult)}` ); data.identifiedSessions.set(sessionId, identityResult); publishEvent(server, identifyEvent); } else { writeToLog( `Warning: Supplied identify function returned null for session ${sessionId}` ); } } catch (error) { writeToLog( `Warning: Supplied identify function threw an error while identifying session ${sessionId} - ${error}` ); identifyEvent.duration = identifyEvent.timestamp && (/* @__PURE__ */ new Date()).getTime() - identifyEvent.timestamp.getTime() || void 0; identifyEvent.isError = true; identifyEvent.error = { message: getMCPCompatibleErrorMessage(error) }; publishEvent(server, identifyEvent); } } if (data.options.enableToolCallContext && request.params?.name !== "get_more_tools") { const hasContext = request.params?.arguments && typeof request.params.arguments === "object" && "context" in request.params.arguments; if (hasContext) { event.userIntent = request.params.arguments.context; } } let result; if (request.params?.name === "get_more_tools") { result = await handleReportMissing(request.params.arguments); event.userIntent = request.params.arguments.context; } else if (originalCallToolHandler) { result = await originalCallToolHandler(request, extra); } else { event.isError = true; event.error = { message: `Tool call handler not found for ${request.params?.name || "unknown"}` }; event.duration = event.timestamp && (/* @__PURE__ */ new Date()).getTime() - event.timestamp.getTime() || void 0; publishEvent(server, event); throw new Error(`Unknown tool: ${request.params?.name || "unknown"}`); } if (isToolResultError(result)) { event.isError = true; event.error = { message: getMCPCompatibleErrorMessage(result) }; } event.response = result; publishEvent(server, event); return result; } catch (error) { event.isError = true; event.error = { message: getMCPCompatibleErrorMessage(error) }; publishEvent(server, event); throw error; } }); } catch (error) { writeToLog(`Warning: Failed to setup tool call tracing - ${error}`); throw error; } } // src/index.ts function track(server, projectId, options = {}) { try { const validatedServer = isCompatibleServerType(server); const sessionInfo = getSessionInfo(validatedServer, void 0); const mcpcatData = { projectId, sessionId: newSessionId(), lastActivity: /* @__PURE__ */ new Date(), identifiedSessions: /* @__PURE__ */ new Map(), sessionInfo, options: { enableReportMissing: options.enableReportMissing ?? true, enableTracing: options.enableTracing ?? true, enableToolCallContext: options.enableToolCallContext ?? true, identify: options.identify, redactSensitiveInformation: options.redactSensitiveInformation } }; setServerTrackingData(validatedServer, mcpcatData); if (mcpcatData.options.enableReportMissing) { try { setupMCPCatTools(validatedServer); } catch (error) { writeToLog(`Warning: Failed to setup report missing tool - ${error}`); } } if (mcpcatData.options.enableTracing) { try { setupToolCallTracing(validatedServer); } catch (error) { writeToLog(`Warning: Failed to setup tool call tracing - ${error}`); } } return validatedServer; } catch (error) { writeToLog(`Warning: Failed to track server - ${error}`); return server; } } export { track }; //# sourceMappingURL=index.mjs.map