@attrkit/mcp 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +32 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -14070,14 +14070,15 @@ async function callApi(config, path, options = {}) {
|
|
|
14070
14070
|
clearTimeout(timer);
|
|
14071
14071
|
}
|
|
14072
14072
|
}
|
|
14073
|
+
var redact = (text) => text.replace(/\b(?:mk|pk|sk|rk)_[A-Za-z0-9_-]{8,}/g, "[redacted-key]").replace(/\b[Bb]earer\s+[A-Za-z0-9._-]{8,}/g, "Bearer [redacted]").replace(/\bAttriKit-(?:Management|Publishable)\s+\S+/g, "AttriKit-$1 [redacted]").replace(/([?&](?:token|secret|key|password|code|signature)=)[^&\s]+/gi, "$1[redacted]");
|
|
14073
14074
|
function toToolResult(result) {
|
|
14074
14075
|
if (!result.ok) {
|
|
14075
14076
|
const envelope = result.json;
|
|
14076
14077
|
const code = envelope?.error?.code ?? `http_${result.status}`;
|
|
14077
|
-
const
|
|
14078
|
+
const rawMessage = envelope?.error?.message;
|
|
14079
|
+
const message = typeof rawMessage === "string" && rawMessage !== "" ? `: ${redact(rawMessage).slice(0, 400)}` : "";
|
|
14078
14080
|
const retryAfter = result.status === 429 && result.retryAfter ? ` (Retry-After: ${result.retryAfter})` : "";
|
|
14079
|
-
const
|
|
14080
|
-
const excerpt = !envelope?.error?.message && result.rawBody ? ` — body: ${redact(result.rawBody.replace(/\s+/g, " ").trim()).slice(0, 300)}` : "";
|
|
14081
|
+
const excerpt = typeof rawMessage !== "string" && result.rawBody ? ` — body: ${redact(result.rawBody.replace(/\s+/g, " ").trim()).slice(0, 300)}` : "";
|
|
14081
14082
|
const hint = result.status === 401 ? " Check ATTRKIT_API_KEY: this is the same error for a missing, wrong, revoked or expired key." : "";
|
|
14082
14083
|
return {
|
|
14083
14084
|
content: [{
|
|
@@ -14091,12 +14092,21 @@ function toToolResult(result) {
|
|
|
14091
14092
|
const isEmptySuccess = result.status === 204 && (result.rawBody === undefined || result.rawBody === "");
|
|
14092
14093
|
const isJsonEnvelope = result.json !== null && typeof result.json === "object" && !Array.isArray(result.json) && Object.hasOwn(result.json, "data");
|
|
14093
14094
|
const contentType = (result.contentType ?? "").toLowerCase();
|
|
14094
|
-
const
|
|
14095
|
-
|
|
14095
|
+
const looksLikeCsv = (() => {
|
|
14096
|
+
if (typeof result.json !== "string")
|
|
14097
|
+
return false;
|
|
14098
|
+
const firstLine = result.json.split(`
|
|
14099
|
+
`, 1)[0]?.trim() ?? "";
|
|
14100
|
+
if (!firstLine || firstLine.startsWith("<"))
|
|
14101
|
+
return false;
|
|
14102
|
+
const columns = firstLine.split(",").map((field) => field.trim().replace(/^"(.*)"$/s, "$1"));
|
|
14103
|
+
return columns.length >= 2 && columns.every((field) => /^[A-Za-z][A-Za-z0-9_]*$/.test(field));
|
|
14104
|
+
})();
|
|
14105
|
+
const isExpectedText = expectedResponse === "text" && typeof result.json === "string" && !contentType.includes("text/html") && looksLikeCsv;
|
|
14096
14106
|
if (!isEmptySuccess && !isJsonEnvelope && !isExpectedText) {
|
|
14097
14107
|
const reportedContentType = result.contentType ?? "unknown content type";
|
|
14098
14108
|
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`;
|
|
14099
|
-
const body = result.rawBody ?? (typeof result.json === "string" ? result.json : JSON.stringify(result.json, null, 2));
|
|
14109
|
+
const body = redact(result.rawBody ?? (typeof result.json === "string" ? result.json : JSON.stringify(result.json, null, 2))).slice(0, 600);
|
|
14100
14110
|
return {
|
|
14101
14111
|
content: [{
|
|
14102
14112
|
type: "text",
|
|
@@ -14112,12 +14122,14 @@ ${body}`
|
|
|
14112
14122
|
structuredContent: structured
|
|
14113
14123
|
};
|
|
14114
14124
|
}
|
|
14115
|
-
var SERVER_VERSION = "0.4.
|
|
14125
|
+
var SERVER_VERSION = "0.4.6";
|
|
14116
14126
|
var periodSchema = z4.enum(["7d", "30d", "90d", "all"]).describe("Reporting window. Defaults to 30d.").optional();
|
|
14117
14127
|
var appIdSchema = z4.string().describe("Optional app id to scope the result to one app.").optional();
|
|
14118
14128
|
var requiredAppIdSchema = z4.string().describe("The AttrKit app id.");
|
|
14119
14129
|
var idempotencyKeySchema = z4.string().max(255).describe("Stable retry key for this exact mutation.").optional();
|
|
14120
14130
|
var ownersSchema = z4.record(z4.string(), z4.enum(["attrkit", "revenuecat", "customer"]));
|
|
14131
|
+
var cursorSchema = z4.string().optional().describe("Pass the next_cursor from the previous response to fetch the following page. Omit for the first page.");
|
|
14132
|
+
var listLimitSchema = z4.number().int().min(1).max(200).optional().describe("Rows per page, 1-200. The API defaults to 50, so a workspace with more than that needs paging.");
|
|
14121
14133
|
var spendRowSchema = z4.object({
|
|
14122
14134
|
campaign_id: z4.string().optional().describe("The AttrKit campaign UUID, NOT the ad network's own campaign id. It is the opposite of create_campaign_link.campaign_id and is the most common mistake here: read it from list_campaigns. The API rejects a network id by name. Preferred snake_case spelling."),
|
|
14123
14135
|
campaignId: z4.string().optional().describe("Accepted camelCase alias for campaign_id."),
|
|
@@ -14214,8 +14226,10 @@ function createServer(config, fetchImpl) {
|
|
|
14214
14226
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
14215
14227
|
title: "List apps",
|
|
14216
14228
|
description: "List the iOS and Android apps in your AttrKit workspace (id, name, platform, bundle id, store id, status).",
|
|
14217
|
-
inputSchema: {}
|
|
14218
|
-
}, async () => toToolResult(await call("/api/v1/apps"
|
|
14229
|
+
inputSchema: { cursor: cursorSchema, limit: listLimitSchema }
|
|
14230
|
+
}, async ({ cursor, limit }) => toToolResult(await call("/api/v1/apps", {
|
|
14231
|
+
query: { ...cursor ? { cursor } : {}, ...limit === undefined ? {} : { limit: String(limit) } }
|
|
14232
|
+
})));
|
|
14219
14233
|
server.registerTool("create_app", {
|
|
14220
14234
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
14221
14235
|
title: "Create an app",
|
|
@@ -14362,8 +14376,15 @@ function createServer(config, fetchImpl) {
|
|
|
14362
14376
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
14363
14377
|
title: "List campaigns",
|
|
14364
14378
|
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.",
|
|
14365
|
-
inputSchema: { app_id: appIdSchema, period: periodSchema }
|
|
14366
|
-
}, async ({ app_id, period }) => toToolResult(await call("/api/v1/reports/campaigns", {
|
|
14379
|
+
inputSchema: { app_id: appIdSchema, period: periodSchema, cursor: cursorSchema, limit: listLimitSchema }
|
|
14380
|
+
}, async ({ app_id, period, cursor, limit }) => toToolResult(await call("/api/v1/reports/campaigns", {
|
|
14381
|
+
query: {
|
|
14382
|
+
appId: app_id,
|
|
14383
|
+
period,
|
|
14384
|
+
...cursor ? { cursor } : {},
|
|
14385
|
+
...limit === undefined ? {} : { limit: String(limit) }
|
|
14386
|
+
}
|
|
14387
|
+
})));
|
|
14367
14388
|
server.registerTool("list_ads", {
|
|
14368
14389
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
14369
14390
|
title: "List creative-level ad performance",
|