@attrkit/mcp 1.2.1 → 1.4.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 +28 -0
- package/README.md +12 -2
- package/dist/cli.js +107 -23
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.0 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `create_integration` accepts two optional options for a Meta or TikTok integration:
|
|
8
|
+
`delivery_scope` (`all` or `attributed`) and `renewal_event` (`purchase` or `custom`). Each is
|
|
9
|
+
sent only when you give it, so a create without them sends exactly what 1.3.0 sent, and the API
|
|
10
|
+
applies its defaults, `all` and `purchase`. Existing integrations are unchanged.
|
|
11
|
+
- `update_integration` has a new action, `set_delivery_options`, which sets `delivery_scope`,
|
|
12
|
+
`renewal_event` or both in any status and leaves the credential and status as they were. The
|
|
13
|
+
API refuses the action when it names neither option, and refuses `attributed` or `custom` on a
|
|
14
|
+
Google integration.
|
|
15
|
+
- `list_event_deliveries` names `attributed_only_suppressed` among the reasons it can return: the
|
|
16
|
+
connection sends only the events of installs attributed to its network, and this install is
|
|
17
|
+
organic or attributed to another network.
|
|
18
|
+
|
|
19
|
+
## 1.3.0 (2026-10-20)
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `list_event_deliveries` (`GET /api/v1/apps/{appId}/deliveries`, read scope): what happened to
|
|
24
|
+
each event the app sent to its ad networks, one row per delivery, newest first. Each row gives
|
|
25
|
+
the state (`held`, `queued`, `sending`, `retrying`, `sent`, `suppressed` or `failed`), the reason
|
|
26
|
+
with a one-line explanation, the attempt count, the last attempt's time and HTTP status, and for
|
|
27
|
+
a held row the time it is expected to be released. Filters: `install_id`, `event_id`,
|
|
28
|
+
`connection_id`, `network`, `state`, `from`, `to`; paged with `cursor` and `limit` (50 by
|
|
29
|
+
default, 200 at most). No other tool changes.
|
|
30
|
+
|
|
3
31
|
## 1.2.1 (2026-10-03)
|
|
4
32
|
|
|
5
33
|
### Fixed
|
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` |
|
|
@@ -28,6 +28,16 @@ The server provides 38 tools.
|
|
|
28
28
|
`get_campaign_decision` returns the engine's call verbatim. The model reports it;
|
|
29
29
|
it does not recompute it.
|
|
30
30
|
|
|
31
|
+
A Meta or TikTok integration has two delivery options. `delivery_scope` is `all`
|
|
32
|
+
(the default: every install with measurement consent, organic ones included) or
|
|
33
|
+
`attributed` (only installs AttriKit attributes to that network: a click ID, a link
|
|
34
|
+
token, or a device match at or above the delivery floor). `renewal_event`
|
|
35
|
+
is `purchase` (the default: a renewal is sent as Purchase) or `custom` (it is
|
|
36
|
+
sent as the custom event `subscription_renewed`, with its value). Set them on
|
|
37
|
+
`create_integration`, or later with `update_integration` and the action
|
|
38
|
+
`set_delivery_options`, which leaves the credential and status as they are. A
|
|
39
|
+
Google integration refuses `attributed` and `custom`.
|
|
40
|
+
|
|
31
41
|
## Before you connect
|
|
32
42
|
|
|
33
43
|
Install Node.js 18 or newer. The `npx -y @attrkit/mcp` command downloads and
|
package/dist/cli.js
CHANGED
|
@@ -13710,6 +13710,33 @@ 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]);
|
|
13733
|
+
var META_ACCESS_CODES = new Set([
|
|
13734
|
+
10,
|
|
13735
|
+
...Array.from({ length: 100 }, (_, offset) => 200 + offset)
|
|
13736
|
+
]);
|
|
13737
|
+
var META_ACCESS_SUBCODES = new Map([
|
|
13738
|
+
[100, new Set([33])]
|
|
13739
|
+
]);
|
|
13713
13740
|
// ../shared/src/canonical-events.ts
|
|
13714
13741
|
var LEDGER_ONLY_CANONICAL_EVENT_NAMES = Object.freeze(["refund_reversed"]);
|
|
13715
13742
|
// ../shared/src/derived-events.ts
|
|
@@ -14850,11 +14877,11 @@ function invalidResult(code, message) {
|
|
|
14850
14877
|
json: { error: { code, message } }
|
|
14851
14878
|
};
|
|
14852
14879
|
}
|
|
14853
|
-
function buildRequestUrl(
|
|
14880
|
+
function buildRequestUrl(baseUrl, path, query) {
|
|
14854
14881
|
if (!path.startsWith("/") || hasUnsafePathCharacter(path)) {
|
|
14855
14882
|
throw new Error("path must be an absolute URL path without whitespace or control characters");
|
|
14856
14883
|
}
|
|
14857
|
-
const url = new URL(`${
|
|
14884
|
+
const url = new URL(`${baseUrl.href.replace(/\/+$/, "")}${path}`);
|
|
14858
14885
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
14859
14886
|
if (value !== undefined && value !== "")
|
|
14860
14887
|
url.searchParams.set(key, value);
|
|
@@ -14919,15 +14946,15 @@ function apiResultFromResponse(response, text, expectedResponse) {
|
|
|
14919
14946
|
}
|
|
14920
14947
|
async function callApi(config, path, options = {}) {
|
|
14921
14948
|
const doFetch = options.fetchImpl ?? fetch;
|
|
14922
|
-
let
|
|
14949
|
+
let baseUrl;
|
|
14923
14950
|
try {
|
|
14924
|
-
|
|
14951
|
+
baseUrl = validatedApiBaseUrl(config.apiUrl);
|
|
14925
14952
|
} catch (error) {
|
|
14926
14953
|
return invalidResult("invalid_api_url", `ATTRKIT_API_URL is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
14927
14954
|
}
|
|
14928
14955
|
let url;
|
|
14929
14956
|
try {
|
|
14930
|
-
url = buildRequestUrl(
|
|
14957
|
+
url = buildRequestUrl(baseUrl, path, options.query);
|
|
14931
14958
|
} catch (error) {
|
|
14932
14959
|
return invalidResult("invalid_path", `AttriKit API path is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
14933
14960
|
}
|
|
@@ -15055,13 +15082,15 @@ ${body}`
|
|
|
15055
15082
|
structuredContent: structured
|
|
15056
15083
|
}, result.nextCursor);
|
|
15057
15084
|
}
|
|
15058
|
-
var SERVER_VERSION = "1.
|
|
15085
|
+
var SERVER_VERSION = "1.4.0";
|
|
15059
15086
|
var periodSchema = z5.enum(["7d", "30d", "90d", "all"]).describe("Reporting window. Omit to use the endpoint's documented default.").optional();
|
|
15060
15087
|
var appIdSchema = z5.string().describe("Optional app id to scope the result to one app.").optional();
|
|
15061
15088
|
var requiredAppIdSchema = z5.string().min(1).describe("The AttriKit app id.");
|
|
15062
15089
|
var encodeOpaquePathSegment = (value) => encodeURIComponent(value).replaceAll(".", "%252E");
|
|
15063
15090
|
var idempotencyKeySchema = z5.string().max(255).describe("Stable retry key for this exact mutation.").optional();
|
|
15064
15091
|
var ownersSchema = z5.record(z5.string(), z5.enum(["attrkit", "revenuecat", "customer"]));
|
|
15092
|
+
var deliveryScopeSchema = z5.enum(["all", "attributed"]).optional().describe("Which events a Meta or TikTok integration sends. all (the API's default): the events of every install with measurement consent, organic ones included. attributed: send only events of installs AttriKit attributes to this connection's network (click ID, link token, or device match at or above the delivery floor); others are suppressed with attributed_only_suppressed. Meta and TikTok only: a Google integration refuses attributed.");
|
|
15093
|
+
var renewalEventSchema = z5.enum(["purchase", "custom"]).optional().describe("How a renewal reaches Meta website and TikTok web events. purchase (the API's default): as Purchase with its price and currency. custom: as the custom event subscription_renewed with its price and currency, so Purchase counts one-time purchases only. App events are unchanged. A Google integration refuses custom.");
|
|
15065
15094
|
var cursorSchema = z5.string().optional().describe("Pass the next_cursor from the previous response to fetch the following page. Omit for the first page.");
|
|
15066
15095
|
var listLimitSchema = z5.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.");
|
|
15067
15096
|
var calendarDaySchema = z5.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use a UTC calendar day, YYYY-MM-DD.");
|
|
@@ -15413,6 +15442,35 @@ function createServer(config, fetchImpl) {
|
|
|
15413
15442
|
limit: limit === undefined ? undefined : String(limit)
|
|
15414
15443
|
}
|
|
15415
15444
|
})));
|
|
15445
|
+
server.registerTool("list_event_deliveries", {
|
|
15446
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
15447
|
+
title: "List what happened to each event sent to ad networks",
|
|
15448
|
+
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, attributed_only_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.",
|
|
15449
|
+
inputSchema: {
|
|
15450
|
+
app_id: requiredAppIdSchema,
|
|
15451
|
+
install_id: z5.string().optional().describe("Only this install: the install epoch id the events feed and the export call the install id."),
|
|
15452
|
+
event_id: z5.string().optional().describe("Only this event's deliveries, one per connection that receives it."),
|
|
15453
|
+
connection_id: z5.string().optional().describe("Only this network connection, as returned by list_integrations."),
|
|
15454
|
+
network: z5.string().optional().describe("Only connections to this network, for example meta."),
|
|
15455
|
+
state: z5.enum(["held", "queued", "sending", "retrying", "sent", "suppressed", "failed"]).optional().describe("Only deliveries in this state."),
|
|
15456
|
+
from: calendarDaySchema.optional().describe("First UTC day the event was received, YYYY-MM-DD. May be sent without to."),
|
|
15457
|
+
to: calendarDaySchema.optional().describe("Last UTC day the event was received, YYYY-MM-DD, inclusive. May be sent without from."),
|
|
15458
|
+
cursor: cursorSchema,
|
|
15459
|
+
limit: listLimitSchema
|
|
15460
|
+
}
|
|
15461
|
+
}, 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`, {
|
|
15462
|
+
query: {
|
|
15463
|
+
installId: install_id,
|
|
15464
|
+
eventId: event_id,
|
|
15465
|
+
connectionId: connection_id,
|
|
15466
|
+
network,
|
|
15467
|
+
state,
|
|
15468
|
+
from,
|
|
15469
|
+
to,
|
|
15470
|
+
cursor,
|
|
15471
|
+
limit: limit === undefined ? undefined : String(limit)
|
|
15472
|
+
}
|
|
15473
|
+
})));
|
|
15416
15474
|
server.registerTool("get_campaign_decision", {
|
|
15417
15475
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
15418
15476
|
title: "Get campaign decisions (scale / cut / hold)",
|
|
@@ -15661,33 +15719,48 @@ function createServer(config, fetchImpl) {
|
|
|
15661
15719
|
server.registerTool("create_integration", {
|
|
15662
15720
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
15663
15721
|
title: "Create an ad-network integration",
|
|
15664
|
-
description: "Store encrypted network credentials and choose, for each of the eight event families (install, session, trial, subscription, purchase, refund, paywall, business), which source's copy AttriKit forwards to the network. AttriKit always does the sending; the owner only picks the copy. attrkit = the events your app reports through the AttriKit SDK (the RevenueCat copy of that family is dropped). revenuecat = the events AttriKit received from your RevenueCat webhook (the SDK copy is dropped); RevenueCat itself sends nothing to the network. customer = events your own servers post to AttriKit. If your app reports subscriptions only through RevenueCat, set trial, subscription, purchase and refund to revenuecat, or none of them reach the network.",
|
|
15722
|
+
description: "Store encrypted network credentials and choose, for each of the eight event families (install, session, trial, subscription, purchase, refund, paywall, business), which source's copy AttriKit forwards to the network. AttriKit always does the sending; the owner only picks the copy. attrkit = the events your app reports through the AttriKit SDK (the RevenueCat copy of that family is dropped). revenuecat = the events AttriKit received from your RevenueCat webhook (the SDK copy is dropped); RevenueCat itself sends nothing to the network. customer = events your own servers post to AttriKit. If your app reports subscriptions only through RevenueCat, set trial, subscription, purchase and refund to revenuecat, or none of them reach the network. For Meta and TikTok, delivery_scope and renewal_event are optional; left out, the integration sends every install's events and renewals as Purchase.",
|
|
15665
15723
|
inputSchema: {
|
|
15666
15724
|
network: z5.enum(["meta", "google", "tiktok"]),
|
|
15667
15725
|
account_ref: z5.string().min(1),
|
|
15668
15726
|
credential: z5.string().min(1),
|
|
15669
15727
|
owners: ownersSchema.describe("For each event family, whose copy is forwarded: attrkit, revenuecat or customer (see the tool description). Enabling a destination requires ALL eight families to be present, or the API rejects the call."),
|
|
15670
15728
|
enable: z5.boolean().optional(),
|
|
15729
|
+
delivery_scope: deliveryScopeSchema,
|
|
15730
|
+
renewal_event: renewalEventSchema,
|
|
15671
15731
|
idempotency_key: idempotencyKeySchema
|
|
15672
15732
|
}
|
|
15673
|
-
}, async ({ network, account_ref, credential, owners, enable, idempotency_key }) =>
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15733
|
+
}, async ({ network, account_ref, credential, owners, enable, delivery_scope, renewal_event, idempotency_key }) => {
|
|
15734
|
+
const body = { network, account_ref, credential, owners, enable: enable ?? false };
|
|
15735
|
+
if (delivery_scope !== undefined)
|
|
15736
|
+
body.delivery_scope = delivery_scope;
|
|
15737
|
+
if (renewal_event !== undefined)
|
|
15738
|
+
body.renewal_event = renewal_event;
|
|
15739
|
+
return toToolResult(await call("/api/v1/integrations", {
|
|
15740
|
+
method: "POST",
|
|
15741
|
+
idempotencyKey: idempotency_key,
|
|
15742
|
+
body
|
|
15743
|
+
}));
|
|
15744
|
+
});
|
|
15678
15745
|
server.registerTool("update_integration", {
|
|
15679
15746
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
15680
15747
|
title: "Update an ad-network integration",
|
|
15681
|
-
description: "Rotate credentials or enable/disable an integration.",
|
|
15748
|
+
description: "Rotate credentials or enable/disable an integration. set_delivery_options changes delivery_scope, renewal_event or both on a Meta or TikTok integration, in any status, without touching its credential or status; give at least one of the two.",
|
|
15682
15749
|
inputSchema: {
|
|
15683
15750
|
connection_id: z5.string(),
|
|
15684
|
-
action: z5.enum(["rotate", "disable", "enable"]),
|
|
15751
|
+
action: z5.enum(["rotate", "disable", "enable", "set_delivery_options"]),
|
|
15685
15752
|
credential: z5.string().optional(),
|
|
15686
15753
|
owners: ownersSchema.optional(),
|
|
15754
|
+
delivery_scope: deliveryScopeSchema,
|
|
15755
|
+
renewal_event: renewalEventSchema,
|
|
15687
15756
|
idempotency_key: idempotencyKeySchema
|
|
15688
15757
|
}
|
|
15689
|
-
}, async ({ connection_id, action, credential, owners, idempotency_key }) => {
|
|
15758
|
+
}, async ({ connection_id, action, credential, owners, delivery_scope, renewal_event, idempotency_key }) => {
|
|
15690
15759
|
const body = { action };
|
|
15760
|
+
if (delivery_scope !== undefined)
|
|
15761
|
+
body.delivery_scope = delivery_scope;
|
|
15762
|
+
if (renewal_event !== undefined)
|
|
15763
|
+
body.renewal_event = renewal_event;
|
|
15691
15764
|
if (credential !== undefined)
|
|
15692
15765
|
body.credential = credential;
|
|
15693
15766
|
if (owners)
|
|
@@ -15957,8 +16030,15 @@ function readHidden(input, prompt, write, idleMs = 50) {
|
|
|
15957
16030
|
};
|
|
15958
16031
|
const onData = (chunk) => {
|
|
15959
16032
|
for (const character of chunk) {
|
|
16033
|
+
if (character === "\x03" || character === "\x04")
|
|
16034
|
+
return finish(null);
|
|
15960
16035
|
if (mode === "drain")
|
|
15961
16036
|
continue;
|
|
16037
|
+
if (character === "\r" || character === `
|
|
16038
|
+
`) {
|
|
16039
|
+
mode = "drain";
|
|
16040
|
+
continue;
|
|
16041
|
+
}
|
|
15962
16042
|
if (mode === "escape") {
|
|
15963
16043
|
mode = character === "[" ? "csi" : character === "O" ? "ss3" : "text";
|
|
15964
16044
|
} else if (mode === "csi") {
|
|
@@ -15966,13 +16046,8 @@ function readHidden(input, prompt, write, idleMs = 50) {
|
|
|
15966
16046
|
mode = "text";
|
|
15967
16047
|
} else if (mode === "ss3") {
|
|
15968
16048
|
mode = "text";
|
|
15969
|
-
} else if (character === "\x03") {
|
|
15970
|
-
return finish(null);
|
|
15971
16049
|
} else if (character === "\x1B") {
|
|
15972
16050
|
mode = "escape";
|
|
15973
|
-
} else if (character === "\r" || character === `
|
|
15974
|
-
`) {
|
|
15975
|
-
mode = "drain";
|
|
15976
16051
|
} else if (character === "" || character === "\b") {
|
|
15977
16052
|
value = value.slice(0, -1);
|
|
15978
16053
|
} else if (character >= " ") {
|
|
@@ -16071,10 +16146,19 @@ async function runLogout(options = {}) {
|
|
|
16071
16146
|
return 1;
|
|
16072
16147
|
}
|
|
16073
16148
|
const result = await (options.runSecurity ?? defaultSecurityRunner)(["delete-generic-password", "-s", KEYCHAIN_SERVICE]);
|
|
16074
|
-
|
|
16075
|
-
`
|
|
16149
|
+
if (result.code === 0) {
|
|
16150
|
+
write(`Removed the attrkit-mcp item from the macOS Keychain.
|
|
16076
16151
|
`);
|
|
16077
|
-
|
|
16152
|
+
return 0;
|
|
16153
|
+
}
|
|
16154
|
+
if (result.code === 44) {
|
|
16155
|
+
write(`No attrkit-mcp item was found in the macOS Keychain; nothing to remove.
|
|
16156
|
+
`);
|
|
16157
|
+
return 0;
|
|
16158
|
+
}
|
|
16159
|
+
write(`Could not remove the attrkit-mcp item from the macOS Keychain (security exit ${result.code}). It may still be stored.
|
|
16160
|
+
`);
|
|
16161
|
+
return 1;
|
|
16078
16162
|
}
|
|
16079
16163
|
|
|
16080
16164
|
// src/main.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attrkit/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "AttriKit's stdio MCP server for its public Management API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"attrkit",
|
|
@@ -32,19 +32,19 @@
|
|
|
32
32
|
"scripts": {
|
|
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
|
-
"typecheck": "
|
|
36
|
-
"lint": "eslint
|
|
35
|
+
"typecheck": "tsrs --noEmit",
|
|
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"
|