@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 +160 -0
- package/dist/blocks.d.ts +117 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +103 -0
- package/dist/blocks.js.map +1 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +60 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/activate-workflow.d.ts +67 -0
- package/dist/tools/activate-workflow.d.ts.map +1 -0
- package/dist/tools/activate-workflow.js +95 -0
- package/dist/tools/activate-workflow.js.map +1 -0
- package/dist/tools/create-workflow.d.ts +154 -0
- package/dist/tools/create-workflow.d.ts.map +1 -0
- package/dist/tools/create-workflow.js +139 -0
- package/dist/tools/create-workflow.js.map +1 -0
- package/dist/tools/fire-manual.d.ts +33 -0
- package/dist/tools/fire-manual.d.ts.map +1 -0
- package/dist/tools/fire-manual.js +49 -0
- package/dist/tools/fire-manual.js.map +1 -0
- package/dist/tools/get-execution-logs.d.ts +49 -0
- package/dist/tools/get-execution-logs.d.ts.map +1 -0
- package/dist/tools/get-execution-logs.js +80 -0
- package/dist/tools/get-execution-logs.js.map +1 -0
- package/package.json +53 -0
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)
|
package/dist/blocks.d.ts
ADDED
|
@@ -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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"}
|
package/dist/client.d.ts
ADDED
|
@@ -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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|