@daxaur/ctrl-mcp 0.1.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/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @daxaur/ctrl-mcp
2
+
3
+ **Base MCP plugin for CTRL — turn natural language into onchain workflow automations.**
4
+
5
+ Tell Claude, Cursor, or ChatGPT what your agent should do. Watch the workflow assemble. Sign once. CTRL's keeper runs it on Base mainnet — forever, or until you pause it.
6
+
7
+ > Built for the [Base Agent Quest](https://x.com/buildonbase/status/2059713951974236219) contest. Live at [ctrl.build/mcp](https://ctrl.build/mcp).
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ### 1. Get your API key
14
+
15
+ Visit [ctrl.build/settings/api-keys](https://ctrl.build/settings/api-keys), sign with your wallet, and mint a key. You'll see it ONCE — paste it into your MCP client config below.
16
+
17
+ ### 2. Add to your MCP client
18
+
19
+ #### Claude Desktop
20
+
21
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "ctrl": {
27
+ "command": "npx",
28
+ "args": ["-y", "@daxaur/ctrl-mcp"],
29
+ "env": {
30
+ "CTRL_API_KEY": "sk_ctrl_YOUR_KEY_HERE"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ Restart Claude Desktop.
38
+
39
+ #### Cursor
40
+
41
+ Edit `~/.cursor/mcp.json` (or via Settings → MCP Servers):
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "ctrl": {
47
+ "command": "npx",
48
+ "args": ["-y", "@daxaur/ctrl-mcp"],
49
+ "env": {
50
+ "CTRL_API_KEY": "sk_ctrl_YOUR_KEY_HERE"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ #### Other MCP clients
58
+
59
+ Run as a stdio MCP server with `CTRL_API_KEY` set:
60
+
61
+ ```bash
62
+ CTRL_API_KEY=sk_ctrl_YOUR_KEY_HERE npx @daxaur/ctrl-mcp
63
+ ```
64
+
65
+ ---
66
+
67
+ ## What you get
68
+
69
+ Four tools registered with your AI client:
70
+
71
+ | Tool | What it does |
72
+ |------|--------------|
73
+ | `ctrl_create_workflow` | Assemble a CTRL workflow — trigger + chain of actions/conditions. Stored as a draft. |
74
+ | `ctrl_activate` | Returns an EIP-5792 `transactions[]` batch the user signs ONCE to deploy the vault + activate. |
75
+ | `ctrl_fire_manual` | Test-fire a workflow without waiting for the natural trigger. |
76
+ | `ctrl_get_execution_logs` | Read recent executions — trigger, status, BaseScan tx hash, gas used. |
77
+
78
+ ---
79
+
80
+ ## Try it
81
+
82
+ Paste any of these into Claude or Cursor after install:
83
+
84
+ ```
85
+ build me an agent that snipes new flaunch tokens with $25 each and auto-sells at 2x.
86
+ cap me at 0.5 eth per day.
87
+ ```
88
+
89
+ ```
90
+ watch wallet 0x6cc5...c01b. when they buy anything for >$10k, copy with $50 of my own.
91
+ ```
92
+
93
+ ```
94
+ every monday at noon UTC, swap $100 of usdc for eth. pause if eth is above $5000.
95
+ ```
96
+
97
+ The AI calls `ctrl_create_workflow` → `ctrl_activate` → you click the returned link → sign once → done.
98
+
99
+ ---
100
+
101
+ ## How sign-once works
102
+
103
+ CTRL's V3 vault on Base mainnet is **rule-based**. When you activate a workflow:
104
+
105
+ 1. **One EIP-5792 batch** is composed: `[createVault + deposit, createRule(maxPerSwap, maxPerDay, expiry)]`
106
+ 2. You sign it ONCE in your Base Account (atomic — both calls succeed or both revert)
107
+ 3. From that moment, CTRL's keeper executes your workflow autonomously per the on-chain spending rules
108
+ 4. The keeper **physically cannot exceed** your caps — they're enforced in the audited vault contract
109
+
110
+ Kill switches: `pauseVault()` or `revokeRule()` anytime from ctrl.build. The workflow stops instantly.
111
+
112
+ ---
113
+
114
+ ## Architecture
115
+
116
+ - **Plugin (`@daxaur/ctrl-mcp`)** — stdio MCP server using `@modelcontextprotocol/sdk`. Authenticates to ctrl.build with your API key. Wraps 4 REST endpoints under `/api/mcp/*`.
117
+ - **Sign-once envelope** — `ctrl_activate` returns `{ transactions: [{ to, data, value, chainId: 8453 }, ...] }`, the canonical Base MCP shape used by Moonwell, Uniswap, and Aerodrome native plugins.
118
+ - **Vault contracts** — V3 vault factory at `0x8E547D7758C79e23146f77b783149435feeA3e45` on Base. CREATE2 vault addresses are predicted server-side so the batch's second call (`createRule`) knows its target before the first call (`createVault`) executes.
119
+ - **Keeper** — 8-wallet fleet runs in prod (Render). Watches `trigger_configs` table on a 5s tick. Signs and broadcasts the vault execute call when the trigger condition is met.
120
+
121
+ ---
122
+
123
+ ## Environment variables
124
+
125
+ | Var | Required | Default | Notes |
126
+ |-----|----------|---------|-------|
127
+ | `CTRL_API_KEY` | yes | — | Your `sk_ctrl_...` key from ctrl.build/settings/api-keys |
128
+ | `CTRL_API_BASE` | no | `https://ctrl.build` | Override for staging / local dev |
129
+
130
+ ---
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ git clone https://github.com/ctrl-build/ctrl
136
+ cd ctrl/mcp-plugin
137
+ npm install
138
+ npm run build
139
+ ```
140
+
141
+ To test against a local CTRL stack:
142
+
143
+ ```bash
144
+ CTRL_API_BASE=http://localhost:3737 CTRL_API_KEY=sk_ctrl_dev npx tsx src/index.ts
145
+ ```
146
+
147
+ ---
148
+
149
+ ## License
150
+
151
+ MIT — CTRL Labs
152
+
153
+ ---
154
+
155
+ ## Related
156
+
157
+ - [CTRL homepage](https://ctrl.build)
158
+ - [Base MCP docs](https://docs.base.org/ai-agents)
159
+ - [Custom plugin spec](https://docs.base.org/ai-agents/plugins/custom-plugins)
160
+ - [Base Agent Quest tweet](https://x.com/buildonbase/status/2059713951974236219)
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Block catalog schemas — mirrors lib/terminal/block-definitions.ts in the main
3
+ * CTRL repo. We re-declare here so the MCP plugin is self-contained (Claude
4
+ * can reason about valid block types without an extra API roundtrip).
5
+ *
6
+ * Keep in sync with main repo on every block addition.
7
+ */
8
+ import { z } from 'zod';
9
+ export declare const TRIGGER_TYPES: readonly ["time.interval", "trigger.manual", "price.above", "price.below", "price.change", "pool.created", "watch.whale", "event.transfer", "event.balance"];
10
+ export declare const ACTION_TYPES: readonly ["cypher.swap", "read.balance", "notify.telegram", "notify.discord", "util.webhook"];
11
+ export declare const CONDITION_TYPES: readonly ["cond.price", "cond.balance", "cond.allowed_weekdays", "cond.time_window"];
12
+ export declare const UTILITY_TYPES: readonly ["util.delay", "util.note", "util.log", "util.stop", "util.snapshot"];
13
+ export type TriggerType = (typeof TRIGGER_TYPES)[number];
14
+ export type ActionType = (typeof ACTION_TYPES)[number];
15
+ export type ConditionType = (typeof CONDITION_TYPES)[number];
16
+ export type UtilityType = (typeof UTILITY_TYPES)[number];
17
+ /** A workflow is a trigger + an ordered chain of action/condition/utility blocks. */
18
+ export declare const WorkflowDefinitionSchema: z.ZodObject<{
19
+ name: z.ZodString;
20
+ description: z.ZodOptional<z.ZodString>;
21
+ trigger: z.ZodObject<{
22
+ type: z.ZodEnum<["time.interval", "trigger.manual", "price.above", "price.below", "price.change", "pool.created", "watch.whale", "event.transfer", "event.balance"]>;
23
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ type: "time.interval" | "trigger.manual" | "price.above" | "price.below" | "price.change" | "pool.created" | "watch.whale" | "event.transfer" | "event.balance";
26
+ config?: Record<string, any> | undefined;
27
+ }, {
28
+ type: "time.interval" | "trigger.manual" | "price.above" | "price.below" | "price.change" | "pool.created" | "watch.whale" | "event.transfer" | "event.balance";
29
+ config?: Record<string, any> | undefined;
30
+ }>;
31
+ chain: z.ZodArray<z.ZodUnion<[z.ZodObject<{
32
+ type: z.ZodEnum<["cypher.swap", "read.balance", "notify.telegram", "notify.discord", "util.webhook"]>;
33
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
34
+ }, "strip", z.ZodTypeAny, {
35
+ type: "cypher.swap" | "read.balance" | "notify.telegram" | "notify.discord" | "util.webhook";
36
+ config?: Record<string, any> | undefined;
37
+ }, {
38
+ type: "cypher.swap" | "read.balance" | "notify.telegram" | "notify.discord" | "util.webhook";
39
+ config?: Record<string, any> | undefined;
40
+ }>, z.ZodObject<{
41
+ type: z.ZodEnum<["cond.price", "cond.balance", "cond.allowed_weekdays", "cond.time_window"]>;
42
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ type: "cond.price" | "cond.balance" | "cond.allowed_weekdays" | "cond.time_window";
45
+ config?: Record<string, any> | undefined;
46
+ }, {
47
+ type: "cond.price" | "cond.balance" | "cond.allowed_weekdays" | "cond.time_window";
48
+ config?: Record<string, any> | undefined;
49
+ }>, z.ZodObject<{
50
+ type: z.ZodEnum<["util.delay", "util.note", "util.log", "util.stop", "util.snapshot"]>;
51
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
52
+ }, "strip", z.ZodTypeAny, {
53
+ type: "util.delay" | "util.note" | "util.log" | "util.stop" | "util.snapshot";
54
+ config?: Record<string, any> | undefined;
55
+ }, {
56
+ type: "util.delay" | "util.note" | "util.log" | "util.stop" | "util.snapshot";
57
+ config?: Record<string, any> | undefined;
58
+ }>]>, "many">;
59
+ }, "strip", z.ZodTypeAny, {
60
+ name: string;
61
+ trigger: {
62
+ type: "time.interval" | "trigger.manual" | "price.above" | "price.below" | "price.change" | "pool.created" | "watch.whale" | "event.transfer" | "event.balance";
63
+ config?: Record<string, any> | undefined;
64
+ };
65
+ chain: ({
66
+ type: "cypher.swap" | "read.balance" | "notify.telegram" | "notify.discord" | "util.webhook";
67
+ config?: Record<string, any> | undefined;
68
+ } | {
69
+ type: "cond.price" | "cond.balance" | "cond.allowed_weekdays" | "cond.time_window";
70
+ config?: Record<string, any> | undefined;
71
+ } | {
72
+ type: "util.delay" | "util.note" | "util.log" | "util.stop" | "util.snapshot";
73
+ config?: Record<string, any> | undefined;
74
+ })[];
75
+ description?: string | undefined;
76
+ }, {
77
+ name: string;
78
+ trigger: {
79
+ type: "time.interval" | "trigger.manual" | "price.above" | "price.below" | "price.change" | "pool.created" | "watch.whale" | "event.transfer" | "event.balance";
80
+ config?: Record<string, any> | undefined;
81
+ };
82
+ chain: ({
83
+ type: "cypher.swap" | "read.balance" | "notify.telegram" | "notify.discord" | "util.webhook";
84
+ config?: Record<string, any> | undefined;
85
+ } | {
86
+ type: "cond.price" | "cond.balance" | "cond.allowed_weekdays" | "cond.time_window";
87
+ config?: Record<string, any> | undefined;
88
+ } | {
89
+ type: "util.delay" | "util.note" | "util.log" | "util.stop" | "util.snapshot";
90
+ config?: Record<string, any> | undefined;
91
+ })[];
92
+ description?: string | undefined;
93
+ }>;
94
+ export type WorkflowDefinition = z.infer<typeof WorkflowDefinitionSchema>;
95
+ /**
96
+ * Convert a simple {trigger, chain} schema into CTRL's internal
97
+ * {nodes, edges} shape used by the canvas + keeper.
98
+ */
99
+ export declare function compileToWorkflowData(def: WorkflowDefinition): {
100
+ nodes: Array<{
101
+ id: string;
102
+ type: string;
103
+ blockType: string;
104
+ blockSubtype: string;
105
+ data: Record<string, unknown>;
106
+ position: {
107
+ x: number;
108
+ y: number;
109
+ };
110
+ }>;
111
+ edges: Array<{
112
+ id: string;
113
+ source: string;
114
+ target: string;
115
+ }>;
116
+ };
117
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,aAAa,8JAUhB,CAAA;AAEV,eAAO,MAAM,YAAY,+FAMf,CAAA;AAEV,eAAO,MAAM,eAAe,sFAKlB,CAAA;AAEV,eAAO,MAAM,aAAa,gFAMhB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACtD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC5D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAsBxD,qFAAqF;AACrF,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQnC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAEzE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,GAAG;IAC9D,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,QAAQ,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;IACtJ,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7D,CA0CA"}
package/dist/blocks.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Block catalog schemas — mirrors lib/terminal/block-definitions.ts in the main
3
+ * CTRL repo. We re-declare here so the MCP plugin is self-contained (Claude
4
+ * can reason about valid block types without an extra API roundtrip).
5
+ *
6
+ * Keep in sync with main repo on every block addition.
7
+ */
8
+ import { z } from 'zod';
9
+ export const TRIGGER_TYPES = [
10
+ 'time.interval',
11
+ 'trigger.manual',
12
+ 'price.above',
13
+ 'price.below',
14
+ 'price.change',
15
+ 'pool.created',
16
+ 'watch.whale',
17
+ 'event.transfer',
18
+ 'event.balance',
19
+ ];
20
+ export const ACTION_TYPES = [
21
+ 'cypher.swap',
22
+ 'read.balance',
23
+ 'notify.telegram',
24
+ 'notify.discord',
25
+ 'util.webhook',
26
+ ];
27
+ export const CONDITION_TYPES = [
28
+ 'cond.price',
29
+ 'cond.balance',
30
+ 'cond.allowed_weekdays',
31
+ 'cond.time_window',
32
+ ];
33
+ export const UTILITY_TYPES = [
34
+ 'util.delay',
35
+ 'util.note',
36
+ 'util.log',
37
+ 'util.stop',
38
+ 'util.snapshot',
39
+ ];
40
+ const triggerBlock = z.object({
41
+ type: z.enum(TRIGGER_TYPES),
42
+ config: z.record(z.any()).optional(),
43
+ });
44
+ const actionBlock = z.object({
45
+ type: z.enum(ACTION_TYPES),
46
+ config: z.record(z.any()).optional(),
47
+ });
48
+ const conditionBlock = z.object({
49
+ type: z.enum(CONDITION_TYPES),
50
+ config: z.record(z.any()).optional(),
51
+ });
52
+ const utilityBlock = z.object({
53
+ type: z.enum(UTILITY_TYPES),
54
+ config: z.record(z.any()).optional(),
55
+ });
56
+ /** A workflow is a trigger + an ordered chain of action/condition/utility blocks. */
57
+ export const WorkflowDefinitionSchema = z.object({
58
+ name: z.string().min(1).max(120),
59
+ description: z.string().max(500).optional(),
60
+ trigger: triggerBlock,
61
+ chain: z
62
+ .array(z.union([actionBlock, conditionBlock, utilityBlock]))
63
+ .min(1)
64
+ .max(20),
65
+ });
66
+ /**
67
+ * Convert a simple {trigger, chain} schema into CTRL's internal
68
+ * {nodes, edges} shape used by the canvas + keeper.
69
+ */
70
+ export function compileToWorkflowData(def) {
71
+ const nodes = [];
72
+ const edges = [];
73
+ const triggerId = 'n0_trigger';
74
+ const [triggerCategory, triggerSubtype] = def.trigger.type.split('.');
75
+ nodes.push({
76
+ id: triggerId,
77
+ type: 'automation',
78
+ blockType: 'trigger',
79
+ blockSubtype: `${triggerCategory}.${triggerSubtype}`,
80
+ data: { blockId: def.trigger.type, config: def.trigger.config ?? {} },
81
+ position: { x: 80, y: 80 },
82
+ });
83
+ let prevId = triggerId;
84
+ def.chain.forEach((block, idx) => {
85
+ const id = `n${idx + 1}_${block.type.replace('.', '_')}`;
86
+ const [category, subtype] = block.type.split('.');
87
+ const isAction = ACTION_TYPES.includes(block.type);
88
+ const isCondition = CONDITION_TYPES.includes(block.type);
89
+ const blockType = isAction ? 'action' : isCondition ? 'condition' : 'utility';
90
+ nodes.push({
91
+ id,
92
+ type: 'automation',
93
+ blockType,
94
+ blockSubtype: `${category}.${subtype}`,
95
+ data: { blockId: block.type, config: block.config ?? {} },
96
+ position: { x: 80 + (idx + 1) * 320, y: 80 },
97
+ });
98
+ edges.push({ id: `e_${prevId}_${id}`, source: prevId, target: id });
99
+ prevId = id;
100
+ });
101
+ return { nodes, edges };
102
+ }
103
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Thin HTTP client for ctrl.build with API-key auth + sensible defaults.
3
+ */
4
+ interface RequestOpts {
5
+ method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
6
+ body?: unknown;
7
+ query?: Record<string, string | number | undefined>;
8
+ timeoutMs?: number;
9
+ }
10
+ export declare class CtrlApiError extends Error {
11
+ readonly status: number;
12
+ readonly body?: unknown | undefined;
13
+ constructor(message: string, status: number, body?: unknown | undefined);
14
+ }
15
+ export declare function ctrlFetch<T = unknown>(path: string, opts?: RequestOpts): Promise<T>;
16
+ export {};
17
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAA;IACnD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,YAAa,SAAQ,KAAK;aAGnB,MAAM,EAAE,MAAM;aACd,IAAI,CAAC,EAAE,OAAO;gBAF9B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,OAAO,YAAA;CAKjC;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,CAAC,CAAC,CA+CZ"}
package/dist/client.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Thin HTTP client for ctrl.build with API-key auth + sensible defaults.
3
+ */
4
+ import { CTRL_API_BASE, requireApiKey } from './config.js';
5
+ export class CtrlApiError extends Error {
6
+ status;
7
+ body;
8
+ constructor(message, status, body) {
9
+ super(message);
10
+ this.status = status;
11
+ this.body = body;
12
+ this.name = 'CtrlApiError';
13
+ }
14
+ }
15
+ export async function ctrlFetch(path, opts = {}) {
16
+ const apiKey = requireApiKey();
17
+ const url = new URL(path, CTRL_API_BASE);
18
+ if (opts.query) {
19
+ for (const [k, v] of Object.entries(opts.query)) {
20
+ if (v !== undefined && v !== null)
21
+ url.searchParams.set(k, String(v));
22
+ }
23
+ }
24
+ const controller = new AbortController();
25
+ const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 15_000);
26
+ let res;
27
+ try {
28
+ res = await fetch(url, {
29
+ method: opts.method ?? 'GET',
30
+ headers: {
31
+ Authorization: `Bearer ${apiKey}`,
32
+ 'Content-Type': 'application/json',
33
+ Accept: 'application/json',
34
+ 'User-Agent': '@daxaur/ctrl-mcp',
35
+ },
36
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
37
+ signal: controller.signal,
38
+ });
39
+ }
40
+ finally {
41
+ clearTimeout(timeout);
42
+ }
43
+ let parsed = undefined;
44
+ const text = await res.text();
45
+ if (text) {
46
+ try {
47
+ parsed = JSON.parse(text);
48
+ }
49
+ catch {
50
+ parsed = text;
51
+ }
52
+ }
53
+ if (!res.ok) {
54
+ const message = parsed?.error ||
55
+ `CTRL API ${opts.method ?? 'GET'} ${path} failed: HTTP ${res.status}`;
56
+ throw new CtrlApiError(message, res.status, parsed);
57
+ }
58
+ return parsed;
59
+ }
60
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAS1D,MAAM,OAAO,YAAa,SAAQ,KAAK;IAGnB;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,IAAc;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAA;QAHE,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAU;QAG9B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAY,EACZ,OAAoB,EAAE;IAEtB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAA;IAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IACxC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACvE,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAA;IAE9E,IAAI,GAAa,CAAA;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,EAAE;gBACjC,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,YAAY,EAAE,kBAAkB;aACjC;YACD,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACvD,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC;IAED,IAAI,MAAM,GAAY,SAAS,CAAA;IAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,OAAO,GACV,MAA6B,EAAE,KAAK;YACrC,YAAY,IAAI,CAAC,MAAM,IAAI,KAAK,IAAI,IAAI,iBAAiB,GAAG,CAAC,MAAM,EAAE,CAAA;QACvE,MAAM,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrD,CAAC;IAED,OAAO,MAAW,CAAA;AACpB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @daxaur/ctrl-mcp configuration
3
+ *
4
+ * All settings come from environment variables. The user sets these in their
5
+ * MCP client config (Claude Desktop / Cursor) — see README.md for examples.
6
+ */
7
+ export declare const CTRL_API_BASE: string;
8
+ export declare const CTRL_API_KEY: string;
9
+ export declare const CTRL_CHAIN_ID = 8453;
10
+ /** Default rule caps when user doesn't specify — conservative for safety. */
11
+ export declare const DEFAULT_MAX_PER_SWAP_ETH = "0.01";
12
+ export declare const DEFAULT_MAX_PER_DAY_ETH = "0.1";
13
+ export declare const DEFAULT_EXPIRY_DAYS = 30;
14
+ /** V13 factory on Base mainnet. Source: lib/blockchain/contracts/automation-vault.ts */
15
+ export declare const V13_FACTORY_ADDRESS: "0x8E547D7758C79e23146f77b783149435feeA3e45";
16
+ export declare function requireApiKey(): string;
17
+ export declare function activateUrl(workflowId: string): string;
18
+ export declare function workflowUrl(workflowId: string): string;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,aAAa,QAC6C,CAAA;AAEvE,eAAO,MAAM,YAAY,QAAiC,CAAA;AAE1D,eAAO,MAAM,aAAa,OAAO,CAAA;AAEjC,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB,SAAS,CAAA;AAC9C,eAAO,MAAM,uBAAuB,QAAQ,CAAA;AAC5C,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,EAC9B,4CAAqD,CAAA;AAEvD,wBAAgB,aAAa,IAAI,MAAM,CAOtC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD"}
package/dist/config.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @daxaur/ctrl-mcp configuration
3
+ *
4
+ * All settings come from environment variables. The user sets these in their
5
+ * MCP client config (Claude Desktop / Cursor) — see README.md for examples.
6
+ */
7
+ export const CTRL_API_BASE = process.env.CTRL_API_BASE?.replace(/\/$/, '') || 'https://ctrl.build';
8
+ export const CTRL_API_KEY = process.env.CTRL_API_KEY || '';
9
+ export const CTRL_CHAIN_ID = 8453; // Base mainnet
10
+ /** Default rule caps when user doesn't specify — conservative for safety. */
11
+ export const DEFAULT_MAX_PER_SWAP_ETH = '0.01';
12
+ export const DEFAULT_MAX_PER_DAY_ETH = '0.1';
13
+ export const DEFAULT_EXPIRY_DAYS = 30;
14
+ /** V13 factory on Base mainnet. Source: lib/blockchain/contracts/automation-vault.ts */
15
+ export const V13_FACTORY_ADDRESS = '0x8E547D7758C79e23146f77b783149435feeA3e45';
16
+ export function requireApiKey() {
17
+ if (!CTRL_API_KEY) {
18
+ throw new Error('CTRL_API_KEY env var is required. Generate one at https://ctrl.build/settings/api-keys and add it to your MCP client config.');
19
+ }
20
+ return CTRL_API_KEY;
21
+ }
22
+ export function activateUrl(workflowId) {
23
+ return `${CTRL_API_BASE}/activate/${workflowId}`;
24
+ }
25
+ export function workflowUrl(workflowId) {
26
+ return `${CTRL_API_BASE}/workflow/${workflowId}`;
27
+ }
28
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,oBAAoB,CAAA;AAEvE,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAA;AAE1D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAA,CAAC,eAAe;AAEjD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAA;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAA;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAErC,wFAAwF;AACxF,MAAM,CAAC,MAAM,mBAAmB,GAC9B,4CAAqD,CAAA;AAEvD,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,8HAA8H,CAC/H,CAAA;IACH,CAAC;IACD,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,GAAG,aAAa,aAAa,UAAU,EAAE,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,GAAG,aAAa,aAAa,UAAU,EAAE,CAAA;AAClD,CAAC"}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @daxaur/ctrl-mcp — Base MCP plugin for CTRL.
4
+ *
5
+ * Exposes 4 tools to any MCP-aware AI client (Claude Desktop, Cursor, ChatGPT)
6
+ * so users can build persistent onchain workflows on Base via natural language.
7
+ *
8
+ * The keystone is the "sign once" pattern: tools either run server-side via
9
+ * the user's API key (off-chain writes + reads), or return EIP-5792
10
+ * transactions[] envelopes for the user to sign once in Base Account.
11
+ *
12
+ * After one signature, CTRL's keeper executes the workflow autonomously per
13
+ * on-chain spending rules. No further signatures needed.
14
+ *
15
+ * Setup: see README.md for Claude Desktop and Cursor config snippets.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @daxaur/ctrl-mcp — Base MCP plugin for CTRL.
4
+ *
5
+ * Exposes 4 tools to any MCP-aware AI client (Claude Desktop, Cursor, ChatGPT)
6
+ * so users can build persistent onchain workflows on Base via natural language.
7
+ *
8
+ * The keystone is the "sign once" pattern: tools either run server-side via
9
+ * the user's API key (off-chain writes + reads), or return EIP-5792
10
+ * transactions[] envelopes for the user to sign once in Base Account.
11
+ *
12
+ * After one signature, CTRL's keeper executes the workflow autonomously per
13
+ * on-chain spending rules. No further signatures needed.
14
+ *
15
+ * Setup: see README.md for Claude Desktop and Cursor config snippets.
16
+ */
17
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
20
+ import { CREATE_WORKFLOW_TOOL, handleCreateWorkflow, } from './tools/create-workflow.js';
21
+ import { ACTIVATE_TOOL, handleActivate, } from './tools/activate-workflow.js';
22
+ import { FIRE_MANUAL_TOOL, handleFireManual } from './tools/fire-manual.js';
23
+ import { GET_EXECUTION_LOGS_TOOL, handleGetExecutionLogs, } from './tools/get-execution-logs.js';
24
+ import { CtrlApiError } from './client.js';
25
+ const server = new Server({
26
+ name: '@daxaur/ctrl-mcp',
27
+ version: '0.1.0',
28
+ }, {
29
+ capabilities: {
30
+ tools: {},
31
+ },
32
+ });
33
+ const TOOLS = [
34
+ CREATE_WORKFLOW_TOOL,
35
+ ACTIVATE_TOOL,
36
+ FIRE_MANUAL_TOOL,
37
+ GET_EXECUTION_LOGS_TOOL,
38
+ ];
39
+ const HANDLERS = {
40
+ ctrl_create_workflow: handleCreateWorkflow,
41
+ ctrl_activate: handleActivate,
42
+ ctrl_fire_manual: handleFireManual,
43
+ ctrl_get_execution_logs: handleGetExecutionLogs,
44
+ };
45
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
46
+ tools: TOOLS,
47
+ }));
48
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
49
+ const { name, arguments: args } = req.params;
50
+ const handler = HANDLERS[name];
51
+ if (!handler) {
52
+ return {
53
+ content: [
54
+ {
55
+ type: 'text',
56
+ text: `Unknown tool: ${name}. Available: ${Object.keys(HANDLERS).join(', ')}`,
57
+ },
58
+ ],
59
+ isError: true,
60
+ };
61
+ }
62
+ try {
63
+ const result = await handler(args ?? {});
64
+ return result;
65
+ }
66
+ catch (err) {
67
+ const isApi = err instanceof CtrlApiError;
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ return {
70
+ content: [
71
+ {
72
+ type: 'text',
73
+ text: isApi
74
+ ? `CTRL API error (HTTP ${err.status}): ${message}`
75
+ : `Tool error: ${message}`,
76
+ },
77
+ ],
78
+ isError: true,
79
+ };
80
+ }
81
+ });
82
+ async function main() {
83
+ const transport = new StdioServerTransport();
84
+ await server.connect(transport);
85
+ // Log to stderr so it doesn't corrupt the stdio protocol on stdout.
86
+ console.error('[@daxaur/ctrl-mcp] server running on stdio');
87
+ }
88
+ main().catch((err) => {
89
+ console.error('[@daxaur/ctrl-mcp] fatal:', err);
90
+ process.exit(1);
91
+ });
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}