@kaminari-ad/mcp 0.1.0 → 0.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/bin.js CHANGED
@@ -6,6 +6,7 @@ import createClient from 'openapi-fetch';
6
6
  import { fetch } from 'undici';
7
7
  import { stdTimeFunctions, pino } from 'pino';
8
8
  import pinoPretty from 'pino-pretty';
9
+ import { ListResourcesRequestSchema, ListPromptsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
9
10
  import process4 from 'process';
10
11
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
12
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -31,7 +32,7 @@ var NAME, VERSION;
31
32
  var init_version = __esm({
32
33
  "src/shared/version.ts"() {
33
34
  NAME = "@kaminari-ad/mcp";
34
- VERSION = "0.1.0";
35
+ VERSION = "0.2.0";
35
36
  }
36
37
  });
37
38
  var REDACTED, BEARER_HASH_PREFIX_LEN, MAX_HEADER_LEN, BearerToken;
@@ -172,7 +173,10 @@ function toApiError(status2, parsed, retryAfterHeader) {
172
173
  return code === void 0 ? { kind: "forbidden", detail: message } : { kind: "forbidden", detail: message, code };
173
174
  }
174
175
  if (status2 === 404) return { kind: "not-found", detail: message };
175
- if (status2 === 422 || status2 === 400) return { kind: "invalid-input", detail: message };
176
+ if (status2 === 422 || status2 === 400) {
177
+ const code = errCode(parsed);
178
+ return code === void 0 ? { kind: "invalid-input", detail: message } : { kind: "invalid-input", detail: message, code };
179
+ }
176
180
  if (status2 === 429) {
177
181
  const ra = Array.isArray(retryAfterHeader) ? retryAfterHeader[0] : retryAfterHeader;
178
182
  const retryMs = ra === void 0 ? void 0 : Number.parseInt(ra, 10) * 1e3;
@@ -1253,6 +1257,21 @@ var init_parse_campaign_group = __esm({
1253
1257
  parseCampaignGroupArray = (raw) => parseArrayOrItemsWithSchema(CampaignGroupSchema, raw, "campaign-groups");
1254
1258
  }
1255
1259
  });
1260
+ var CampaignPickerSchema, CampaignPickerArraySchema, parseCampaignPickerArray;
1261
+ var init_parse_campaign_picker = __esm({
1262
+ "src/infrastructure/api/parsers/parse-campaign-picker.ts"() {
1263
+ init_zod_schemas();
1264
+ init_parse_with_schema();
1265
+ CampaignPickerSchema = schemas.CampaignPickerItem.pick({
1266
+ id: true,
1267
+ name: true,
1268
+ group_id: true,
1269
+ is_archived: true
1270
+ }).strip();
1271
+ CampaignPickerArraySchema = z.array(CampaignPickerSchema);
1272
+ parseCampaignPickerArray = (raw) => parseWithSchema(CampaignPickerArraySchema, raw, "campaigns-picker");
1273
+ }
1274
+ });
1256
1275
 
1257
1276
  // src/infrastructure/api/parsers/shared.ts
1258
1277
  function isStringRecord(v) {
@@ -1282,7 +1301,7 @@ var init_parse_count_envelope = __esm({
1282
1301
  });
1283
1302
 
1284
1303
  // src/infrastructure/api/parsers/parse-custom-rule.ts
1285
- var CustomRuleSchema, parseCustomRule, parseCustomRuleArray;
1304
+ var CustomRuleSchema, parseCustomRule;
1286
1305
  var init_parse_custom_rule = __esm({
1287
1306
  "src/infrastructure/api/parsers/parse-custom-rule.ts"() {
1288
1307
  init_zod_schemas();
@@ -1299,7 +1318,27 @@ var init_parse_custom_rule = __esm({
1299
1318
  created_at: true
1300
1319
  }).strip();
1301
1320
  parseCustomRule = (raw) => parseWithSchema(CustomRuleSchema, raw, "custom-rule");
1302
- parseCustomRuleArray = (raw) => parseArrayOrItemsWithSchema(CustomRuleSchema, raw, "custom-rules");
1321
+ }
1322
+ });
1323
+
1324
+ // src/infrastructure/api/parsers/parse-custom-rule-page.ts
1325
+ var CustomRuleSchema2, parseCustomRulePage;
1326
+ var init_parse_custom_rule_page = __esm({
1327
+ "src/infrastructure/api/parsers/parse-custom-rule-page.ts"() {
1328
+ init_zod_schemas();
1329
+ init_parse_with_schema();
1330
+ CustomRuleSchema2 = schemas.CustomRuleResponse.pick({
1331
+ id: true,
1332
+ organization_id: true,
1333
+ name: true,
1334
+ tag_slug: true,
1335
+ rule_type: true,
1336
+ config: true,
1337
+ target: true,
1338
+ is_active: true,
1339
+ created_at: true
1340
+ }).strip();
1341
+ parseCustomRulePage = (raw) => parsePagedWithItemSchema(CustomRuleSchema2, raw, "custom-rules");
1303
1342
  }
1304
1343
  });
1305
1344
 
@@ -1359,7 +1398,7 @@ function parseArrayOf(parseItem) {
1359
1398
  return ok(out);
1360
1399
  };
1361
1400
  }
1362
- var OrgSchema, parseOrg, UserSchema, parseUser, RoleSchema, parseRole, ApiKeyCreatedSchema, parseApiKeyCreated, ScanTagSchema, parseScanTag, TagDetailSchema, parseTagDetail, RuleTestSchema, parseRuleTest, AlertStatsSchema, parseAlertStats, UsageSchema, parseUsage, UsageSummarySchema, parseUsageSummary, BalanceTxSchema, parseBalanceTx, InvoiceSchema, parseInvoice, WebhookDeliverySchema, parseWebhookDelivery, EventCatalogSchema, parseEventCatalog, AlertDestinationSchema, parseAlertDestination, CampaignOverridesSchema, parseCampaignAlertOverrides, BulkReplaySchema, parseBulkReplay, GroupActionSchema, parseGroupAction;
1401
+ var OrgSchema, parseOrg, UserSchema, parseUser, RoleSchema, parseRole, ApiKeyCreatedSchema, parseApiKeyCreated, ScanTagSchema, parseScanTag, RuleTestSchema, parseRuleTest, AlertStatsSchema, parseAlertStats, UsageSchema, parseUsage, UsageSummarySchema, parseUsageSummary, BalanceTxSchema, parseBalanceTx, InvoiceSchema, parseInvoice, WebhookDeliverySchema, parseWebhookDelivery, EventCatalogSchema, parseEventCatalog, AlertDestinationSchema, parseAlertDestination, CampaignOverridesSchema, parseCampaignAlertOverrides, BulkReplaySchema, parseBulkReplay, GroupActionSchema, parseGroupAction;
1363
1402
  var init_parse_generic = __esm({
1364
1403
  "src/infrastructure/api/parsers/parse-generic.ts"() {
1365
1404
  init_zod_schemas();
@@ -1411,20 +1450,6 @@ var init_parse_generic = __esm({
1411
1450
  created_at: true
1412
1451
  }).strip();
1413
1452
  parseScanTag = (raw) => parseWithSchema(ScanTagSchema, raw, "scan-tag");
1414
- TagDetailSchema = schemas.TagDefinitionWithStatsResponse.pick({
1415
- slug: true,
1416
- category: true,
1417
- source: true,
1418
- display_name: true,
1419
- description: true,
1420
- severity: true,
1421
- is_system: true,
1422
- organization_id: true,
1423
- show_in_public_report: true,
1424
- scans_count: true,
1425
- rules_count: true
1426
- }).strip();
1427
- parseTagDetail = (raw) => parseWithSchema(TagDetailSchema, raw, "tag-definition");
1428
1453
  RuleTestSchema = schemas.RuleTestResponse.pick({
1429
1454
  matched: true,
1430
1455
  elapsed_ms: true,
@@ -1567,7 +1592,7 @@ var init_parse_geo_list = __esm({
1567
1592
  });
1568
1593
 
1569
1594
  // src/infrastructure/api/parsers/parse-policy-set.ts
1570
- var PolicySetSchema, PolicySetListItemSchema, parsePolicySet, parsePolicySetList;
1595
+ var PolicySetSchema, parsePolicySet;
1571
1596
  var init_parse_policy_set = __esm({
1572
1597
  "src/infrastructure/api/parsers/parse-policy-set.ts"() {
1573
1598
  init_zod_schemas();
@@ -1582,6 +1607,16 @@ var init_parse_policy_set = __esm({
1582
1607
  entries: true,
1583
1608
  created_at: true
1584
1609
  }).strip();
1610
+ parsePolicySet = (raw) => parseWithSchema(PolicySetSchema, raw, "policy-set");
1611
+ }
1612
+ });
1613
+
1614
+ // src/infrastructure/api/parsers/parse-policy-set-page.ts
1615
+ var PolicySetListItemSchema, parsePolicySetPage;
1616
+ var init_parse_policy_set_page = __esm({
1617
+ "src/infrastructure/api/parsers/parse-policy-set-page.ts"() {
1618
+ init_zod_schemas();
1619
+ init_parse_with_schema();
1585
1620
  PolicySetListItemSchema = schemas.PolicySetListItem.pick({
1586
1621
  id: true,
1587
1622
  name: true,
@@ -1591,8 +1626,7 @@ var init_parse_policy_set = __esm({
1591
1626
  is_approved: true,
1592
1627
  created_at: true
1593
1628
  }).strip();
1594
- parsePolicySet = (raw) => parseWithSchema(PolicySetSchema, raw, "policy-set");
1595
- parsePolicySetList = (raw) => parseArrayOrItemsWithSchema(PolicySetListItemSchema, raw, "policy-sets");
1629
+ parsePolicySetPage = (raw) => parsePagedWithItemSchema(PolicySetListItemSchema, raw, "policy-sets");
1596
1630
  }
1597
1631
  });
1598
1632
 
@@ -1617,6 +1651,25 @@ var init_parse_run = __esm({
1617
1651
  parseRun = (raw) => parseWithSchema(RunSchema, raw, "run");
1618
1652
  }
1619
1653
  });
1654
+
1655
+ // src/infrastructure/api/parsers/parse-run-scan-page.ts
1656
+ var ScanTileSchema, parseRunScanPage;
1657
+ var init_parse_run_scan_page = __esm({
1658
+ "src/infrastructure/api/parsers/parse-run-scan-page.ts"() {
1659
+ init_zod_schemas();
1660
+ init_parse_with_schema();
1661
+ ScanTileSchema = schemas.ScanTileResponse.pick({
1662
+ id: true,
1663
+ country_code: true,
1664
+ status: true,
1665
+ offer_url: true,
1666
+ screenshot_url: true,
1667
+ elapsed_ms: true,
1668
+ error: true
1669
+ }).strip();
1670
+ parseRunScanPage = (raw) => parsePagedWithItemSchema(ScanTileSchema, raw, "run-scans");
1671
+ }
1672
+ });
1620
1673
  var ScanSchema, ScanArraySchema, parseScan, parseScanArray;
1621
1674
  var init_parse_scan = __esm({
1622
1675
  "src/infrastructure/api/parsers/parse-scan.ts"() {
@@ -1670,7 +1723,7 @@ var init_parse_scan_page = __esm({
1670
1723
  parseScanPage = (raw) => parsePagedWithItemSchema(ScanBriefSchema, raw, "scans");
1671
1724
  }
1672
1725
  });
1673
- var TagDefinitionSchema, TagDefinitionArraySchema, parseTagDefinitionArray;
1726
+ var TagDefinitionSchema, TagDefinitionArraySchema, LinkedRuleSchema, TagDetailSchema, parseTagDefinitionArray, parseTagDetail;
1674
1727
  var init_parse_tag = __esm({
1675
1728
  "src/infrastructure/api/parsers/parse-tag.ts"() {
1676
1729
  init_zod_schemas();
@@ -1689,7 +1742,26 @@ var init_parse_tag = __esm({
1689
1742
  rules_count: true
1690
1743
  }).strip();
1691
1744
  TagDefinitionArraySchema = z.array(TagDefinitionSchema);
1745
+ LinkedRuleSchema = schemas.LinkedRuleResponse.pick({
1746
+ id: true,
1747
+ name: true,
1748
+ is_active: true
1749
+ }).strip();
1750
+ TagDetailSchema = schemas.TagDefinitionDetailResponse.pick({
1751
+ slug: true,
1752
+ category: true,
1753
+ source: true,
1754
+ display_name: true,
1755
+ description: true,
1756
+ severity: true,
1757
+ is_system: true,
1758
+ organization_id: true,
1759
+ show_in_public_report: true,
1760
+ scans_count: true,
1761
+ rules_count: true
1762
+ }).extend({ linked_rules: z.array(LinkedRuleSchema).optional() }).strip();
1692
1763
  parseTagDefinitionArray = (raw) => parseWithSchema(TagDefinitionArraySchema, raw, "tag-definitions");
1764
+ parseTagDetail = (raw) => parseWithSchema(TagDetailSchema, raw, "tag-definition");
1693
1765
  }
1694
1766
  });
1695
1767
  var WebhookSchema, WebhookListSchema, WebhookCreatedSchema, TestWebhookResponseSchema, parseWebhook, parseWebhookList, parseWebhookCreated, parseTestWebhookResponse;
@@ -1966,6 +2038,9 @@ function createHttpApiGateway(config) {
1966
2038
  parsePageOf(parseRun)
1967
2039
  );
1968
2040
  },
2041
+ async listCampaignsPicker() {
2042
+ return call("GET", "/api/v1/campaigns/picker", {}, parseCampaignPickerArray);
2043
+ },
1969
2044
  // ── Runs ──────────────────────────────────────────────────────
1970
2045
  async getRun(id) {
1971
2046
  return call("GET", "/api/v1/runs/{run_id}", { params: { path: { run_id: id } } }, parseRun);
@@ -1983,7 +2058,7 @@ function createHttpApiGateway(config) {
1983
2058
  "GET",
1984
2059
  "/api/v1/runs/{run_id}/scans",
1985
2060
  { params: { path: { run_id: runId }, query: filters } },
1986
- parseScanPage
2061
+ parseRunScanPage
1987
2062
  );
1988
2063
  },
1989
2064
  // ── Campaign groups ───────────────────────────────────────────
@@ -2096,7 +2171,7 @@ function createHttpApiGateway(config) {
2096
2171
  "GET",
2097
2172
  "/api/v1/custom-rules",
2098
2173
  { params: { query: filters } },
2099
- parseCustomRuleArray
2174
+ parseCustomRulePage
2100
2175
  );
2101
2176
  },
2102
2177
  async getCustomRule(id) {
@@ -2130,8 +2205,8 @@ function createHttpApiGateway(config) {
2130
2205
  return call("POST", "/api/v1/custom-rules/test", { body }, parseRuleTest);
2131
2206
  },
2132
2207
  // ── Policy sets ───────────────────────────────────────────────
2133
- async listPolicySets() {
2134
- return call("GET", "/api/v1/policy-sets", {}, parsePolicySetList);
2208
+ async listPolicySets(filters) {
2209
+ return call("GET", "/api/v1/policy-sets", { params: { query: filters } }, parsePolicySetPage);
2135
2210
  },
2136
2211
  async getPolicySet(id) {
2137
2212
  return call(
@@ -2341,14 +2416,18 @@ var init_http_api_gateway = __esm({
2341
2416
  init_parse_billing_summary();
2342
2417
  init_parse_campaign();
2343
2418
  init_parse_campaign_group();
2419
+ init_parse_campaign_picker();
2344
2420
  init_parse_count_envelope();
2345
2421
  init_parse_custom_rule();
2422
+ init_parse_custom_rule_page();
2346
2423
  init_parse_empty();
2347
2424
  init_parse_emulator();
2348
2425
  init_parse_generic();
2349
2426
  init_parse_geo_list();
2350
2427
  init_parse_policy_set();
2428
+ init_parse_policy_set_page();
2351
2429
  init_parse_run();
2430
+ init_parse_run_scan_page();
2352
2431
  init_parse_scan();
2353
2432
  init_parse_scan_page();
2354
2433
  init_parse_tag();
@@ -2415,6 +2494,21 @@ var init_pino_logger = __esm({
2415
2494
  ];
2416
2495
  }
2417
2496
  });
2497
+ function declareEmptyResourcesAndPrompts(server) {
2498
+ server.server.registerCapabilities({ resources: {}, prompts: {} });
2499
+ server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
2500
+ await Promise.resolve();
2501
+ return { resources: [] };
2502
+ });
2503
+ server.server.setRequestHandler(ListPromptsRequestSchema, async () => {
2504
+ await Promise.resolve();
2505
+ return { prompts: [] };
2506
+ });
2507
+ }
2508
+ var init_declare_empty_caps = __esm({
2509
+ "src/presentation/shared/declare-empty-caps.ts"() {
2510
+ }
2511
+ });
2418
2512
 
2419
2513
  // src/application/services/api-error-mapper.ts
2420
2514
  function mapApiError(apiError) {
@@ -2427,8 +2521,12 @@ function mapApiError(apiError) {
2427
2521
  return { kind: "not-found", message: apiError.detail };
2428
2522
  case "rate-limited":
2429
2523
  return apiError.retryAfterMs === void 0 ? { kind: "rate-limited", message: apiError.detail } : { kind: "rate-limited", message: apiError.detail, retryAfterMs: apiError.retryAfterMs };
2430
- case "invalid-input":
2431
- return apiError.fieldErrors === void 0 ? { kind: "invalid-input", message: apiError.detail } : { kind: "invalid-input", message: apiError.detail, fieldErrors: apiError.fieldErrors };
2524
+ case "invalid-input": {
2525
+ const out = { kind: "invalid-input", message: apiError.detail };
2526
+ if (apiError.fieldErrors !== void 0) out.fieldErrors = apiError.fieldErrors;
2527
+ if (apiError.code !== void 0) out.code = apiError.code;
2528
+ return out;
2529
+ }
2432
2530
  case "upstream":
2433
2531
  return apiError.status === void 0 ? { kind: "upstream", message: apiError.detail } : { kind: "upstream", message: apiError.detail, status: apiError.status };
2434
2532
  }
@@ -3587,6 +3685,31 @@ var init_list_campaigns_tool = __esm({
3587
3685
  };
3588
3686
  }
3589
3687
  });
3688
+ var ListCampaignsPickerInputShape, listCampaignsPickerTool;
3689
+ var init_list_campaigns_picker_tool = __esm({
3690
+ "src/application/tools/campaigns/list-campaigns-picker.tool.ts"() {
3691
+ init_result();
3692
+ init_api_error_mapper();
3693
+ ListCampaignsPickerInputShape = {};
3694
+ listCampaignsPickerTool = {
3695
+ name: "list_campaigns_picker",
3696
+ description: "Slim per-row campaign list for selection UIs \u2014 id, name, group_id, is_archived. Cheaper than `list_campaigns` for orgs with thousands of campaigns. Use `get_campaign(id)` after a selection to fetch full details.",
3697
+ annotations: {
3698
+ title: "List Campaigns (Picker)",
3699
+ readOnlyHint: true,
3700
+ destructiveHint: false,
3701
+ idempotentHint: true,
3702
+ openWorldHint: false
3703
+ },
3704
+ inputSchema: z.object(ListCampaignsPickerInputShape),
3705
+ handler: async (_input, ctx) => {
3706
+ const result = await ctx.api.listCampaignsPicker();
3707
+ if (result.isErr()) return err(mapApiError(result.error));
3708
+ return ok(result.value);
3709
+ }
3710
+ };
3711
+ }
3712
+ });
3590
3713
  var RunCampaignInputShape, runCampaignTool;
3591
3714
  var init_run_campaign_tool = __esm({
3592
3715
  "src/application/tools/campaigns/run-campaign.tool.ts"() {
@@ -3690,7 +3813,9 @@ var init_create_custom_rule_tool = __esm({
3690
3813
  tag_slug: z.string().max(100).optional().describe("Tag slug to assign on match. Empty = create-only (advanced)."),
3691
3814
  rule_type: z.string().max(50).describe("Rule engine: regex | substring | iab_category | etc. (API validates)."),
3692
3815
  config: z.record(z.unknown()).describe("Rule-type-specific configuration object. Shape depends on rule_type."),
3693
- target: z.string().max(30).optional().describe("Where to apply: page | offer_url | html. Default: page.")
3816
+ target: z.string().max(30).optional().describe(
3817
+ "Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
3818
+ )
3694
3819
  };
3695
3820
  createCustomRuleTool = {
3696
3821
  name: "create_custom_rule",
@@ -3781,7 +3906,7 @@ var init_list_custom_rules_tool = __esm({
3781
3906
  };
3782
3907
  listCustomRulesTool = {
3783
3908
  name: "list_custom_rules",
3784
- description: "List the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag.",
3909
+ description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed.",
3785
3910
  annotations: {
3786
3911
  title: "List Custom Rules",
3787
3912
  readOnlyHint: true,
@@ -3793,7 +3918,7 @@ var init_list_custom_rules_tool = __esm({
3793
3918
  handler: async (input, ctx) => {
3794
3919
  const result = await ctx.api.listCustomRules({ page: input.page, limit: input.limit });
3795
3920
  if (result.isErr()) return err(mapApiError(result.error));
3796
- return ok({ items: result.value, total: result.value.length });
3921
+ return ok(result.value);
3797
3922
  }
3798
3923
  };
3799
3924
  }
@@ -3806,7 +3931,9 @@ var init_test_custom_rule_tool = __esm({
3806
3931
  TestCustomRuleInputShape = {
3807
3932
  rule_type: z.string().max(50).describe("Rule engine type (regex, substring, ...)."),
3808
3933
  config: z.record(z.unknown()).describe("Rule-type-specific config to test."),
3809
- target: z.string().max(30).describe("Where to apply the rule: page / offer_url / html."),
3934
+ target: z.string().max(30).describe(
3935
+ "Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
3936
+ ),
3810
3937
  scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
3811
3938
  };
3812
3939
  testCustomRuleTool = {
@@ -3843,7 +3970,9 @@ var init_update_custom_rule_tool = __esm({
3843
3970
  name: z.string().min(1).max(200).optional().describe("New display name."),
3844
3971
  tag_slug: z.string().max(100).optional().describe("New tag slug to assign on match."),
3845
3972
  config: z.record(z.unknown()).optional().describe("New rule-type-specific config object."),
3846
- target: z.string().max(30).optional().describe("Where to apply: page / offer_url / html."),
3973
+ target: z.string().max(30).optional().describe(
3974
+ "Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
3975
+ ),
3847
3976
  is_active: z.boolean().optional().describe("Enable/disable the rule.")
3848
3977
  };
3849
3978
  updateCustomRuleTool = {
@@ -3998,7 +4127,7 @@ var init_delete_policy_set_tool = __esm({
3998
4127
  };
3999
4128
  deletePolicySetTool = {
4000
4129
  name: "delete_policy_set",
4001
- description: "Permanently delete a policy set. Campaigns bound to it lose the binding; alerts persist (their policy_set_id becomes null).",
4130
+ description: "Permanently delete a policy set. IMPORTANT: API returns HTTP 400 if any active campaign is still bound to this set. To unbind first, call `list_campaigns` with a `policy_set_id` filter (when available) or scan your campaigns for matches, then `update_campaign` for each match setting `policy_set_id=null`, then retry delete. Alerts created under this set persist (their `policy_set_id` becomes `null`).",
4002
4131
  annotations: {
4003
4132
  title: "Delete Policy Set",
4004
4133
  readOnlyHint: false,
@@ -4047,10 +4176,13 @@ var init_list_policy_sets_tool = __esm({
4047
4176
  "src/application/tools/policy-sets/list-policy-sets.tool.ts"() {
4048
4177
  init_result();
4049
4178
  init_api_error_mapper();
4050
- ListPolicySetsInputShape = {};
4179
+ ListPolicySetsInputShape = {
4180
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
4181
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
4182
+ };
4051
4183
  listPolicySetsTool = {
4052
4184
  name: "list_policy_sets",
4053
- description: "List policy sets: named collections of (tag, country-list) entries that define what counts as a violation. Campaigns bind to one policy set. List items omit `entries` (the tag/country bindings) for payload size \u2014 fetch a single set via `get_policy_set` when you need them.",
4185
+ description: "Paginated list of policy sets: named collections of (tag, country-list) entries that define what counts as a violation. Campaigns bind to one policy set. Returns `{items, total, page, limit}`. List items omit `entries` for payload size \u2014 fetch a single set via `get_policy_set` when you need them.",
4054
4186
  annotations: {
4055
4187
  title: "List Policy Sets",
4056
4188
  readOnlyHint: true,
@@ -4059,10 +4191,10 @@ var init_list_policy_sets_tool = __esm({
4059
4191
  openWorldHint: false
4060
4192
  },
4061
4193
  inputSchema: z.object(ListPolicySetsInputShape),
4062
- handler: async (_input, ctx) => {
4063
- const result = await ctx.api.listPolicySets();
4194
+ handler: async (input, ctx) => {
4195
+ const result = await ctx.api.listPolicySets({ page: input.page, limit: input.limit });
4064
4196
  if (result.isErr()) return err(mapApiError(result.error));
4065
- return ok({ items: result.value, total: result.value.length });
4197
+ return ok(result.value);
4066
4198
  }
4067
4199
  };
4068
4200
  }
@@ -4194,7 +4326,7 @@ var init_list_run_scans_tool = __esm({
4194
4326
  };
4195
4327
  listRunScansTool = {
4196
4328
  name: "list_run_scans",
4197
- description: "List the brief-scan items produced by one run (status, country, URL, timestamps).",
4329
+ description: "List the tile-scan items produced by one run (status, country, offer URL, screenshot, elapsed ms, error). For full scan details (input URL, labels, classification, redirect chain) fetch a specific scan via `get_scan`.",
4198
4330
  annotations: {
4199
4331
  title: "List Run Scans",
4200
4332
  readOnlyHint: true,
@@ -4470,7 +4602,7 @@ var init_get_tag_definition_tool = __esm({
4470
4602
  };
4471
4603
  getTagDefinitionTool = {
4472
4604
  name: "get_tag_definition",
4473
- description: "Get full definition of one tag: display name, description, severity, category, source (system vs custom), public-report visibility, usage counts.",
4605
+ description: "Get full definition of one tag: display name, description, severity, category, source (system vs custom), public-report visibility, usage counts, plus `linked_rules` \u2014 the custom rules currently producing this tag (id, name, active flag). Fetch a specific rule's full config via `get_custom_rule`.",
4474
4606
  annotations: {
4475
4607
  title: "Get Tag Definition",
4476
4608
  readOnlyHint: true,
@@ -4932,6 +5064,7 @@ function registerAllTools(register) {
4932
5064
  register(createCampaignTool);
4933
5065
  register(updateCampaignTool);
4934
5066
  register(listCampaignRunsTool);
5067
+ register(listCampaignsPickerTool);
4935
5068
  register(getRunTool);
4936
5069
  register(listRunScansTool);
4937
5070
  register(cancelRunTool);
@@ -5028,6 +5161,7 @@ var init_tool_registry = __esm({
5028
5161
  init_get_campaign_tool();
5029
5162
  init_list_campaign_runs_tool();
5030
5163
  init_list_campaigns_tool();
5164
+ init_list_campaigns_picker_tool();
5031
5165
  init_run_campaign_tool();
5032
5166
  init_unarchive_campaign_tool();
5033
5167
  init_update_campaign_tool();
@@ -5127,7 +5261,7 @@ function formatToolError(error) {
5127
5261
  case "rate-limited":
5128
5262
  return `Rate limited: ${error.message}${error.retryAfterMs === void 0 ? "" : ` (retry after ${String(error.retryAfterMs)} ms)`}`;
5129
5263
  case "invalid-input":
5130
- return `Invalid input: ${error.message}`;
5264
+ return `Invalid input${error.code === void 0 ? "" : ` (${error.code})`}: ${error.message}`;
5131
5265
  case "upstream":
5132
5266
  return `Upstream error: ${error.message}`;
5133
5267
  case "internal":
@@ -5170,6 +5304,7 @@ async function bootstrapStdio(config) {
5170
5304
  const ctx = { api, logger: scopedLogger, requestId };
5171
5305
  const server = new McpServer({ name: NAME, version: VERSION });
5172
5306
  wireToolsIntoMcpServer(server, () => ctx);
5307
+ declareEmptyResourcesAndPrompts(server);
5173
5308
  const transport = new StdioServerTransport();
5174
5309
  await server.connect(transport);
5175
5310
  scopedLogger.info({}, "stdio.ready");
@@ -5188,6 +5323,7 @@ var init_stdio_bootstrap = __esm({
5188
5323
  init_http_api_gateway();
5189
5324
  init_pino_logger();
5190
5325
  init_version();
5326
+ init_declare_empty_caps();
5191
5327
  init_wire_tools();
5192
5328
  }
5193
5329
  });
@@ -5330,6 +5466,7 @@ async function initNewSession(args) {
5330
5466
  current: { api: seedApi, logger: reqLogger, requestId: args.requestId }
5331
5467
  };
5332
5468
  wireToolsIntoMcpServer(server, () => ctxRef.current);
5469
+ declareEmptyResourcesAndPrompts(server);
5333
5470
  const transport = new StreamableHTTPServerTransport({
5334
5471
  sessionIdGenerator: () => randomUUID(),
5335
5472
  enableJsonResponse: true,
@@ -5358,6 +5495,7 @@ var init_mcp_session_factory = __esm({
5358
5495
  "src/presentation/http/mcp-session-factory.ts"() {
5359
5496
  init_session_id();
5360
5497
  init_version();
5498
+ init_declare_empty_caps();
5361
5499
  init_wire_tools();
5362
5500
  }
5363
5501
  });