@debugg-ai/debugg-ai-mcp 4.3.0 → 4.4.0

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.
@@ -104,7 +104,10 @@ export async function probePageHandler(input, context, rawProgressCallback) {
104
104
  // Per-target tunnel contexts. Index aligns with input.targets[].
105
105
  const targetContexts = [];
106
106
  // Tunnel keys we provisioned this call (for cleanup if creation fails after key acquired).
107
- const acquiredKeyIds = [];
107
+ // The provisions this call acquired, in target order. Provisions rather than
108
+ // bare key ids because revoking follows the transport the backend picked
109
+ // (ngrok keys and debugg tunnels have different endpoints).
110
+ const acquiredProvisions = [];
108
111
  // Progress budget: 1 pre-flight + 1 template + 1 execute + N per-target captures + 1 done
109
112
  const TOTAL_STEPS = 3 + input.targets.length + 1;
110
113
  let progressStep = 0;
@@ -157,10 +160,10 @@ export async function probePageHandler(input, context, rawProgressCallback) {
157
160
  throw new Error(`Failed to provision tunnel for ${ctx.originalUrl}. ` +
158
161
  `(Detail: ${msg})${diag}`);
159
162
  }
160
- acquiredKeyIds.push(tunnel.keyId);
163
+ acquiredProvisions.push(tunnel);
161
164
  let tunneled;
162
165
  try {
163
- tunneled = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.revokeNgrokKey(tunnel.keyId));
166
+ tunneled = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
164
167
  }
165
168
  catch (tunnelError) {
166
169
  const msg = tunnelError instanceof Error ? tunnelError.message : String(tunnelError);
@@ -463,11 +466,15 @@ export async function probePageHandler(input, context, rawProgressCallback) {
463
466
  // Tunnels intentionally NOT torn down — reuse pattern (bead vwd) +
464
467
  // 55-min idle auto-shutoff. Revoke only orphaned keys (we acquired the
465
468
  // key but tunnel creation failed before ensureTunnel completed).
466
- for (let i = 0; i < acquiredKeyIds.length; i++) {
467
- const keyId = acquiredKeyIds[i];
469
+ // NOTE (bead debugg_ai_mcp-xkoh.9): `tc` is undefined when ensureTunnel
470
+ // threw, so this condition does not fire on the very path it describes.
471
+ // That is a pre-existing defect with its own bead and is deliberately NOT
472
+ // fixed here; only the endpoint the revoke goes to changed.
473
+ for (let i = 0; i < acquiredProvisions.length; i++) {
474
+ const provision = acquiredProvisions[i];
468
475
  const tc = targetContexts[i];
469
- if (tc && !tc.tunnelId && keyId) {
470
- client.revokeNgrokKey(keyId).catch(err => logger.warn(`Failed to revoke unused ngrok key ${keyId}: ${err}`));
476
+ if (tc && !tc.tunnelId && provision) {
477
+ client.tunnels.revoke(provision).catch(err => logger.warn(`Failed to revoke unused tunnel ${provision.tunnelId}: ${err}`));
471
478
  }
472
479
  }
473
480
  }
@@ -18,7 +18,7 @@ export async function runTestSuiteHandler(input, _context) {
18
18
  logger.toolStart('run_test_suite', input);
19
19
  const client = new DebuggAIServerClient(config.api.key);
20
20
  await client.init();
21
- let acquiredKeyId = null;
21
+ let acquiredProvision = null;
22
22
  let tunnelId;
23
23
  // Used ONLY to scope the defensive sanitizeResponseUrls call below (bead
24
24
  // debugg_ai_mcp-6cfv.7) — never fed into acquirePortRoute/PortLock. This
@@ -79,10 +79,10 @@ export async function runTestSuiteHandler(input, _context) {
79
79
  const diag = provisionError instanceof TunnelProvisionError ? ` ${provisionError.diagnosticSuffix()}` : '';
80
80
  return errorResp('TunnelProvisionFailed', `Failed to provision tunnel for ${input.targetUrl}. (Detail: ${msg})${diag}`);
81
81
  }
82
- acquiredKeyId = tunnel.keyId;
82
+ acquiredProvision = tunnel;
83
83
  let dedicated;
84
84
  try {
85
- dedicated = await tunnelManager.acquireDedicatedTunnel(ctx.originalUrl, tunnel.tunnelKey, tunnel.keyId, () => client.revokeNgrokKey(tunnel.keyId));
85
+ dedicated = await tunnelManager.acquireDedicatedTunnel(ctx.originalUrl, tunnel.tunnelKey, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
86
86
  }
87
87
  catch (tunnelError) {
88
88
  const msg = tunnelError instanceof Error ? tunnelError.message : String(tunnelError);
@@ -142,8 +142,9 @@ export async function runTestSuiteHandler(input, _context) {
142
142
  finally {
143
143
  // Tunnels are NOT torn down — reuse pattern + 55-min idle auto-shutoff.
144
144
  // Only revoke an orphaned key (acquired but tunnel creation failed).
145
- if (acquiredKeyId && !tunnelId) {
146
- client.revokeNgrokKey(acquiredKeyId).catch((err) => logger.warn(`Failed to revoke unused ngrok key ${acquiredKeyId}: ${err}`));
145
+ if (acquiredProvision && !tunnelId) {
146
+ const orphan = acquiredProvision;
147
+ client.tunnels.revoke(orphan).catch((err) => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
147
148
  }
148
149
  }
149
150
  }
@@ -135,7 +135,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
135
135
  await client.init();
136
136
  const originalUrl = resolveTargetUrl(input);
137
137
  let ctx = buildContext(originalUrl);
138
- let keyId;
138
+ let provision;
139
139
  // Cancellation is driven by the MCP request/transport lifecycle, not
140
140
  // process.stdin. The SDK aborts context.signal when the client cancels the
141
141
  // call OR the transport closes — e.g. an HTTP client drops the connection.
@@ -249,7 +249,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
249
249
  `Make sure your dev server is running on the specified port and try again. ` +
250
250
  `(Detail: ${msg})${diag}`);
251
251
  }
252
- keyId = tunnel.keyId;
252
+ provision = tunnel;
253
253
  try {
254
254
  ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.revokeNgrokKey(tunnel.keyId));
255
255
  }
@@ -309,7 +309,7 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
309
309
  // owned path already revokes it; if we kept the tunnel, the key is that
310
310
  // live tunnel's own credential and revoking it would kill what we just
311
311
  // decided to preserve.
312
- keyId = undefined;
312
+ provision = undefined;
313
313
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], isError: true };
314
314
  }
315
315
  }
@@ -993,9 +993,11 @@ async function testPageChangesHandlerInner(input, context, rawProgressCallback)
993
993
  // subsequent calls to the same port and auto-shutoffs after 55 min idle.
994
994
  // Process-exit cleanup happens via stopAllTunnels() in the SIGINT/SIGTERM
995
995
  // handlers in index.ts.
996
- if (!ctx.tunnelId && keyId) {
997
- // Provisioned a key but tunnel creation failed — revoke the orphaned key.
998
- client.revokeNgrokKey(keyId).catch(err => logger.warn(`Failed to revoke unused ngrok key ${keyId}: ${err}`));
996
+ if (!ctx.tunnelId && provision) {
997
+ // Provisioned a tunnel but creation failed — revoke it, through whichever
998
+ // endpoint its transport uses.
999
+ const orphan = provision;
1000
+ client.tunnels.revoke(orphan).catch(err => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
999
1001
  }
1000
1002
  }
1001
1003
  }
@@ -60,7 +60,7 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
60
60
  await client.init();
61
61
  const originalUrl = resolveTargetUrl(input);
62
62
  let ctx = buildContext(originalUrl);
63
- let keyId;
63
+ let provision;
64
64
  // Bead 56kd.7: cancellation is driven by the MCP request/transport lifecycle
65
65
  // (context.signal), NOT process.stdin. The SDK aborts context.signal when the
66
66
  // client cancels the call OR the transport closes — e.g. an HTTP client drops
@@ -123,8 +123,8 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
123
123
  `The remote browser needs a secure tunnel to reach your local dev server. ` +
124
124
  `(Detail: ${msg})${diag}`);
125
125
  }
126
- keyId = tunnel.keyId;
127
- ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.revokeNgrokKey(tunnel.keyId));
126
+ provision = tunnel;
127
+ ctx = await ensureTunnel(ctx, tunnel.tunnelKey, tunnel.tunnelId, tunnel.keyId, () => client.tunnels.revoke(tunnel), tunnel);
128
128
  }
129
129
  // §2.4: acquire this session's shared Caddy route for our port BEFORE
130
130
  // probing/dispatching — trigger_crawl polls (pollExecution below) for
@@ -169,7 +169,7 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
169
169
  // owned path already revokes it; if we kept the tunnel, the key is that
170
170
  // live tunnel's own credential and revoking it would kill what we just
171
171
  // decided to preserve.
172
- keyId = undefined;
172
+ provision = undefined;
173
173
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }], isError: true };
174
174
  }
175
175
  }
@@ -363,8 +363,9 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
363
363
  releasePortRoute(ctx);
364
364
  // Tunnel intentionally NOT torn down (reuse path per bead vwd).
365
365
  // If tunnel creation failed after key provision, revoke the orphaned key.
366
- if (!ctx.tunnelId && keyId) {
367
- client.revokeNgrokKey(keyId).catch(err => logger.warn(`Failed to revoke unused ngrok key ${keyId}: ${err}`));
366
+ if (!ctx.tunnelId && provision) {
367
+ const orphan = provision;
368
+ client.tunnels.revoke(orphan).catch(err => logger.warn(`Failed to revoke unused tunnel ${orphan.tunnelId}: ${err}`));
368
369
  }
369
370
  }
370
371
  }