@calltelemetry/cucm-mcp 0.1.7 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { mkdirSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\nimport {\n listNodeServiceLogs,\n selectLogs,\n selectLogsMinutes,\n getOneFile,\n getOneFileAnyWithRetry,\n writeDownloadedFile,\n type DimeAuth,\n} from \"./dime.js\";\nimport { guessTimezoneString } from \"./time.js\";\nimport { PacketCaptureManager, type SshAuth } from \"./packetCapture.js\";\nimport { defaultStateStore } from \"./state.js\";\nimport { applyPhone, updatePhonePacketCapture, axlExecute, type AxlAuth } from \"./axl.js\";\nimport { pcapCallSummary, pcapSipCalls, pcapScppMessages, pcapRtpStreams, pcapProtocolFilter } from \"./pcap-analyze.js\";\n\n// Default to accepting self-signed/invalid certs (common on CUCM lab/dev).\n// Opt back into strict verification with CUCM_MCP_TLS_MODE=strict.\nconst tlsMode = (process.env.CUCM_MCP_TLS_MODE || process.env.MCP_TLS_MODE || \"\").toLowerCase();\nconst strictTls = tlsMode === \"strict\" || tlsMode === \"verify\";\n// Default: permissive TLS (accept self-signed). This is the common CUCM lab posture.\n// Set CUCM_MCP_TLS_MODE=strict to enforce verification.\nif (!strictTls) process.env.NODE_TLS_REJECT_UNAUTHORIZED = \"0\";\n\nconst server = new McpServer({ name: \"cucm\", version: \"0.1.8\" });\nconst captures = new PacketCaptureManager();\nconst captureState = defaultStateStore();\n\nconst dimeAuthSchema = z\n .object({\n username: z.string().optional(),\n password: z.string().optional(),\n })\n .optional();\n\nconst sshAuthSchema = z\n .object({\n username: z.string().optional(),\n password: z.string().optional(),\n })\n .optional();\n\nconst axlAuthSchema = z\n .object({\n username: z.string().optional(),\n password: z.string().optional(),\n })\n .optional();\n\nfunction formatUnknownError(e: unknown): string {\n if (e instanceof Error) return e.message;\n if (typeof e === \"string\") return e;\n try {\n return JSON.stringify(e);\n } catch {\n return String(e);\n }\n}\n\nserver.tool(\n \"guess_timezone_string\",\n \"Build a best-effort DIME timezone string for selectLogFiles.\",\n {},\n async () => ({\n content: [{ type: \"text\", text: JSON.stringify({ timezone: guessTimezoneString(new Date()) }, null, 2) }],\n })\n);\n\nserver.tool(\n \"list_node_service_logs\",\n \"List CUCM cluster nodes and their available service logs (DIME listNodeServiceLogs).\",\n {\n host: z.string(),\n port: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema,\n },\n async ({ host, port, auth }) => {\n const result = await listNodeServiceLogs(host, auth as DimeAuth | undefined, port);\n return { content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }] };\n }\n);\n\nserver.tool(\n \"select_logs\",\n \"List log/trace files using DIME selectLogFiles. Supports ServiceLogs and SystemLogs.\",\n {\n host: z.string(),\n port: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema,\n serviceLogs: z.array(z.string()).optional().describe(\"ServiceLogs selections\"),\n systemLogs: z.array(z.string()).optional().describe(\"SystemLogs selections\"),\n searchStr: z.string().optional().describe(\"Optional filename substring filter\"),\n fromDate: z.string(),\n toDate: z.string(),\n timezone: z.string(),\n },\n async ({ host, port, auth, serviceLogs, systemLogs, searchStr, fromDate, toDate, timezone }) => {\n const result = await selectLogs(\n host,\n { serviceLogs, systemLogs, searchStr, fromDate, toDate, timezone },\n auth as DimeAuth | undefined,\n port\n );\n return { content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }] };\n }\n);\n\nserver.tool(\n \"select_logs_minutes\",\n \"Convenience wrapper: select logs using a minutes-back window.\",\n {\n host: z.string(),\n port: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema,\n minutesBack: z.number().int().min(1).max(60 * 24 * 30),\n serviceLogs: z.array(z.string()).optional(),\n systemLogs: z.array(z.string()).optional(),\n searchStr: z.string().optional(),\n timezone: z.string().optional(),\n },\n async ({ host, port, auth, minutesBack, serviceLogs, systemLogs, searchStr, timezone }) => {\n const result = await selectLogsMinutes(\n host,\n minutesBack,\n { serviceLogs, systemLogs, searchStr },\n timezone,\n auth as DimeAuth | undefined,\n port\n );\n return { content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }] };\n }\n);\n\nserver.tool(\n \"select_syslog_minutes\",\n \"Convenience wrapper: select system log files (e.g. Syslog) using a minutes-back window.\",\n {\n host: z.string(),\n port: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema,\n minutesBack: z.number().int().min(1).max(60 * 24 * 30),\n systemLog: z\n .string()\n .optional()\n .describe(\"System log selection name. Default is 'Syslog' (may vary by CUCM version).\"),\n searchStr: z.string().optional(),\n timezone: z.string().optional(),\n },\n async ({ host, port, auth, minutesBack, systemLog, searchStr, timezone }) => {\n const result = await selectLogsMinutes(\n host,\n minutesBack,\n { systemLogs: [systemLog || \"Syslog\"], searchStr },\n timezone,\n auth as DimeAuth | undefined,\n port\n );\n return { content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }] };\n }\n);\n\nserver.tool(\n \"phone_packet_capture_enable\",\n \"Enable phone packet capture via CUCM AXL (updatePhone packetCaptureMode/Duration + applyPhone).\",\n {\n host: z.string().describe(\"CUCM host/IP\"),\n port: z.number().int().min(1).max(65535).optional().describe(\"AXL port (default 8443)\"),\n axlVersion: z.string().optional().describe(\"AXL API version (default env CUCM_AXL_VERSION or 15.0)\"),\n auth: axlAuthSchema.describe(\"AXL auth (optional; defaults to CUCM_AXL_USERNAME/CUCM_AXL_PASSWORD)\"),\n deviceName: z.string().min(1).describe(\"Phone device name (e.g. SEP505C885DF37F)\"),\n mode: z\n .string()\n .optional()\n .describe('packetCaptureMode value (commonly \"Batch Processing Mode\")'),\n durationSeconds: z\n .number()\n .int()\n .min(1)\n .max(60 * 60)\n .optional()\n .describe(\"packetCaptureDuration in seconds (default 60)\"),\n apply: z.boolean().optional().describe(\"Run applyPhone after updatePhone (default true)\"),\n timeoutMs: z.number().int().min(1000).max(5 * 60_000).optional().describe(\"AXL request timeout\"),\n },\n async ({ host, port, axlVersion, auth, deviceName, mode, durationSeconds, apply, timeoutMs }) => {\n const update = await updatePhonePacketCapture(host, {\n deviceName,\n mode: mode || \"Batch Processing Mode\",\n durationSeconds: durationSeconds ?? 60,\n auth: auth as AxlAuth | undefined,\n port,\n version: axlVersion,\n timeoutMs,\n });\n\n const shouldApply = apply ?? true;\n const applied = shouldApply\n ? await applyPhone(host, {\n deviceName,\n auth: auth as AxlAuth | undefined,\n port,\n version: axlVersion,\n timeoutMs,\n })\n : undefined;\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n host: update.host,\n deviceName,\n packetCaptureMode: mode || \"Batch Processing Mode\",\n packetCaptureDuration: durationSeconds ?? 60,\n updatePhoneReturn: update.returnValue,\n applied: shouldApply,\n applyPhoneReturn: applied?.returnValue,\n notes: [\n \"Phone may need to reset to pick up config.\",\n \"Place the call during the duration window; CUCM writes the capture to its TFTP directory (CUCM behavior/version dependent).\",\n ],\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n);\n\nserver.tool(\n \"axl_execute\",\n \"Execute an arbitrary CUCM AXL SOAP operation. \" +\n \"Accepts a JSON-ish data payload and converts it to XML. \" +\n \"Tip: for get* operations, returnedTags can be an array of strings (supports dotted paths like 'lines.line.dirn.pattern').\",\n {\n operation: z.string().min(1).describe(\"AXL operation name, e.g. getPhone, updateLine, updateCtiRoutePoint\"),\n data: z.any().optional().describe(\"Operation payload as an object. Keys become XML tags.\"),\n\n cucm_host: z.string().describe(\"CUCM host/IP\"),\n cucm_port: z.number().int().min(1).max(65535).optional().describe(\"AXL port (default 8443)\"),\n cucm_version: z.string().optional().describe(\"AXL API version (e.g. 15.0)\"),\n cucm_username: z.string().optional().describe(\"AXL username (optional if env CUCM_AXL_USERNAME is set)\"),\n cucm_password: z.string().optional().describe(\"AXL password (optional if env CUCM_AXL_PASSWORD is set)\"),\n\n timeoutMs: z.number().int().min(1000).max(5 * 60_000).optional().describe(\"AXL request timeout\"),\n includeRequestXml: z.boolean().optional().describe(\"Include SOAP request XML in response (debug)\"),\n includeResponseXml: z.boolean().optional().describe(\"Include SOAP response XML in response (debug)\"),\n },\n async ({\n operation,\n data,\n cucm_host,\n cucm_port,\n cucm_version,\n cucm_username,\n cucm_password,\n timeoutMs,\n includeRequestXml,\n includeResponseXml,\n }) => {\n const auth: AxlAuth | undefined =\n cucm_username && cucm_password ? { username: cucm_username, password: cucm_password } : undefined;\n\n try {\n const result = await axlExecute(cucm_host, {\n operation,\n data,\n auth,\n port: cucm_port,\n version: cucm_version,\n timeoutMs,\n includeRequestXml,\n includeResponseXml,\n });\n return { content: [{ type: \"text\", text: JSON.stringify({ ok: true, ...result }, null, 2) }] };\n } catch (e) {\n const msg = formatUnknownError(e);\n\n const guessGetOperation = (op: string): string | null => {\n const s = String(op || \"\").trim();\n if (!s) return null;\n const prefixes = [\"update\", \"add\", \"remove\", \"set\"] as const;\n for (const p of prefixes) {\n if (s.startsWith(p) && s.length > p.length) {\n return `get${s.slice(p.length)}`;\n }\n }\n return null;\n };\n\n const getOp = guessGetOperation(operation);\n\n const hints: string[] = [\n \"If the error is opaque, retry with includeRequestXml=true and includeResponseXml=true to see the SOAP fault.\",\n \"For get* operations, try returnedTags as an array of strings (supports dotted paths).\",\n \"For update* operations, first run the corresponding get* to copy the exact nested shape and/or uuids.\",\n \"If you see schema/version errors, make sure cucm_version matches your CUCM AXL version (Help > About in CUCM).\",\n ];\n if (/self-signed certificate|unable to verify|CERT/i.test(msg)) {\n hints.unshift(\n \"TLS: this MCP defaults to permissive TLS, but your runtime may still be enforcing verification. Set CUCM_MCP_TLS_MODE=permissive (or MCP_TLS_MODE=permissive).\"\n );\n }\n if (/401|403|auth/i.test(msg)) {\n hints.unshift(\n \"Auth: verify AXL credentials and that the CUCM user has AXL permissions for this operation (AXL role/group).\"\n );\n }\n if (/not found|does not exist|unknown/i.test(msg)) {\n hints.unshift(\n \"Existence: confirm the target object exists by calling a get* operation first (e.g. getCtiRoutePoint/getLine).\"\n );\n }\n\n const nextToolCalls = [\n ...(getOp\n ? [\n {\n tool: \"axl_execute\",\n args: {\n operation: getOp,\n cucm_host,\n cucm_port,\n cucm_version,\n cucm_username,\n cucm_password,\n includeResponseXml: true,\n },\n note:\n \"Fetch the current object first (add an appropriate data payload, e.g. {name: \\\"...\\\"} or {uuid: \\\"...\\\"}) to learn nesting/uuid requirements.\",\n },\n ]\n : []),\n {\n tool: \"axl_execute\",\n args: {\n operation,\n cucm_host,\n cucm_port,\n cucm_version,\n cucm_username,\n cucm_password,\n includeRequestXml: true,\n includeResponseXml: true,\n },\n note: \"Re-run with debug XML included to capture the SOAP Fault details.\",\n },\n ];\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n ok: false,\n error: true,\n message: msg,\n operation,\n cucm_host,\n hints,\n nextToolCalls,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n }\n);\n\nserver.tool(\n \"axl_download_wsdl\",\n \"Download the CUCM AXL WSDL to a local file. Useful when you need schema hints for an operation.\",\n {\n cucm_host: z.string().describe(\"CUCM host/IP\"),\n cucm_port: z.number().int().min(1).max(65535).optional().describe(\"AXL port (default 8443)\"),\n cucm_username: z.string().optional().describe(\"AXL username (optional if env CUCM_AXL_USERNAME is set)\"),\n cucm_password: z.string().optional().describe(\"AXL password (optional if env CUCM_AXL_PASSWORD is set)\"),\n outFile: z.string().optional().describe(\"Optional output file path (default /tmp/cucm-mcp/axl.wsdl)\")\n },\n async ({ cucm_host, cucm_port, cucm_username, cucm_password, outFile }) => {\n const port = cucm_port ?? 8443;\n const user = cucm_username || process.env.CUCM_AXL_USERNAME || process.env.CUCM_USERNAME;\n const pass = cucm_password || process.env.CUCM_AXL_PASSWORD || process.env.CUCM_PASSWORD;\n if (!user || !pass) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n ok: false,\n error: true,\n message: \"Missing AXL credentials (provide cucm_username/cucm_password or set CUCM_AXL_USERNAME/CUCM_AXL_PASSWORD)\",\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const url = `https://${cucm_host}:${port}/axl/?wsdl`;\n try {\n const res = await fetch(url, {\n headers: {\n Authorization: `Basic ${Buffer.from(`${user}:${pass}`, \"utf8\").toString(\"base64\")}`,\n Accept: \"application/xml, text/xml, */*\",\n },\n signal: AbortSignal.timeout(30_000),\n });\n\n const text = await res.text().catch(() => \"\");\n const file = outFile || join(\"/tmp\", \"cucm-mcp\", \"axl.wsdl\");\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, text, \"utf8\");\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n ok: res.ok,\n url,\n status: res.status,\n savedPath: file,\n bytes: Buffer.byteLength(text, \"utf8\"),\n note: \"WSDL often imports XSDs; you may need to fetch those referenced URLs too.\",\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (e) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n ok: false,\n error: true,\n url,\n message: formatUnknownError(e),\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n }\n);\n\nserver.tool(\n \"download_file\",\n \"Download a single file via DIME GetOneFile and write it to disk.\",\n {\n host: z.string(),\n port: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema,\n filePath: z.string().min(1).describe(\"Absolute path on CUCM\"),\n outFile: z.string().optional().describe(\"Optional output path. Default: /tmp/cucm-mcp/<basename>\"),\n },\n async ({ host, port, auth, filePath, outFile }) => {\n const dl = await getOneFile(host, filePath, auth as DimeAuth | undefined, port);\n const saved = writeDownloadedFile(dl, outFile);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ server: dl.server, sourcePath: dl.filename, savedPath: saved.filePath, bytes: saved.bytes }, null, 2),\n },\n ],\n };\n }\n);\n\nserver.tool(\n \"packet_capture_start\",\n \"Start a packet capture on CUCM via SSH (utils network capture). Returns a captureId.\",\n {\n host: z.string().describe(\"CUCM host/IP\"),\n sshPort: z.number().int().min(1).max(65535).optional(),\n auth: sshAuthSchema,\n iface: z.string().optional().describe(\"Interface (default eth0)\"),\n fileBase: z.string().optional().describe(\"Capture base name (no dots). Saved as <fileBase>.cap\"),\n count: z.number().int().min(1).max(1_000_000).optional().describe(\"Packet count (common max is 1000000)\"),\n maxPackets: z\n .boolean()\n .optional()\n .describe(\"If true and count is omitted, uses a high capture count (1,000,000)\"),\n size: z.string().optional().describe(\"Packet size (e.g. all)\"),\n hostFilterIp: z.string().optional().describe(\"Optional filter: host ip <addr>\"),\n portFilter: z.number().int().min(1).max(65535).optional().describe(\"Optional filter: port <num>\"),\n maxDurationMs: z\n .number()\n .int()\n .min(250)\n .max(24 * 60 * 60_000)\n .optional()\n .describe(\"Stop after this duration even if packet count isn't reached\"),\n startTimeoutMs: z\n .number()\n .int()\n .min(2000)\n .max(120_000)\n .optional()\n .describe(\"Timeout for starting capture (SSH connect + command start)\"),\n },\n async ({ host, sshPort, auth, iface, fileBase, count, maxPackets, size, hostFilterIp, portFilter, maxDurationMs, startTimeoutMs }) => {\n const resolvedCount = count ?? (maxPackets ? 1_000_000 : undefined);\n const result = await captures.start({\n host,\n sshPort,\n auth: auth as SshAuth | undefined,\n iface,\n fileBase,\n count: resolvedCount,\n size,\n hostFilterIp,\n portFilter,\n maxDurationMs,\n startTimeoutMs,\n });\n const summary =\n `Started CUCM packet capture (SSH). ` +\n `id=${result.id} host=${result.host} fileBase=${result.fileBase} remoteFilePath=${result.remoteFilePath}. ` +\n `Stops when packet count is reached, when you call packet_capture_stop, or via maxDurationMs.`;\n\n return {\n content: [{ type: \"text\", text: `${summary}\\n\\n${JSON.stringify(result, null, 2)}` }],\n };\n }\n);\n\nserver.tool(\n \"packet_capture_list\",\n \"List active packet captures started by this MCP server.\",\n {},\n async () => ({ content: [{ type: \"text\", text: JSON.stringify(captures.list(), null, 2) }] })\n);\n\nserver.tool(\n \"packet_capture_state_list\",\n \"List packet captures from the local state file (survives MCP restarts).\",\n {},\n async () => {\n const pruned = captureState.pruneExpired(captureState.load());\n captureState.save(pruned);\n const items = Object.values(pruned.captures).sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));\n return { content: [{ type: \"text\", text: JSON.stringify({ path: captureState.path, captures: items }, null, 2) }] };\n }\n);\n\nserver.tool(\n \"packet_capture_state_get\",\n \"Get a packet capture record from the local state file.\",\n {\n captureId: z.string().min(1),\n },\n async ({ captureId }) => {\n const pruned = captureState.pruneExpired(captureState.load());\n const rec = pruned.captures[captureId];\n if (!rec) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ path: captureState.path, found: false, captureId }, null, 2),\n },\n ],\n };\n }\n return { content: [{ type: \"text\", text: JSON.stringify({ path: captureState.path, found: true, record: rec }, null, 2) }] };\n }\n);\n\nserver.tool(\n \"packet_capture_state_clear\",\n \"Delete a capture record from the local state file.\",\n {\n captureId: z.string().min(1),\n },\n async ({ captureId }) => {\n captureState.remove(captureId);\n return { content: [{ type: \"text\", text: JSON.stringify({ removed: true, captureId }, null, 2) }] };\n }\n);\n\nserver.tool(\n \"packet_capture_stop\",\n \"Stop a packet capture by captureId (sends Ctrl-C).\",\n {\n captureId: z.string().min(1),\n timeoutMs: z.number().int().min(1000).max(10 * 60_000).optional().describe(\"How long to wait for stop (default ~90s)\"),\n },\n async ({ captureId, timeoutMs }) => {\n try {\n const result = await captures.stop(captureId, timeoutMs);\n const summary = `Stopped capture. id=${result.id} stopTimedOut=${Boolean(result.stopTimedOut)} remoteFilePath=${result.remoteFilePath}`;\n return { content: [{ type: \"text\", text: `${summary}\\n\\n${JSON.stringify(result, null, 2)}` }] };\n } catch (e) {\n const stopError = e instanceof Error ? e.message : String(e || \"\");\n const pruned = captureState.pruneExpired(captureState.load());\n const rec = pruned.captures[captureId];\n if (!rec) throw e;\n const summary = `Failed to stop capture (returning state record). id=${captureId} stopError=${JSON.stringify(stopError)}`;\n return {\n content: [\n {\n type: \"text\",\n text: `${summary}\\n\\n${JSON.stringify({ stopError, record: rec }, null, 2)}`,\n },\n ],\n };\n }\n }\n);\n\nserver.tool(\n \"packet_capture_stop_and_download\",\n \"Stop a packet capture and download the resulting .cap file via DIME.\",\n {\n captureId: z.string().min(1),\n dimePort: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema.describe(\"DIME auth (optional; defaults to CUCM_DIME_USERNAME/CUCM_DIME_PASSWORD)\"),\n outFile: z.string().optional().describe(\"Optional output path for the downloaded .cap file\"),\n stopTimeoutMs: z\n .number()\n .int()\n .min(1000)\n .max(10 * 60_000)\n .optional()\n .describe(\"How long to wait for SSH capture stop (default 300000)\"),\n downloadTimeoutMs: z\n .number()\n .int()\n .min(1000)\n .max(10 * 60_000)\n .optional()\n .describe(\"How long to wait for the capture file to appear in DIME (default 300000)\"),\n downloadPollIntervalMs: z\n .number()\n .int()\n .min(250)\n .max(30_000)\n .optional()\n .describe(\"How often to retry DIME GetOneFile when the file isn't there yet\"),\n },\n async ({ captureId, dimePort, auth, outFile, stopTimeoutMs, downloadTimeoutMs, downloadPollIntervalMs }) => {\n const stopTimeout = stopTimeoutMs ?? 300_000;\n const dlTimeout = downloadTimeoutMs ?? 300_000;\n const dlPoll = downloadPollIntervalMs ?? 2000;\n\n let stopped: { [k: string]: any };\n let stopError: string | undefined;\n try {\n stopped = await captures.stop(captureId, stopTimeout);\n } catch (e) {\n stopError = e instanceof Error ? e.message : String(e || \"\");\n // Fall back to state file (useful if stop failed or MCP restarted).\n const pruned = captureState.pruneExpired(captureState.load());\n const rec = pruned.captures[captureId];\n if (!rec) throw new Error(`Failed to stop capture and capture not found in state: ${captureId}. stopError=${stopError}`);\n stopped = rec;\n }\n\n const candidates = (stopped.remoteFileCandidates || []).length ? stopped.remoteFileCandidates : [stopped.remoteFilePath];\n\n const dl = await getOneFileAnyWithRetry(stopped.host, candidates, {\n auth: auth as DimeAuth | undefined,\n port: dimePort,\n timeoutMs: dlTimeout,\n pollIntervalMs: dlPoll,\n });\n const saved = writeDownloadedFile(dl, outFile);\n\n const summary =\n `Capture downloaded. ` +\n `id=${captureId} stopTimedOut=${Boolean(stopped.stopTimedOut)} remoteFilePath=${dl.filename} ` +\n `savedPath=${saved.filePath} bytes=${saved.bytes}` +\n (stopError ? ` stopError=${JSON.stringify(stopError)}` : \"\");\n\n return {\n content: [\n {\n type: \"text\",\n text: `${summary}\\n\\n${JSON.stringify(\n {\n captureId: stopped.id,\n host: stopped.host,\n remoteFilePath: dl.filename,\n stopTimedOut: stopped.stopTimedOut || false,\n stopError,\n savedPath: saved.filePath,\n bytes: saved.bytes,\n dimeAttempts: dl.attempts,\n dimeWaitedMs: dl.waitedMs,\n },\n null,\n 2\n )}`,\n },\n ],\n };\n }\n);\n\nserver.tool(\n \"packet_capture_download_from_state\",\n \"Download a capture file using the local state record (useful after MCP restart).\",\n {\n captureId: z.string().min(1),\n dimePort: z.number().int().min(1).max(65535).optional(),\n auth: dimeAuthSchema.describe(\"DIME auth (optional; defaults to CUCM_DIME_USERNAME/CUCM_DIME_PASSWORD)\"),\n outFile: z.string().optional().describe(\"Optional output path for the downloaded .cap file\"),\n downloadTimeoutMs: z\n .number()\n .int()\n .min(1000)\n .max(10 * 60_000)\n .optional()\n .describe(\"How long to wait for the capture file to appear in DIME\"),\n downloadPollIntervalMs: z\n .number()\n .int()\n .min(250)\n .max(30_000)\n .optional()\n .describe(\"How often to retry DIME GetOneFile when the file isn't there yet\"),\n },\n async ({ captureId, dimePort, auth, outFile, downloadTimeoutMs, downloadPollIntervalMs }) => {\n const pruned = captureState.pruneExpired(captureState.load());\n const rec = pruned.captures[captureId];\n if (!rec) throw new Error(`Capture not found in state: ${captureId}`);\n\n const dl = await getOneFileAnyWithRetry(rec.host, rec.remoteFileCandidates?.length ? rec.remoteFileCandidates : [rec.remoteFilePath], {\n auth: auth as DimeAuth | undefined,\n port: dimePort,\n timeoutMs: downloadTimeoutMs,\n pollIntervalMs: downloadPollIntervalMs,\n });\n const saved = writeDownloadedFile(dl, outFile);\n const summary =\n `Capture downloaded from state. id=${captureId} remoteFilePath=${dl.filename} savedPath=${saved.filePath} bytes=${saved.bytes}`;\n return {\n content: [\n {\n type: \"text\",\n text: `${summary}\\n\\n${JSON.stringify(\n {\n captureId,\n host: rec.host,\n remoteFilePath: dl.filename,\n savedPath: saved.filePath,\n bytes: saved.bytes,\n dimeAttempts: dl.attempts,\n dimeWaitedMs: dl.waitedMs,\n },\n null,\n 2\n )}`,\n },\n ],\n };\n }\n);\n\n// ---------------------------------------------------------------------------\n// PCAP Analysis Tools (requires tshark / Wireshark CLI)\n// ---------------------------------------------------------------------------\n\nfunction resolveCapturePath(input: string): string {\n // If it looks like a file path, use directly\n if (input.includes(\"/\") || input.includes(\"\\\\\") || input.endsWith(\".cap\") || input.endsWith(\".pcap\")) {\n return input;\n }\n // Otherwise treat as captureId and resolve from state\n const pruned = captureState.pruneExpired(captureState.load());\n const rec = pruned.captures[input];\n if (!rec) throw new Error(`No capture file path and no state record for: ${input}`);\n // Look for a downloaded file in /tmp/cucm-mcp/\n const basename = rec.remoteFilePath.split(\"/\").pop() || `${rec.fileBase}.cap`;\n const localPath = `/tmp/cucm-mcp/${basename}`;\n return localPath;\n}\n\nserver.tool(\n \"pcap_call_summary\",\n \"High-level overview of a packet capture: protocols present, SIP call count, RTP streams, endpoints. \" +\n \"Use this first to understand what's in a capture before drilling into specific calls.\",\n {\n filePath: z.string().min(1).describe(\"Path to .cap/.pcap file, or a captureId from packet_capture_start\"),\n },\n async ({ filePath }) => {\n try {\n const resolved = resolveCapturePath(filePath);\n const result = await pcapCallSummary(resolved);\n return { content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: JSON.stringify({ error: true, message: formatUnknownError(e) }, null, 2) }] };\n }\n }\n);\n\nserver.tool(\n \"pcap_sip_calls\",\n \"Extract SIP call flows from a capture, grouped by Call-ID. \" +\n \"Shows INVITE/100/180/200/BYE sequences, From/To, SDP media info, and call setup timing.\",\n {\n filePath: z.string().min(1).describe(\"Path to .cap/.pcap file, or a captureId\"),\n callId: z.string().optional().describe(\"Filter to a specific SIP Call-ID\"),\n },\n async ({ filePath, callId }) => {\n try {\n const resolved = resolveCapturePath(filePath);\n const result = await pcapSipCalls(resolved, callId);\n const summary = `Found ${result.length} SIP call(s)` +\n (result.length > 0 ? `: ${result.map((c) => `${c.callId} (${c.metrics.messageCount} msgs, ${c.metrics.answered ? \"answered\" : \"unanswered\"})`).join(\", \")}` : \"\");\n return { content: [{ type: \"text\", text: `${summary}\\n\\n${JSON.stringify(result, null, 2)}` }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: JSON.stringify({ error: true, message: formatUnknownError(e) }, null, 2) }] };\n }\n }\n);\n\nserver.tool(\n \"pcap_sccp_messages\",\n \"Extract Skinny/SCCP messages from a capture. \" +\n \"Shows phone registration, call state changes, media channel setup (OpenReceiveChannel, StartMediaTransmission), and key presses.\",\n {\n filePath: z.string().min(1).describe(\"Path to .cap/.pcap file, or a captureId\"),\n deviceFilter: z.string().optional().describe(\"Filter to a specific device IP address\"),\n },\n async ({ filePath, deviceFilter }) => {\n try {\n const resolved = resolveCapturePath(filePath);\n const result = await pcapScppMessages(resolved, deviceFilter);\n const summary = `Found ${result.totalMessages} SCCP message(s) across ${result.devices.length} device(s). ` +\n `Top message types: ${Object.entries(result.messageTypes).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([k, v]) => `${k}(${v})`).join(\", \")}`;\n return { content: [{ type: \"text\", text: `${summary}\\n\\n${JSON.stringify(result, null, 2)}` }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: JSON.stringify({ error: true, message: formatUnknownError(e) }, null, 2) }] };\n }\n }\n);\n\nserver.tool(\n \"pcap_rtp_streams\",\n \"Analyze RTP media streams in a capture. \" +\n \"Shows per-stream jitter, packet loss, codec, duration. Use to assess call quality.\",\n {\n filePath: z.string().min(1).describe(\"Path to .cap/.pcap file, or a captureId\"),\n ssrcFilter: z.string().optional().describe(\"Filter to a specific RTP SSRC (hex, e.g. 0xABCD1234)\"),\n },\n async ({ filePath, ssrcFilter }) => {\n try {\n const resolved = resolveCapturePath(filePath);\n const result = await pcapRtpStreams(resolved, ssrcFilter);\n const summary = `Found ${result.summary.totalStreams} RTP stream(s). ` +\n `Worst loss: ${result.summary.worstLoss}, worst jitter: ${result.summary.worstJitter}`;\n return { content: [{ type: \"text\", text: `${summary}\\n\\n${JSON.stringify(result, null, 2)}` }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: JSON.stringify({ error: true, message: formatUnknownError(e) }, null, 2) }] };\n }\n }\n);\n\nserver.tool(\n \"pcap_protocol_filter\",\n \"Run an arbitrary tshark display filter on a capture and extract specific fields. \" +\n \"Escape hatch for deep protocol investigation. Examples: 'sip.Method == INVITE', 'skinny.callState == 12', 'rtp.ssrc == 0xABCD'.\",\n {\n filePath: z.string().min(1).describe(\"Path to .cap/.pcap file, or a captureId\"),\n displayFilter: z.string().min(1).describe(\"tshark display filter expression\"),\n fields: z\n .array(z.string())\n .optional()\n .describe(\"Specific tshark fields to extract (e.g. ['sip.Call-ID', 'sip.from.addr']). If omitted, returns frame basics.\"),\n maxPackets: z.number().int().min(1).max(1000).optional().describe(\"Max packets to return (default 100, max 1000)\"),\n },\n async ({ filePath, displayFilter, fields, maxPackets }) => {\n try {\n const resolved = resolveCapturePath(filePath);\n const result = await pcapProtocolFilter(resolved, displayFilter, fields, maxPackets);\n return { content: [{ type: \"text\", text: `${result.length} packet(s) matched filter \"${displayFilter}\"\\n\\n${JSON.stringify(result, null, 2)}` }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: JSON.stringify({ error: true, message: formatUnknownError(e) }, null, 2) }] };\n }\n }\n);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/multipart.js CHANGED
@@ -1,73 +1,63 @@
1
- export function extractBoundary(contentTypeHeader) {
2
- if (!contentTypeHeader)
3
- return "";
4
- const m = contentTypeHeader.match(/boundary\s*=\s*"?([^";]+)"?/i);
5
- return (m?.[1] || "").trim();
1
+ function extractBoundary(contentTypeHeader) {
2
+ if (!contentTypeHeader) return "";
3
+ const m = contentTypeHeader.match(/boundary\s*=\s*"?([^";]+)"?/i);
4
+ return (m?.[1] || "").trim();
6
5
  }
7
6
  function parseHeaderLines(lines) {
8
- const out = {};
9
- for (const line of lines) {
10
- const idx = line.indexOf(":");
11
- if (idx <= 0)
12
- continue;
13
- const k = line.slice(0, idx).trim().toLowerCase();
14
- const v = line.slice(idx + 1).trim();
15
- out[k] = v;
16
- }
17
- return out;
7
+ const out = {};
8
+ for (const line of lines) {
9
+ const idx = line.indexOf(":");
10
+ if (idx <= 0) continue;
11
+ const k = line.slice(0, idx).trim().toLowerCase();
12
+ const v = line.slice(idx + 1).trim();
13
+ out[k] = v;
14
+ }
15
+ return out;
18
16
  }
19
- export function parseMultipartRelated(body, boundary) {
20
- if (!boundary)
21
- return [];
22
- const boundaryLine = `--${boundary}`;
23
- const boundaryBuf = Buffer.from(boundaryLine, "utf8");
24
- const parts = [];
25
- const findNextBoundary = (start) => {
26
- for (let p = start; p <= body.length - boundaryBuf.length; p++) {
27
- if (body[p] !== boundaryBuf[0])
28
- continue;
29
- if (body.subarray(p, p + boundaryBuf.length).equals(boundaryBuf))
30
- return p;
31
- }
32
- return -1;
33
- };
34
- let i = findNextBoundary(0);
35
- if (i === -1)
36
- return [];
37
- while (i !== -1) {
38
- const lineEnd = body.indexOf("\n", i);
39
- if (lineEnd === -1)
40
- break;
41
- const boundaryText = body.subarray(i, lineEnd).toString("utf8").trim();
42
- if (boundaryText.endsWith("--"))
43
- break;
44
- let cursor = lineEnd + 1;
45
- const headerLines = [];
46
- while (cursor < body.length) {
47
- const nextNl = body.indexOf("\n", cursor);
48
- if (nextNl === -1)
49
- break;
50
- const rawLine = body.subarray(cursor, nextNl).toString("utf8");
51
- const line = rawLine.replace(/\r$/, "");
52
- cursor = nextNl + 1;
53
- if (line.trim() === "")
54
- break;
55
- headerLines.push(line);
56
- }
57
- const headers = parseHeaderLines(headerLines);
58
- const contentType = (headers["content-type"] || "application/octet-stream")
59
- .split(";")[0]
60
- .trim();
61
- const nextBoundary = findNextBoundary(cursor);
62
- if (nextBoundary === -1)
63
- break;
64
- let end = nextBoundary;
65
- if (end >= 2 && body[end - 2] === 0x0d && body[end - 1] === 0x0a)
66
- end -= 2;
67
- else if (end >= 1 && body[end - 1] === 0x0a)
68
- end -= 1;
69
- parts.push({ contentType, headers, body: Buffer.from(body.subarray(cursor, end)) });
70
- i = nextBoundary;
17
+ function parseMultipartRelated(body, boundary) {
18
+ if (!boundary) return [];
19
+ const boundaryLine = `--${boundary}`;
20
+ const boundaryBuf = Buffer.from(boundaryLine, "utf8");
21
+ const parts = [];
22
+ const findNextBoundary = (start) => {
23
+ for (let p = start; p <= body.length - boundaryBuf.length; p++) {
24
+ if (body[p] !== boundaryBuf[0]) continue;
25
+ if (body.subarray(p, p + boundaryBuf.length).equals(boundaryBuf)) return p;
26
+ }
27
+ return -1;
28
+ };
29
+ let i = findNextBoundary(0);
30
+ if (i === -1) return [];
31
+ while (i !== -1) {
32
+ const lineEnd = body.indexOf("\n", i);
33
+ if (lineEnd === -1) break;
34
+ const boundaryText = body.subarray(i, lineEnd).toString("utf8").trim();
35
+ if (boundaryText.endsWith("--")) break;
36
+ let cursor = lineEnd + 1;
37
+ const headerLines = [];
38
+ while (cursor < body.length) {
39
+ const nextNl = body.indexOf("\n", cursor);
40
+ if (nextNl === -1) break;
41
+ const rawLine = body.subarray(cursor, nextNl).toString("utf8");
42
+ const line = rawLine.replace(/\r$/, "");
43
+ cursor = nextNl + 1;
44
+ if (line.trim() === "") break;
45
+ headerLines.push(line);
71
46
  }
72
- return parts;
47
+ const headers = parseHeaderLines(headerLines);
48
+ const contentType = (headers["content-type"] || "application/octet-stream").split(";")[0].trim();
49
+ const nextBoundary = findNextBoundary(cursor);
50
+ if (nextBoundary === -1) break;
51
+ let end = nextBoundary;
52
+ if (end >= 2 && body[end - 2] === 13 && body[end - 1] === 10) end -= 2;
53
+ else if (end >= 1 && body[end - 1] === 10) end -= 1;
54
+ parts.push({ contentType, headers, body: Buffer.from(body.subarray(cursor, end)) });
55
+ i = nextBoundary;
56
+ }
57
+ return parts;
73
58
  }
59
+ export {
60
+ extractBoundary,
61
+ parseMultipartRelated
62
+ };
63
+ //# sourceMappingURL=multipart.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/multipart.ts"],
4
+ "sourcesContent": ["export type MultipartPart = {\n contentType: string;\n headers: Record<string, string>;\n body: Buffer;\n};\n\nexport function extractBoundary(contentTypeHeader: string | null | undefined): string {\n if (!contentTypeHeader) return \"\";\n const m = contentTypeHeader.match(/boundary\\s*=\\s*\"?([^\";]+)\"?/i);\n return (m?.[1] || \"\").trim();\n}\n\nfunction parseHeaderLines(lines: string[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const line of lines) {\n const idx = line.indexOf(\":\");\n if (idx <= 0) continue;\n const k = line.slice(0, idx).trim().toLowerCase();\n const v = line.slice(idx + 1).trim();\n out[k] = v;\n }\n return out;\n}\n\nexport function parseMultipartRelated(body: Buffer, boundary: string): MultipartPart[] {\n if (!boundary) return [];\n\n const boundaryLine = `--${boundary}`;\n const boundaryBuf = Buffer.from(boundaryLine, \"utf8\");\n const parts: MultipartPart[] = [];\n\n const findNextBoundary = (start: number): number => {\n for (let p = start; p <= body.length - boundaryBuf.length; p++) {\n if (body[p] !== boundaryBuf[0]) continue;\n if (body.subarray(p, p + boundaryBuf.length).equals(boundaryBuf)) return p;\n }\n return -1;\n };\n\n let i = findNextBoundary(0);\n if (i === -1) return [];\n\n while (i !== -1) {\n const lineEnd = body.indexOf(\"\\n\", i);\n if (lineEnd === -1) break;\n\n const boundaryText = body.subarray(i, lineEnd).toString(\"utf8\").trim();\n if (boundaryText.endsWith(\"--\")) break;\n\n let cursor = lineEnd + 1;\n const headerLines: string[] = [];\n while (cursor < body.length) {\n const nextNl = body.indexOf(\"\\n\", cursor);\n if (nextNl === -1) break;\n const rawLine = body.subarray(cursor, nextNl).toString(\"utf8\");\n const line = rawLine.replace(/\\r$/, \"\");\n cursor = nextNl + 1;\n if (line.trim() === \"\") break;\n headerLines.push(line);\n }\n\n const headers = parseHeaderLines(headerLines);\n const contentType = (headers[\"content-type\"] || \"application/octet-stream\")\n .split(\";\")[0]\n .trim();\n\n const nextBoundary = findNextBoundary(cursor);\n if (nextBoundary === -1) break;\n\n let end = nextBoundary;\n if (end >= 2 && body[end - 2] === 0x0d && body[end - 1] === 0x0a) end -= 2;\n else if (end >= 1 && body[end - 1] === 0x0a) end -= 1;\n\n parts.push({ contentType, headers, body: Buffer.from(body.subarray(cursor, end)) });\n i = nextBoundary;\n }\n\n return parts;\n}\n"],
5
+ "mappings": "AAMO,SAAS,gBAAgB,mBAAsD;AACpF,MAAI,CAAC,kBAAmB,QAAO;AAC/B,QAAM,IAAI,kBAAkB,MAAM,8BAA8B;AAChE,UAAQ,IAAI,CAAC,KAAK,IAAI,KAAK;AAC7B;AAEA,SAAS,iBAAiB,OAAyC;AACjE,QAAM,MAA8B,CAAC;AACrC,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,OAAO,EAAG;AACd,UAAM,IAAI,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,YAAY;AAChD,UAAM,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AACnC,QAAI,CAAC,IAAI;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,MAAc,UAAmC;AACrF,MAAI,CAAC,SAAU,QAAO,CAAC;AAEvB,QAAM,eAAe,KAAK,QAAQ;AAClC,QAAM,cAAc,OAAO,KAAK,cAAc,MAAM;AACpD,QAAM,QAAyB,CAAC;AAEhC,QAAM,mBAAmB,CAAC,UAA0B;AAClD,aAAS,IAAI,OAAO,KAAK,KAAK,SAAS,YAAY,QAAQ,KAAK;AAC9D,UAAI,KAAK,CAAC,MAAM,YAAY,CAAC,EAAG;AAChC,UAAI,KAAK,SAAS,GAAG,IAAI,YAAY,MAAM,EAAE,OAAO,WAAW,EAAG,QAAO;AAAA,IAC3E;AACA,WAAO;AAAA,EACT;AAEA,MAAI,IAAI,iBAAiB,CAAC;AAC1B,MAAI,MAAM,GAAI,QAAO,CAAC;AAEtB,SAAO,MAAM,IAAI;AACf,UAAM,UAAU,KAAK,QAAQ,MAAM,CAAC;AACpC,QAAI,YAAY,GAAI;AAEpB,UAAM,eAAe,KAAK,SAAS,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK;AACrE,QAAI,aAAa,SAAS,IAAI,EAAG;AAEjC,QAAI,SAAS,UAAU;AACvB,UAAM,cAAwB,CAAC;AAC/B,WAAO,SAAS,KAAK,QAAQ;AAC3B,YAAM,SAAS,KAAK,QAAQ,MAAM,MAAM;AACxC,UAAI,WAAW,GAAI;AACnB,YAAM,UAAU,KAAK,SAAS,QAAQ,MAAM,EAAE,SAAS,MAAM;AAC7D,YAAM,OAAO,QAAQ,QAAQ,OAAO,EAAE;AACtC,eAAS,SAAS;AAClB,UAAI,KAAK,KAAK,MAAM,GAAI;AACxB,kBAAY,KAAK,IAAI;AAAA,IACvB;AAEA,UAAM,UAAU,iBAAiB,WAAW;AAC5C,UAAM,eAAe,QAAQ,cAAc,KAAK,4BAC7C,MAAM,GAAG,EAAE,CAAC,EACZ,KAAK;AAER,UAAM,eAAe,iBAAiB,MAAM;AAC5C,QAAI,iBAAiB,GAAI;AAEzB,QAAI,MAAM;AACV,QAAI,OAAO,KAAK,KAAK,MAAM,CAAC,MAAM,MAAQ,KAAK,MAAM,CAAC,MAAM,GAAM,QAAO;AAAA,aAChE,OAAO,KAAK,KAAK,MAAM,CAAC,MAAM,GAAM,QAAO;AAEpD,UAAM,KAAK,EAAE,aAAa,SAAS,MAAM,OAAO,KAAK,KAAK,SAAS,QAAQ,GAAG,CAAC,EAAE,CAAC;AAClF,QAAI;AAAA,EACN;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }