@attrkit/mcp 0.2.0 → 0.3.1
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/README.md +129 -21
- package/dist/cli.js +44 -7
- package/package.json +25 -3
package/README.md
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
# @attrkit/mcp
|
|
2
2
|
|
|
3
|
-
A stdio [MCP](https://modelcontextprotocol.io) server that lets you drive
|
|
4
|
-
from Codex, Cursor, Gemini, or another MCP client.
|
|
5
|
-
|
|
3
|
+
A stdio [MCP](https://modelcontextprotocol.io) server that lets you drive
|
|
4
|
+
[AttrKit](https://attrikit.io) from Codex, Cursor, Gemini, or another MCP client.
|
|
5
|
+
It is a thin client over AttrKit's public API. It holds no database access; your
|
|
6
|
+
tenant scope is resolved from your secret management key.
|
|
6
7
|
|
|
7
8
|
## What it exposes
|
|
8
9
|
|
|
10
|
+
The server provides 36 tools.
|
|
11
|
+
|
|
9
12
|
| Area | Tools |
|
|
10
13
|
|---|---|
|
|
11
14
|
| Key scope | `whoami` |
|
|
15
|
+
| Workspace usage | `get_usage_cap`, `set_usage_cap` |
|
|
12
16
|
| Apps | `list_apps`, `create_app`, `get_app`, `update_app`, `archive_app` |
|
|
13
17
|
| SDK keys | `get_publishable_key`, `rotate_publishable_key` |
|
|
14
18
|
| Management keys | `list_api_keys`, `create_api_key`, `revoke_api_key` |
|
|
@@ -20,14 +24,54 @@ holds no database access; your tenant scope is resolved from your secret managem
|
|
|
20
24
|
| Outbound webhooks | `list_webhooks`, `create_webhook`, `delete_webhook` |
|
|
21
25
|
| Data export | `export_events` |
|
|
22
26
|
|
|
23
|
-
`get_campaign_decision` returns the engine's call
|
|
27
|
+
`get_campaign_decision` returns the engine's call verbatim. The model reports it;
|
|
28
|
+
it does not recompute it.
|
|
29
|
+
|
|
30
|
+
## Before you connect
|
|
31
|
+
|
|
32
|
+
Create the first workspace management key under
|
|
33
|
+
[Dashboard → Settings → API keys](https://attrikit.io/en/dashboard/settings/api-keys).
|
|
34
|
+
This one-time bootstrap requires the dashboard.
|
|
35
|
+
|
|
36
|
+
After that first key exists, a workspace-scoped read/write key can manage every
|
|
37
|
+
customer operation available through the Management API. This includes apps,
|
|
38
|
+
delegated keys, usage caps, integrations, and exports.
|
|
39
|
+
|
|
40
|
+
Use a read-only or app-scoped key when the client needs less access. App-scoped
|
|
41
|
+
keys cannot read or change the workspace usage cap, create sibling apps, or
|
|
42
|
+
manage workspace-wide integrations.
|
|
43
|
+
|
|
44
|
+
Load the key into the shell that starts your MCP client:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
printf "AttrKit management key: "
|
|
48
|
+
IFS= read -r -s ATTRKIT_API_KEY
|
|
49
|
+
export ATTRKIT_API_KEY
|
|
50
|
+
printf "\n"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Codex
|
|
54
|
+
|
|
55
|
+
Add this server to `~/.codex/config.toml`:
|
|
56
|
+
|
|
57
|
+
```toml
|
|
58
|
+
[mcp_servers.attrkit]
|
|
59
|
+
command = "npx"
|
|
60
|
+
args = ["-y", "@attrkit/mcp"]
|
|
61
|
+
env = { ATTRKIT_API_URL = "https://attrikit.io" }
|
|
62
|
+
env_vars = ["ATTRKIT_API_KEY"]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
codex mcp get attrkit
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See the [Codex setup guide](https://attrikit.io/en/docs/mcp#codex).
|
|
24
70
|
|
|
25
|
-
##
|
|
71
|
+
## Cursor
|
|
26
72
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Keep `write` only on agents that should mutate the workspace.
|
|
30
|
-
2. Add the server to your client config:
|
|
73
|
+
Add the server to `.cursor/mcp.json` for one project or `~/.cursor/mcp.json` for
|
|
74
|
+
every project:
|
|
31
75
|
|
|
32
76
|
```json
|
|
33
77
|
{
|
|
@@ -36,7 +80,7 @@ holds no database access; your tenant scope is resolved from your secret managem
|
|
|
36
80
|
"command": "npx",
|
|
37
81
|
"args": ["-y", "@attrkit/mcp"],
|
|
38
82
|
"env": {
|
|
39
|
-
"ATTRKIT_API_KEY": "
|
|
83
|
+
"ATTRKIT_API_KEY": "${env:ATTRKIT_API_KEY}",
|
|
40
84
|
"ATTRKIT_API_URL": "https://attrikit.io"
|
|
41
85
|
}
|
|
42
86
|
}
|
|
@@ -44,7 +88,47 @@ holds no database access; your tenant scope is resolved from your secret managem
|
|
|
44
88
|
}
|
|
45
89
|
```
|
|
46
90
|
|
|
47
|
-
|
|
91
|
+
Then enable it and inspect the available tools:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cursor-agent mcp list
|
|
95
|
+
cursor-agent mcp list-tools attrkit
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See the [Cursor setup guide](https://attrikit.io/en/docs/mcp#cursor).
|
|
99
|
+
|
|
100
|
+
## Gemini CLI
|
|
101
|
+
|
|
102
|
+
Add this server to `~/.gemini/settings.json`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"attrkit": {
|
|
108
|
+
"command": "npx",
|
|
109
|
+
"args": ["-y", "@attrkit/mcp"],
|
|
110
|
+
"env": {
|
|
111
|
+
"ATTRKIT_API_KEY": "$ATTRKIT_API_KEY",
|
|
112
|
+
"ATTRKIT_API_URL": "https://attrikit.io"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
gemini mcp list
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
See the [Gemini setup guide](https://attrikit.io/en/docs/mcp#gemini).
|
|
124
|
+
|
|
125
|
+
## Other MCP clients
|
|
126
|
+
|
|
127
|
+
Configure a stdio server with command `npx`, arguments
|
|
128
|
+
`["-y", "@attrkit/mcp"]`, and the `ATTRKIT_API_KEY` environment variable.
|
|
129
|
+
Environment-variable interpolation differs between clients, so follow the
|
|
130
|
+
client's secret-handling documentation. `ATTRKIT_API_URL` is optional and
|
|
131
|
+
defaults to `https://attrikit.io`.
|
|
48
132
|
|
|
49
133
|
## Test it locally
|
|
50
134
|
|
|
@@ -52,16 +136,40 @@ holds no database access; your tenant scope is resolved from your secret managem
|
|
|
52
136
|
ATTRKIT_API_KEY=sk_... npx @modelcontextprotocol/inspector npx -y @attrkit/mcp
|
|
53
137
|
```
|
|
54
138
|
|
|
55
|
-
The Inspector lists the tools and lets you invoke them against your live
|
|
139
|
+
The Inspector lists the tools and lets you invoke them against your live
|
|
140
|
+
workspace.
|
|
141
|
+
|
|
142
|
+
## Access boundaries
|
|
143
|
+
|
|
144
|
+
| Control | Management API and MCP |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Customer operations after the first key | Available with a workspace-scoped read/write key |
|
|
147
|
+
| First workspace management key | Dashboard bootstrap only |
|
|
148
|
+
| Billing and subscription | Not available |
|
|
149
|
+
| Sign-in and organization membership | Not available |
|
|
150
|
+
| Workspace deletion | Not available |
|
|
151
|
+
| Internal administration | Not available |
|
|
152
|
+
|
|
153
|
+
See the [Management API guide](https://attrikit.io/en/docs/management-api) and
|
|
154
|
+
[OpenAPI contract](https://attrikit.io/openapi.json) for the complete HTTP
|
|
155
|
+
surface.
|
|
56
156
|
|
|
57
157
|
## Notes
|
|
58
158
|
|
|
59
|
-
- stdio only
|
|
60
|
-
- Key scopes gate reads vs writes
|
|
61
|
-
|
|
62
|
-
- App
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
159
|
+
- stdio only. The package does not open a network port.
|
|
160
|
+
- Key scopes gate reads vs writes. A read-only key gets `403 forbidden_scope` on
|
|
161
|
+
every mutation.
|
|
162
|
+
- App-scoped keys cannot create other apps, reach sibling apps, or manage
|
|
163
|
+
workspace-wide integrations.
|
|
164
|
+
- App creation, management-key creation, and webhook creation return secret
|
|
165
|
+
values once. Capture them from the tool result; list tools only return masked
|
|
166
|
+
metadata.
|
|
167
|
+
- Mutation tools accept an optional `idempotency_key`. Reuse the same value only
|
|
168
|
+
for an exact retry. One-time secret responses are encrypted at rest and safely
|
|
169
|
+
replayed.
|
|
170
|
+
- Management keys are protected by distributed per-key global, mutation, and
|
|
171
|
+
expensive-operation limits.
|
|
172
|
+
- All diagnostics go to stderr. stdout is reserved for the MCP transport.
|
|
173
|
+
- MCP clients store environment values in local configuration. Never commit a
|
|
174
|
+
configuration containing a key. Restrict access to the file and revoke any
|
|
175
|
+
key that is exposed.
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
function readConfig(env = process.env) {
|
|
10
10
|
const apiKey = env.ATTRKIT_API_KEY;
|
|
11
11
|
if (!apiKey) {
|
|
12
|
-
throw new Error("ATTRKIT_API_KEY is required (create one in
|
|
12
|
+
throw new Error("ATTRKIT_API_KEY is required (create one in AttrKit → Settings → API keys).");
|
|
13
13
|
}
|
|
14
14
|
const apiUrl = (env.ATTRKIT_API_URL ?? "https://attrikit.io").replace(/\/+$/, "");
|
|
15
15
|
return { apiKey, apiUrl };
|
|
@@ -61,7 +61,7 @@ function toToolResult(result) {
|
|
|
61
61
|
const code = envelope?.error?.code ?? `http_${result.status}`;
|
|
62
62
|
const message = envelope?.error?.message ? `: ${envelope.error.message}` : "";
|
|
63
63
|
return {
|
|
64
|
-
content: [{ type: "text", text: `
|
|
64
|
+
content: [{ type: "text", text: `AttrKit API error (${result.status || "network"}): ${code}${message}` }],
|
|
65
65
|
isError: true
|
|
66
66
|
};
|
|
67
67
|
}
|
|
@@ -71,9 +71,10 @@ function toToolResult(result) {
|
|
|
71
71
|
structuredContent: structured
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
var SERVER_VERSION = "0.3.1";
|
|
74
75
|
var periodSchema = z.enum(["7d", "30d", "90d", "all"]).describe("Reporting window. Defaults to 30d.").optional();
|
|
75
76
|
var appIdSchema = z.string().describe("Optional app id to scope the result to one app.").optional();
|
|
76
|
-
var requiredAppIdSchema = z.string().describe("The
|
|
77
|
+
var requiredAppIdSchema = z.string().describe("The AttrKit app id.");
|
|
77
78
|
var idempotencyKeySchema = z.string().max(255).describe("Stable retry key for this exact mutation.").optional();
|
|
78
79
|
var ownersSchema = z.record(z.string(), z.enum(["attrkit", "revenuecat", "customer"]));
|
|
79
80
|
var spendRowSchema = z.object({
|
|
@@ -88,16 +89,52 @@ var spendRowSchema = z.object({
|
|
|
88
89
|
spendMinorUnits: z.number().int().nonnegative()
|
|
89
90
|
});
|
|
90
91
|
function createServer(config, fetchImpl) {
|
|
91
|
-
const server = new McpServer({ name: "attrkit", version:
|
|
92
|
+
const server = new McpServer({ name: "attrkit", version: SERVER_VERSION });
|
|
92
93
|
const call = (path, options) => callApi(config, path, { fetchImpl, ...options });
|
|
93
94
|
server.registerTool("whoami", {
|
|
94
95
|
title: "Inspect management-key scope",
|
|
95
96
|
description: "Return the authenticated workspace, plan, app restriction and read/write scopes.",
|
|
96
97
|
inputSchema: {}
|
|
97
98
|
}, async () => toToolResult(await call("/api/v1/whoami")));
|
|
99
|
+
server.registerTool("get_usage_cap", {
|
|
100
|
+
title: "Get the workspace usage cap",
|
|
101
|
+
description: "Return the workspace's monthly qualified-install cap, alert email, current billing period and usage. Requires a workspace-scoped read key.",
|
|
102
|
+
inputSchema: {}
|
|
103
|
+
}, async () => toToolResult(await call("/api/v1/workspace/usage-cap")));
|
|
104
|
+
server.registerTool("set_usage_cap", {
|
|
105
|
+
title: "Set the workspace usage cap",
|
|
106
|
+
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.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
monthly_qualified_install_cap: z.number().int().min(1e4).max(Number.MAX_SAFE_INTEGER).nullable(),
|
|
109
|
+
alert_email: z.string().trim().email().max(320).nullable(),
|
|
110
|
+
idempotency_key: idempotencyKeySchema
|
|
111
|
+
}
|
|
112
|
+
}, async ({
|
|
113
|
+
monthly_qualified_install_cap,
|
|
114
|
+
alert_email,
|
|
115
|
+
idempotency_key
|
|
116
|
+
}) => {
|
|
117
|
+
if (monthly_qualified_install_cap === null !== (alert_email === null)) {
|
|
118
|
+
return {
|
|
119
|
+
content: [{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: "AttrKit usage-cap validation error: set both cap and alert email, or pass null for both to clear them."
|
|
122
|
+
}],
|
|
123
|
+
isError: true
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return toToolResult(await call("/api/v1/workspace/usage-cap", {
|
|
127
|
+
method: "PATCH",
|
|
128
|
+
idempotencyKey: idempotency_key,
|
|
129
|
+
body: {
|
|
130
|
+
monthly_qualified_install_cap,
|
|
131
|
+
alert_email
|
|
132
|
+
}
|
|
133
|
+
}));
|
|
134
|
+
});
|
|
98
135
|
server.registerTool("list_apps", {
|
|
99
136
|
title: "List apps",
|
|
100
|
-
description: "List the iOS and Android apps in your
|
|
137
|
+
description: "List the iOS and Android apps in your AttrKit workspace (id, name, platform, bundle id, store id, status).",
|
|
101
138
|
inputSchema: {}
|
|
102
139
|
}, async () => toToolResult(await call("/api/v1/apps")));
|
|
103
140
|
server.registerTool("create_app", {
|
|
@@ -267,7 +304,7 @@ function createServer(config, fetchImpl) {
|
|
|
267
304
|
})));
|
|
268
305
|
server.registerTool("get_campaign_decision", {
|
|
269
306
|
title: "Get campaign decisions (scale / cut / hold)",
|
|
270
|
-
description: "Return
|
|
307
|
+
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.",
|
|
271
308
|
inputSchema: { app_id: appIdSchema }
|
|
272
309
|
}, async ({ app_id }) => toToolResult(await call("/api/v1/decisions", { query: { appId: app_id } })));
|
|
273
310
|
server.registerTool("record_campaign_decision", {
|
|
@@ -531,7 +568,7 @@ function createServer(config, fetchImpl) {
|
|
|
531
568
|
}, async ({ connection_id }) => toToolResult(await call(`/api/v1/integrations/${encodeURIComponent(connection_id)}`, { method: "DELETE" })));
|
|
532
569
|
server.registerTool("get_revenuecat", {
|
|
533
570
|
title: "Get RevenueCat configuration",
|
|
534
|
-
description: "Return redacted RevenueCat status and the
|
|
571
|
+
description: "Return redacted RevenueCat status and the AttrKit webhook URL.",
|
|
535
572
|
inputSchema: { app_id: requiredAppIdSchema }
|
|
536
573
|
}, async ({ app_id }) => toToolResult(await call(`/api/v1/apps/${encodeURIComponent(app_id)}/revenuecat`)));
|
|
537
574
|
server.registerTool("configure_revenuecat", {
|
package/package.json
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attrkit/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "AttrKit's stdio MCP server for its public Management API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"attrkit",
|
|
7
|
+
"attribution",
|
|
8
|
+
"mcp",
|
|
9
|
+
"model-context-protocol",
|
|
10
|
+
"automation",
|
|
11
|
+
"agent-tools"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://attrikit.io/en/docs/mcp",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/KamyarTaher/attrikit.git",
|
|
17
|
+
"directory": "packages/mcp"
|
|
18
|
+
},
|
|
19
|
+
"license": "UNLICENSED",
|
|
5
20
|
"type": "module",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
6
24
|
"bin": {
|
|
7
|
-
"attrkit-mcp": "
|
|
25
|
+
"attrkit-mcp": "dist/cli.js"
|
|
8
26
|
},
|
|
9
27
|
"files": [
|
|
10
28
|
"dist",
|
|
@@ -12,6 +30,7 @@
|
|
|
12
30
|
],
|
|
13
31
|
"scripts": {
|
|
14
32
|
"build": "bun build ./src/cli.ts --outdir ./dist --target=node --format=esm --external @modelcontextprotocol/sdk --external zod",
|
|
33
|
+
"prepack": "bun run build",
|
|
15
34
|
"typecheck": "../../node_modules/typescript-7/bin/tsc --noEmit",
|
|
16
35
|
"test": "cd ../.. && vitest run packages/mcp/src/index.test.ts"
|
|
17
36
|
},
|
|
@@ -24,5 +43,8 @@
|
|
|
24
43
|
"@types/node": "26.1.1",
|
|
25
44
|
"typescript": "6.0.3",
|
|
26
45
|
"vitest": "4.1.10"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
27
49
|
}
|
|
28
50
|
}
|