@noelclaw/mcp 1.4.1 → 1.5.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 CHANGED
@@ -1,238 +1,258 @@
1
- # @noelclaw/mcp
2
-
3
- [![npm version](https://img.shields.io/npm/v/@noelclaw/mcp.svg)](https://www.npmjs.com/package/@noelclaw/mcp)
4
-
5
- Noelclaw as an MCP skill — persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks. Works with Claude, Cursor, Hermes, Windsurf, and any MCP-compatible client.
6
-
7
- ```bash
8
- npx @noelclaw/mcp
9
- ```
10
-
11
- ---
12
-
13
- ## Quick Install
14
-
15
- ### Claude Code
16
- ```bash
17
- claude mcp add noelclaw -- npx @noelclaw/mcp
18
- ```
19
-
20
- Set your API key:
21
- ```bash
22
- claude mcp add noelclaw -e NOELCLAW_API_KEY=noel_... -- npx @noelclaw/mcp
23
- ```
24
-
25
- ### Claude Desktop
26
- Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
27
-
28
- ```json
29
- {
30
- "mcpServers": {
31
- "noelclaw": {
32
- "command": "npx",
33
- "args": ["@noelclaw/mcp"],
34
- "env": {
35
- "NOELCLAW_API_KEY": "noel_..."
36
- }
37
- }
38
- }
39
- }
40
- ```
41
-
42
- ### Cursor / Windsurf
43
- ```json
44
- {
45
- "mcpServers": {
46
- "noelclaw": {
47
- "command": "npx",
48
- "args": ["@noelclaw/mcp"],
49
- "env": {
50
- "NOELCLAW_API_KEY": "noel_..."
51
- }
52
- }
53
- }
54
- }
55
- ```
56
-
57
- ### Hermes
58
- ```yaml
59
- mcp_servers:
60
- noelclaw:
61
- command: npx
62
- args:
63
- - "@noelclaw/mcp"
64
- env:
65
- NOELCLAW_API_KEY: "noel_..."
66
- ```
67
-
68
- ---
69
-
70
- ## Authentication
71
-
72
- Get a key instantly — no signup:
73
-
74
- ```bash
75
- curl -X POST https://api.noelclaw.com/auth/key
76
- # → { "apiKey": "noel_..." }
77
- ```
78
-
79
- Set `NOELCLAW_API_KEY` in your MCP config. That's it.
80
-
81
- ---
82
-
83
- ## Tools
84
-
85
- ### Research & AI
86
-
87
- | Tool | Description |
88
- |------|-------------|
89
- | `research` | Deep research via Bankr (real-time). Returns overview, key findings, market impact, sentiment |
90
- | `ask_noel` | Ask Noel AI for analysis, trade ideas, and research |
91
- | `humanize_text` | Remove AI tells from text — makes output sound natural and human-written |
92
-
93
- ### Noel-Vault
94
-
95
- > Persistent memory across sessions. Save findings, recall by key, search full-text. Every save auto-versions.
96
-
97
- | Tool | Description |
98
- |------|-------------|
99
- | `vault_save` | Save any content — research, execution logs, workflows, prompts, files |
100
- | `vault_read` | Read an entry by key |
101
- | `vault_list` | List all entries with type, title, version, last updated |
102
- | `vault_search` | Full-text search across all content |
103
- | `vault_history` | Version history with commit messages |
104
- | `vault_diff` | Line-by-line diff between two versions |
105
- | `vault_export` | Export as markdown or JSON |
106
-
107
- ### Noel-Swarm
108
-
109
- > Shared memory bus for multi-agent coordination. All agents read/write the same store with freshness tracking.
110
-
111
- | Tool | Description |
112
- |------|-------------|
113
- | `start_swarm` | Start a swarm session |
114
- | `stop_swarm` | Stop the active session |
115
- | `get_swarm_status` | Session state, memory snapshot, execution scores |
116
- | `write_swarm_memory` | Write a key-value entry with optional TTL |
117
- | `get_swarm_memory` | Read by key — returns value + freshness metadata |
118
- | `get_execution_scores` | Per-agent, per-skill scores |
119
-
120
- ### MiroShark
121
-
122
- > Scenario simulation engine — drop in any scenario and get back strategic insights from a network of AI agents reacting hour by hour. Requires `MIROSHARK_URL` + `MIROSHARK_ADMIN_TOKEN`.
123
-
124
- | Tool | Description |
125
- |------|-------------|
126
- | `miroshark_simulate` | Run a multi-agent simulation from a plain-English scenario. Returns a simulation ID |
127
- | `miroshark_status` | Poll simulation results by ID — surfaces insights and consensus when complete |
128
-
129
- ### Wallet & DeFi `beta`
130
-
131
- > On-chain operations on Base mainnet. Transactions are built for client-side signing — no private key ever leaves your machine.
132
-
133
- | Tool | Description |
134
- |------|-------------|
135
- | `get_wallet_address` | Show your local MCP wallet address |
136
- | `get_portfolio` | Full token portfolio with ETH and ERC-20 balances and USD values |
137
- | `swap_tokens` | Swap tokens on Base mainnet |
138
- | `send_token` | Send ETH or any ERC-20 to any address |
139
-
140
- ### Automations `beta`
141
-
142
- | Tool | Description |
143
- |------|-------------|
144
- | `create_automation` | Create an automation in plain English — DCA, price alerts, conditional buys/sells |
145
- | `list_automations` | List all automations with status and next scheduled run |
146
- | `pause_automation` | Pause or resume an automation |
147
- | `delete_automation` | Permanently delete an automation |
148
-
149
- ### Noel Framework `beta`
150
-
151
- > Sentinel-gated agent execution. Define what your AI can and can't do — before it runs. Every action checked against 5 mechanical rules before execution.
152
-
153
- | Tool | Description |
154
- |------|-------------|
155
- | `create_task_packet` | Convert plain-English intent into a structured task scope with permissions and constraints |
156
- | `list_task_packets` | List all task packets |
157
- | `list_playbooks` | List available playbooks |
158
- | `run_playbook` | Execute a Sentinel-gated playbook — halts if any step is blocked |
159
- | `get_noel_ledger` | Full audit trail of every Sentinel decision |
160
- | `get_sentinel_rules` | Exact rules per agent role |
161
-
162
- ### Notifications
163
-
164
- | Tool | Description |
165
- |------|-------------|
166
- | `set_telegram` | Connect Telegram for push notifications |
167
-
168
- ---
169
-
170
- ## Environment Variables
171
-
172
- ### Required
173
-
174
- | Var | Description |
175
- |-----|-------------|
176
- | `NOELCLAW_API_KEY` | Your API key (`noel_...`) — get one at `POST https://api.noelclaw.com/auth/key` |
177
-
178
- ### BYOK (optional)
179
-
180
- | Var | Used for |
181
- |-----|---------|
182
- | `BANKR_API_KEY` | Bankr Agent — research, DeFi |
183
- | `TELEGRAM_BOT_TOKEN` | Your own Telegram bot |
184
- | `TELEGRAM_CHAT_ID` | Your Telegram chat ID |
185
- | `ALCHEMY_API_KEY` | Faster Base RPC for swaps and portfolio |
186
-
187
- ---
188
-
189
- ## Usage Examples
190
-
191
- ```
192
- # Research anything
193
- research(query: "What is happening with the Base ecosystem this week?")
194
- ask_noel(question: "What are the risks of holding ETH through a Fed meeting?")
195
-
196
- # Save findings to vault
197
- vault_save(type: "research", key: "research/base-may-2026", title: "Base Ecosystem", content: "...")
198
- vault_search(query: "Base ecosystem")
199
-
200
- # Coordinate agents via swarm
201
- start_swarm
202
- write_swarm_memory(agentId: "analyst", key: "research/btc", value: "bullish", ttlSeconds: 3600)
203
- get_swarm_memory(key: "research/btc")
204
-
205
- # Run a MiroShark simulation
206
- miroshark_simulate(scenario: "What happens if a major L1 announces a 50% fee reduction?")
207
- miroshark_status(simulation_id: "sim_abc123")
208
-
209
- # Clean up AI-generated text
210
- humanize_text(text: "Certainly! I'd be happy to assist you with...")
211
-
212
- # DeFi (beta)
213
- get_portfolio
214
- swap_tokens(fromToken: "ETH", toToken: "USDC", amount: "0.01")
215
-
216
- # Sentinel-gated execution (beta)
217
- create_task_packet(task: "Monitor portfolio, max $0 spend, read only")
218
- run_playbook(playbook_name: "Daily Market Scan")
219
- get_noel_ledger
220
- ```
221
-
222
- ---
223
-
224
- ## Troubleshooting
225
-
226
- | Error | Fix |
227
- |-------|-----|
228
- | Tools not appearing | Restart your MCP client after adding the config |
229
- | `401 Unauthorized` | Check `NOELCLAW_API_KEY` is set — get one at `POST https://api.noelclaw.com/auth/key` |
230
- | Server starts but no response | Expected — server waits for MCP stdin, not HTTP |
231
-
232
- ---
233
-
234
- ## Links
235
-
236
- - npm: [npmjs.com/package/@noelclaw/mcp](https://npmjs.com/package/@noelclaw/mcp)
237
- - GitHub: [github.com/noelclaw/mcp](https://github.com/noelclaw/mcp)
238
- - Platform: [noelclaw.com](https://noelclaw.com)
1
+ # @noelclaw/mcp
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@noelclaw/mcp.svg)](https://www.npmjs.com/package/@noelclaw/mcp)
4
+
5
+ Noelclaw as an MCP skill persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks. Works with Claude, Cursor, Hermes, Windsurf, and any MCP-compatible client.
6
+
7
+ **37 tools** across market data, research, vault, swarm, MiroShark simulation, DeFi, automation, and framework.
8
+
9
+ ```bash
10
+ npx @noelclaw/mcp
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Quick Install
16
+
17
+ ### Claude Code
18
+ ```bash
19
+ claude mcp add noelclaw -e NOELCLAW_API_KEY=noel_... -- npx @noelclaw/mcp
20
+ ```
21
+
22
+ ### Claude Desktop
23
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "noelclaw": {
29
+ "command": "npx",
30
+ "args": ["@noelclaw/mcp"],
31
+ "env": {
32
+ "NOELCLAW_API_KEY": "noel_..."
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### Cursor / Windsurf
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "noelclaw": {
44
+ "command": "npx",
45
+ "args": ["@noelclaw/mcp"],
46
+ "env": {
47
+ "NOELCLAW_API_KEY": "noel_..."
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### Hermes
55
+ ```yaml
56
+ mcp_servers:
57
+ noelclaw:
58
+ command: npx
59
+ args:
60
+ - "@noelclaw/mcp"
61
+ env:
62
+ NOELCLAW_API_KEY: "noel_..."
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Authentication
68
+
69
+ Get a key instantly — no signup:
70
+
71
+ ```bash
72
+ curl -X POST https://api.noelclaw.com/auth/key
73
+ # → { "apiKey": "noel_..." }
74
+ ```
75
+
76
+ Set `NOELCLAW_API_KEY` in your MCP config. That's it.
77
+
78
+ ---
79
+
80
+ ## Tools
81
+
82
+ ### Market Data
83
+
84
+ | Tool | Description |
85
+ |------|-------------|
86
+ | `get_market_data` | Live prices for BTC, ETH, SOL — sourced from CoinGecko via swarm. Pass a token symbol to focus |
87
+ | `get_token_data` | Price, 24h change, market cap, and volume for any specific token |
88
+
89
+ ### Research & AI
90
+
91
+ | Tool | Description |
92
+ |------|-------------|
93
+ | `research` | Deep research via Bankr (real-time). Returns overview, key findings, market impact, sentiment |
94
+ | `get_insight` | Noel's daily crypto + macro insight — price action, narratives, on-chain signals, one takeaway |
95
+ | `ask_noel` | Ask Noel AI for DeFi analysis, trade ideas, and market research with live context |
96
+ | `humanize_text` | Remove AI tells from text — fixes 29 AI patterns, makes output sound natural. Powered by MiniMax-M2.7. Requires `MINIMAX_API_KEY` |
97
+
98
+ ### Noel-Vault
99
+
100
+ > Persistent memory across sessions. Save findings, recall by key, search full-text. Every save auto-versions.
101
+
102
+ | Tool | Description |
103
+ |------|-------------|
104
+ | `vault_save` | Save any content research, execution logs, workflows, prompts, files, memory |
105
+ | `vault_read` | Read an entry by key |
106
+ | `vault_list` | List all entries with type, title, version, last updated |
107
+ | `vault_search` | Full-text search across all content |
108
+ | `vault_history` | Version history with commit messages |
109
+ | `vault_diff` | Line-by-line diff between two versions |
110
+ | `vault_export` | Export full vault or filter by type |
111
+
112
+ ### Noel-Swarm
113
+
114
+ > Shared memory bus for multi-agent coordination. All agents read/write the same store with freshness tracking and execution scoring.
115
+
116
+ | Tool | Description |
117
+ |------|-------------|
118
+ | `start_swarm` | Start a swarm session |
119
+ | `stop_swarm` | Stop the active session |
120
+ | `get_swarm_status` | Session state, memory snapshot, execution scores |
121
+ | `write_swarm_memory` | Write a key-value entry with optional TTL. Use `market/*` / `dex/*` keys for live CoinGecko / DexScreener data |
122
+ | `get_swarm_memory` | Read by key returns value + freshness metadata |
123
+ | `get_execution_scores` | Per-agent, per-skill performance scores |
124
+
125
+ ### MiroShark
126
+
127
+ > Multi-agent scenario simulation — describe any scenario in plain English, get back a full simulation with AI agents acting as market participants, analysts, and social actors.
128
+
129
+ | Tool | Description |
130
+ |------|-------------|
131
+ | `miroshark_simulate` | Run a simulation from a plain-English scenario. Handles full setup automatically (knowledge graph, 42+ agents). Returns a simulation ID |
132
+ | `miroshark_status` | Poll progress — surfaces agent actions, round count, consensus when complete |
133
+
134
+ No extra env vars needed. MiroShark is hosted on Noelclaw's infrastructure.
135
+
136
+ ### Wallet & DeFi `beta`
137
+
138
+ > On-chain operations on Base mainnet. Transactions are signed client-side no private key ever leaves your machine.
139
+
140
+ | Tool | Description |
141
+ |------|-------------|
142
+ | `get_wallet_address` | Show your local MCP wallet address |
143
+ | `swap_tokens` | Swap tokens on Base mainnet via 0x Permit2 (ETH, USDC, USDT, DAI, WETH) |
144
+ | `send_token` | Send ETH or any ERC-20 to any address |
145
+ | `claim_fees` | Claim accumulated ETH from Flaunch token swap fees |
146
+
147
+ ### Automations `beta`
148
+
149
+ | Tool | Description |
150
+ |------|-------------|
151
+ | `create_automation` | Create an automation in plain English DCA, price alerts, conditional buys/sells |
152
+ | `list_automations` | List all automations with status and next scheduled run |
153
+ | `pause_automation` | Pause or resume an automation |
154
+ | `delete_automation` | Permanently delete an automation |
155
+
156
+ ### Noel Framework `beta`
157
+
158
+ > Sentinel-gated agent execution. Every action checked against 5 mechanical rules before it runs.
159
+
160
+ | Tool | Description |
161
+ |------|-------------|
162
+ | `create_task_packet` | Convert plain-English intent into a structured task scope with permissions and constraints |
163
+ | `list_task_packets` | List all task packets |
164
+ | `list_playbooks` | List available playbooks |
165
+ | `run_playbook` | Execute a Sentinel-gated playbook — halts if any step is blocked |
166
+ | `get_noel_ledger` | Full audit trail of every Sentinel decision |
167
+ | `get_sentinel_rules` | Exact rules per agent role |
168
+
169
+ ### Social
170
+
171
+ | Tool | Description |
172
+ |------|-------------|
173
+ | `post_tweet` | Post a tweet. Requires `TWITTER_BEARER_TOKEN` or delegated via Noelclaw |
174
+ | `set_telegram` | Connect Telegram for push notifications from market and swarm events |
175
+
176
+ ---
177
+
178
+ ## Environment Variables
179
+
180
+ ### Required
181
+
182
+ | Var | Description |
183
+ |-----|-------------|
184
+ | `NOELCLAW_API_KEY` | Your API key (`noel_...`) — get one at `POST https://api.noelclaw.com/auth/key` |
185
+
186
+ ### Optional
187
+
188
+ | Var | Used for |
189
+ |-----|---------|
190
+ | `MINIMAX_API_KEY` | `humanize_text` tool — powered by MiniMax-M2.7 |
191
+ | `BANKR_API_KEY` | `research` tool — Bankr Agent deep research |
192
+ | `TELEGRAM_BOT_TOKEN` | Your own Telegram bot for notifications |
193
+ | `TELEGRAM_CHAT_ID` | Your Telegram chat ID |
194
+ | `ALCHEMY_API_KEY` | Faster Base RPC for swaps |
195
+ | `GROK_API_KEY` | Grok integration via BYOK |
196
+
197
+ ---
198
+
199
+ ## Usage Examples
200
+
201
+ ```
202
+ # Live market data
203
+ get_market_data() # BTC, ETH, SOL prices
204
+ get_token_data(question: "What is the price of HYPE?")
205
+
206
+ # Research
207
+ research(query: "What is happening with the Base ecosystem this week?")
208
+ get_insight()
209
+ ask_noel(question: "What are the risks of holding ETH through a Fed meeting?")
210
+
211
+ # Humanize AI-generated text
212
+ humanize_text(text: "In today's rapidly evolving landscape, it is worth noting...")
213
+
214
+ # Save findings to vault
215
+ vault_save(type: "research", key: "research/base-may-2026", title: "Base Ecosystem", content: "...")
216
+ vault_search(query: "Base ecosystem")
217
+ vault_history(key: "research/base-may-2026")
218
+
219
+ # Coordinate agents via swarm
220
+ start_swarm()
221
+ write_swarm_memory(agentId: "analyst", key: "research/btc", value: "bullish", ttlSeconds: 3600)
222
+ get_swarm_memory(key: "market/BTC") # real-time CoinGecko price
223
+ get_swarm_memory(key: "dex/PEPE") # real-time DexScreener data
224
+
225
+ # Run a MiroShark simulation
226
+ miroshark_simulate(scenario: "What happens if the US passes a Bitcoin strategic reserve bill?")
227
+ miroshark_status(simulation_id: "sim_abc123")
228
+
229
+ # DeFi (beta)
230
+ swap_tokens(fromToken: "ETH", toToken: "USDC", amount: "0.01")
231
+ send_token(token: "USDC", toAddress: "0x...", amount: "10")
232
+ claim_fees()
233
+
234
+ # Sentinel-gated execution (beta)
235
+ create_task_packet(task: "Monitor portfolio, max $0 spend, read only")
236
+ run_playbook(playbook_name: "Daily Market Scan")
237
+ get_noel_ledger()
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Troubleshooting
243
+
244
+ | Error | Fix |
245
+ |-------|-----|
246
+ | Tools not appearing | Restart your MCP client after adding the config |
247
+ | `401 Unauthorized` | Check `NOELCLAW_API_KEY` is set — get one at `POST https://api.noelclaw.com/auth/key` |
248
+ | `humanize_text` fails | Set `MINIMAX_API_KEY` in your MCP env config |
249
+ | `research` fails | Set `BANKR_API_KEY` — Bankr access required for deep research |
250
+ | Server starts but no response | Expected — server waits for MCP stdin, not HTTP |
251
+
252
+ ---
253
+
254
+ ## Links
255
+
256
+ - npm: [npmjs.com/package/@noelclaw/mcp](https://npmjs.com/package/@noelclaw/mcp)
257
+ - GitHub: [github.com/noelclaw/mcp](https://github.com/noelclaw/mcp)
258
+ - Platform: [noelclaw.com](https://noelclaw.com)
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ async function main() {
19
19
  try {
20
20
  const wallet = await (0, wallet_js_1.getOrCreateWallet)();
21
21
  console.error(`\x1b[36m ◆ wallet\x1b[0m ${wallet.address}`);
22
- console.error(`\x1b[36m ◆ status\x1b[0m ready · 46 tools loaded\n`);
22
+ console.error(`\x1b[36m ◆ status\x1b[0m ready · 34 tools loaded\n`);
23
23
  }
24
24
  catch (err) {
25
25
  console.error(`[noelclaw] wallet init failed: ${err}`);
package/dist/server.js CHANGED
@@ -34,9 +34,8 @@ function containsSensitiveRequest(args) {
34
34
  }
35
35
  exports.ALL_TOOLS = [
36
36
  ...market_js_1.MARKET_TOOLS, // 2 — market data, token data
37
- ...research_js_1.RESEARCH_TOOLS, // 1 — deep research
38
- ...insight_js_1.INSIGHT_TOOLS, // 2 — get_insight, ask_noel
39
- ...defi_js_1.DEFI_TOOLS, // 3 — swap, send, claim
37
+ ...insight_js_1.INSIGHT_TOOLS, // 1 — ask_noel
38
+ ...defi_js_1.DEFI_TOOLS, // 2 — swap, send
40
39
  ...automation_js_1.AUTOMATION_TOOLS, // 4 — create, list, pause, delete
41
40
  ...swarm_js_1.SWARM_TOOLS, // 6 — start, stop, status, memory, scores
42
41
  ...framework_js_1.FRAMEWORK_TOOLS, // 6 — task packets, playbooks, sentinel, ledger
@@ -45,7 +44,7 @@ exports.ALL_TOOLS = [
45
44
  ...twitter_js_1.TWITTER_TOOLS, // 1 — post tweet
46
45
  ...miroshark_js_1.MIROSHARK_TOOLS, // 2 — simulate, status
47
46
  ...humanizer_js_1.HUMANIZER_TOOLS, // 1 — humanize_text
48
- // total: 37
47
+ // total: 34
49
48
  ];
50
49
  exports.server = new index_js_1.Server({ name: "noelclaw", version: "2.1.0" }, { capabilities: { tools: {} } });
51
50
  exports.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: exports.ALL_TOOLS }));
@@ -32,15 +32,6 @@ exports.DEFI_TOOLS = [
32
32
  required: ["token", "toAddress", "amount"],
33
33
  },
34
34
  },
35
- {
36
- name: "claim_fees",
37
- description: "Claim accumulated ETH from your Flaunch token swap fees. Calls claim() on the Flaunch PositionManager — pulls all pending ETH from your deployed tokens to your wallet.",
38
- inputSchema: {
39
- type: "object",
40
- properties: {},
41
- required: [],
42
- },
43
- },
44
35
  ];
45
36
  const SwapSchema = zod_1.z.object({ fromToken: zod_1.z.string().min(1), toToken: zod_1.z.string().min(1), amount: zod_1.z.string().min(1) });
46
37
  const SendSchema = zod_1.z.object({ token: zod_1.z.string().min(1), toAddress: zod_1.z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a valid 0x address"), amount: zod_1.z.string().min(1) });
@@ -50,7 +41,7 @@ async function handleDefiTool(name, args) {
50
41
  const parsed = SwapSchema.safeParse(args);
51
42
  if (!parsed.success)
52
43
  return { content: [{ type: "text", text: `Invalid input: ${String(parsed.error.issues[0].path[0])} ${parsed.error.issues[0].message}` }], isError: true };
53
- let { fromToken, toToken, amount } = parsed.data;
44
+ const { fromToken, toToken, amount } = parsed.data;
54
45
  const wallet = await (0, wallet_js_1.getOrCreateWallet)();
55
46
  const result = await (0, convex_js_1.callConvex)("/mcp/defi/swap", "POST", { fromToken, toToken, amount }, "swap_tokens");
56
47
  if (!result.success)
@@ -74,7 +65,6 @@ async function handleDefiTool(name, args) {
74
65
  if (!result.success)
75
66
  return { content: [{ type: "text", text: `Send failed: ${result.error}` }], isError: true };
76
67
  const txHash = await (0, wallet_js_1.signAndBroadcast)(wallet, result.txData);
77
- // Collect 0.5% platform fee as a separate tx if the server returned fee data
78
68
  if (result.feeTxData) {
79
69
  try {
80
70
  await (0, wallet_js_1.signAndBroadcast)(wallet, result.feeTxData);
@@ -88,19 +78,6 @@ async function handleDefiTool(name, args) {
88
78
  }],
89
79
  };
90
80
  }
91
- case "claim_fees": {
92
- const wallet = await (0, wallet_js_1.getOrCreateWallet)();
93
- const result = await (0, convex_js_1.callConvex)("/mcp/token/claim", "POST", {}, "claim_fees");
94
- if (result.error)
95
- return { content: [{ type: "text", text: `Claim failed: ${result.error}` }], isError: true };
96
- const txHash = await (0, wallet_js_1.signAndBroadcast)(wallet, result);
97
- return {
98
- content: [{
99
- type: "text",
100
- text: [`✅ ETH claimed successfully!`, `Tx Hash: \`${txHash}\``, `https://basescan.org/tx/${txHash}`].join("\n"),
101
- }],
102
- };
103
- }
104
81
  default:
105
82
  return null;
106
83
  }
@@ -5,11 +5,6 @@ exports.handleInsightTool = handleInsightTool;
5
5
  const zod_1 = require("zod");
6
6
  const convex_js_1 = require("../convex.js");
7
7
  exports.INSIGHT_TOOLS = [
8
- {
9
- name: "get_insight",
10
- description: "Get Noel's daily crypto + macro insight. Covers Bitcoin/ETH price action, macro events, trending narratives on X/Twitter, and one actionable takeaway.",
11
- inputSchema: { type: "object", properties: {}, required: [] },
12
- },
13
8
  {
14
9
  name: "ask_noel",
15
10
  description: "Ask Noel AI for DeFi analysis, trade ideas, market outlook, and crypto research. Noel has live market context.",
@@ -36,24 +31,15 @@ const AskNoelSchema = zod_1.z.object({
36
31
  messages: zod_1.z.array(zod_1.z.object({ role: zod_1.z.enum(["user", "assistant"]), content: zod_1.z.string() })).optional(),
37
32
  });
38
33
  async function handleInsightTool(name, args) {
39
- switch (name) {
40
- case "get_insight": {
41
- const data = await (0, convex_js_1.callConvex)("/insights/now", "GET", undefined, "get_insight");
42
- const insight = data.insight ?? data.text ?? data.choices?.[0]?.message?.content ?? "Failed to get insight";
43
- return { content: [{ type: "text", text: insight }] };
44
- }
45
- case "ask_noel": {
46
- const parsed = AskNoelSchema.safeParse(args);
47
- if (!parsed.success)
48
- return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
49
- const data = await (0, convex_js_1.callConvex)("/mcp/chat", "POST", {
50
- question: parsed.data.question,
51
- agentId: "noel-default",
52
- messages: parsed.data.messages ?? [],
53
- }, "ask_noel");
54
- return { content: [{ type: "text", text: data.answer ?? JSON.stringify(data) }] };
55
- }
56
- default:
57
- return null;
58
- }
34
+ if (name !== "ask_noel")
35
+ return null;
36
+ const parsed = AskNoelSchema.safeParse(args);
37
+ if (!parsed.success)
38
+ return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
39
+ const data = await (0, convex_js_1.callConvex)("/mcp/chat", "POST", {
40
+ question: parsed.data.question,
41
+ agentId: "noel-default",
42
+ messages: parsed.data.messages ?? [],
43
+ }, "ask_noel");
44
+ return { content: [{ type: "text", text: data.answer ?? JSON.stringify(data) }] };
59
45
  }
@@ -1,70 +1,163 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MARKET_TOOLS = void 0;
4
+ exports.fetchMarketSnapshot = fetchMarketSnapshot;
4
5
  exports.handleMarketTool = handleMarketTool;
5
6
  const zod_1 = require("zod");
6
- const convex_js_1 = require("../convex.js");
7
+ const COINGECKO = "https://api.coingecko.com/api/v3";
8
+ const SYMBOL_TO_ID = {
9
+ BTC: "bitcoin", ETH: "ethereum", SOL: "solana", BNB: "binancecoin",
10
+ USDT: "tether", USDC: "usd-coin", XRP: "ripple", DOGE: "dogecoin",
11
+ ADA: "cardano", AVAX: "avalanche-2", DOT: "polkadot", LINK: "chainlink",
12
+ UNI: "uniswap", OP: "optimism", ARB: "arbitrum", PEPE: "pepe",
13
+ SUI: "sui", APT: "aptos", NEAR: "near", INJ: "injective-protocol",
14
+ TIA: "celestia", MATIC: "matic-network", TON: "the-open-network",
15
+ SHIB: "shiba-inu", WIF: "dogwifcoin", BONK: "bonk", HYPE: "hyperliquid",
16
+ };
17
+ async function cgFetch(path) {
18
+ const res = await fetch(`${COINGECKO}${path}`, {
19
+ headers: { Accept: "application/json" },
20
+ signal: AbortSignal.timeout(15000),
21
+ });
22
+ if (!res.ok)
23
+ throw new Error(`CoinGecko ${res.status}`);
24
+ return res.json();
25
+ }
26
+ function fmt(n, decimals = 2) {
27
+ if (n == null)
28
+ return "—";
29
+ return n.toLocaleString("en-US", { maximumFractionDigits: decimals });
30
+ }
31
+ function fmtPrice(n) {
32
+ if (n == null)
33
+ return "—";
34
+ if (n >= 1)
35
+ return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
36
+ return `$${n.toPrecision(4)}`;
37
+ }
38
+ function fmtB(n) {
39
+ if (n == null)
40
+ return "—";
41
+ if (n >= 1e9)
42
+ return `$${(n / 1e9).toFixed(2)}B`;
43
+ if (n >= 1e6)
44
+ return `$${(n / 1e6).toFixed(1)}M`;
45
+ return `$${fmt(n)}`;
46
+ }
7
47
  exports.MARKET_TOOLS = [
8
48
  {
9
49
  name: "get_market_data",
10
- description: "Get live crypto market data: top 20 coins by market cap, trending coins, and key prices for BTC/ETH/SOL. Results are also sent to your Telegram if configured.",
50
+ description: "Get live crypto market data: top 20 coins by market cap, trending coins, and key prices for BTC/ETH/SOL.",
11
51
  inputSchema: {
12
52
  type: "object",
13
- properties: { token: { type: "string", description: "Optional: specific token to focus on, e.g. 'BTC', 'ETH'" } },
53
+ properties: { token: { type: "string", description: "Optional: focus on a specific token, e.g. 'BTC', 'ETH'" } },
14
54
  required: [],
15
55
  },
16
56
  },
17
57
  {
18
58
  name: "get_token_data",
19
- description: "Get market data for specific tokens. Returns price, 24h change, market cap, and volume. Results are sent to your Telegram if configured.",
59
+ description: "Get live market data for a specific token. Returns price, 24h change, market cap, and volume.",
20
60
  inputSchema: {
21
61
  type: "object",
22
- properties: { question: { type: "string", description: "Describe which tokens to look up" } },
62
+ properties: { question: { type: "string", description: "Token to look up, e.g. 'ETH', 'show me SOL', 'PEPE price'" } },
23
63
  required: ["question"],
24
64
  },
25
65
  },
26
66
  ];
27
67
  const GetMarketDataSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
28
68
  const GetTokenDataSchema = zod_1.z.object({ question: zod_1.z.string().min(1) });
69
+ async function fetchMarketSnapshot() {
70
+ try {
71
+ const data = await cgFetch("/coins/markets?vs_currency=usd&ids=bitcoin,ethereum,solana&sparkline=false&price_change_percentage=24h");
72
+ const find = (id) => data.find((c) => c.id === id)?.current_price ?? null;
73
+ return { btc: find("bitcoin"), eth: find("ethereum"), sol: find("solana") };
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
29
79
  async function handleMarketTool(name, args) {
30
80
  switch (name) {
31
81
  case "get_market_data": {
32
82
  const parsed = GetMarketDataSchema.safeParse(args ?? {});
33
83
  if (!parsed.success)
34
- return { content: [{ type: "text", text: `Invalid input: token ${parsed.error.issues[0].message}` }], isError: true };
84
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
35
85
  const { token } = parsed.data;
36
- const symbols = token ? [token.toUpperCase()] : ["BTC", "ETH", "SOL"];
37
- const results = await Promise.all(symbols.map((s) => (0, convex_js_1.callConvex)(`/swarm/memory/read?key=market/${s}`, "GET", undefined, "get_market_data")));
38
- const lines = [`**Market Data** — ${new Date().toISOString()}`, ""];
39
- for (const d of results) {
40
- if (d.error)
86
+ if (token) {
87
+ const sym = token.toUpperCase();
88
+ const id = SYMBOL_TO_ID[sym];
89
+ if (!id)
90
+ return { content: [{ type: "text", text: `Unknown token: ${sym}. Try get_token_data for specific lookup.` }], isError: true };
91
+ const [data, trending] = await Promise.all([
92
+ cgFetch(`/coins/markets?vs_currency=usd&ids=${id}&sparkline=false&price_change_percentage=24h`),
93
+ cgFetch("/search/trending"),
94
+ ]);
95
+ const c = data[0];
96
+ if (!c)
97
+ return { content: [{ type: "text", text: `No data for ${sym}` }], isError: true };
98
+ const sign = (c.price_change_percentage_24h ?? 0) >= 0 ? "+" : "";
99
+ const lines = [
100
+ `**${c.symbol?.toUpperCase()} — ${c.name}**`,
101
+ `Price: ${fmtPrice(c.current_price)} (${sign}${fmt(c.price_change_percentage_24h)}% 24h)`,
102
+ `Market Cap: ${fmtB(c.market_cap)} (rank #${c.market_cap_rank ?? "—"})`,
103
+ `Volume 24h: ${fmtB(c.total_volume)}`,
104
+ `High/Low 24h: ${fmtPrice(c.high_24h)} / ${fmtPrice(c.low_24h)}`,
105
+ "",
106
+ `_Source: CoinGecko · ${new Date().toUTCString()}_`,
107
+ ];
108
+ return { content: [{ type: "text", text: lines.join("\n") }] };
109
+ }
110
+ const [top20, trending] = await Promise.all([
111
+ cgFetch("/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=20&page=1&sparkline=false&price_change_percentage=24h"),
112
+ cgFetch("/search/trending"),
113
+ ]);
114
+ const lines = [`**Crypto Market Overview** — ${new Date().toUTCString()}`, ""];
115
+ lines.push("**Key Prices**");
116
+ for (const sym of ["BTC", "ETH", "SOL"]) {
117
+ const c = top20.find((x) => x.symbol?.toUpperCase() === sym);
118
+ if (!c)
41
119
  continue;
42
- const price = d.price_usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
43
- const ch = d.change_24h_pct?.toFixed(2);
44
- const sign = (d.change_24h_pct ?? 0) >= 0 ? "+" : "";
45
- lines.push(`• **${d.symbol}**: ${price} (${sign}${ch}%) mcap $${(d.market_cap_usd / 1e9).toFixed(1)}B`);
120
+ const sign = (c.price_change_percentage_24h ?? 0) >= 0 ? "+" : "";
121
+ lines.push(`• **${sym}**: ${fmtPrice(c.current_price)} (${sign}${fmt(c.price_change_percentage_24h)}% 24h) — mcap ${fmtB(c.market_cap)}`);
122
+ }
123
+ lines.push("", "**Top 20 by Market Cap**");
124
+ for (const c of top20) {
125
+ const sym = c.symbol?.toUpperCase();
126
+ const sign = (c.price_change_percentage_24h ?? 0) >= 0 ? "+" : "";
127
+ lines.push(`${c.market_cap_rank}. **${sym}** ${fmtPrice(c.current_price)} (${sign}${fmt(c.price_change_percentage_24h)}%) — ${fmtB(c.market_cap)}`);
128
+ }
129
+ const trendingCoins = trending?.coins?.slice(0, 7) ?? [];
130
+ if (trendingCoins.length > 0) {
131
+ lines.push("", "**Trending**");
132
+ for (const t of trendingCoins) {
133
+ const item = t.item;
134
+ lines.push(`• **${item.symbol}** (#${item.market_cap_rank ?? "—"}) — ${item.name}`);
135
+ }
46
136
  }
137
+ lines.push("", `_Source: CoinGecko_`);
47
138
  return { content: [{ type: "text", text: lines.join("\n") }] };
48
139
  }
49
140
  case "get_token_data": {
50
141
  const parsed = GetTokenDataSchema.safeParse(args);
51
142
  if (!parsed.success)
52
- return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
143
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
53
144
  const q = parsed.data.question.toUpperCase();
54
- const KNOWN = ["BTC", "ETH", "SOL", "BNB", "USDT", "USDC", "XRP", "DOGE", "ADA", "AVAX", "DOT", "LINK", "UNI", "OP", "ARB", "HYPE", "PEPE", "SUI", "APT", "NEAR", "INJ", "TIA", "MATIC", "TON", "SHIB", "WIF", "BONK"];
55
- const found = KNOWN.find((t) => new RegExp(`\\b${t}\\b`).test(q)) ?? "BTC";
56
- const data = await (0, convex_js_1.callConvex)(`/swarm/memory/read?key=market/${found}`, "GET", undefined, "get_token_data");
57
- if (data.error)
58
- return { content: [{ type: "text", text: `Error fetching ${found}: ${data.error}` }], isError: true };
59
- const price = data.price_usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
60
- const ch = data.change_24h_pct?.toFixed(2);
61
- const sign = (data.change_24h_pct ?? 0) >= 0 ? "+" : "";
145
+ const sym = Object.keys(SYMBOL_TO_ID).find((s) => new RegExp(`\\b${s}\\b`).test(q)) ?? "BTC";
146
+ const id = SYMBOL_TO_ID[sym];
147
+ const data = await cgFetch(`/coins/markets?vs_currency=usd&ids=${id}&sparkline=false&price_change_percentage=24h`);
148
+ const c = data[0];
149
+ if (!c)
150
+ return { content: [{ type: "text", text: `No data found for ${sym}` }], isError: true };
151
+ const sign = (c.price_change_percentage_24h ?? 0) >= 0 ? "+" : "";
62
152
  const lines = [
63
- `**${data.symbol}** — ${data.fetchedAt}`,
64
- `Price: ${price} (${sign}${ch}% 24h)`,
65
- `Market Cap: $${(data.market_cap_usd / 1e9).toFixed(2)}B`,
66
- `Volume 24h: $${(data.volume_24h_usd / 1e9).toFixed(2)}B`,
67
- `Source: ${data.source}`,
153
+ `**${c.symbol?.toUpperCase()} — ${c.name}**`,
154
+ `Price: ${fmtPrice(c.current_price)} (${sign}${fmt(c.price_change_percentage_24h)}% 24h)`,
155
+ `Market Cap: ${fmtB(c.market_cap)} (rank #${c.market_cap_rank ?? "—"})`,
156
+ `Volume 24h: ${fmtB(c.total_volume)}`,
157
+ `High/Low 24h: ${fmtPrice(c.high_24h)} / ${fmtPrice(c.low_24h)}`,
158
+ `All-Time High: ${fmtPrice(c.ath)} (${c.ath_change_percentage != null ? fmt(c.ath_change_percentage) + "% from ATH" : "—"})`,
159
+ "",
160
+ `_Source: CoinGecko · ${new Date().toUTCString()}_`,
68
161
  ];
69
162
  return { content: [{ type: "text", text: lines.join("\n") }] };
70
163
  }
@@ -2,28 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RESEARCH_TOOLS = void 0;
4
4
  exports.handleResearchTool = handleResearchTool;
5
- const zod_1 = require("zod");
6
- const convex_js_1 = require("../convex.js");
7
- exports.RESEARCH_TOOLS = [
8
- {
9
- name: "research",
10
- description: "Research any crypto topic on demand — like Perplexity but for crypto. Ask about a token, protocol, market event, or trend. Noel searches the web and returns a structured analysis.",
11
- inputSchema: {
12
- type: "object",
13
- properties: { query: { type: "string", description: "Topic to research" } },
14
- required: ["query"],
15
- },
16
- },
17
- ];
18
- const ResearchSchema = zod_1.z.object({ query: zod_1.z.string().min(1) });
19
- async function handleResearchTool(name, args) {
20
- if (name !== "research")
21
- return null;
22
- const parsed = ResearchSchema.safeParse(args);
23
- if (!parsed.success)
24
- return { content: [{ type: "text", text: `Invalid input: query ${parsed.error.issues[0].message}` }], isError: true };
25
- const data = await (0, convex_js_1.callConvex)("/mcp/research", "POST", { query: parsed.data.query }, "research");
26
- if (!data.success)
27
- return { content: [{ type: "text", text: `Research failed: ${data.error ?? "unknown error"}` }] };
28
- return { content: [{ type: "text", text: data.text ?? "No results returned." }] };
5
+ exports.RESEARCH_TOOLS = [];
6
+ async function handleResearchTool(_name, _args) {
7
+ return null;
29
8
  }
@@ -4,6 +4,7 @@ exports.SWARM_TOOLS = void 0;
4
4
  exports.handleSwarmTool = handleSwarmTool;
5
5
  const zod_1 = require("zod");
6
6
  const convex_js_1 = require("../convex.js");
7
+ const market_js_1 = require("./market.js");
7
8
  exports.SWARM_TOOLS = [
8
9
  {
9
10
  name: "start_swarm",
@@ -79,10 +80,27 @@ async function handleSwarmTool(name, args) {
79
80
  const data = await (0, convex_js_1.callConvex)("/swarm/start", "POST", { config: parsed.data.config }, "start_swarm");
80
81
  if (!data.success)
81
82
  return { content: [{ type: "text", text: `Failed: ${data.error}` }], isError: true };
83
+ const snapshot = await (0, market_js_1.fetchMarketSnapshot)();
84
+ if (snapshot) {
85
+ const ts = new Date().toUTCString();
86
+ const fmt = (n) => `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} (${ts})`;
87
+ await Promise.all([
88
+ (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", { agentId: "market-monitor", key: "BTC/USD", value: fmt(snapshot.btc) }, "write_swarm_memory"),
89
+ (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", { agentId: "market-monitor", key: "ETH/USD", value: fmt(snapshot.eth) }, "write_swarm_memory"),
90
+ (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", { agentId: "market-monitor", key: "SOL/USD", value: fmt(snapshot.sol) }, "write_swarm_memory"),
91
+ ]);
92
+ }
82
93
  return {
83
94
  content: [{
84
95
  type: "text",
85
- text: [`🤖 **Swarm Started**`, `Session ID: ${data.sessionId}`, `Started at: ${data.startedAt}`, ``, `Use \`get_swarm_status\` to monitor, \`stop_swarm\` to stop.`].join("\n"),
96
+ text: [
97
+ `🤖 **Swarm Started**`,
98
+ `Session ID: ${data.sessionId}`,
99
+ `Started at: ${data.startedAt}`,
100
+ snapshot ? `Market: BTC $${snapshot.btc.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | ETH $${snapshot.eth.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | SOL $${snapshot.sol.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : "",
101
+ ``,
102
+ `Use \`get_swarm_status\` to monitor, \`stop_swarm\` to stop.`,
103
+ ].filter(Boolean).join("\n"),
86
104
  }],
87
105
  };
88
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "description": "Noelclaw as an MCP skill — persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {