@attrkit/mcp 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +46 -7
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -14082,15 +14082,16 @@ function toToolResult(result) {
14082
14082
  const expectedResponse = result.expectedResponse ?? "json";
14083
14083
  const isEmptySuccess = result.status === 204 && (result.rawBody === undefined || result.rawBody === "");
14084
14084
  const isJsonEnvelope = result.json !== null && typeof result.json === "object" && !Array.isArray(result.json) && Object.hasOwn(result.json, "data");
14085
- const isExpectedText = expectedResponse === "text" && typeof result.json === "string";
14085
+ const contentType = (result.contentType ?? "").toLowerCase();
14086
+ const isExpectedText = expectedResponse === "text" && typeof result.json === "string" && (contentType.includes("text/csv") || contentType.includes("application/csv"));
14086
14087
  if (!isEmptySuccess && !isJsonEnvelope && !isExpectedText) {
14087
- const contentType = result.contentType ?? "unknown content type";
14088
+ const reportedContentType = result.contentType ?? "unknown content type";
14088
14089
  const received = result.json === null ? "an empty body" : Array.isArray(result.json) ? "a JSON array" : typeof result.json === "object" ? "a JSON object without a data field" : `a ${typeof result.json} body`;
14089
14090
  const body = result.rawBody ?? (typeof result.json === "string" ? result.json : JSON.stringify(result.json, null, 2));
14090
14091
  return {
14091
14092
  content: [{
14092
14093
  type: "text",
14093
- text: `AttrKit API error (${result.status}): invalid_response: expected a JSON data envelope but received ${received} with Content-Type ${contentType}. Body:
14094
+ text: `AttrKit API error (${result.status}): invalid_response: expected a JSON data envelope but received ${received} with Content-Type ${reportedContentType}. Body:
14094
14095
  ${body}`
14095
14096
  }],
14096
14097
  isError: true
@@ -14102,7 +14103,7 @@ ${body}`
14102
14103
  structuredContent: structured
14103
14104
  };
14104
14105
  }
14105
- var SERVER_VERSION = "0.4.0";
14106
+ var SERVER_VERSION = "0.4.2";
14106
14107
  var periodSchema = z4.enum(["7d", "30d", "90d", "all"]).describe("Reporting window. Defaults to 30d.").optional();
14107
14108
  var appIdSchema = z4.string().describe("Optional app id to scope the result to one app.").optional();
14108
14109
  var requiredAppIdSchema = z4.string().describe("The AttrKit app id.");
@@ -14157,16 +14158,19 @@ function createServer(config, fetchImpl) {
14157
14158
  const server = new McpServer({ name: "attrkit", version: SERVER_VERSION });
14158
14159
  const call = (path, options) => callApi(config, path, { fetchImpl, ...options });
14159
14160
  server.registerTool("whoami", {
14161
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14160
14162
  title: "Inspect management-key scope",
14161
14163
  description: "Return the authenticated workspace, plan, app restriction and read/write scopes.",
14162
14164
  inputSchema: {}
14163
14165
  }, async () => toToolResult(await call("/api/v1/whoami")));
14164
14166
  server.registerTool("get_usage_cap", {
14167
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14165
14168
  title: "Get the workspace usage cap",
14166
14169
  description: "Return the workspace's monthly qualified-install cap, alert email, current billing period and usage. Requires a workspace-scoped read key.",
14167
14170
  inputSchema: {}
14168
14171
  }, async () => toToolResult(await call("/api/v1/workspace/usage-cap")));
14169
14172
  server.registerTool("set_usage_cap", {
14173
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14170
14174
  title: "Set the workspace usage cap",
14171
14175
  description: "Set a monthly qualified-install cap of at least 10,000 and its alert email. Pass null for both values to clear the cap. Requires a workspace-scoped write key.",
14172
14176
  inputSchema: {
@@ -14198,11 +14202,13 @@ function createServer(config, fetchImpl) {
14198
14202
  }));
14199
14203
  });
14200
14204
  server.registerTool("list_apps", {
14205
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14201
14206
  title: "List apps",
14202
14207
  description: "List the iOS and Android apps in your AttrKit workspace (id, name, platform, bundle id, store id, status).",
14203
14208
  inputSchema: {}
14204
14209
  }, async () => toToolResult(await call("/api/v1/apps")));
14205
14210
  server.registerTool("create_app", {
14211
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14206
14212
  title: "Create an app",
14207
14213
  description: "Create an iOS or Android app without the web UI. Returns the publishable key and a one-time app-scoped management key.",
14208
14214
  inputSchema: {
@@ -14235,11 +14241,13 @@ function createServer(config, fetchImpl) {
14235
14241
  }
14236
14242
  })));
14237
14243
  server.registerTool("get_app", {
14244
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14238
14245
  title: "Get an app",
14239
14246
  description: "Return one app's configuration.",
14240
14247
  inputSchema: { app_id: requiredAppIdSchema }
14241
14248
  }, async ({ app_id }) => toToolResult(await call(`/api/v1/apps/${encodeURIComponent(app_id)}`)));
14242
14249
  server.registerTool("update_app", {
14250
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14243
14251
  title: "Update an app",
14244
14252
  description: "Update app metadata, store destination, timezone or device-matching mode.",
14245
14253
  inputSchema: {
@@ -14274,6 +14282,7 @@ function createServer(config, fetchImpl) {
14274
14282
  }
14275
14283
  })));
14276
14284
  server.registerTool("archive_app", {
14285
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
14277
14286
  title: "Archive an app",
14278
14287
  description: "Archive an app and remove it from active customer operations.",
14279
14288
  inputSchema: { app_id: requiredAppIdSchema }
@@ -14281,11 +14290,13 @@ function createServer(config, fetchImpl) {
14281
14290
  method: "DELETE"
14282
14291
  })));
14283
14292
  server.registerTool("get_publishable_key", {
14293
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14284
14294
  title: "Get an app publishable key",
14285
14295
  description: "Reveal the recoverable SDK publishable key for an app.",
14286
14296
  inputSchema: { app_id: requiredAppIdSchema }
14287
14297
  }, async ({ app_id }) => toToolResult(await call(`/api/v1/apps/${encodeURIComponent(app_id)}/publishable-key`)));
14288
14298
  server.registerTool("rotate_publishable_key", {
14299
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
14289
14300
  title: "Rotate an app publishable key",
14290
14301
  description: "Issue a new SDK publishable key and return the old-key grace deadline.",
14291
14302
  inputSchema: {
@@ -14294,11 +14305,13 @@ function createServer(config, fetchImpl) {
14294
14305
  }
14295
14306
  }, async ({ app_id, idempotency_key }) => toToolResult(await call(`/api/v1/apps/${encodeURIComponent(app_id)}/publishable-key`, { method: "POST", idempotencyKey: idempotency_key })));
14296
14307
  server.registerTool("list_api_keys", {
14308
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14297
14309
  title: "List management keys",
14298
14310
  description: "List masked workspace and app management keys. Secret plaintext is never returned.",
14299
14311
  inputSchema: {}
14300
14312
  }, async () => toToolResult(await call("/api/v1/api-keys")));
14301
14313
  server.registerTool("create_api_key", {
14314
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14302
14315
  title: "Create a management key",
14303
14316
  description: "Create a read-only or read/write workspace or app key. The secret is returned once.",
14304
14317
  inputSchema: {
@@ -14313,6 +14326,7 @@ function createServer(config, fetchImpl) {
14313
14326
  body: { label, scopes, app_id: app_id ?? null }
14314
14327
  })));
14315
14328
  server.registerTool("revoke_api_key", {
14329
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
14316
14330
  title: "Revoke a management key",
14317
14331
  description: "Immediately revoke a management key by id.",
14318
14332
  inputSchema: { key_id: z4.string().uuid() }
@@ -14320,6 +14334,7 @@ function createServer(config, fetchImpl) {
14320
14334
  method: "DELETE"
14321
14335
  })));
14322
14336
  server.registerTool("list_links", {
14337
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14323
14338
  title: "List campaign links",
14324
14339
  description: "List tracking links, optionally for one app.",
14325
14340
  inputSchema: {
@@ -14335,11 +14350,13 @@ function createServer(config, fetchImpl) {
14335
14350
  }
14336
14351
  })));
14337
14352
  server.registerTool("list_campaigns", {
14353
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14338
14354
  title: "List campaigns",
14339
14355
  description: "List ad campaigns with their honest profit surface: the scale/cut/hold call, profit p05/p50/p95, spend, retention, and payback. Use this to see how campaigns are performing.",
14340
14356
  inputSchema: { app_id: appIdSchema, period: periodSchema }
14341
14357
  }, async ({ app_id, period }) => toToolResult(await call("/api/v1/reports/campaigns", { query: { appId: app_id, period } })));
14342
14358
  server.registerTool("list_ads", {
14359
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14343
14360
  title: "List creative-level ad performance",
14344
14361
  description: "Return ad and ad-set performance, evidence tiers, ROAS, CPA, CPC and modeled remainders.",
14345
14362
  inputSchema: {
@@ -14355,6 +14372,7 @@ function createServer(config, fetchImpl) {
14355
14372
  }
14356
14373
  })));
14357
14374
  server.registerTool("list_recent_events", {
14375
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14358
14376
  title: "List recent events and installs",
14359
14377
  description: "Return the same recent attribution events and installs visible on the Live dashboard.",
14360
14378
  inputSchema: {
@@ -14368,11 +14386,13 @@ function createServer(config, fetchImpl) {
14368
14386
  }
14369
14387
  })));
14370
14388
  server.registerTool("get_campaign_decision", {
14389
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14371
14390
  title: "Get campaign decisions (scale / cut / hold)",
14372
14391
  description: "Return AttrKit's profit DECISIONS for your campaigns: the scale/cut/hold call and confidence, computed by the attribution engine. Report the `call` VERBATIM — do not recompute or override it; when it says not_enough_data, say so. This is the tool to drive budget automation.",
14373
14392
  inputSchema: { app_id: appIdSchema }
14374
14393
  }, async ({ app_id }) => toToolResult(await call("/api/v1/decisions", { query: { appId: app_id } })));
14375
14394
  server.registerTool("record_campaign_decision", {
14395
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14376
14396
  title: "Record a campaign decision",
14377
14397
  description: "Freeze a scale/cut/hold decision and its exact model inputs for later maturity evaluation.",
14378
14398
  inputSchema: {
@@ -14438,11 +14458,13 @@ function createServer(config, fetchImpl) {
14438
14458
  }
14439
14459
  })));
14440
14460
  server.registerTool("get_attribution_rollup", {
14461
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14441
14462
  title: "Get attribution rollup",
14442
14463
  description: "Return the account-level attribution overview: installs accounted-for by evidence class (verified / device-matched / modeled / organic), MER, revenue and data freshness.",
14443
14464
  inputSchema: { app_id: appIdSchema, period: periodSchema }
14444
14465
  }, async ({ app_id, period }) => toToolResult(await call("/api/v1/reports/overview", { query: { appId: app_id, period } })));
14445
14466
  server.registerTool("create_campaign_link", {
14467
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14446
14468
  title: "Create a campaign tracking link",
14447
14469
  description: "Create a web-to-app tracking link for an ad campaign. Requires a write-scoped API key. Returns the link id and url.",
14448
14470
  inputSchema: {
@@ -14450,10 +14472,11 @@ function createServer(config, fetchImpl) {
14450
14472
  network: z4.enum(["meta", "google", "tiktok"]).describe("The ad network delivering the campaign."),
14451
14473
  destination_url: z4.string().url().describe("The App Store URL to send users to."),
14452
14474
  campaign_name: z4.string().max(120).optional().describe("Optional human-readable campaign name."),
14453
- campaign_id: z4.string().optional().describe("Optional external campaign id."),
14475
+ campaign_id: z4.string().optional().describe("Optional external campaign id. This is the AD NETWORK's own campaign id, not an AttrKit uuid; it is the opposite of import_spend.campaign_id, which requires the AttrKit one."),
14476
+ serve_landing: z4.boolean().optional().describe("Serve the interstitial landing bridge on this link. Defaults to true at the API. The bridge is what captures browser-side context for device matching, so turning it off narrows what can be matched."),
14454
14477
  idempotency_key: idempotencyKeySchema
14455
14478
  }
14456
- }, async ({ app_id, network, destination_url, campaign_name, campaign_id, idempotency_key }) => toToolResult(await call("/api/v1/links", {
14479
+ }, async ({ app_id, network, destination_url, campaign_name, campaign_id, serve_landing, idempotency_key }) => toToolResult(await call("/api/v1/links", {
14457
14480
  method: "POST",
14458
14481
  idempotencyKey: idempotency_key,
14459
14482
  body: {
@@ -14461,10 +14484,12 @@ function createServer(config, fetchImpl) {
14461
14484
  network,
14462
14485
  destinationUrl: destination_url,
14463
14486
  ...campaign_name ? { campaignName: campaign_name } : {},
14464
- ...campaign_id ? { campaignId: campaign_id } : {}
14487
+ ...campaign_id ? { campaignId: campaign_id } : {},
14488
+ ...serve_landing === undefined ? {} : { serveLanding: serve_landing }
14465
14489
  }
14466
14490
  })));
14467
14491
  server.registerTool("update_link_status", {
14492
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14468
14493
  title: "Enable or disable a campaign link",
14469
14494
  description: "Change a tracking link between active and disabled.",
14470
14495
  inputSchema: {
@@ -14474,6 +14499,7 @@ function createServer(config, fetchImpl) {
14474
14499
  }
14475
14500
  }, async ({ link_id, status, idempotency_key }) => toToolResult(await call(`/api/v1/links/${encodeURIComponent(link_id)}`, { method: "PATCH", idempotencyKey: idempotency_key, body: { status } })));
14476
14501
  server.registerTool("get_spend", {
14502
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14477
14503
  title: "Get spend and cost models",
14478
14504
  description: "Return imported ad spend, cost models, completeness and spend freshness.",
14479
14505
  inputSchema: { app_id: appIdSchema }
@@ -14481,6 +14507,7 @@ function createServer(config, fetchImpl) {
14481
14507
  query: { appId: app_id }
14482
14508
  })));
14483
14509
  server.registerTool("import_spend", {
14510
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14484
14511
  title: "Import ad spend",
14485
14512
  description: "Import campaign or creative-level spend rows.",
14486
14513
  inputSchema: {
@@ -14529,6 +14556,7 @@ function createServer(config, fetchImpl) {
14529
14556
  }
14530
14557
  })));
14531
14558
  server.registerTool("set_cost_model", {
14559
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14532
14560
  title: "Set an app cost model",
14533
14561
  description: "Version commission, payment fees and variable COGS used in profit decisions.",
14534
14562
  inputSchema: {
@@ -14564,6 +14592,7 @@ function createServer(config, fetchImpl) {
14564
14592
  }
14565
14593
  })));
14566
14594
  server.registerTool("pull_network_spend", {
14595
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14567
14596
  title: "Pull spend from an ad network",
14568
14597
  description: "Fetch and import creative-level spend from a configured Meta, Google or TikTok connection.",
14569
14598
  inputSchema: {
@@ -14587,11 +14616,13 @@ function createServer(config, fetchImpl) {
14587
14616
  }
14588
14617
  })));
14589
14618
  server.registerTool("list_integrations", {
14619
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14590
14620
  title: "List ad-network integrations",
14591
14621
  description: "List redacted ad-network connections and their readiness.",
14592
14622
  inputSchema: {}
14593
14623
  }, async () => toToolResult(await call("/api/v1/integrations")));
14594
14624
  server.registerTool("create_integration", {
14625
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14595
14626
  title: "Create an ad-network integration",
14596
14627
  description: "Store encrypted network credentials and configure event ownership.",
14597
14628
  inputSchema: {
@@ -14608,6 +14639,7 @@ function createServer(config, fetchImpl) {
14608
14639
  body: { network, account_ref, credential, owners, enable: enable ?? false }
14609
14640
  })));
14610
14641
  server.registerTool("update_integration", {
14642
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14611
14643
  title: "Update an ad-network integration",
14612
14644
  description: "Rotate credentials or enable/disable an integration.",
14613
14645
  inputSchema: {
@@ -14627,16 +14659,19 @@ function createServer(config, fetchImpl) {
14627
14659
  }
14628
14660
  })));
14629
14661
  server.registerTool("delete_integration", {
14662
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
14630
14663
  title: "Delete an ad-network integration",
14631
14664
  description: "Delete a disabled integration. Enabled integrations must be disabled first.",
14632
14665
  inputSchema: { connection_id: z4.string() }
14633
14666
  }, async ({ connection_id }) => toToolResult(await call(`/api/v1/integrations/${encodeURIComponent(connection_id)}`, { method: "DELETE" })));
14634
14667
  server.registerTool("get_revenuecat", {
14668
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14635
14669
  title: "Get RevenueCat configuration",
14636
14670
  description: "Return redacted RevenueCat status and the AttrKit webhook URL.",
14637
14671
  inputSchema: { app_id: requiredAppIdSchema }
14638
14672
  }, async ({ app_id }) => toToolResult(await call(`/api/v1/apps/${encodeURIComponent(app_id)}/revenuecat`)));
14639
14673
  server.registerTool("configure_revenuecat", {
14674
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14640
14675
  title: "Configure RevenueCat",
14641
14676
  description: "Store the RevenueCat provider app id and encrypted authorization secret for an app.",
14642
14677
  inputSchema: {
@@ -14651,11 +14686,13 @@ function createServer(config, fetchImpl) {
14651
14686
  body: { provider_app_id, authorization_secret }
14652
14687
  })));
14653
14688
  server.registerTool("list_webhooks", {
14689
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14654
14690
  title: "List outbound webhooks",
14655
14691
  description: "List redacted outbound webhook subscriptions.",
14656
14692
  inputSchema: {}
14657
14693
  }, async () => toToolResult(await call("/api/v1/webhooks")));
14658
14694
  server.registerTool("create_webhook", {
14695
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
14659
14696
  title: "Create an outbound webhook",
14660
14697
  description: "Create an SSRF-checked webhook subscription. Returns the signing secret once.",
14661
14698
  inputSchema: {
@@ -14676,11 +14713,13 @@ function createServer(config, fetchImpl) {
14676
14713
  }
14677
14714
  })));
14678
14715
  server.registerTool("delete_webhook", {
14716
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
14679
14717
  title: "Delete an outbound webhook",
14680
14718
  description: "Delete a webhook subscription.",
14681
14719
  inputSchema: { subscription_id: z4.string().uuid() }
14682
14720
  }, async ({ subscription_id }) => toToolResult(await call(`/api/v1/webhooks/${encodeURIComponent(subscription_id)}`, { method: "DELETE" })));
14683
14721
  server.registerTool("export_events", {
14722
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14684
14723
  title: "Export events and installs",
14685
14724
  description: "Export one app's events and installs as CSV for an inclusive date range.",
14686
14725
  inputSchema: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attrkit/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "AttrKit's stdio MCP server for its public Management API",
5
5
  "keywords": [
6
6
  "attrkit",