@debugbundle/mcp 1.1.1 → 1.3.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(),
@@ -15111,13 +15111,13 @@ var ProjectRecordSchema = external_exports.object({
15111
15111
  metrics: ProjectMetricsSchema,
15112
15112
  created_at: external_exports.string(),
15113
15113
  updated_at: external_exports.string()
15114
- }).strict();
15114
+ });
15115
15115
  var ProjectListResponseSchema = external_exports.object({
15116
15116
  projects: external_exports.array(ProjectRecordSchema)
15117
- }).strict();
15117
+ });
15118
15118
  var ProjectCreateResponseSchema = external_exports.object({
15119
15119
  project: ProjectRecordSchema
15120
- }).strict();
15120
+ });
15121
15121
  var DeletedProjectRecordSchema = external_exports.object({
15122
15122
  project_id: external_exports.string(),
15123
15123
  organization_id: external_exports.string(),
@@ -15133,10 +15133,10 @@ var DeletedProjectRecordSchema = external_exports.object({
15133
15133
  organization_plan: external_exports.enum(["free", "solo", "team"]),
15134
15134
  created_at: external_exports.string(),
15135
15135
  updated_at: external_exports.string()
15136
- }).strict();
15136
+ });
15137
15137
  var ProjectDeleteResponseSchema = external_exports.object({
15138
15138
  project: DeletedProjectRecordSchema
15139
- }).strict();
15139
+ });
15140
15140
  var ApiErrorResponseSchema4 = external_exports.object({
15141
15141
  error: external_exports.string()
15142
15142
  }).strict();
@@ -16501,25 +16501,74 @@ var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
16501
16501
  var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
16502
16502
  var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
16503
16503
  var RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES = [401, 403, 409, 422];
16504
+ var HTTP_METHOD_VALUES = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
16504
16505
  var ImmediateClientErrorStatusSchema = external_exports.number().int().min(400).max(499);
16506
+ var HttpMethodSchema = external_exports.enum(HTTP_METHOD_VALUES);
16505
16507
  function normalizeImmediateClientErrorStatuses(statuses) {
16506
16508
  return Array.from(new Set(statuses)).sort((left, right) => left - right);
16507
16509
  }
16508
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));
16509
16556
  var ResolvedCapturePolicySchema = external_exports.object({
16510
16557
  preset: CapturePresetSchema,
16511
16558
  capture_logs: CaptureLogsSchema,
16512
16559
  capture_request_events: CaptureRequestEventsSchema,
16513
16560
  capture_breadcrumbs: CaptureBreadcrumbsSchema,
16514
16561
  capture_probe_events: CaptureProbeEventsSchema,
16515
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema
16562
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema,
16563
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.default([])
16516
16564
  });
16517
16565
  var CapturePolicyOverridesSchema = external_exports.object({
16518
16566
  capture_logs: CaptureLogsSchema.nullable(),
16519
16567
  capture_request_events: CaptureRequestEventsSchema.nullable(),
16520
16568
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
16521
16569
  capture_probe_events: CaptureProbeEventsSchema.nullable(),
16522
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable()
16570
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable(),
16571
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().default(null)
16523
16572
  });
16524
16573
  var CapturePolicyResponseSchema = external_exports.object({
16525
16574
  access_mode: external_exports.enum(["manage", "preview"]),
@@ -16534,6 +16583,7 @@ var CapturePolicySchema = external_exports.object({
16534
16583
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
16535
16584
  capture_probe_events: CaptureProbeEventsSchema.nullable(),
16536
16585
  immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable(),
16586
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().default(null),
16537
16587
  updated_at: external_exports.string().datetime()
16538
16588
  });
16539
16589
  var CapturePolicyUpdateSchema = external_exports.object({
@@ -16542,7 +16592,8 @@ var CapturePolicyUpdateSchema = external_exports.object({
16542
16592
  capture_request_events: CaptureRequestEventsSchema.nullable().optional(),
16543
16593
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
16544
16594
  capture_probe_events: CaptureProbeEventsSchema.nullable().optional(),
16545
- immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable().optional()
16595
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable().optional(),
16596
+ immediate_client_error_path_rules: ImmediateClientErrorPathRulesSchema.nullable().optional()
16546
16597
  });
16547
16598
  var PRESET_DEFAULTS = {
16548
16599
  minimal: {
@@ -16550,30 +16601,69 @@ var PRESET_DEFAULTS = {
16550
16601
  capture_request_events: "failures_only",
16551
16602
  capture_breadcrumbs: "local_only",
16552
16603
  capture_probe_events: "buffer_only",
16553
- immediate_client_error_statuses: []
16604
+ immediate_client_error_statuses: [],
16605
+ immediate_client_error_path_rules: []
16554
16606
  },
16555
16607
  balanced: {
16556
16608
  capture_logs: "warning",
16557
16609
  capture_request_events: "failures_only",
16558
16610
  capture_breadcrumbs: "exception_only",
16559
16611
  capture_probe_events: "buffer_only",
16560
- immediate_client_error_statuses: []
16612
+ immediate_client_error_statuses: [],
16613
+ immediate_client_error_path_rules: []
16561
16614
  },
16562
16615
  investigative: {
16563
16616
  capture_logs: "info",
16564
16617
  capture_request_events: "all",
16565
16618
  capture_breadcrumbs: "standalone",
16566
16619
  capture_probe_events: "standalone_when_activated",
16567
- 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: []
16568
16622
  }
16569
16623
  };
16570
16624
  var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
16571
16625
  var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
16572
- var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
16573
- var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
16574
- 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
+ }
16575
16665
  function classifyRequestStatus(input) {
16576
- const { responseStatus, capturePreset, immediateClientErrorStatuses = [] } = input;
16666
+ const { responseStatus, capturePreset, immediateClientErrorStatuses = [], immediateClientErrorPathRules = [] } = input;
16577
16667
  if (responseStatus === null || !Number.isFinite(responseStatus)) {
16578
16668
  return "context_signal";
16579
16669
  }
@@ -16583,6 +16673,14 @@ function classifyRequestStatus(input) {
16583
16673
  if (immediateClientErrorStatuses.includes(responseStatus)) {
16584
16674
  return "incident_signal";
16585
16675
  }
16676
+ if (matchesImmediateClientErrorPathRule({
16677
+ responseStatus,
16678
+ requestPath: input.requestPath,
16679
+ httpMethod: input.httpMethod,
16680
+ immediateClientErrorPathRules
16681
+ })) {
16682
+ return "incident_signal";
16683
+ }
16586
16684
  if (capturePreset === "investigative") {
16587
16685
  return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
16588
16686
  }
@@ -16592,35 +16690,103 @@ function classifyRequestStatus(input) {
16592
16690
  return "context_signal";
16593
16691
  }
16594
16692
  function getRequestAnomalyThreshold(input) {
16595
- const { responseStatus, capturePreset } = input;
16693
+ const { responseStatus } = input;
16596
16694
  if (responseStatus === null || !Number.isFinite(responseStatus) || responseStatus < 400 || responseStatus >= 500) {
16597
16695
  return null;
16598
16696
  }
16599
- if (capturePreset === "minimal") {
16600
- 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;
16601
16704
  }
16602
- if (capturePreset === "investigative") {
16603
- return INVESTIGATIVE_ANOMALY_STATUSES.has(responseStatus) ? {
16604
- minimum_occurrences_5m: 8,
16605
- minimum_ratio_5m_to_1h: 2
16606
- } : 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
+ }
16607
16712
  }
16608
- if (BALANCED_STANDARD_ANOMALY_STATUSES.has(responseStatus)) {
16609
- return {
16610
- minimum_occurrences_5m: 20,
16611
- minimum_ratio_5m_to_1h: 3
16612
- };
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;
16613
16746
  }
16614
- if (BALANCED_HIGH_VOLUME_ANOMALY_STATUSES.has(responseStatus)) {
16615
- return {
16616
- minimum_occurrences_5m: 50,
16617
- minimum_ratio_5m_to_1h: 5
16618
- };
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];
16619
16781
  }
16620
16782
  return null;
16621
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
+ }
16622
16788
 
16623
- // ../../packages/shared-types/src/capture-rules.ts
16789
+ // ../../packages/shared-types/src/capture-rule-schemas.ts
16624
16790
  var CAPTURE_RULE_EVENT_TYPES = [
16625
16791
  "backend_exception",
16626
16792
  "request_event",
@@ -16648,6 +16814,7 @@ var CaptureRuleSampleEventClassSchema = external_exports.enum(CaptureRuleSampleE
16648
16814
  var CaptureRuleRuntimeSchema = external_exports.enum(CAPTURE_RULE_RUNTIME_VALUES);
16649
16815
  var CaptureRuleEventTypeSchema = external_exports.enum(CAPTURE_RULE_EVENT_TYPES);
16650
16816
  var BrowserEventKindSchema = external_exports.enum(["window_error", "resource_error"]);
16817
+ var CaptureRuleClientKindSchema = external_exports.enum(["human", "bot", "unknown"]);
16651
16818
  function normalizeOptionalTrimmedString(value) {
16652
16819
  const trimmed = value?.trim();
16653
16820
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
@@ -16725,6 +16892,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
16725
16892
  message_contains: external_exports.string().min(1).max(500).optional(),
16726
16893
  message_equals: external_exports.string().min(1).max(500).optional(),
16727
16894
  browser_event_kind: BrowserEventKindSchema.optional(),
16895
+ browser_event_opaque: external_exports.boolean().optional(),
16896
+ client_kind: CaptureRuleClientKindSchema.optional(),
16897
+ bot_family: external_exports.string().min(1).max(120).optional(),
16728
16898
  resource_url: UrlMatcherSchema.optional(),
16729
16899
  request_url: UrlMatcherSchema.optional(),
16730
16900
  status_codes: external_exports.array(external_exports.number().int().min(100).max(599)).min(1).optional(),
@@ -16739,6 +16909,7 @@ var CaptureRuleMatcherSchema = external_exports.object({
16739
16909
  const errorName = normalizeOptionalTrimmedString(value.error_name);
16740
16910
  const messageContains = normalizeOptionalTrimmedString(value.message_contains);
16741
16911
  const messageEquals = normalizeOptionalTrimmedString(value.message_equals);
16912
+ const botFamily = normalizeOptionalTrimmedString(value.bot_family);
16742
16913
  const statusCodes = normalizeNumberArray(value.status_codes);
16743
16914
  if (eventTypes !== void 0) {
16744
16915
  normalized.event_types = eventTypes;
@@ -16767,6 +16938,15 @@ var CaptureRuleMatcherSchema = external_exports.object({
16767
16938
  if (value.browser_event_kind !== void 0) {
16768
16939
  normalized.browser_event_kind = value.browser_event_kind;
16769
16940
  }
16941
+ if (value.browser_event_opaque !== void 0) {
16942
+ normalized.browser_event_opaque = value.browser_event_opaque;
16943
+ }
16944
+ if (value.client_kind !== void 0) {
16945
+ normalized.client_kind = value.client_kind;
16946
+ }
16947
+ if (botFamily !== void 0) {
16948
+ normalized.bot_family = botFamily;
16949
+ }
16770
16950
  if (value.resource_url !== void 0) {
16771
16951
  normalized.resource_url = value.resource_url;
16772
16952
  }
@@ -16793,6 +16973,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
16793
16973
  "message_contains",
16794
16974
  "message_equals",
16795
16975
  "browser_event_kind",
16976
+ "browser_event_opaque",
16977
+ "client_kind",
16978
+ "bot_family",
16796
16979
  "resource_url",
16797
16980
  "request_url",
16798
16981
  "status_codes",
@@ -16975,6 +17158,8 @@ var CaptureRulesFileSchema = external_exports.object({
16975
17158
  version: external_exports.literal(1),
16976
17159
  rules: external_exports.array(CaptureRuleSchema)
16977
17160
  });
17161
+
17162
+ // ../../packages/shared-types/src/capture-rule-evaluation.ts
16978
17163
  var CaptureRuleEvaluationUrlSchema = external_exports.object({
16979
17164
  host: external_exports.string().min(1).transform((value) => value.toLowerCase()).optional(),
16980
17165
  path: external_exports.string().min(1).transform((value) => value.startsWith("/") ? value : `/${value}`)
@@ -16990,11 +17175,40 @@ var CaptureRuleEvaluationContextSchema = external_exports.object({
16990
17175
  error_name: external_exports.string().min(1).optional(),
16991
17176
  message: external_exports.string().min(1).optional(),
16992
17177
  browser_event_kind: BrowserEventKindSchema.optional(),
17178
+ browser_event_opaque: external_exports.boolean().optional(),
17179
+ client_kind: CaptureRuleClientKindSchema.optional(),
17180
+ bot_family: external_exports.string().min(1).max(120).optional(),
16993
17181
  resource_url: CaptureRuleEvaluationUrlSchema.optional(),
16994
17182
  request_url: CaptureRuleEvaluationUrlSchema.optional(),
16995
17183
  status_code: external_exports.number().int().min(0).max(599).optional(),
16996
17184
  fingerprint: CaptureRuleFingerprintSchema.optional()
16997
17185
  });
17186
+ function classifyCaptureRuleClientFromUserAgent(userAgent) {
17187
+ if (userAgent === null || userAgent === void 0) {
17188
+ return { client_kind: "unknown" };
17189
+ }
17190
+ const lower = userAgent.toLowerCase();
17191
+ const knownBots = [
17192
+ { family: "Googlebot", markers: ["googlebot", "adsbot-google", "google-inspectiontool"] },
17193
+ { family: "Bingbot", markers: ["bingbot", "msnbot"] },
17194
+ { family: "DuckDuckBot", markers: ["duckduckbot"] },
17195
+ { family: "Applebot", markers: ["applebot"] },
17196
+ { family: "YandexBot", markers: ["yandexbot"] },
17197
+ { family: "Baiduspider", markers: ["baiduspider"] },
17198
+ { family: "FacebookBot", markers: ["facebookexternalhit", "facebot"] },
17199
+ { family: "LinkedInBot", markers: ["linkedinbot"] },
17200
+ { family: "TwitterBot", markers: ["twitterbot"] },
17201
+ { family: "Slackbot", markers: ["slackbot"] }
17202
+ ];
17203
+ const knownBot = knownBots.find((entry) => entry.markers.some((marker) => lower.includes(marker)));
17204
+ if (knownBot !== void 0) {
17205
+ return { client_kind: "bot", bot_family: knownBot.family };
17206
+ }
17207
+ if (["bot", "crawler", "spider", "slurp"].some((marker) => lower.includes(marker))) {
17208
+ return { client_kind: "bot", bot_family: "OtherBot" };
17209
+ }
17210
+ return { client_kind: "human" };
17211
+ }
16998
17212
 
16999
17213
  // ../../packages/shared-types/src/capture-rule-suggestions.ts
17000
17214
  var CaptureRuleSuggestionConfidenceSchema = external_exports.enum(["high", "medium", "low"]);
@@ -17204,6 +17418,12 @@ var BrowserExceptionEventSchema = external_exports.object({
17204
17418
  }).strict().optional(),
17205
17419
  opaque: external_exports.boolean()
17206
17420
  }).strict();
17421
+ var FrontendRejectionReasonSchema = external_exports.object({
17422
+ kind: external_exports.enum(["error", "string", "object", "null", "undefined", "unknown"]),
17423
+ name: external_exports.string().min(1).optional(),
17424
+ message: external_exports.string().min(1).optional(),
17425
+ preview: external_exports.string().min(1).optional()
17426
+ }).strict();
17207
17427
  var FrontendExceptionPayloadSchema = external_exports.object({
17208
17428
  name: external_exports.string().min(1),
17209
17429
  message: external_exports.string().min(1),
@@ -17216,6 +17436,7 @@ var FrontendExceptionPayloadSchema = external_exports.object({
17216
17436
  breadcrumbs: external_exports.array(FrontendExceptionBreadcrumbSchema).optional(),
17217
17437
  device: DeviceInfoSchema.nullable().optional(),
17218
17438
  browser_event: BrowserExceptionEventSchema.optional(),
17439
+ rejection_reason: FrontendRejectionReasonSchema.optional(),
17219
17440
  dom_context: external_exports.object({
17220
17441
  mode: external_exports.literal("lightweight"),
17221
17442
  html_excerpt: external_exports.string().min(1)
@@ -17650,7 +17871,7 @@ function buildSkill() {
17650
17871
  "2. Inspect the incident bundle and reproduction artifact before proposing a fix.",
17651
17872
  "3. Run `debugbundle analyze --type improvement --local` after local processing when you need a deterministic change plan.",
17652
17873
  "4. Apply the narrowest fix, then validate it with the repository test workflow from `.debugbundle/profile.json`.",
17653
- "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id>` or MCP `resolve_incident` so the open queue stays actionable.",
17874
+ "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id> [incident-id ...]` or MCP `resolve_incident` / `resolve_incidents` so the open queue stays actionable.",
17654
17875
  "",
17655
17876
  "## Incident Hygiene",
17656
17877
  "",
@@ -17659,6 +17880,15 @@ function buildSkill() {
17659
17880
  "- Reopen or leave open if the failure is still present, the validation is incomplete, or the incident represents a live unresolved problem.",
17660
17881
  "- If a resolved incident regresses, let the platform move it back to `regressed` through normal incident lifecycle behavior.",
17661
17882
  "",
17883
+ "## Noise Management",
17884
+ "",
17885
+ "When incident evidence shows repeated low-value operational noise rather than a product bug, evaluate whether a scoped capture rule or capture-policy path rule should handle future matches.",
17886
+ "",
17887
+ "- Run `debugbundle capture-rule suggest <incident-id> --json` before creating a manual rule. Apply deterministic suggestions with `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id>` after confirming the scope is safe.",
17888
+ "- Prefer project capture rules for operational noise because they are centralized, auditable, and enforced by ingestion and processing. Use SDK `beforeSend` only for app-owned local policy such as final redaction or events that must never leave the runtime.",
17889
+ "- Scope frontend noise by structured evidence such as service, environment, `browser_event_kind`, `browser_event_opaque`, `client_kind`, `bot_family`, and message fields. Do not broadly demote generic `Unhandled promise rejection` incidents without bot-scoped or otherwise narrow evidence.",
17890
+ "- For expected or intentionally promoted 4xx responses on known routes, use capture-policy client-error path rules instead of promoting all client errors: `debugbundle capture-policy set --client-error-path-rule <status=/path/*@GET>`.",
17891
+ "",
17662
17892
  "## Profile Validation",
17663
17893
  "",
17664
17894
  "Use this task after setup or whenever architecture changes make the static profile stale.",
@@ -17721,10 +17951,21 @@ function buildCliReference() {
17721
17951
  "- `debugbundle explain <incident-id> [--source <local|cloud>] [--json]`",
17722
17952
  "- `debugbundle bundle <incident-id> [--source <local|cloud>] [--json]`",
17723
17953
  "- `debugbundle reproduce <incident-id> [--source <local|cloud>] [--json]`",
17724
- "- `debugbundle resolve <incident-id> [--source <local|cloud>] [--json]`",
17725
- "- `debugbundle reopen <incident-id> [--source <local|cloud>] [--json]`",
17954
+ "- `debugbundle resolve <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
17955
+ "- `debugbundle reopen <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
17726
17956
  "- `debugbundle analyze --type improvement --local`",
17727
17957
  "",
17958
+ "## Noise Management",
17959
+ "",
17960
+ "- `debugbundle capture-rule suggest <incident-id> [--auth-file <path>] [--json]`",
17961
+ "- `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id> [--name <name>] [--expires-at <ISO8601>] [--auth-file <path>] [--json]`",
17962
+ "- `debugbundle capture-rule list --project-id <id> [--auth-file <path>] [--json]`",
17963
+ "- `debugbundle capture-rule create --project-id <id> --name <name> --action <demote|sample|drop> --matcher-json <json> [--auth-file <path>] [--json]`",
17964
+ "- `debugbundle capture-policy get [--project <id>] [--json]`",
17965
+ "- `debugbundle capture-policy set [--project <id>] --client-error-path-rule <404=/path/*@GET,POST> [--json]`",
17966
+ "",
17967
+ "Use capture-rule suggestions for repeated operational noise after inspecting an incident bundle. Use capture-policy client-error path rules for route-scoped 4xx incidents instead of promoting all client errors.",
17968
+ "",
17728
17969
  "## Operational Paths",
17729
17970
  "",
17730
17971
  "- `.debugbundle/profile.json` \u2014 committed project map and agent validation state",
@@ -17755,7 +17996,7 @@ function buildCliReference() {
17755
17996
  "```bash",
17756
17997
  "debugbundle incidents --status open --json \\",
17757
17998
  ` | jq -r '.incidents[] | select(.title | test("smoke test|dogfood|verification|synthetic"; "i")) | .incident_id' \\`,
17758
- " | xargs -n1 debugbundle resolve",
17999
+ " | xargs debugbundle resolve",
17759
18000
  "```",
17760
18001
  ""
17761
18002
  ].join("\n");
@@ -17774,19 +18015,28 @@ function buildMcpReference() {
17774
18015
  "- `get_incident_context` \u2014 fetch deterministic explanation context for triage.",
17775
18016
  "- `get_bundle` \u2014 fetch the full debug bundle before proposing a fix.",
17776
18017
  "- `get_reproduction` \u2014 fetch reproduction guidance before editing code.",
17777
- "- `resolve_incident` / `reopen_incident` \u2014 update lifecycle state after validation.",
18018
+ "- `resolve_incident` / `resolve_incidents` / `reopen_incident` / `reopen_incidents` \u2014 update lifecycle state after validation.",
17778
18019
  "- `analyze` \u2014 run local agent-oriented analysis from local bundles and skill schemas.",
17779
18020
  "",
17780
18021
  "- Prefer bundle retrieval tools before reading raw repository files.",
17781
18022
  "- Use MCP bundle access when the current issue originated in production.",
17782
- "- Resolve fixed or intentionally generated incidents with `resolve_incident` so open incidents stay actionable.",
18023
+ "- Resolve fixed or intentionally generated incidents with `resolve_incident` or `resolve_incidents` so open incidents stay actionable.",
17783
18024
  "- Fall back to local CLI processing when the project is local-only.",
17784
18025
  "",
18026
+ "## Noise and Capture Policy Tools",
18027
+ "",
18028
+ "- `suggest_capture_rules_from_incident` \u2014 generate deterministic capture-rule suggestions from an incident bundle.",
18029
+ "- `create_capture_rule_from_incident_suggestion` \u2014 apply a confirmed suggestion.",
18030
+ "- `list_capture_rules`, `create_capture_rule`, `update_capture_rule`, `delete_capture_rule` \u2014 manage project capture rules.",
18031
+ "- `get_capture_policy`, `update_capture_policy` \u2014 review or update capture policy, including path-scoped client-error incident rules.",
18032
+ "",
18033
+ "Use these tools for repeated low-value operational noise only after inspecting incident evidence. Keep frontend suppression scoped by structured browser and client signals, and use path-scoped capture policy for known 4xx routes.",
18034
+ "",
17785
18035
  "## Smoke-Test Cleanup Recipe",
17786
18036
  "",
17787
18037
  '1. Call `list_incidents` with `status: "open"`.',
17788
18038
  "2. Filter incidents whose titles show they were intentionally generated for smoke, dogfood, verification, or synthetic checks.",
17789
- "3. Call `resolve_incident` for each verified synthetic incident.",
18039
+ "3. Call `resolve_incidents` for verified synthetic incidents, or `resolve_incident` for a single incident.",
17790
18040
  "4. Call `list_incidents` again and confirm the open queue only contains actionable failures.",
17791
18041
  ""
17792
18042
  ].join("\n");
@@ -17906,6 +18156,16 @@ function buildSkillEvals() {
17906
18156
  "Leave unresolved incidents open when the failure is still live or unverified."
17907
18157
  ]
17908
18158
  },
18159
+ {
18160
+ name: "noise_management_guidance",
18161
+ prompt: "The same low-value frontend incident keeps reopening. Confirm the skill tells the agent how to evaluate operational noise without hiding real bugs.",
18162
+ expected_behavior: [
18163
+ "Inspect incident evidence before creating a rule.",
18164
+ "Use capture-rule suggestions for repeated operational noise.",
18165
+ "Keep generic frontend suppression narrow with structured browser or bot signals.",
18166
+ "Use capture-policy path rules for known route-scoped 4xx incidents."
18167
+ ]
18168
+ },
17909
18169
  {
17910
18170
  name: "artifact_path_discovery",
17911
18171
  prompt: "The user reports an unknown local runtime error. Confirm the skill tells the agent which DebugBundle paths and commands to inspect first.",
@@ -19236,7 +19496,7 @@ function getRequestResponseStatus(payload) {
19236
19496
  const status = payload?.["response_status"];
19237
19497
  return typeof status === "number" && Number.isFinite(status) ? status : null;
19238
19498
  }
19239
- function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal", immediateClientErrorStatuses = []) {
19499
+ function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal", immediateClientErrorStatuses = [], immediateClientErrorPathRules = []) {
19240
19500
  switch (eventType) {
19241
19501
  case "backend_exception":
19242
19502
  case "frontend_exception":
@@ -19248,7 +19508,14 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload, captureP
19248
19508
  return "context_signal";
19249
19509
  case "request_event": {
19250
19510
  const responseStatus = getRequestResponseStatus(payload);
19251
- return classifyRequestStatus({ responseStatus, capturePreset, immediateClientErrorStatuses });
19511
+ return classifyRequestStatus({
19512
+ responseStatus,
19513
+ requestPath: payload?.["path"],
19514
+ httpMethod: payload?.["method"],
19515
+ capturePreset,
19516
+ immediateClientErrorStatuses,
19517
+ immediateClientErrorPathRules
19518
+ });
19252
19519
  }
19253
19520
  case "frontend_breadcrumb":
19254
19521
  case "deploy_metadata":
@@ -19955,6 +20222,33 @@ function buildRedactionRecord(bundleBody) {
19955
20222
  notes: readString(redaction["notes"])
19956
20223
  };
19957
20224
  }
20225
+ function buildBrowserSignalRecord(bundleBody) {
20226
+ const bundle = isRecord(bundleBody) ? bundleBody : {};
20227
+ const context = isRecord(bundle["context"]) ? bundle["context"] : {};
20228
+ const frontend = isRecord(context["frontend"]) ? context["frontend"] : {};
20229
+ const exceptions = Array.isArray(frontend["exceptions"]) ? frontend["exceptions"] : [];
20230
+ let exception;
20231
+ for (let index = exceptions.length - 1; index >= 0; index -= 1) {
20232
+ const candidate = exceptions[index];
20233
+ if (isRecord(candidate) && isRecord(candidate["browser_event"])) {
20234
+ exception = candidate;
20235
+ break;
20236
+ }
20237
+ }
20238
+ const browserEvent = isRecord(exception) && isRecord(exception["browser_event"]) ? exception["browser_event"] : null;
20239
+ const device = isRecord(context["device"]) ? context["device"] : {};
20240
+ const client = classifyCaptureRuleClientFromUserAgent(readString(device["user_agent"]) ?? void 0);
20241
+ if (browserEvent === null && client.client_kind === "unknown") {
20242
+ return null;
20243
+ }
20244
+ return {
20245
+ browser_event_kind: readString(browserEvent?.["kind"]),
20246
+ browser_event_opaque: readBoolean(browserEvent?.["opaque"]),
20247
+ browser_event_message: readString(browserEvent?.["message"]),
20248
+ client_kind: client.client_kind,
20249
+ bot_family: client.bot_family ?? null
20250
+ };
20251
+ }
19958
20252
  function buildVisibilityRecord(input) {
19959
20253
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
19960
20254
  const matchedFields = input.incident.matched_fields.length === 0 ? "none" : input.incident.matched_fields.join(", ");
@@ -19992,6 +20286,14 @@ function buildSuggestedNextChecks(input) {
19992
20286
  if (input.deploy.regression_window === true || input.incident.status === "regressed") {
19993
20287
  suggestions.push("Compare this incident against the most recent deploy and recent regressions.");
19994
20288
  }
20289
+ if (input.browserSignal?.browser_event_opaque === true) {
20290
+ suggestions.push("Treat the browser event as opaque; inspect CSP, cross-origin scripts, resource loading, and framework error boundaries before changing application code.");
20291
+ }
20292
+ if (input.browserSignal?.client_kind === "bot") {
20293
+ suggestions.push(
20294
+ `Review whether ${input.browserSignal.bot_family ?? "bot"} traffic is operational noise before applying a bot-scoped capture rule.`
20295
+ );
20296
+ }
19995
20297
  if (input.reproduction.status === "pending") {
19996
20298
  suggestions.push("Recheck reproduction guidance after the reproduction artifact is ready.");
19997
20299
  }
@@ -20012,6 +20314,7 @@ function buildIncidentContextRecord(input) {
20012
20314
  primarySignal
20013
20315
  });
20014
20316
  const redaction = buildRedactionRecord(bundleBody);
20317
+ const browserSignal = buildBrowserSignalRecord(bundleBody);
20015
20318
  return {
20016
20319
  incident: input.incident,
20017
20320
  incident_reason: incidentReason,
@@ -20027,13 +20330,15 @@ function buildIncidentContextRecord(input) {
20027
20330
  },
20028
20331
  visibility,
20029
20332
  redaction,
20333
+ browser_signal: browserSignal,
20030
20334
  suggested_next_checks: buildSuggestedNextChecks({
20031
20335
  incident: input.incident,
20032
20336
  bundle: input.bundle,
20033
20337
  reproduction: input.reproduction,
20034
20338
  logs,
20035
20339
  primarySignal,
20036
- deploy
20340
+ deploy,
20341
+ browserSignal
20037
20342
  })
20038
20343
  };
20039
20344
  }
@@ -20445,6 +20750,7 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
20445
20750
  capture_breadcrumbs text,
20446
20751
  capture_probe_events text,
20447
20752
  immediate_client_error_statuses jsonb,
20753
+ immediate_client_error_path_rules jsonb,
20448
20754
  updated_at timestamptz NOT NULL DEFAULT now()
20449
20755
  )
20450
20756
  `,
@@ -21800,6 +22106,13 @@ var STORAGE_SCHEMA_MIGRATIONS = [
21800
22106
  ON plan_cleanup_tasks (completed_at, next_attempt_at, created_at)
21801
22107
  `
21802
22108
  ]
22109
+ }),
22110
+ defineStorageSchemaMigration({
22111
+ id: "202606080001_add_capture_policy_client_error_path_rules",
22112
+ description: "Add path-scoped client error incident promotion rules to capture policies.",
22113
+ statements: [
22114
+ "ALTER TABLE capture_policies ADD COLUMN IF NOT EXISTS immediate_client_error_path_rules jsonb"
22115
+ ]
21803
22116
  })
21804
22117
  ];
21805
22118
 
@@ -23080,6 +23393,15 @@ function collectRequestAnomalyAggregates(batches, capturePreset) {
23080
23393
  if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
23081
23394
  continue;
23082
23395
  }
23396
+ if (isLowValueExternalProbeRequestFailure404({
23397
+ httpMethod: method,
23398
+ requestPath: event.payload.path,
23399
+ routeTemplate,
23400
+ responseStatus,
23401
+ headers: event.payload.headers
23402
+ })) {
23403
+ continue;
23404
+ }
23083
23405
  const projectId = requireProjectId(event);
23084
23406
  const incidentFingerprint = buildRequestAnomalyFingerprint({
23085
23407
  projectId,
@@ -28486,7 +28808,7 @@ var zodToJsonSchema = (schema, options) => {
28486
28808
  var package_default = {
28487
28809
  name: "@debugbundle/mcp",
28488
28810
  mcpName: "com.debugbundle/mcp",
28489
- version: "1.1.1",
28811
+ version: "1.3.0",
28490
28812
  private: false,
28491
28813
  description: "Model Context Protocol server for DebugBundle",
28492
28814
  license: "AGPL-3.0-only",
@@ -29247,7 +29569,12 @@ var MCP_TOOL_CATALOG = [
29247
29569
  capture_request_events: external_exports.string().nullable().optional(),
29248
29570
  capture_breadcrumbs: external_exports.string().nullable().optional(),
29249
29571
  capture_probe_events: external_exports.string().nullable().optional(),
29250
- immediate_client_error_statuses: external_exports.array(external_exports.number().int().min(400).max(499)).nullable().optional()
29572
+ immediate_client_error_statuses: external_exports.array(external_exports.number().int().min(400).max(499)).nullable().optional(),
29573
+ immediate_client_error_path_rules: external_exports.array(external_exports.object({
29574
+ status_code: external_exports.number().int().min(400).max(499),
29575
+ path_pattern: external_exports.string(),
29576
+ methods: external_exports.array(external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"])).optional()
29577
+ })).nullable().optional()
29251
29578
  })
29252
29579
  })
29253
29580
  },
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.1",
4
+ "version": "1.3.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.1",
11
+ "version": "1.3.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.1",
17
+ "version": "1.3.0",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },