@attrkit/mcp 1.2.0 → 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/CHANGELOG.md +20 -0
- package/README.md +2 -2
- package/dist/cli.js +75 -15
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.0 (2026-10-20)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `list_event_deliveries` (`GET /api/v1/apps/{appId}/deliveries`, read scope): what happened to
|
|
8
|
+
each event the app sent to its ad networks, one row per delivery, newest first. Each row gives
|
|
9
|
+
the state (`held`, `queued`, `sending`, `retrying`, `sent`, `suppressed` or `failed`), the reason
|
|
10
|
+
with a one-line explanation, the attempt count, the last attempt's time and HTTP status, and for
|
|
11
|
+
a held row the time it is expected to be released. Filters: `install_id`, `event_id`,
|
|
12
|
+
`connection_id`, `network`, `state`, `from`, `to`; paged with `cursor` and `limit` (50 by
|
|
13
|
+
default, 200 at most). No other tool changes.
|
|
14
|
+
|
|
15
|
+
## 1.2.1 (2026-10-03)
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `attrkit-mcp login` printed its prompt before it turned echo off, so a key that arrived in
|
|
20
|
+
between (a paste fired the moment the prompt appeared) was echoed on screen. Echo is now off
|
|
21
|
+
before the prompt is printed.
|
|
22
|
+
|
|
3
23
|
## 1.2.0 (2026-10-03)
|
|
4
24
|
|
|
5
25
|
### Added
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ tenant scope is resolved from your secret management key.
|
|
|
7
7
|
|
|
8
8
|
## What it exposes
|
|
9
9
|
|
|
10
|
-
The server provides
|
|
10
|
+
The server provides 39 tools.
|
|
11
11
|
|
|
12
12
|
| Area | Tools |
|
|
13
13
|
|---|---|
|
|
@@ -17,7 +17,7 @@ The server provides 38 tools.
|
|
|
17
17
|
| SDK keys | `get_publishable_key`, `rotate_publishable_key` |
|
|
18
18
|
| Management keys | `list_api_keys`, `create_api_key`, `revoke_api_key` |
|
|
19
19
|
| Campaign links | `list_links`, `create_campaign_link`, `update_link_status` |
|
|
20
|
-
| Decisions and reporting | `list_campaigns`, `list_ads`, `list_recent_events`, `get_campaign_decision`, `record_campaign_decision`, `get_attribution_rollup`, `lookup_customer_attribution` |
|
|
20
|
+
| Decisions and reporting | `list_campaigns`, `list_ads`, `list_recent_events`, `list_event_deliveries`, `get_campaign_decision`, `record_campaign_decision`, `get_attribution_rollup`, `lookup_customer_attribution` |
|
|
21
21
|
| Spend and costs | `get_spend`, `import_spend`, `set_cost_model`, `pull_network_spend` |
|
|
22
22
|
| Ad integrations | `list_integrations`, `create_integration`, `update_integration`, `delete_integration` |
|
|
23
23
|
| RevenueCat | `get_revenuecat`, `configure_revenuecat` |
|
package/dist/cli.js
CHANGED
|
@@ -13710,6 +13710,26 @@ var CLASS_LISTS = [
|
|
|
13710
13710
|
{ ipClass: "relay", parsed: parseRanges(ip_class_ranges_default.apple_private_relay_egress, "relay") }
|
|
13711
13711
|
];
|
|
13712
13712
|
var IP_CLASS_RANGES_VERSION = ip_class_ranges_default.ranges_version;
|
|
13713
|
+
// ../shared/src/meta-errors.ts
|
|
13714
|
+
var META_NOT_SERVING_CODES = new Set([
|
|
13715
|
+
2,
|
|
13716
|
+
4,
|
|
13717
|
+
17,
|
|
13718
|
+
32,
|
|
13719
|
+
341,
|
|
13720
|
+
613,
|
|
13721
|
+
80000,
|
|
13722
|
+
80001,
|
|
13723
|
+
80002,
|
|
13724
|
+
80003,
|
|
13725
|
+
80004,
|
|
13726
|
+
80005,
|
|
13727
|
+
80006,
|
|
13728
|
+
80008,
|
|
13729
|
+
80009,
|
|
13730
|
+
80014
|
|
13731
|
+
]);
|
|
13732
|
+
var META_TOKEN_CODES = new Set([102, 190]);
|
|
13713
13733
|
// ../shared/src/canonical-events.ts
|
|
13714
13734
|
var LEDGER_ONLY_CANONICAL_EVENT_NAMES = Object.freeze(["refund_reversed"]);
|
|
13715
13735
|
// ../shared/src/derived-events.ts
|
|
@@ -14850,11 +14870,11 @@ function invalidResult(code, message) {
|
|
|
14850
14870
|
json: { error: { code, message } }
|
|
14851
14871
|
};
|
|
14852
14872
|
}
|
|
14853
|
-
function buildRequestUrl(
|
|
14873
|
+
function buildRequestUrl(baseUrl, path, query) {
|
|
14854
14874
|
if (!path.startsWith("/") || hasUnsafePathCharacter(path)) {
|
|
14855
14875
|
throw new Error("path must be an absolute URL path without whitespace or control characters");
|
|
14856
14876
|
}
|
|
14857
|
-
const url = new URL(`${
|
|
14877
|
+
const url = new URL(`${baseUrl.href.replace(/\/+$/, "")}${path}`);
|
|
14858
14878
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
14859
14879
|
if (value !== undefined && value !== "")
|
|
14860
14880
|
url.searchParams.set(key, value);
|
|
@@ -14919,15 +14939,15 @@ function apiResultFromResponse(response, text, expectedResponse) {
|
|
|
14919
14939
|
}
|
|
14920
14940
|
async function callApi(config, path, options = {}) {
|
|
14921
14941
|
const doFetch = options.fetchImpl ?? fetch;
|
|
14922
|
-
let
|
|
14942
|
+
let baseUrl;
|
|
14923
14943
|
try {
|
|
14924
|
-
|
|
14944
|
+
baseUrl = validatedApiBaseUrl(config.apiUrl);
|
|
14925
14945
|
} catch (error) {
|
|
14926
14946
|
return invalidResult("invalid_api_url", `ATTRKIT_API_URL is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
14927
14947
|
}
|
|
14928
14948
|
let url;
|
|
14929
14949
|
try {
|
|
14930
|
-
url = buildRequestUrl(
|
|
14950
|
+
url = buildRequestUrl(baseUrl, path, options.query);
|
|
14931
14951
|
} catch (error) {
|
|
14932
14952
|
return invalidResult("invalid_path", `AttriKit API path is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
14933
14953
|
}
|
|
@@ -15055,7 +15075,7 @@ ${body}`
|
|
|
15055
15075
|
structuredContent: structured
|
|
15056
15076
|
}, result.nextCursor);
|
|
15057
15077
|
}
|
|
15058
|
-
var SERVER_VERSION = "1.
|
|
15078
|
+
var SERVER_VERSION = "1.3.0";
|
|
15059
15079
|
var periodSchema = z5.enum(["7d", "30d", "90d", "all"]).describe("Reporting window. Omit to use the endpoint's documented default.").optional();
|
|
15060
15080
|
var appIdSchema = z5.string().describe("Optional app id to scope the result to one app.").optional();
|
|
15061
15081
|
var requiredAppIdSchema = z5.string().min(1).describe("The AttriKit app id.");
|
|
@@ -15413,6 +15433,35 @@ function createServer(config, fetchImpl) {
|
|
|
15413
15433
|
limit: limit === undefined ? undefined : String(limit)
|
|
15414
15434
|
}
|
|
15415
15435
|
})));
|
|
15436
|
+
server.registerTool("list_event_deliveries", {
|
|
15437
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
15438
|
+
title: "List what happened to each event sent to ad networks",
|
|
15439
|
+
description: "Return one row per delivery of an event to a network connection, newest first: its state (held, queued, sending, retrying, sent, suppressed or failed), the reason with a one-line explanation (for example consent_suppressed or destination_event_too_old), attempts, the last HTTP status, and for a held row when it is expected to be released. Use it to answer why an event did not reach Meta, TikTok or Google. Read scope is enough.",
|
|
15440
|
+
inputSchema: {
|
|
15441
|
+
app_id: requiredAppIdSchema,
|
|
15442
|
+
install_id: z5.string().optional().describe("Only this install: the install epoch id the events feed and the export call the install id."),
|
|
15443
|
+
event_id: z5.string().optional().describe("Only this event's deliveries, one per connection that receives it."),
|
|
15444
|
+
connection_id: z5.string().optional().describe("Only this network connection, as returned by list_integrations."),
|
|
15445
|
+
network: z5.string().optional().describe("Only connections to this network, for example meta."),
|
|
15446
|
+
state: z5.enum(["held", "queued", "sending", "retrying", "sent", "suppressed", "failed"]).optional().describe("Only deliveries in this state."),
|
|
15447
|
+
from: calendarDaySchema.optional().describe("First UTC day the event was received, YYYY-MM-DD. May be sent without to."),
|
|
15448
|
+
to: calendarDaySchema.optional().describe("Last UTC day the event was received, YYYY-MM-DD, inclusive. May be sent without from."),
|
|
15449
|
+
cursor: cursorSchema,
|
|
15450
|
+
limit: listLimitSchema
|
|
15451
|
+
}
|
|
15452
|
+
}, async ({ app_id, install_id, event_id, connection_id, network, state, from, to, cursor, limit }) => toToolResult(await call(`/api/v1/apps/${encodeOpaquePathSegment(app_id)}/deliveries`, {
|
|
15453
|
+
query: {
|
|
15454
|
+
installId: install_id,
|
|
15455
|
+
eventId: event_id,
|
|
15456
|
+
connectionId: connection_id,
|
|
15457
|
+
network,
|
|
15458
|
+
state,
|
|
15459
|
+
from,
|
|
15460
|
+
to,
|
|
15461
|
+
cursor,
|
|
15462
|
+
limit: limit === undefined ? undefined : String(limit)
|
|
15463
|
+
}
|
|
15464
|
+
})));
|
|
15416
15465
|
server.registerTool("get_campaign_decision", {
|
|
15417
15466
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
15418
15467
|
title: "Get campaign decisions (scale / cut / hold)",
|
|
@@ -15933,8 +15982,8 @@ function addKeyCommand(username, quotedKey) {
|
|
|
15933
15982
|
}
|
|
15934
15983
|
function readHidden(input, prompt, write, idleMs = 50) {
|
|
15935
15984
|
return new Promise((resolve) => {
|
|
15936
|
-
write(prompt);
|
|
15937
15985
|
input.setRawMode(true);
|
|
15986
|
+
write(prompt);
|
|
15938
15987
|
input.resume();
|
|
15939
15988
|
input.setEncoding("utf8");
|
|
15940
15989
|
let value = "";
|
|
@@ -15957,8 +16006,15 @@ function readHidden(input, prompt, write, idleMs = 50) {
|
|
|
15957
16006
|
};
|
|
15958
16007
|
const onData = (chunk) => {
|
|
15959
16008
|
for (const character of chunk) {
|
|
16009
|
+
if (character === "\x03" || character === "\x04")
|
|
16010
|
+
return finish(null);
|
|
15960
16011
|
if (mode === "drain")
|
|
15961
16012
|
continue;
|
|
16013
|
+
if (character === "\r" || character === `
|
|
16014
|
+
`) {
|
|
16015
|
+
mode = "drain";
|
|
16016
|
+
continue;
|
|
16017
|
+
}
|
|
15962
16018
|
if (mode === "escape") {
|
|
15963
16019
|
mode = character === "[" ? "csi" : character === "O" ? "ss3" : "text";
|
|
15964
16020
|
} else if (mode === "csi") {
|
|
@@ -15966,13 +16022,8 @@ function readHidden(input, prompt, write, idleMs = 50) {
|
|
|
15966
16022
|
mode = "text";
|
|
15967
16023
|
} else if (mode === "ss3") {
|
|
15968
16024
|
mode = "text";
|
|
15969
|
-
} else if (character === "\x03") {
|
|
15970
|
-
return finish(null);
|
|
15971
16025
|
} else if (character === "\x1B") {
|
|
15972
16026
|
mode = "escape";
|
|
15973
|
-
} else if (character === "\r" || character === `
|
|
15974
|
-
`) {
|
|
15975
|
-
mode = "drain";
|
|
15976
16027
|
} else if (character === "" || character === "\b") {
|
|
15977
16028
|
value = value.slice(0, -1);
|
|
15978
16029
|
} else if (character >= " ") {
|
|
@@ -16071,10 +16122,19 @@ async function runLogout(options = {}) {
|
|
|
16071
16122
|
return 1;
|
|
16072
16123
|
}
|
|
16073
16124
|
const result = await (options.runSecurity ?? defaultSecurityRunner)(["delete-generic-password", "-s", KEYCHAIN_SERVICE]);
|
|
16074
|
-
|
|
16075
|
-
`
|
|
16125
|
+
if (result.code === 0) {
|
|
16126
|
+
write(`Removed the attrkit-mcp item from the macOS Keychain.
|
|
16076
16127
|
`);
|
|
16077
|
-
|
|
16128
|
+
return 0;
|
|
16129
|
+
}
|
|
16130
|
+
if (result.code === 44) {
|
|
16131
|
+
write(`No attrkit-mcp item was found in the macOS Keychain; nothing to remove.
|
|
16132
|
+
`);
|
|
16133
|
+
return 0;
|
|
16134
|
+
}
|
|
16135
|
+
write(`Could not remove the attrkit-mcp item from the macOS Keychain (security exit ${result.code}). It may still be stored.
|
|
16136
|
+
`);
|
|
16137
|
+
return 1;
|
|
16078
16138
|
}
|
|
16079
16139
|
|
|
16080
16140
|
// src/main.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attrkit/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "AttriKit's stdio MCP server for its public Management API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"attrkit",
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
"build": "bun build ./src/cli.ts --outdir ./dist --target=node --format=esm --external @modelcontextprotocol/sdk --external zod",
|
|
34
34
|
"prepack": "bun run build",
|
|
35
35
|
"typecheck": "../../node_modules/typescript-7/bin/tsc --noEmit",
|
|
36
|
-
"lint": "eslint
|
|
36
|
+
"lint": "node ../../scripts/checks/code-quality-eslint.mjs --workspace packages/mcp",
|
|
37
37
|
"test": "cd ../.. && vitest run packages/mcp/src"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@modelcontextprotocol/sdk": "1.
|
|
41
|
-
"zod": "4.
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.32.1",
|
|
41
|
+
"zod": "4.6.7"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@attrkit/shared": "workspace:*",
|
|
45
|
-
"@types/node": "26.
|
|
45
|
+
"@types/node": "26.6.5",
|
|
46
46
|
"typescript": "6.0.3",
|
|
47
|
-
"vitest": "
|
|
47
|
+
"vitest": "5.0.4"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|