@debugbundle/mcp 0.1.5 → 0.1.6

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 +1 -1
  2. package/dist/main.cjs +53 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ debugbundle-mcp
30
30
  }
31
31
  ```
32
32
 
33
- The server uses stdio transport and exposes the same DebugBundle incident, incident-context, bundle, webhook, alert, token, project, member, billing, GitHub, probe, and diagnostic tools documented at https://debugbundle.com/docs/mcp. Hosted verification supports the active V1 proof path through `verify_cloud` with `trigger5xx: true`, incident triage can start with the one-call `get_incident_context` tool, and `doctor` accepts `privacy: true` to return the same deterministic redaction preview as `debugbundle doctor --privacy`.
33
+ The server uses stdio transport and exposes the same DebugBundle incident, incident-context, bundle, webhook, alert, token, project, member, billing, GitHub, probe, and diagnostic tools documented at https://debugbundle.com/docs/mcp. Hosted verification supports the active V1 proof path through `verify_cloud` with `trigger5xx: true` and the configured-client-error proof path with `trigger4xxStatus: 403`, incident triage can start with the one-call `get_incident_context` tool, and `doctor` accepts `privacy: true` to return the same deterministic redaction preview as `debugbundle doctor --privacy`.
34
34
 
35
35
  ## Authentication
36
36
 
package/dist/main.cjs CHANGED
@@ -21125,7 +21125,7 @@ function formatResult(input, exitCode, checks, errors, incidentId) {
21125
21125
  };
21126
21126
  }
21127
21127
  function buildCloudSuggestedActions(status, incidentId, mode = "passive_recent_incident") {
21128
- if (status === "healthy" && incidentId !== void 0 && mode === "active_5xx") {
21128
+ if (status === "healthy" && incidentId !== void 0 && (mode === "active_5xx" || mode === "active_4xx")) {
21129
21129
  return [
21130
21130
  `Run debugbundle inspect ${incidentId} --source cloud to inspect why the incident fired.`,
21131
21131
  `Run debugbundle bundle ${incidentId} --source cloud to fetch the generated debug bundle.`
@@ -21195,11 +21195,11 @@ function localFailureStepName(checks) {
21195
21195
  function cloudVerificationRunId(now) {
21196
21196
  return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
21197
21197
  }
21198
- function requestFailureReason() {
21198
+ function requestFailureReason(responseStatus) {
21199
21199
  const incidentReason = deriveIncidentReasonFromSignal({
21200
21200
  event_type: "request_event",
21201
21201
  event_class: "incident_signal",
21202
- response_status: 503
21202
+ response_status: responseStatus
21203
21203
  });
21204
21204
  if (incidentReason === null) {
21205
21205
  throw new Error("request_failure_reason_unavailable");
@@ -21208,6 +21208,9 @@ function requestFailureReason() {
21208
21208
  }
21209
21209
  function buildCloudVerificationEvent(input) {
21210
21210
  const runId = cloudVerificationRunId(input.now);
21211
+ const is5xxVerification = input.responseStatus >= 500;
21212
+ const routeTemplate = is5xxVerification ? "/debugbundle/verify/cloud" : `/debugbundle/verify/cloud/client-error/${input.responseStatus}`;
21213
+ const verificationLabel = is5xxVerification ? "true" : `client-error-${input.responseStatus}`;
21211
21214
  return createEventEnvelope({
21212
21215
  event_type: "request_event",
21213
21216
  sdk_name: "debugbundle-cli",
@@ -21221,28 +21224,39 @@ function buildCloudVerificationEvent(input) {
21221
21224
  occurred_at: input.now.toISOString(),
21222
21225
  payload: {
21223
21226
  method: "GET",
21224
- path: "/debugbundle/verify/cloud",
21225
- route_template: "/debugbundle/verify/cloud",
21227
+ path: routeTemplate,
21228
+ route_template: routeTemplate,
21226
21229
  query: {
21227
21230
  debugbundle_verification: true,
21228
- run_id: runId
21231
+ run_id: runId,
21232
+ synthetic_status: input.responseStatus
21229
21233
  },
21230
21234
  headers: {
21231
- "x-debugbundle-verification": "true"
21235
+ "x-debugbundle-verification": verificationLabel
21232
21236
  },
21233
- response_status: 503,
21237
+ response_status: input.responseStatus,
21234
21238
  duration_ms: 37,
21235
21239
  response_headers: {
21236
- "x-debugbundle-verification": "true"
21240
+ "x-debugbundle-verification": verificationLabel
21237
21241
  },
21238
21242
  response_body: {
21239
- error: "debugbundle_cloud_verification",
21243
+ error: is5xxVerification ? "debugbundle_cloud_verification" : "debugbundle_cloud_client_error_verification",
21240
21244
  synthetic: true,
21241
- run_id: runId
21245
+ run_id: runId,
21246
+ response_status: input.responseStatus
21242
21247
  }
21243
21248
  }
21244
21249
  });
21245
21250
  }
21251
+ function validateActiveCloudVerificationInput(input) {
21252
+ if (input.trigger5xx === true && input.trigger4xxStatus !== void 0) {
21253
+ return "Choose either --trigger-5xx or --trigger-4xx, not both.";
21254
+ }
21255
+ if (input.trigger4xxStatus !== void 0 && (input.trigger4xxStatus < 400 || input.trigger4xxStatus > 499)) {
21256
+ return "--trigger-4xx must be an integer status between 400 and 499.";
21257
+ }
21258
+ return null;
21259
+ }
21246
21260
  async function sendEventsToApi(input, dependencies = {}) {
21247
21261
  const fetchImpl = dependencies.fetchImpl ?? fetch;
21248
21262
  const baseUrl = input.baseUrl.endsWith("/") ? input.baseUrl.slice(0, -1) : input.baseUrl;
@@ -21434,6 +21448,15 @@ async function verifyCloudCommand(input, dependencies = {}) {
21434
21448
  const checks = [];
21435
21449
  const environment = input.environment ?? "production";
21436
21450
  const maxAgeMinutes = input.maxAgeMinutes ?? 15;
21451
+ const activeInputError = validateActiveCloudVerificationInput(input);
21452
+ if (activeInputError !== null) {
21453
+ checks.push({
21454
+ name: "trigger-input",
21455
+ status: "error",
21456
+ message: activeInputError
21457
+ });
21458
+ return formatCloudResult(input, 4, checks, [activeInputError]);
21459
+ }
21437
21460
  const readAuthState = dependencies.readAuthState ?? readCliAuthState;
21438
21461
  let authState;
21439
21462
  try {
@@ -21466,7 +21489,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21466
21489
  requestInput,
21467
21490
  dependencies.fetchImpl === void 0 ? {} : { fetchImpl: dependencies.fetchImpl }
21468
21491
  ));
21469
- if (input.trigger5xx === true) {
21492
+ if (input.trigger5xx === true || input.trigger4xxStatus !== void 0) {
21470
21493
  const verificationStartedAt = now();
21471
21494
  const runId = cloudVerificationRunId(verificationStartedAt);
21472
21495
  const serviceName = input.service ?? `debugbundle-verify-cloud-${runId}`;
@@ -21474,16 +21497,20 @@ async function verifyCloudCommand(input, dependencies = {}) {
21474
21497
  const pollAttempts = dependencies.pollAttempts ?? 6;
21475
21498
  const pollIntervalMs = dependencies.pollIntervalMs ?? 2e3;
21476
21499
  const sleep = dependencies.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
21500
+ const responseStatus = input.trigger4xxStatus ?? 503;
21501
+ const activeMode = input.trigger4xxStatus !== void 0 ? "active_4xx" : "active_5xx";
21502
+ const activeCheckName = input.trigger4xxStatus !== void 0 ? "active-4xx-event" : "active-5xx-event";
21503
+ const statusLabel = input.trigger4xxStatus !== void 0 ? `${responseStatus}` : "5xx";
21477
21504
  const verification = {
21478
- mode: "active_5xx",
21505
+ mode: activeMode,
21479
21506
  bundle_status: "unknown",
21480
- classification_reason: requestFailureReason()
21507
+ classification_reason: requestFailureReason(responseStatus)
21481
21508
  };
21482
21509
  const errors = [];
21483
21510
  let exitCode = 0;
21484
21511
  let tokenId = null;
21485
21512
  let incidentId;
21486
- let activeStep = "active-5xx-event";
21513
+ let activeStep = activeCheckName;
21487
21514
  try {
21488
21515
  const token = await createProjectToken({
21489
21516
  bearerToken: authState.bearer_token,
@@ -21497,7 +21524,8 @@ async function verifyCloudCommand(input, dependencies = {}) {
21497
21524
  const event = buildCloudVerificationEvent({
21498
21525
  now: verificationStartedAt,
21499
21526
  serviceName,
21500
- environment
21527
+ environment,
21528
+ responseStatus
21501
21529
  });
21502
21530
  const ingestion = await sendEvents({
21503
21531
  baseUrl: authState.base_url,
@@ -21506,12 +21534,12 @@ async function verifyCloudCommand(input, dependencies = {}) {
21506
21534
  });
21507
21535
  verification.accepted_event_count = ingestion.accepted;
21508
21536
  if (ingestion.accepted < 1 || ingestion.rejected > 0 || ingestion.errors.length > 0) {
21509
- throw new Error(`Synthetic 5xx ingestion was not fully accepted: accepted=${ingestion.accepted}, rejected=${ingestion.rejected}.`);
21537
+ throw new Error(`Synthetic ${statusLabel} ingestion was not fully accepted: accepted=${ingestion.accepted}, rejected=${ingestion.rejected}.`);
21510
21538
  }
21511
21539
  checks.push({
21512
- name: "active-5xx-event",
21540
+ name: activeCheckName,
21513
21541
  status: "ok",
21514
- message: "Sent synthetic 5xx request_event through cloud ingestion."
21542
+ message: `Sent synthetic ${statusLabel} request_event through cloud ingestion.`
21515
21543
  });
21516
21544
  activeStep = "incident-retrieval";
21517
21545
  for (let attempt = 1; attempt <= pollAttempts; attempt += 1) {
@@ -21529,7 +21557,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
21529
21557
  if (candidate !== void 0) {
21530
21558
  incidentId = candidate.incident_id;
21531
21559
  verification.incident_id = candidate.incident_id;
21532
- verification.classification_reason = candidate.incident_reason ?? requestFailureReason();
21560
+ verification.classification_reason = candidate.incident_reason ?? requestFailureReason(responseStatus);
21533
21561
  break;
21534
21562
  }
21535
21563
  if (attempt < pollAttempts) {
@@ -21537,12 +21565,12 @@ async function verifyCloudCommand(input, dependencies = {}) {
21537
21565
  }
21538
21566
  }
21539
21567
  if (incidentId === void 0) {
21540
- throw new Error("Synthetic 5xx request was accepted but no matching cloud incident was visible yet.");
21568
+ throw new Error(`Synthetic ${statusLabel} request was accepted but no matching cloud incident was visible yet.`);
21541
21569
  }
21542
21570
  checks.push({
21543
21571
  name: "incident-retrieval",
21544
21572
  status: "ok",
21545
- message: `Retrieved cloud incident ${incidentId} for the synthetic 5xx request.`
21573
+ message: `Retrieved cloud incident ${incidentId} for the synthetic ${statusLabel} request.`
21546
21574
  });
21547
21575
  activeStep = "bundle-status";
21548
21576
  const bundle = await getBundle({
@@ -23282,6 +23310,7 @@ function createSetupMcpTools(commands) {
23282
23310
  ...typeof input["environment"] === "string" ? { environment: input["environment"] } : {},
23283
23311
  ...typeof input["maxAgeMinutes"] === "number" ? { maxAgeMinutes: input["maxAgeMinutes"] } : {},
23284
23312
  ...input["trigger5xx"] === true ? { trigger5xx: true } : {},
23313
+ ...typeof input["trigger4xxStatus"] === "number" ? { trigger4xxStatus: input["trigger4xxStatus"] } : {},
23285
23314
  ...typeof input["authFilePath"] === "string" ? { authFilePath: input["authFilePath"] } : {},
23286
23315
  json: true
23287
23316
  })
@@ -25054,13 +25083,14 @@ var MCP_TOOL_CATALOG = [
25054
25083
  {
25055
25084
  name: "verify_cloud",
25056
25085
  group: "setup",
25057
- description: "Verify that a hosted project is sending data to DebugBundle.",
25086
+ description: "Verify hosted ingestion or actively prove hosted incident creation.",
25058
25087
  inputSchema: external_exports.object({
25059
25088
  projectId: external_exports.string(),
25060
25089
  service: external_exports.string().optional(),
25061
25090
  environment: external_exports.string().optional(),
25062
25091
  maxAgeMinutes: external_exports.number().optional(),
25063
25092
  trigger5xx: external_exports.boolean().optional(),
25093
+ trigger4xxStatus: external_exports.number().int().min(400).max(499).optional(),
25064
25094
  authFilePath: external_exports.string().optional()
25065
25095
  })
25066
25096
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "Model Context Protocol server for DebugBundle",
6
6
  "license": "AGPL-3.0-only",