@debugbundle/mcp 1.1.0 → 1.2.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.
package/dist/main.cjs CHANGED
@@ -15094,7 +15094,7 @@ var ProjectMetricsSchema = external_exports.object({
15094
15094
  monthly_raw_ingested_events: external_exports.number().int().nonnegative(),
15095
15095
  retained_bundles: external_exports.number().int().nonnegative(),
15096
15096
  monthly_alert_deliveries: external_exports.number().int().nonnegative()
15097
- }).strict();
15097
+ });
15098
15098
  var ProjectRecordSchema = external_exports.object({
15099
15099
  project_id: external_exports.string(),
15100
15100
  organization_id: external_exports.string(),
@@ -15103,6 +15103,7 @@ var ProjectRecordSchema = external_exports.object({
15103
15103
  relationship: external_exports.enum(["owned", "shared"]),
15104
15104
  sharing_state: external_exports.enum(["private", "shared_by_you", "shared_with_you"]),
15105
15105
  effective_role: external_exports.enum(["owner", "admin", "member"]),
15106
+ shared_access_suspended: external_exports.boolean().optional(),
15106
15107
  name: external_exports.string(),
15107
15108
  slug: external_exports.string(),
15108
15109
  environment_default: external_exports.string(),
@@ -15110,13 +15111,13 @@ var ProjectRecordSchema = external_exports.object({
15110
15111
  metrics: ProjectMetricsSchema,
15111
15112
  created_at: external_exports.string(),
15112
15113
  updated_at: external_exports.string()
15113
- }).strict();
15114
+ });
15114
15115
  var ProjectListResponseSchema = external_exports.object({
15115
15116
  projects: external_exports.array(ProjectRecordSchema)
15116
- }).strict();
15117
+ });
15117
15118
  var ProjectCreateResponseSchema = external_exports.object({
15118
15119
  project: ProjectRecordSchema
15119
- }).strict();
15120
+ });
15120
15121
  var DeletedProjectRecordSchema = external_exports.object({
15121
15122
  project_id: external_exports.string(),
15122
15123
  organization_id: external_exports.string(),
@@ -15125,16 +15126,17 @@ var DeletedProjectRecordSchema = external_exports.object({
15125
15126
  relationship: external_exports.enum(["owned", "shared"]),
15126
15127
  sharing_state: external_exports.enum(["private", "shared_by_you", "shared_with_you"]),
15127
15128
  effective_role: external_exports.enum(["owner", "admin", "member"]),
15129
+ shared_access_suspended: external_exports.boolean().optional(),
15128
15130
  name: external_exports.string(),
15129
15131
  slug: external_exports.string(),
15130
15132
  environment_default: external_exports.string(),
15131
15133
  organization_plan: external_exports.enum(["free", "solo", "team"]),
15132
15134
  created_at: external_exports.string(),
15133
15135
  updated_at: external_exports.string()
15134
- }).strict();
15136
+ });
15135
15137
  var ProjectDeleteResponseSchema = external_exports.object({
15136
15138
  project: DeletedProjectRecordSchema
15137
- }).strict();
15139
+ });
15138
15140
  var ApiErrorResponseSchema4 = external_exports.object({
15139
15141
  error: external_exports.string()
15140
15142
  }).strict();
@@ -15326,6 +15328,9 @@ var IncidentsResponseSchema = external_exports.object({
15326
15328
  var IncidentResponseSchema = external_exports.object({
15327
15329
  incident: IncidentSchema
15328
15330
  }).strict();
15331
+ var BulkIncidentResponseSchema = external_exports.object({
15332
+ incidents: external_exports.array(IncidentSchema)
15333
+ }).strict();
15329
15334
  var ImprovementResponseSchema = external_exports.object({
15330
15335
  improvement: ImprovementSchema
15331
15336
  }).strict();
@@ -15532,6 +15537,20 @@ function createRetrievalApi(client) {
15532
15537
  );
15533
15538
  return parsed.incident;
15534
15539
  },
15540
+ async resolveIncidents(input) {
15541
+ const parsed = await expectParsed(
15542
+ client.request({
15543
+ method: "POST",
15544
+ path: "/v1/incidents/resolve",
15545
+ bearerToken: input.bearerToken,
15546
+ body: {
15547
+ incident_ids: input.incidentIds
15548
+ }
15549
+ }),
15550
+ BulkIncidentResponseSchema
15551
+ );
15552
+ return parsed.incidents;
15553
+ },
15535
15554
  async reopenIncident(input) {
15536
15555
  const parsed = await expectParsed(
15537
15556
  client.request({
@@ -15543,6 +15562,20 @@ function createRetrievalApi(client) {
15543
15562
  );
15544
15563
  return parsed.incident;
15545
15564
  },
15565
+ async reopenIncidents(input) {
15566
+ const parsed = await expectParsed(
15567
+ client.request({
15568
+ method: "POST",
15569
+ path: "/v1/incidents/reopen",
15570
+ bearerToken: input.bearerToken,
15571
+ body: {
15572
+ incident_ids: input.incidentIds
15573
+ }
15574
+ }),
15575
+ BulkIncidentResponseSchema
15576
+ );
15577
+ return parsed.incidents;
15578
+ },
15546
15579
  async getBundle(input) {
15547
15580
  const bundle = await expectParsed(
15548
15581
  client.request({
@@ -16468,25 +16501,74 @@ var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
16468
16501
  var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
16469
16502
  var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
16470
16503
  var RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES = [401, 403, 409, 422];
16504
+ var HTTP_METHOD_VALUES = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
16471
16505
  var ImmediateClientErrorStatusSchema = external_exports.number().int().min(400).max(499);
16506
+ var HttpMethodSchema = external_exports.enum(HTTP_METHOD_VALUES);
16472
16507
  function normalizeImmediateClientErrorStatuses(statuses) {
16473
16508
  return Array.from(new Set(statuses)).sort((left, right) => left - right);
16474
16509
  }
16475
16510
  var ImmediateClientErrorStatusesSchema = external_exports.array(ImmediateClientErrorStatusSchema).max(12).transform((statuses) => normalizeImmediateClientErrorStatuses(statuses));
16511
+ function normalizePathPattern(value) {
16512
+ return value.trim().replace(/\/{2,}/g, "/");
16513
+ }
16514
+ function isValidPathPattern(value) {
16515
+ const normalized = normalizePathPattern(value);
16516
+ if (!normalized.startsWith("/") || normalized.includes("?") || normalized.includes("#")) {
16517
+ return false;
16518
+ }
16519
+ const wildcardIndex = normalized.indexOf("*");
16520
+ return wildcardIndex === -1 || wildcardIndex === normalized.length - 1;
16521
+ }
16522
+ function normalizeHttpMethods(methods) {
16523
+ if (methods === void 0 || methods.length === 0) {
16524
+ return [];
16525
+ }
16526
+ const normalized = methods.map((method) => method.toUpperCase()).filter(
16527
+ (method) => HTTP_METHOD_VALUES.includes(method)
16528
+ );
16529
+ return Array.from(new Set(normalized)).sort();
16530
+ }
16531
+ function normalizeImmediateClientErrorPathRules(rules) {
16532
+ const normalized = rules.map((rule) => ({
16533
+ status_code: rule.status_code,
16534
+ path_pattern: normalizePathPattern(rule.path_pattern),
16535
+ methods: normalizeHttpMethods(rule.methods)
16536
+ }));
16537
+ const deduped = /* @__PURE__ */ new Map();
16538
+ for (const rule of normalized) {
16539
+ deduped.set(`${rule.status_code}:${rule.path_pattern}:${rule.methods.join(",")}`, rule);
16540
+ }
16541
+ return Array.from(deduped.values()).sort((left, right) => {
16542
+ if (left.status_code !== right.status_code) return left.status_code - right.status_code;
16543
+ const pathComparison = left.path_pattern.localeCompare(right.path_pattern);
16544
+ if (pathComparison !== 0) return pathComparison;
16545
+ return left.methods.join(",").localeCompare(right.methods.join(","));
16546
+ });
16547
+ }
16548
+ var ImmediateClientErrorPathRuleSchema = external_exports.object({
16549
+ status_code: ImmediateClientErrorStatusSchema,
16550
+ path_pattern: external_exports.string().min(1).max(256).transform(normalizePathPattern).refine(isValidPathPattern, {
16551
+ message: "path_pattern must start with / and may only use a terminal * wildcard"
16552
+ }),
16553
+ methods: external_exports.array(HttpMethodSchema).max(7).optional().default([]).transform(normalizeHttpMethods)
16554
+ });
16555
+ var ImmediateClientErrorPathRulesSchema = external_exports.array(ImmediateClientErrorPathRuleSchema).max(25).transform((rules) => normalizeImmediateClientErrorPathRules(rules));
16476
16556
  var ResolvedCapturePolicySchema = external_exports.object({
16477
16557
  preset: CapturePresetSchema,
16478
16558
  capture_logs: CaptureLogsSchema,
16479
16559
  capture_request_events: CaptureRequestEventsSchema,
16480
16560
  capture_breadcrumbs: CaptureBreadcrumbsSchema,
16481
16561
  capture_probe_events: CaptureProbeEventsSchema,
16482
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema
16562
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema,
16563
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.default([])
16483
16564
  });
16484
16565
  var CapturePolicyOverridesSchema = external_exports.object({
16485
16566
  capture_logs: CaptureLogsSchema.nullable(),
16486
16567
  capture_request_events: CaptureRequestEventsSchema.nullable(),
16487
16568
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
16488
16569
  capture_probe_events: CaptureProbeEventsSchema.nullable(),
16489
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable()
16570
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable(),
16571
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().default(null)
16490
16572
  });
16491
16573
  var CapturePolicyResponseSchema = external_exports.object({
16492
16574
  access_mode: external_exports.enum(["manage", "preview"]),
@@ -16501,6 +16583,7 @@ var CapturePolicySchema = external_exports.object({
16501
16583
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
16502
16584
  capture_probe_events: CaptureProbeEventsSchema.nullable(),
16503
16585
  immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable(),
16586
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().default(null),
16504
16587
  updated_at: external_exports.string().datetime()
16505
16588
  });
16506
16589
  var CapturePolicyUpdateSchema = external_exports.object({
@@ -16509,7 +16592,8 @@ var CapturePolicyUpdateSchema = external_exports.object({
16509
16592
  capture_request_events: CaptureRequestEventsSchema.nullable().optional(),
16510
16593
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
16511
16594
  capture_probe_events: CaptureProbeEventsSchema.nullable().optional(),
16512
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable().optional()
16595
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable().optional(),
16596
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().optional()
16513
16597
  });
16514
16598
  var PRESET_DEFAULTS = {
16515
16599
  minimal: {
@@ -16517,30 +16601,69 @@ var PRESET_DEFAULTS = {
16517
16601
  capture_request_events: "failures_only",
16518
16602
  capture_breadcrumbs: "local_only",
16519
16603
  capture_probe_events: "buffer_only",
16520
- immediate_client_error_statuses: []
16604
+ immediate_client_error_statuses: [],
16605
+ immediate_client_error_path_rules: []
16521
16606
  },
16522
16607
  balanced: {
16523
16608
  capture_logs: "warning",
16524
16609
  capture_request_events: "failures_only",
16525
16610
  capture_breadcrumbs: "exception_only",
16526
16611
  capture_probe_events: "buffer_only",
16527
- immediate_client_error_statuses: []
16612
+ immediate_client_error_statuses: [],
16613
+ immediate_client_error_path_rules: []
16528
16614
  },
16529
16615
  investigative: {
16530
16616
  capture_logs: "info",
16531
16617
  capture_request_events: "all",
16532
16618
  capture_breadcrumbs: "standalone",
16533
16619
  capture_probe_events: "standalone_when_activated",
16534
- immediate_client_error_statuses: [...RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES]
16620
+ immediate_client_error_statuses: [...RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES],
16621
+ immediate_client_error_path_rules: []
16535
16622
  }
16536
16623
  };
16537
16624
  var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
16538
16625
  var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
16539
- var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
16540
- var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
16541
- var INVESTIGATIVE_ANOMALY_STATUSES = /* @__PURE__ */ new Set([...BALANCED_STANDARD_ANOMALY_STATUSES, ...BALANCED_HIGH_VOLUME_ANOMALY_STATUSES]);
16626
+ function normalizeRequestPath(value) {
16627
+ if (typeof value !== "string") {
16628
+ return null;
16629
+ }
16630
+ const trimmed = value.trim();
16631
+ if (trimmed.length === 0) {
16632
+ return null;
16633
+ }
16634
+ const path = trimmed.startsWith("http://") || trimmed.startsWith("https://") ? (() => {
16635
+ try {
16636
+ return new URL(trimmed).pathname;
16637
+ } catch {
16638
+ return trimmed;
16639
+ }
16640
+ })() : trimmed;
16641
+ return (path.split(/[?#]/, 1)[0] ?? path).replace(/\/{2,}/g, "/");
16642
+ }
16643
+ function pathPatternMatches(rulePattern, requestPath) {
16644
+ const pattern = normalizePathPattern(rulePattern);
16645
+ if (!pattern.endsWith("*")) {
16646
+ return requestPath === pattern;
16647
+ }
16648
+ const prefix = pattern.slice(0, -1);
16649
+ return requestPath.startsWith(prefix);
16650
+ }
16651
+ function matchesImmediateClientErrorPathRule(input) {
16652
+ const { responseStatus, immediateClientErrorPathRules = [] } = input;
16653
+ if (responseStatus === null || !Number.isFinite(responseStatus) || immediateClientErrorPathRules.length === 0) {
16654
+ return false;
16655
+ }
16656
+ const requestPath = normalizeRequestPath(input.requestPath);
16657
+ if (requestPath === null) {
16658
+ return false;
16659
+ }
16660
+ const httpMethod = typeof input.httpMethod === "string" ? input.httpMethod.toUpperCase() : null;
16661
+ return immediateClientErrorPathRules.some(
16662
+ (rule) => rule.status_code === responseStatus && (rule.methods.length === 0 || httpMethod !== null && rule.methods.includes(httpMethod)) && pathPatternMatches(rule.path_pattern, requestPath)
16663
+ );
16664
+ }
16542
16665
  function classifyRequestStatus(input) {
16543
- const { responseStatus, capturePreset, immediateClientErrorStatuses = [] } = input;
16666
+ const { responseStatus, capturePreset, immediateClientErrorStatuses = [], immediateClientErrorPathRules = [] } = input;
16544
16667
  if (responseStatus === null || !Number.isFinite(responseStatus)) {
16545
16668
  return "context_signal";
16546
16669
  }
@@ -16550,6 +16673,14 @@ function classifyRequestStatus(input) {
16550
16673
  if (immediateClientErrorStatuses.includes(responseStatus)) {
16551
16674
  return "incident_signal";
16552
16675
  }
16676
+ if (matchesImmediateClientErrorPathRule({
16677
+ responseStatus,
16678
+ requestPath: input.requestPath,
16679
+ httpMethod: input.httpMethod,
16680
+ immediateClientErrorPathRules
16681
+ })) {
16682
+ return "incident_signal";
16683
+ }
16553
16684
  if (capturePreset === "investigative") {
16554
16685
  return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
16555
16686
  }
@@ -16559,33 +16690,101 @@ function classifyRequestStatus(input) {
16559
16690
  return "context_signal";
16560
16691
  }
16561
16692
  function getRequestAnomalyThreshold(input) {
16562
- const { responseStatus, capturePreset } = input;
16693
+ const { responseStatus } = input;
16563
16694
  if (responseStatus === null || !Number.isFinite(responseStatus) || responseStatus < 400 || responseStatus >= 500) {
16564
16695
  return null;
16565
16696
  }
16566
- if (capturePreset === "minimal") {
16567
- return null;
16697
+ return null;
16698
+ }
16699
+
16700
+ // ../../packages/shared-types/src/request-failure-noise.ts
16701
+ function isLowValueExternalProbeRequestFailure404(input) {
16702
+ if (input.responseStatus !== 404 || input.httpMethod.toUpperCase() !== "GET") {
16703
+ return false;
16568
16704
  }
16569
- if (capturePreset === "investigative") {
16570
- return INVESTIGATIVE_ANOMALY_STATUSES.has(responseStatus) ? {
16571
- minimum_occurrences_5m: 8,
16572
- minimum_ratio_5m_to_1h: 2
16573
- } : null;
16705
+ const normalizedRoute = input.routeTemplate.toLowerCase().replace(/\/+$/, "") || "/";
16706
+ const normalizedPath = normalizePath(input.requestPath ?? input.routeTemplate);
16707
+ const routesToCheck = /* @__PURE__ */ new Set([normalizedRoute, normalizedPath]);
16708
+ for (const route of routesToCheck) {
16709
+ if (isRouteOnlyExternalProbe(route)) {
16710
+ return true;
16711
+ }
16574
16712
  }
16575
- if (BALANCED_STANDARD_ANOMALY_STATUSES.has(responseStatus)) {
16576
- return {
16577
- minimum_occurrences_5m: 20,
16578
- minimum_ratio_5m_to_1h: 3
16579
- };
16713
+ return isDirectIpRequest(input.headers ?? null) && [...routesToCheck].some(isGenericDirectIpProbeRoute);
16714
+ }
16715
+ function normalizePath(path) {
16716
+ const withoutQuery = path.split("?")[0] ?? path;
16717
+ return withoutQuery.toLowerCase().replace(/\/+$/, "") || "/";
16718
+ }
16719
+ function isRouteOnlyExternalProbe(normalizedRoute) {
16720
+ const exactRoutes = /* @__PURE__ */ new Set([
16721
+ "/.env",
16722
+ "/__debug__/render_panel",
16723
+ "/actuator",
16724
+ "/autodiscover/autodiscover.json",
16725
+ "/developmentserver/metadatauploader",
16726
+ "/cpanel",
16727
+ "/favicon.ico",
16728
+ "/geoserver/web",
16729
+ "/hnap1",
16730
+ "/logon/logonpoint/index.html",
16731
+ "/owa/auth/logon.aspx",
16732
+ "/robots.txt",
16733
+ "/rdweb/pages",
16734
+ "/web",
16735
+ "/webclient/login.xhtml",
16736
+ "/webconsole",
16737
+ "/webui",
16738
+ "/whm",
16739
+ "/wp-admin",
16740
+ "/wp-login.php",
16741
+ "/wsman",
16742
+ "/xmlrpc.php"
16743
+ ]);
16744
+ if (exactRoutes.has(normalizedRoute)) {
16745
+ return true;
16580
16746
  }
16581
- if (BALANCED_HIGH_VOLUME_ANOMALY_STATUSES.has(responseStatus)) {
16582
- return {
16583
- minimum_occurrences_5m: 50,
16584
- minimum_ratio_5m_to_1h: 5
16585
- };
16747
+ return normalizedRoute.includes("/.git/") || normalizedRoute.includes("/.svn/") || normalizedRoute.includes("/api_keys") || normalizedRoute.includes("/backup/api_keys") || normalizedRoute.includes("/phpmyadmin") || normalizedRoute.includes("/pma/") || normalizedRoute.includes("/vendor/phpunit/") || normalizedRoute.startsWith("/autodiscover/") || normalizedRoute.startsWith("/cgi-bin/") || normalizedRoute.startsWith("/ecp/") || normalizedRoute.endsWith("/.git/config") || normalizedRoute.endsWith("/composer.json") || normalizedRoute.endsWith("/composer.lock") || normalizedRoute.endsWith("/package-lock.json") || normalizedRoute.endsWith("/package.json") || normalizedRoute.endsWith("/server-status") || normalizedRoute.includes("wp-config") || normalizedRoute.startsWith("/owa/") || normalizedRoute.startsWith("/rdweb/") || normalizedRoute.startsWith("/vpn/") || normalizedRoute.startsWith("/wp-") || isSensitiveBackupFileProbe(normalizedRoute);
16748
+ }
16749
+ function isSensitiveBackupFileProbe(normalizedRoute) {
16750
+ if (!/\.(?:bak|backup|dump|old|orig|save|sql|swp|tar|tar\.gz|zip)$/.test(normalizedRoute)) {
16751
+ return false;
16752
+ }
16753
+ return /(?:^|\/|\.)(?:backup|config|database|db|dump|env|secret|site|www|wp-config)(?:\/|\.|_|-|$)/.test(normalizedRoute);
16754
+ }
16755
+ function isGenericDirectIpProbeRoute(normalizedRoute) {
16756
+ return [
16757
+ "/admin",
16758
+ "/administrator",
16759
+ "/login",
16760
+ "/logincheck",
16761
+ "/remote/logincheck"
16762
+ ].includes(normalizedRoute);
16763
+ }
16764
+ function isDirectIpRequest(headers) {
16765
+ if (headers === null) {
16766
+ return false;
16767
+ }
16768
+ const host = readHeader(headers, "x-forwarded-host") ?? readHeader(headers, "host");
16769
+ if (host === null) {
16770
+ return false;
16771
+ }
16772
+ return isIpLikeHost(host);
16773
+ }
16774
+ function readHeader(headers, name) {
16775
+ const direct = headers[name] ?? headers[name.toLowerCase()];
16776
+ if (typeof direct === "string") {
16777
+ return direct;
16778
+ }
16779
+ if (Array.isArray(direct) && typeof direct[0] === "string") {
16780
+ return direct[0];
16586
16781
  }
16587
16782
  return null;
16588
16783
  }
16784
+ function isIpLikeHost(value) {
16785
+ const host = value.trim().replace(/:\d+$/, "");
16786
+ return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(host) || /^\[[0-9a-f:]+\]$/i.test(host) || host.includes(":") && /^[0-9a-f:]+$/i.test(host);
16787
+ }
16589
16788
 
16590
16789
  // ../../packages/shared-types/src/capture-rules.ts
16591
16790
  var CAPTURE_RULE_EVENT_TYPES = [
@@ -19203,7 +19402,7 @@ function getRequestResponseStatus(payload) {
19203
19402
  const status = payload?.["response_status"];
19204
19403
  return typeof status === "number" && Number.isFinite(status) ? status : null;
19205
19404
  }
19206
- function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal", immediateClientErrorStatuses = []) {
19405
+ function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal", immediateClientErrorStatuses = [], immediateClientErrorPathRules = []) {
19207
19406
  switch (eventType) {
19208
19407
  case "backend_exception":
19209
19408
  case "frontend_exception":
@@ -19215,7 +19414,14 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload, captureP
19215
19414
  return "context_signal";
19216
19415
  case "request_event": {
19217
19416
  const responseStatus = getRequestResponseStatus(payload);
19218
- return classifyRequestStatus({ responseStatus, capturePreset, immediateClientErrorStatuses });
19417
+ return classifyRequestStatus({
19418
+ responseStatus,
19419
+ requestPath: payload?.["path"],
19420
+ httpMethod: payload?.["method"],
19421
+ capturePreset,
19422
+ immediateClientErrorStatuses,
19423
+ immediateClientErrorPathRules
19424
+ });
19219
19425
  }
19220
19426
  case "frontend_breadcrumb":
19221
19427
  case "deploy_metadata":
@@ -20412,6 +20618,7 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
20412
20618
  capture_breadcrumbs text,
20413
20619
  capture_probe_events text,
20414
20620
  immediate_client_error_statuses jsonb,
20621
+ immediate_client_error_path_rules jsonb,
20415
20622
  updated_at timestamptz NOT NULL DEFAULT now()
20416
20623
  )
20417
20624
  `,
@@ -20963,7 +21170,8 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
20963
21170
  `
20964
21171
  CREATE TABLE github_dispatch_deliveries (
20965
21172
  id uuid PRIMARY KEY,
20966
- rule_id uuid NOT NULL REFERENCES github_dispatch_rules(id) ON DELETE CASCADE,
21173
+ rule_id uuid NOT NULL,
21174
+ rule_name text NOT NULL,
20967
21175
  project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
20968
21176
  incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
20969
21177
  improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
@@ -21051,6 +21259,26 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
21051
21259
  `
21052
21260
  CREATE INDEX trial_lifecycle_events_org_event_created_idx
21053
21261
  ON trial_lifecycle_events (organization_id, event_type, created_at DESC)
21262
+ `,
21263
+ `
21264
+ CREATE TABLE plan_cleanup_tasks (
21265
+ id uuid PRIMARY KEY,
21266
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
21267
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
21268
+ cleanup_type text NOT NULL
21269
+ CHECK (cleanup_type IN ('delete_improvement_bundle_objects')),
21270
+ attempt_count integer NOT NULL DEFAULT 0,
21271
+ last_error text,
21272
+ next_attempt_at timestamptz NOT NULL DEFAULT now(),
21273
+ completed_at timestamptz,
21274
+ created_at timestamptz NOT NULL DEFAULT now(),
21275
+ updated_at timestamptz NOT NULL DEFAULT now(),
21276
+ UNIQUE (project_id, cleanup_type)
21277
+ )
21278
+ `,
21279
+ `
21280
+ CREATE INDEX plan_cleanup_tasks_pending_idx
21281
+ ON plan_cleanup_tasks (completed_at, next_attempt_at, created_at)
21054
21282
  `
21055
21283
  ];
21056
21284
 
@@ -21702,6 +21930,57 @@ var STORAGE_SCHEMA_MIGRATIONS = [
21702
21930
  )
21703
21931
  `
21704
21932
  ]
21933
+ }),
21934
+ defineStorageSchemaMigration({
21935
+ id: "202606050001_preserve_github_dispatch_history_when_rules_are_deleted",
21936
+ description: "Snapshot GitHub rule names onto deliveries and decouple delivery history from live rule rows.",
21937
+ statements: [
21938
+ "ALTER TABLE github_dispatch_deliveries ADD COLUMN IF NOT EXISTS rule_name text",
21939
+ `
21940
+ UPDATE github_dispatch_deliveries deliveries
21941
+ SET rule_name = rules.name
21942
+ FROM github_dispatch_rules rules
21943
+ WHERE deliveries.rule_id = rules.id
21944
+ AND deliveries.rule_name IS NULL
21945
+ `,
21946
+ "ALTER TABLE github_dispatch_deliveries ALTER COLUMN rule_name SET DEFAULT ''",
21947
+ "UPDATE github_dispatch_deliveries SET rule_name = '' WHERE rule_name IS NULL",
21948
+ "ALTER TABLE github_dispatch_deliveries ALTER COLUMN rule_name SET NOT NULL",
21949
+ "ALTER TABLE github_dispatch_deliveries DROP CONSTRAINT IF EXISTS github_dispatch_deliveries_rule_id_fkey"
21950
+ ]
21951
+ }),
21952
+ defineStorageSchemaMigration({
21953
+ id: "202606050002_add_durable_plan_cleanup_tasks",
21954
+ description: "Persist retryable external cleanup tasks for side effects that cannot be completed transactionally.",
21955
+ statements: [
21956
+ `
21957
+ CREATE TABLE IF NOT EXISTS plan_cleanup_tasks (
21958
+ id uuid PRIMARY KEY,
21959
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
21960
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
21961
+ cleanup_type text NOT NULL
21962
+ CHECK (cleanup_type IN ('delete_improvement_bundle_objects')),
21963
+ attempt_count integer NOT NULL DEFAULT 0,
21964
+ last_error text,
21965
+ next_attempt_at timestamptz NOT NULL DEFAULT now(),
21966
+ completed_at timestamptz,
21967
+ created_at timestamptz NOT NULL DEFAULT now(),
21968
+ updated_at timestamptz NOT NULL DEFAULT now(),
21969
+ UNIQUE (project_id, cleanup_type)
21970
+ )
21971
+ `,
21972
+ `
21973
+ CREATE INDEX IF NOT EXISTS plan_cleanup_tasks_pending_idx
21974
+ ON plan_cleanup_tasks (completed_at, next_attempt_at, created_at)
21975
+ `
21976
+ ]
21977
+ }),
21978
+ defineStorageSchemaMigration({
21979
+ id: "202606080001_add_capture_policy_client_error_path_rules",
21980
+ description: "Add path-scoped client error incident promotion rules to capture policies.",
21981
+ statements: [
21982
+ "ALTER TABLE capture_policies ADD COLUMN IF NOT EXISTS immediate_client_error_path_rules jsonb"
21983
+ ]
21705
21984
  })
21706
21985
  ];
21707
21986
 
@@ -22982,6 +23261,15 @@ function collectRequestAnomalyAggregates(batches, capturePreset) {
22982
23261
  if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
22983
23262
  continue;
22984
23263
  }
23264
+ if (isLowValueExternalProbeRequestFailure404({
23265
+ httpMethod: method,
23266
+ requestPath: event.payload.path,
23267
+ routeTemplate,
23268
+ responseStatus,
23269
+ headers: event.payload.headers
23270
+ })) {
23271
+ continue;
23272
+ }
22985
23273
  const projectId = requireProjectId(event);
22986
23274
  const incidentFingerprint = buildRequestAnomalyFingerprint({
22987
23275
  projectId,
@@ -26312,6 +26600,67 @@ function createRetrievalMcpTools(api) {
26312
26600
  mapMcpError12(error);
26313
26601
  }
26314
26602
  },
26603
+ async resolve_incidents(input) {
26604
+ try {
26605
+ const incidentIds = Array.from(
26606
+ new Set((Array.isArray(input["incidentIds"]) ? input["incidentIds"] : []).map((value) => String(value)))
26607
+ );
26608
+ const localIncidents = /* @__PURE__ */ new Map();
26609
+ const cloudIncidentIds = [];
26610
+ if (await shouldUseLocalSource(input)) {
26611
+ return {
26612
+ incidents: await Promise.all(
26613
+ incidentIds.map((incidentId) => resolveLocalIncident({ incidentId }))
26614
+ )
26615
+ };
26616
+ }
26617
+ if (await shouldCombineLocalAndCloudSource(input)) {
26618
+ for (const incidentId of incidentIds) {
26619
+ try {
26620
+ localIncidents.set(incidentId, await resolveLocalIncident({ incidentId }));
26621
+ } catch (error) {
26622
+ if (!isNotFoundRetrievalError(error)) {
26623
+ throw error;
26624
+ }
26625
+ cloudIncidentIds.push(incidentId);
26626
+ }
26627
+ }
26628
+ } else {
26629
+ cloudIncidentIds.push(...incidentIds);
26630
+ }
26631
+ if (cloudIncidentIds.length > 0) {
26632
+ const cloudIncidents = api.resolveIncidents === void 0 ? await Promise.all(
26633
+ cloudIncidentIds.map(
26634
+ async (incidentId) => attachSourceToRecord(
26635
+ await api.resolveIncident({
26636
+ bearerToken: await requireCloudBearerToken(input),
26637
+ incidentId
26638
+ }),
26639
+ "cloud"
26640
+ )
26641
+ )
26642
+ ) : (await api.resolveIncidents({
26643
+ bearerToken: await requireCloudBearerToken(input),
26644
+ incidentIds: cloudIncidentIds
26645
+ })).map((incident) => attachSourceToRecord(incident, "cloud"));
26646
+ for (const incident of cloudIncidents) {
26647
+ await syncCloudIncidentCacheStatus({
26648
+ incidentId: String(incident["incident_id"]),
26649
+ incident: {
26650
+ ...typeof incident["status"] === "string" ? { status: incident["status"] } : {},
26651
+ resolved_at: typeof incident["resolved_at"] === "string" || incident["resolved_at"] === null ? incident["resolved_at"] : null
26652
+ }
26653
+ });
26654
+ localIncidents.set(String(incident["incident_id"]), incident);
26655
+ }
26656
+ }
26657
+ return {
26658
+ incidents: incidentIds.map((incidentId) => localIncidents.get(incidentId))
26659
+ };
26660
+ } catch (error) {
26661
+ mapMcpError12(error);
26662
+ }
26663
+ },
26315
26664
  async reopen_incident(input) {
26316
26665
  try {
26317
26666
  if (await shouldUseLocalSource(input)) {
@@ -26357,6 +26706,67 @@ function createRetrievalMcpTools(api) {
26357
26706
  mapMcpError12(error);
26358
26707
  }
26359
26708
  },
26709
+ async reopen_incidents(input) {
26710
+ try {
26711
+ const incidentIds = Array.from(
26712
+ new Set((Array.isArray(input["incidentIds"]) ? input["incidentIds"] : []).map((value) => String(value)))
26713
+ );
26714
+ const localIncidents = /* @__PURE__ */ new Map();
26715
+ const cloudIncidentIds = [];
26716
+ if (await shouldUseLocalSource(input)) {
26717
+ return {
26718
+ incidents: await Promise.all(
26719
+ incidentIds.map((incidentId) => reopenLocalIncident({ incidentId }))
26720
+ )
26721
+ };
26722
+ }
26723
+ if (await shouldCombineLocalAndCloudSource(input)) {
26724
+ for (const incidentId of incidentIds) {
26725
+ try {
26726
+ localIncidents.set(incidentId, await reopenLocalIncident({ incidentId }));
26727
+ } catch (error) {
26728
+ if (!isNotFoundRetrievalError(error)) {
26729
+ throw error;
26730
+ }
26731
+ cloudIncidentIds.push(incidentId);
26732
+ }
26733
+ }
26734
+ } else {
26735
+ cloudIncidentIds.push(...incidentIds);
26736
+ }
26737
+ if (cloudIncidentIds.length > 0) {
26738
+ const cloudIncidents = api.reopenIncidents === void 0 ? await Promise.all(
26739
+ cloudIncidentIds.map(
26740
+ async (incidentId) => attachSourceToRecord(
26741
+ await api.reopenIncident({
26742
+ bearerToken: await requireCloudBearerToken(input),
26743
+ incidentId
26744
+ }),
26745
+ "cloud"
26746
+ )
26747
+ )
26748
+ ) : (await api.reopenIncidents({
26749
+ bearerToken: await requireCloudBearerToken(input),
26750
+ incidentIds: cloudIncidentIds
26751
+ })).map((incident) => attachSourceToRecord(incident, "cloud"));
26752
+ for (const incident of cloudIncidents) {
26753
+ await syncCloudIncidentCacheStatus({
26754
+ incidentId: String(incident["incident_id"]),
26755
+ incident: {
26756
+ ...typeof incident["status"] === "string" ? { status: incident["status"] } : {},
26757
+ resolved_at: null
26758
+ }
26759
+ });
26760
+ localIncidents.set(String(incident["incident_id"]), incident);
26761
+ }
26762
+ }
26763
+ return {
26764
+ incidents: incidentIds.map((incidentId) => localIncidents.get(incidentId))
26765
+ };
26766
+ } catch (error) {
26767
+ mapMcpError12(error);
26768
+ }
26769
+ },
26360
26770
  async get_bundle(input) {
26361
26771
  try {
26362
26772
  if (await shouldUseLocalSource(input)) {
@@ -28266,7 +28676,7 @@ var zodToJsonSchema = (schema, options) => {
28266
28676
  var package_default = {
28267
28677
  name: "@debugbundle/mcp",
28268
28678
  mcpName: "com.debugbundle/mcp",
28269
- version: "1.1.0",
28679
+ version: "1.2.0",
28270
28680
  private: false,
28271
28681
  description: "Model Context Protocol server for DebugBundle",
28272
28682
  license: "AGPL-3.0-only",
@@ -28328,6 +28738,11 @@ var incidentLookupInputSchema = external_exports.object({
28328
28738
  source: sourceSchema,
28329
28739
  incidentId: external_exports.string()
28330
28740
  });
28741
+ var bulkIncidentLookupInputSchema = external_exports.object({
28742
+ bearerToken: optionalBearerTokenSchema,
28743
+ source: sourceSchema,
28744
+ incidentIds: external_exports.array(external_exports.string()).min(1).max(1e3)
28745
+ });
28331
28746
  var improvementLookupInputSchema = external_exports.object({
28332
28747
  bearerToken: external_exports.string(),
28333
28748
  improvementId: external_exports.string()
@@ -28415,12 +28830,24 @@ var MCP_TOOL_CATALOG = [
28415
28830
  description: "Resolve an incident by incident id.",
28416
28831
  inputSchema: incidentLookupInputSchema
28417
28832
  },
28833
+ {
28834
+ name: "resolve_incidents",
28835
+ group: "retrieval",
28836
+ description: "Resolve incidents in bulk by incident id.",
28837
+ inputSchema: bulkIncidentLookupInputSchema
28838
+ },
28418
28839
  {
28419
28840
  name: "reopen_incident",
28420
28841
  group: "retrieval",
28421
- description: "Reopen a locally stored resolved incident.",
28842
+ description: "Reopen an incident by incident id.",
28422
28843
  inputSchema: incidentLookupInputSchema
28423
28844
  },
28845
+ {
28846
+ name: "reopen_incidents",
28847
+ group: "retrieval",
28848
+ description: "Reopen incidents in bulk by incident id.",
28849
+ inputSchema: bulkIncidentLookupInputSchema
28850
+ },
28424
28851
  {
28425
28852
  name: "get_bundle",
28426
28853
  group: "retrieval",
@@ -29010,7 +29437,12 @@ var MCP_TOOL_CATALOG = [
29010
29437
  capture_request_events: external_exports.string().nullable().optional(),
29011
29438
  capture_breadcrumbs: external_exports.string().nullable().optional(),
29012
29439
  capture_probe_events: external_exports.string().nullable().optional(),
29013
- immediate_client_error_statuses: external_exports.array(external_exports.number().int().min(400).max(499)).nullable().optional()
29440
+ immediate_client_error_statuses: external_exports.array(external_exports.number().int().min(400).max(499)).nullable().optional(),
29441
+ immediate_client_error_path_rules: external_exports.array(external_exports.object({
29442
+ status_code: external_exports.number().int().min(400).max(499),
29443
+ path_pattern: external_exports.string(),
29444
+ methods: external_exports.array(external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"])).optional()
29445
+ })).nullable().optional()
29014
29446
  })
29015
29447
  })
29016
29448
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@debugbundle/mcp",
3
3
  "mcpName": "com.debugbundle/mcp",
4
- "version": "1.1.0",
4
+ "version": "1.2.0",
5
5
  "private": false,
6
6
  "description": "Model Context Protocol server for DebugBundle",
7
7
  "license": "AGPL-3.0-only",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "source": "github",
9
9
  "subfolder": "apps/mcp"
10
10
  },
11
- "version": "1.1.0",
11
+ "version": "1.2.0",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@debugbundle/mcp",
17
- "version": "1.1.0",
17
+ "version": "1.2.0",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },