@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.
Files changed (3) hide show
  1. package/README.md +129 -21
  2. package/dist/cli.js +44 -7
  3. 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 [AttriKit](https://attrikit.io)
4
- from Codex, Cursor, Gemini, or another MCP client. It is a thin client over AttriKit's public API — it
5
- holds no database access; your tenant scope is resolved from your secret management key.
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 **verbatim** the model reports it, it does not recompute it.
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
- ## Setup
71
+ ## Cursor
26
72
 
27
- 1. Obtain one workspace management key (`sk_…`). Once you have that bootstrap key, all operational
28
- setup—including apps and delegated app keys—can be completed through MCP without the web dashboard.
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": "sk_your_key_here",
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
- `ATTRKIT_API_URL` is optional (defaults to `https://attrikit.io`).
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 workspace.
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 no network listener, no port bind.
60
- - Key scopes gate reads vs writes; a read-only key gets `403 forbidden_scope` on every mutation.
61
- - App-scoped keys cannot create other apps, reach sibling apps, or manage workspace-wide integrations.
62
- - App creation, management-key creation, and webhook creation return secret values once. Capture them
63
- from the tool result; list tools only return masked metadata.
64
- - Mutation tools accept an optional `idempotency_key`. Reuse the same value only for an exact retry;
65
- one-time secret responses are encrypted at rest and safely replayed.
66
- - Management keys are protected by distributed per-key global, mutation, and expensive-operation limits.
67
- - All diagnostics go to stderr (stdout is the MCP transport).
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 AttriKit → Settings → API keys).");
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: `AttriKit API error (${result.status || "network"}): ${code}${message}` }],
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 AttriKit app id.");
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: "0.2.0" });
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 AttriKit workspace (id, name, platform, bundle id, store id, status).",
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 AttriKit'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.",
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 AttriKit webhook URL.",
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.2.0",
4
- "description": "AttriKit's stdio MCP server for its public management API",
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": "./dist/cli.js"
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
  }