@calltelemetry/cucm-mcp 0.1.6 → 0.1.7

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.
Files changed (3) hide show
  1. package/README.md +48 -0
  2. package/dist/index.js +30 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -90,3 +90,51 @@ Live tests are opt-in via env vars; see `test/live.test.js`.
90
90
  - Use the platform/OS admin for SSH (`administrator` user on most lab systems)
91
91
  - To request a high packet count without specifying an exact number, pass `maxPackets: true` to `packet_capture_start`
92
92
  - If traffic is low, a small `count` can still run “forever” waiting for packets; use `packet_capture_stop` to cancel, or set `maxDurationMs` to auto-stop
93
+
94
+ ### Auth Note (DIME vs SSH)
95
+
96
+ CUCM deployments vary:
97
+
98
+ - SSH and DIME may accept different usernames/passwords.
99
+ - Quick check: the right DIME user returns HTTP 200 for the WSDL.
100
+
101
+ ```bash
102
+ curl -k -u "<user>:<pass>" \
103
+ "https://<cucm-host>:8443/logcollectionservice2/services/LogCollectionPortTypeService?wsdl" \
104
+ -o /dev/null -w "%{http_code}\n"
105
+ ```
106
+
107
+ ### Recommended Workflow
108
+
109
+ 1) Start capture (returns quickly; capture continues on CUCM):
110
+
111
+ Tool: `packet_capture_start`
112
+
113
+ Useful options:
114
+
115
+ - `count`: stop after N packets (can wait indefinitely if traffic is low)
116
+ - `maxDurationMs`: stop after a fixed time even if packet count isn’t reached
117
+ - `startTimeoutMs`: fail fast if the CUCM CLI prompt isn’t reachable
118
+ - `maxPackets: true`: sets a high capture count (1,000,000) when `count` is omitted
119
+
120
+ 2) Stop + download the capture:
121
+
122
+ Tool: `packet_capture_stop_and_download`
123
+
124
+ This:
125
+
126
+ - stops the SSH capture (best-effort)
127
+ - retries DIME downloads until the file appears
128
+ - tries rolled filenames (`.cap01`, `.cap02`, ...)
129
+
130
+ ### Viewing the Capture (macOS)
131
+
132
+ After download, you’ll get a `savedPath` like `/tmp/foo.cap`.
133
+
134
+ ```bash
135
+ # Reveal in Finder
136
+ open -R "/tmp/foo.cap"
137
+
138
+ # Open in Wireshark
139
+ open -a Wireshark "/tmp/foo.cap"
140
+ ```
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ const strictTls = tlsMode === "strict" || tlsMode === "verify";
14
14
  // Set CUCM_MCP_TLS_MODE=strict to enforce verification.
15
15
  if (!strictTls)
16
16
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
17
- const server = new McpServer({ name: "cucm", version: "0.1.6" });
17
+ const server = new McpServer({ name: "cucm", version: "0.1.7" });
18
18
  const captures = new PacketCaptureManager();
19
19
  const captureState = defaultStateStore();
20
20
  const dimeAuthSchema = z
@@ -187,14 +187,20 @@ server.tool("packet_capture_stop_and_download", "Stop a packet capture and downl
187
187
  dimePort: z.number().int().min(1).max(65535).optional(),
188
188
  auth: dimeAuthSchema.describe("DIME auth (optional; defaults to CUCM_DIME_USERNAME/CUCM_DIME_PASSWORD)"),
189
189
  outFile: z.string().optional().describe("Optional output path for the downloaded .cap file"),
190
- stopTimeoutMs: z.number().int().min(1000).max(10 * 60_000).optional().describe("How long to wait for SSH capture stop"),
190
+ stopTimeoutMs: z
191
+ .number()
192
+ .int()
193
+ .min(1000)
194
+ .max(10 * 60_000)
195
+ .optional()
196
+ .describe("How long to wait for SSH capture stop (default 300000)"),
191
197
  downloadTimeoutMs: z
192
198
  .number()
193
199
  .int()
194
200
  .min(1000)
195
201
  .max(10 * 60_000)
196
202
  .optional()
197
- .describe("How long to wait for the capture file to appear in DIME"),
203
+ .describe("How long to wait for the capture file to appear in DIME (default 300000)"),
198
204
  downloadPollIntervalMs: z
199
205
  .number()
200
206
  .int()
@@ -203,15 +209,29 @@ server.tool("packet_capture_stop_and_download", "Stop a packet capture and downl
203
209
  .optional()
204
210
  .describe("How often to retry DIME GetOneFile when the file isn't there yet"),
205
211
  }, async ({ captureId, dimePort, auth, outFile, stopTimeoutMs, downloadTimeoutMs, downloadPollIntervalMs }) => {
206
- const stopped = await captures.stop(captureId, stopTimeoutMs);
207
- const candidates = (stopped.remoteFileCandidates || []).length
208
- ? stopped.remoteFileCandidates
209
- : [stopped.remoteFilePath];
212
+ const stopTimeout = stopTimeoutMs ?? 300_000;
213
+ const dlTimeout = downloadTimeoutMs ?? 300_000;
214
+ const dlPoll = downloadPollIntervalMs ?? 2000;
215
+ let stopped;
216
+ let stopError;
217
+ try {
218
+ stopped = await captures.stop(captureId, stopTimeout);
219
+ }
220
+ catch (e) {
221
+ stopError = e instanceof Error ? e.message : String(e || "");
222
+ // Fall back to state file (useful if stop failed or MCP restarted).
223
+ const pruned = captureState.pruneExpired(captureState.load());
224
+ const rec = pruned.captures[captureId];
225
+ if (!rec)
226
+ throw new Error(`Failed to stop capture and capture not found in state: ${captureId}. stopError=${stopError}`);
227
+ stopped = rec;
228
+ }
229
+ const candidates = (stopped.remoteFileCandidates || []).length ? stopped.remoteFileCandidates : [stopped.remoteFilePath];
210
230
  const dl = await getOneFileAnyWithRetry(stopped.host, candidates, {
211
231
  auth: auth,
212
232
  port: dimePort,
213
- timeoutMs: downloadTimeoutMs,
214
- pollIntervalMs: downloadPollIntervalMs,
233
+ timeoutMs: dlTimeout,
234
+ pollIntervalMs: dlPoll,
215
235
  });
216
236
  const saved = writeDownloadedFile(dl, outFile);
217
237
  return {
@@ -223,6 +243,7 @@ server.tool("packet_capture_stop_and_download", "Stop a packet capture and downl
223
243
  host: stopped.host,
224
244
  remoteFilePath: dl.filename,
225
245
  stopTimedOut: stopped.stopTimedOut || false,
246
+ stopError,
226
247
  savedPath: saved.filePath,
227
248
  bytes: saved.bytes,
228
249
  dimeAttempts: dl.attempts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calltelemetry/cucm-mcp",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "MCP server for CUCM tooling (DIME logs, syslog, packet capture)",