asrai-mcp 0.5.5 → 0.5.7

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
@@ -21,7 +21,7 @@ Crypto market analysis MCP server for Claude Desktop — pay-per-use via x402 on
21
21
  "command": "npx",
22
22
  "args": ["-y", "asrai-mcp"],
23
23
  "env": {
24
- "PRIVATE_KEY": "0x<your_base_wallet_private_key>"
24
+ "ASRAI_PRIVATE_KEY": "0x<your_base_wallet_private_key>"
25
25
  }
26
26
  }
27
27
  }
@@ -31,7 +31,7 @@ Crypto market analysis MCP server for Claude Desktop — pay-per-use via x402 on
31
31
  **Or** store your key in `~/.env` (loaded automatically — no `env` block needed in config):
32
32
 
33
33
  ```
34
- PRIVATE_KEY=0x<your_base_wallet_private_key>
34
+ ASRAI_PRIVATE_KEY=0x<your_base_wallet_private_key>
35
35
  ```
36
36
 
37
37
  ```json
@@ -47,7 +47,7 @@ PRIVATE_KEY=0x<your_base_wallet_private_key>
47
47
 
48
48
  **Step 2** — Restart Claude Desktop. Done.
49
49
 
50
- Each API call costs **$0.001 USDC** from your wallet on Base mainnet. Make sure your wallet has a small USDC balance (~$1–2 to start).
50
+ Each API call costs **$0.005 USDC** from your wallet on Base mainnet. Make sure your wallet has a small USDC balance (~$1–2 to start).
51
51
 
52
52
  ## What you get
53
53
 
@@ -55,21 +55,21 @@ Each API call costs **$0.001 USDC** from your wallet on Base mainnet. Make sure
55
55
 
56
56
  | Tool | What it does | Cost |
57
57
  |---|---|---|
58
- | `market_overview` | Trending, gainers/losers, RSI, screeners, sentiment, narratives, cashflow | $0.019 |
59
- | `technical_analysis` | ALSAT, SuperALSAT, PSAR, MACD-DEMA, AlphaTrend, TD, forecast, SMC, S/R, Elliott Wave, Ichimoku | $0.012 |
60
- | `sentiment` | CBBI, CMC sentiment, AI insights | $0.003 |
61
- | `forecast` | AI 3-7 day price prediction | $0.001 |
62
- | `screener` | Find coins by criteria | $0.001 |
63
- | `smart_money` | Order blocks, FVGs, support/resistance | $0.002 |
64
- | `elliott_wave` | Elliott Wave analysis | $0.001 |
65
- | `ichimoku` | Ichimoku cloud | $0.001 |
66
- | `cashflow` | Capital flow data | $0.001 |
67
- | `coin_info` | Stats, info, price, tags, CMC AI + auto DEX data | $0.005–$0.006 |
68
- | `dexscreener` | DEX trading data | $0.001 |
69
- | `chain_tokens` | Low-cap tokens on chain | $0.001 |
70
- | `portfolio` | Model portfolio — investment reference | $0.001 |
71
- | `channel_summary` | Latest crypto narratives | $0.001 |
72
- | `ask_ai` | AI analyst freeform answer | $0.002 |
58
+ | `market_overview` | Trending, gainers/losers, RSI, screeners, sentiment, narratives, cashflow | $0.095 |
59
+ | `technical_analysis` | ALSAT, SuperALSAT, PSAR, MACD-DEMA, AlphaTrend, TD, forecast, SMC, S/R, Elliott Wave, Ichimoku | $0.06 |
60
+ | `sentiment` | CBBI, CMC sentiment, AI insights | $0.015 |
61
+ | `forecast` | AI 3-7 day price prediction | $0.005 |
62
+ | `screener` | Find coins by criteria | $0.005 |
63
+ | `smart_money` | Order blocks, FVGs, support/resistance | $0.01 |
64
+ | `elliott_wave` | Elliott Wave analysis | $0.005 |
65
+ | `ichimoku` | Ichimoku cloud | $0.005 |
66
+ | `cashflow` | Capital flow data | $0.005 |
67
+ | `coin_info` | Stats, info, price, tags, CMC AI + auto DEX data | $0.025–$0.03 |
68
+ | `dexscreener` | DEX trading data | $0.005 |
69
+ | `chain_tokens` | Low-cap tokens on chain | $0.005 |
70
+ | `portfolio` | Model portfolio — investment reference | $0.005 |
71
+ | `channel_summary` | Latest crypto narratives | $0.005 |
72
+ | `ask_ai` | AI analyst freeform answer | $0.01 |
73
73
  | `indicator_guide` | Asrai indicator reference | FREE |
74
74
 
75
75
  ## Spend limit
@@ -78,7 +78,7 @@ Default session cap: **$2.00 USDC**. To change:
78
78
 
79
79
  ```json
80
80
  "env": {
81
- "PRIVATE_KEY": "0x...",
81
+ "ASRAI_PRIVATE_KEY": "0x...",
82
82
  "ASRAI_MAX_SPEND": "5.0"
83
83
  }
84
84
  ```
package/bin/asrai-mcp.js CHANGED
@@ -14,8 +14,8 @@ config({ path: join(homedir(), ".env") });
14
14
  config();
15
15
 
16
16
  // Warn if no key set — one message, once
17
- if (!process.env.PRIVATE_KEY) {
18
- process.stderr.write("[asrai-mcp] Warning: PRIVATE_KEY not set — x402 payments will fail.\n");
17
+ if (!process.env.ASRAI_PRIVATE_KEY) {
18
+ process.stderr.write("[asrai-mcp] Warning: ASRAI_PRIVATE_KEY not set — x402 payments will fail.\n");
19
19
  process.stderr.write("[asrai-mcp] Set it in your Claude Desktop config or ~/.env file.\n");
20
20
  process.stderr.write("[asrai-mcp] See: https://github.com/abuzerasr/asrai-mcp\n");
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asrai-mcp",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Asrai crypto analysis MCP server — pay-per-use via x402 on Base. Zero install: just npx.",
5
5
  "keywords": [
6
6
  "mcp",
package/src/server.js CHANGED
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Asrai MCP server — tool definitions and request handlers.
3
+ * TOOLS imported from tools-definitions.js (synced from asrai-shared/).
4
+ * Do NOT edit shared TOOLS here — edit in asrai-shared/tools-definitions.js and run sync.py.
3
5
  */
4
6
 
5
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -11,180 +13,12 @@ import {
11
13
 
12
14
  import * as tools from "./tools.js";
13
15
  import { indicator_guide } from "./indicator_guide.js";
16
+ import { TOOLS as BASE_TOOLS } from "./tools-definitions.js";
14
17
 
15
- // ── Tool definitions ────────────────────────────────────────────────────────
18
+ // ── x402-only tools (added on top of shared TOOLS) ───────────────────────────
16
19
 
17
20
  const TOOLS = [
18
- {
19
- name: "market_overview",
20
- description:
21
- "Get current crypto market pulse: trending coins, gainers/losers, RSI extremes, top/bottom signals. " +
22
- "Use for general market questions like 'what's moving today' or 'give me a market brief'.",
23
- inputSchema: { type: "object", properties: {}, required: [] },
24
- },
25
- {
26
- name: "technical_analysis",
27
- description:
28
- "Get full technical analysis for a specific coin: signal, ALSAT, SuperALSAT, PSAR, MACD-DEMA, AlphaTrend. " +
29
- "Use when asked about TA, buy/sell signals, or indicators for a coin.",
30
- inputSchema: {
31
- type: "object",
32
- properties: {
33
- symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH, SOL" },
34
- timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Timeframe. Default: 1D" },
35
- },
36
- required: ["symbol"],
37
- },
38
- },
39
- {
40
- name: "sentiment",
41
- description:
42
- "Get market sentiment: CBBI (crypto bull/bear index), CMC sentiment, CMC AI insights. " +
43
- "Use for questions about market mood, fear/greed, or cycle position.",
44
- inputSchema: { type: "object", properties: {}, required: [] },
45
- },
46
- {
47
- name: "forecast",
48
- description: "Get AI-powered 3-7 day price forecast for a coin: direction, confidence, price targets.",
49
- inputSchema: {
50
- type: "object",
51
- properties: { symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" } },
52
- required: ["symbol"],
53
- },
54
- },
55
- {
56
- name: "screener",
57
- description:
58
- "Run a market screener to find coins matching specific criteria. " +
59
- "Types: ichimoku-trend, sar-coins, macd-coins, emacross, techrating, vwap, volume, " +
60
- "highvolumelowcap, bounce-dip, galaxyscore, socialdominance, late-unlocked-coins, ath, rsi, rsi-heatmap, ao.",
61
- inputSchema: {
62
- type: "object",
63
- properties: {
64
- screener_type: {
65
- type: "string",
66
- enum: [
67
- "ichimoku-trend", "sar-coins", "macd-coins", "emacross",
68
- "techrating", "vwap", "volume", "highvolumelowcap",
69
- "bounce-dip", "galaxyscore", "socialdominance", "late-unlocked-coins",
70
- "ath", "rsi", "rsi-heatmap", "ao",
71
- ],
72
- description: "Type of screener to run",
73
- },
74
- },
75
- required: ["screener_type"],
76
- },
77
- },
78
- {
79
- name: "smart_money",
80
- description:
81
- "Get Smart Money Concepts (SMC): order blocks, fair value gaps, liquidity zones, BOS/CHoCH, " +
82
- "plus support/resistance levels.",
83
- inputSchema: {
84
- type: "object",
85
- properties: {
86
- symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
87
- timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
88
- },
89
- required: ["symbol"],
90
- },
91
- },
92
- {
93
- name: "elliott_wave",
94
- description: "Get Elliott Wave analysis: current wave position, impulse/corrective structure, price targets.",
95
- inputSchema: {
96
- type: "object",
97
- properties: {
98
- symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
99
- timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
100
- },
101
- required: ["symbol"],
102
- },
103
- },
104
- {
105
- name: "ichimoku",
106
- description: "Get Ichimoku cloud analysis: cloud position, Tenkan/Kijun cross, kumo twist, trend bias for a coin.",
107
- inputSchema: {
108
- type: "object",
109
- properties: {
110
- symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
111
- timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
112
- },
113
- required: ["symbol"],
114
- },
115
- },
116
- {
117
- name: "cashflow",
118
- description:
119
- "Get capital flow data showing where money is moving. " +
120
- "Modes: 'market' (overall), 'coin' (single coin), 'group' (comma-separated coins).",
121
- inputSchema: {
122
- type: "object",
123
- properties: {
124
- mode: {
125
- type: "string",
126
- enum: ["market", "coin", "group"],
127
- description: "Scope of cashflow data",
128
- },
129
- symbol: {
130
- type: "string",
131
- description: "Coin symbol (required for coin/group modes)",
132
- },
133
- },
134
- required: ["mode"],
135
- },
136
- },
137
- {
138
- name: "coin_info",
139
- description: "Get detailed info for a coin: market cap, volume, supply, social stats, tokenomics.",
140
- inputSchema: {
141
- type: "object",
142
- properties: { symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH, SOL" } },
143
- required: ["symbol"],
144
- },
145
- },
146
- {
147
- name: "dexscreener",
148
- description: "Get DEX trading data for a token: liquidity, volume, buys/sells, price change. Use contract_address alone for symbol search, or provide chain + contract_address for a specific token.",
149
- inputSchema: {
150
- type: "object",
151
- properties: {
152
- contract_address: { type: "string", description: "Token contract address or symbol" },
153
- chain: { type: "string", description: "Optional chain e.g. ethereum, bsc, base, solana" },
154
- },
155
- required: ["contract_address"],
156
- },
157
- },
158
- {
159
- name: "chain_tokens",
160
- description: "Get low-cap tokens on a specific blockchain filtered by max market cap.",
161
- inputSchema: {
162
- type: "object",
163
- properties: {
164
- chain: { type: "string", description: "Chain e.g. ethereum, bsc, base, solana, avax" },
165
- max_mcap: { type: "string", description: "Max market cap e.g. 10000000 (10M)" },
166
- },
167
- required: ["chain", "max_mcap"],
168
- },
169
- },
170
- {
171
- name: "portfolio",
172
- description:
173
- "Get Abu's curated crypto portfolio — a model portfolio of carefully selected holdings. " +
174
- "Use when users ask 'what should I invest in?', 'build me a portfolio', 'what coins to buy', " +
175
- "or 'give me investment advice'. No symbol = full portfolio overview. " +
176
- "Symbol provided = Abu's position and analysis for that specific coin.",
177
- inputSchema: {
178
- type: "object",
179
- properties: { symbol: { type: "string", description: "Optional coin symbol to filter" } },
180
- required: [],
181
- },
182
- },
183
- {
184
- name: "channel_summary",
185
- description: "Get a summary of latest crypto narratives and discussions from monitored channels.",
186
- inputSchema: { type: "object", properties: {}, required: [] },
187
- },
21
+ ...BASE_TOOLS,
188
22
  {
189
23
  name: "ask_ai",
190
24
  description:
@@ -196,26 +30,6 @@ const TOOLS = [
196
30
  required: ["question"],
197
31
  },
198
32
  },
199
- {
200
- name: "indicator_guide",
201
- description:
202
- "Reference guide for Asrai-specific indicators. FREE — no payment. " +
203
- "WHEN TO CALL: only when you encounter an unfamiliar indicator name in tool output " +
204
- "(e.g. ALSAT, SuperALSAT, AlphaTrend, PMax, MavilimW). " +
205
- "Standard indicators (RSI, MACD, Ichimoku, Elliott Wave, BB) are well-known — skip them. " +
206
- "indicator='' or 'list' → compact 1-line summary of all. " +
207
- "indicator='ALSAT' → full detail. indicator='all' → everything (avoid unless needed).",
208
- inputSchema: {
209
- type: "object",
210
- properties: {
211
- indicator: {
212
- type: "string",
213
- description: "Indicator name e.g. 'ALSAT', 'SuperALSAT', 'PMax'. Empty = compact list.",
214
- },
215
- },
216
- required: [],
217
- },
218
- },
219
33
  ];
220
34
 
221
35
  // ── Request handlers ─────────────────────────────────────────────────────────
@@ -256,7 +70,7 @@ async function handleTool(name, args) {
256
70
 
257
71
  export function createServer() {
258
72
  const server = new Server(
259
- { name: "asrai", version: "0.5.0" },
73
+ { name: "asrai", version: "0.5.5" },
260
74
  { capabilities: { tools: {} } }
261
75
  );
262
76
 
package/src/sse-server.js CHANGED
@@ -43,7 +43,7 @@ const streamableTransports = {};
43
43
  // ── Key extraction helper ─────────────────────────────────────────────────────
44
44
 
45
45
  function extractKey(req, res, endpoint) {
46
- const key = (req.query.key ?? process.env.PRIVATE_KEY ?? "").trim();
46
+ const key = (req.query.key ?? process.env.ASRAI_PRIVATE_KEY ?? "").trim();
47
47
  if (!key) {
48
48
  res.status(401).send(`Missing wallet key. Connect with: ${endpoint}?key=0x<private_key>`);
49
49
  return null;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Shared endpoint lists and tool handlers for asrai-mcp and asrai-mcp-apikey.
3
+ * SOURCE OF TRUTH — edit here, then run sync.py to propagate.
4
+ *
5
+ * Add/remove endpoints here → sync.py copies to both projects.
6
+ * pricing.json call counts must match the arrays here.
7
+ * Add new tool handlers to createHandlers() below — auto-available in both projects.
8
+ */
9
+
10
+ // ── Static endpoint arrays ────────────────────────────────────────────────────
11
+
12
+ export const MARKET_OVERVIEW_ENDPOINTS = [
13
+ "/api/trending/",
14
+ "/api/gainers-losers/",
15
+ "/api/rsi/",
16
+ "/api/top-bottom/",
17
+ "/api/cmcai/",
18
+ "/api/cbbi/",
19
+ "/api/channel-summary/",
20
+ "/api/cashflow/market",
21
+ "/api/cmc-sentiment/",
22
+ "/api/socialdominance/",
23
+ "/api/ath/",
24
+ "/api/ichimoku-trend/",
25
+ "/api/bounce-dip/",
26
+ "/api/sar-coins/",
27
+ "/api/macd-coins/",
28
+ "/api/emacross/",
29
+ "/api/techrating/",
30
+ "/api/volume/",
31
+ "/api/highvolumelowcap/",
32
+ ];
33
+
34
+ export const SENTIMENT_ENDPOINTS = [
35
+ "/api/cbbi/",
36
+ "/api/cmc-sentiment/",
37
+ "/api/cmcai/",
38
+ ];
39
+
40
+ // ── Dynamic endpoint builders (return arrays) ─────────────────────────────────
41
+
42
+ export function technicalAnalysisEndpoints(s, tf) {
43
+ return [
44
+ `/api/signal/${s}usdt/${tf}`,
45
+ `/api/alsat/${s}usdt/${tf}`,
46
+ `/api/superalsat/${s}usdt`,
47
+ `/api/psar/${s}usdt/${tf}`,
48
+ `/api/macd-dema/${s}usdt/${tf}`,
49
+ `/api/alphatrend/${s}usdt/${tf}`,
50
+ `/api/td/${s}usdt/${tf}`,
51
+ `/api/forecasting/${s}usdt`,
52
+ `/api/smartmoney/${s}usdt/${tf}`,
53
+ `/api/support-resistance/${s}usdt/${tf}`,
54
+ `/api/ew/${s}usdt/${tf}`,
55
+ `/api/ichimoku/${s}usdt/${tf}`,
56
+ ];
57
+ }
58
+
59
+ export function smartMoneyEndpoints(s, tf) {
60
+ return [
61
+ `/api/smartmoney/${s}usdt/${tf}`,
62
+ `/api/support-resistance/${s}usdt/${tf}`,
63
+ ];
64
+ }
65
+
66
+ export function coinInfoEndpoints(s) {
67
+ return [
68
+ `/api/coinstats/${s}`,
69
+ `/api/info/${s}`,
70
+ `/api/price/${s}`,
71
+ `/api/tags/${s}`,
72
+ `/api/cmcai/${s}`,
73
+ ];
74
+ }
75
+
76
+ // ── Screener valid types ──────────────────────────────────────────────────────
77
+
78
+ export const SCREENER_TYPES = [
79
+ "ichimoku-trend", "sar-coins", "macd-coins", "emacross",
80
+ "techrating", "vwap", "volume", "highvolumelowcap",
81
+ "bounce-dip", "galaxyscore", "socialdominance", "late-unlocked-coins",
82
+ "ath", "rsi", "rsi-heatmap", "ao",
83
+ ];
84
+
85
+ // ── Symbol normalisation ──────────────────────────────────────────────────────
86
+
87
+ function sym(symbol) {
88
+ return symbol.toLowerCase().replace(/usdt$/, "");
89
+ }
90
+
91
+ // ── Shared tool handlers ──────────────────────────────────────────────────────
92
+ // _get and _gather are injected per-project (x402 vs apikey transport differs).
93
+ // Add new tools here — sync.py copies this file to both projects automatically.
94
+
95
+ export function createHandlers(_get, _gather) {
96
+ return {
97
+ async market_overview() {
98
+ return JSON.stringify(await _gather(...MARKET_OVERVIEW_ENDPOINTS), null, 2);
99
+ },
100
+
101
+ async technical_analysis(symbol, timeframe = "1D") {
102
+ const s = sym(symbol);
103
+ return JSON.stringify(await _gather(...technicalAnalysisEndpoints(s, timeframe)), null, 2);
104
+ },
105
+
106
+ async sentiment() {
107
+ return JSON.stringify(await _gather(...SENTIMENT_ENDPOINTS), null, 2);
108
+ },
109
+
110
+ async forecast(symbol) {
111
+ return JSON.stringify(await _get(`/api/forecasting/${sym(symbol)}usdt`), null, 2);
112
+ },
113
+
114
+ async screener(screener_type) {
115
+ if (!SCREENER_TYPES.includes(screener_type)) {
116
+ return JSON.stringify({ error: `Invalid screener. Choose from: ${SCREENER_TYPES.join(", ")}` });
117
+ }
118
+ return JSON.stringify(await _get(`/api/${screener_type}/`), null, 2);
119
+ },
120
+
121
+ async smart_money(symbol, timeframe = "1D") {
122
+ const s = sym(symbol);
123
+ return JSON.stringify(await _gather(...smartMoneyEndpoints(s, timeframe)), null, 2);
124
+ },
125
+
126
+ async elliott_wave(symbol, timeframe = "1D") {
127
+ return JSON.stringify(await _get(`/api/ew/${sym(symbol)}usdt/${timeframe}`), null, 2);
128
+ },
129
+
130
+ async ichimoku(symbol, timeframe = "1D") {
131
+ return JSON.stringify(await _get(`/api/ichimoku/${sym(symbol)}usdt/${timeframe}`), null, 2);
132
+ },
133
+
134
+ async cashflow(mode, symbol = "") {
135
+ if (mode === "market") {
136
+ return JSON.stringify(await _get("/api/cashflow/market"), null, 2);
137
+ }
138
+ if ((mode === "coin" || mode === "group") && symbol) {
139
+ return JSON.stringify(await _get(`/api/cashflow/${mode}/${symbol.toLowerCase()}`), null, 2);
140
+ }
141
+ return JSON.stringify({ error: "mode must be 'market', 'coin', or 'group'. coin/group require symbol." });
142
+ },
143
+
144
+ async coin_info(symbol) {
145
+ const s = symbol.toLowerCase();
146
+ const data = await _gather(...coinInfoEndpoints(s));
147
+ const contractAddress = data[`/api/info/${s}`]?.info_data?.contract_address;
148
+ if (contractAddress) {
149
+ data[`/api/dexscreener/${contractAddress}`] = await _get(`/api/dexscreener/${contractAddress}`);
150
+ }
151
+ return JSON.stringify(data, null, 2);
152
+ },
153
+
154
+ async dexscreener(contract_address, chain = "") {
155
+ const path = chain
156
+ ? `/api/dexscreener/${chain}/${contract_address}`
157
+ : `/api/dexscreener/${contract_address}`;
158
+ return JSON.stringify(await _get(path), null, 2);
159
+ },
160
+
161
+ async chain_tokens(chain, max_mcap) {
162
+ return JSON.stringify(await _get(`/api/chain/${chain}/${max_mcap}`), null, 2);
163
+ },
164
+
165
+ async portfolio(symbol = "") {
166
+ const path = symbol ? `/api/portfolio/${symbol.toLowerCase()}` : "/api/portfolio/";
167
+ return JSON.stringify(await _get(path), null, 2);
168
+ },
169
+
170
+ async channel_summary() {
171
+ return JSON.stringify(await _get("/api/channel-summary/"), null, 2);
172
+ },
173
+ };
174
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Shared tool definitions for asrai-mcp and asrai-mcp-apikey.
3
+ * SOURCE OF TRUTH — edit here, then run sync.py to propagate.
4
+ *
5
+ * Note: ask_ai is x402-only and is added in asrai-mcp/src/server.js directly.
6
+ */
7
+
8
+ export const TOOLS = [
9
+ {
10
+ name: "market_overview",
11
+ description:
12
+ "Get current crypto market pulse: trending coins, gainers/losers, RSI extremes, top/bottom signals. " +
13
+ "Use for general market questions like 'what's moving today' or 'give me a market brief'.",
14
+ inputSchema: { type: "object", properties: {}, required: [] },
15
+ },
16
+ {
17
+ name: "technical_analysis",
18
+ description:
19
+ "Get full technical analysis for a specific coin: signal, ALSAT, SuperALSAT, PSAR, MACD-DEMA, AlphaTrend. " +
20
+ "Use when asked about TA, buy/sell signals, or indicators for a coin.",
21
+ inputSchema: {
22
+ type: "object",
23
+ properties: {
24
+ symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH, SOL" },
25
+ timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Timeframe. Default: 1D" },
26
+ },
27
+ required: ["symbol"],
28
+ },
29
+ },
30
+ {
31
+ name: "sentiment",
32
+ description:
33
+ "Get market sentiment: CBBI (crypto bull/bear index), CMC sentiment, CMC AI insights. " +
34
+ "Use for questions about market mood, fear/greed, or cycle position.",
35
+ inputSchema: { type: "object", properties: {}, required: [] },
36
+ },
37
+ {
38
+ name: "forecast",
39
+ description: "Get AI-powered 3-7 day price forecast for a coin: direction, confidence, price targets.",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: { symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" } },
43
+ required: ["symbol"],
44
+ },
45
+ },
46
+ {
47
+ name: "screener",
48
+ description:
49
+ "Run a market screener to find coins matching specific criteria. " +
50
+ "Types: ichimoku-trend, sar-coins, macd-coins, emacross, techrating, vwap, volume, " +
51
+ "highvolumelowcap, bounce-dip, galaxyscore, socialdominance, late-unlocked-coins, ath, rsi, rsi-heatmap, ao.",
52
+ inputSchema: {
53
+ type: "object",
54
+ properties: {
55
+ screener_type: {
56
+ type: "string",
57
+ enum: [
58
+ "ichimoku-trend", "sar-coins", "macd-coins", "emacross",
59
+ "techrating", "vwap", "volume", "highvolumelowcap",
60
+ "bounce-dip", "galaxyscore", "socialdominance", "late-unlocked-coins",
61
+ "ath", "rsi", "rsi-heatmap", "ao",
62
+ ],
63
+ description: "Type of screener to run",
64
+ },
65
+ },
66
+ required: ["screener_type"],
67
+ },
68
+ },
69
+ {
70
+ name: "smart_money",
71
+ description:
72
+ "Get Smart Money Concepts (SMC): order blocks, fair value gaps, liquidity zones, BOS/CHoCH, " +
73
+ "plus support/resistance levels.",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {
77
+ symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
78
+ timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
79
+ },
80
+ required: ["symbol"],
81
+ },
82
+ },
83
+ {
84
+ name: "elliott_wave",
85
+ description: "Get Elliott Wave analysis: current wave position, impulse/corrective structure, price targets.",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
90
+ timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
91
+ },
92
+ required: ["symbol"],
93
+ },
94
+ },
95
+ {
96
+ name: "ichimoku",
97
+ description: "Get Ichimoku cloud analysis: cloud position, Tenkan/Kijun cross, kumo twist, trend bias for a coin.",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {
101
+ symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH" },
102
+ timeframe: { type: "string", enum: ["1D", "4H", "1W"], description: "Default: 1D" },
103
+ },
104
+ required: ["symbol"],
105
+ },
106
+ },
107
+ {
108
+ name: "cashflow",
109
+ description:
110
+ "Get capital flow data showing where money is moving. " +
111
+ "Modes: 'market' (overall), 'coin' (single coin), 'group' (comma-separated coins).",
112
+ inputSchema: {
113
+ type: "object",
114
+ properties: {
115
+ mode: {
116
+ type: "string",
117
+ enum: ["market", "coin", "group"],
118
+ description: "Scope of cashflow data",
119
+ },
120
+ symbol: {
121
+ type: "string",
122
+ description: "Coin symbol (required for coin/group modes)",
123
+ },
124
+ },
125
+ required: ["mode"],
126
+ },
127
+ },
128
+ {
129
+ name: "coin_info",
130
+ description: "Get detailed info for a coin: market cap, volume, supply, social stats, tokenomics.",
131
+ inputSchema: {
132
+ type: "object",
133
+ properties: { symbol: { type: "string", description: "Coin symbol e.g. BTC, ETH, SOL" } },
134
+ required: ["symbol"],
135
+ },
136
+ },
137
+ {
138
+ name: "dexscreener",
139
+ description:
140
+ "Get DEX trading data for a token: liquidity, volume, buys/sells, price change. " +
141
+ "Use contract_address alone for symbol search, or provide chain + contract_address for a specific token.",
142
+ inputSchema: {
143
+ type: "object",
144
+ properties: {
145
+ contract_address: { type: "string", description: "Token contract address or symbol" },
146
+ chain: { type: "string", description: "Optional chain e.g. ethereum, bsc, base, solana" },
147
+ },
148
+ required: ["contract_address"],
149
+ },
150
+ },
151
+ {
152
+ name: "chain_tokens",
153
+ description: "Get low-cap tokens on a specific blockchain filtered by max market cap.",
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {
157
+ chain: { type: "string", description: "Chain e.g. ethereum, bsc, base, solana, avax" },
158
+ max_mcap: { type: "string", description: "Max market cap e.g. 10000000 (10M)" },
159
+ },
160
+ required: ["chain", "max_mcap"],
161
+ },
162
+ },
163
+ {
164
+ name: "portfolio",
165
+ description:
166
+ "Get Abu's curated crypto portfolio — a model portfolio of carefully selected holdings. " +
167
+ "Use when users ask 'what should I invest in?', 'build me a portfolio', 'what coins to buy', " +
168
+ "or 'give me investment advice'. No symbol = full portfolio overview. " +
169
+ "Symbol provided = Abu's position and analysis for that specific coin.",
170
+ inputSchema: {
171
+ type: "object",
172
+ properties: { symbol: { type: "string", description: "Optional coin symbol to filter" } },
173
+ required: [],
174
+ },
175
+ },
176
+ {
177
+ name: "channel_summary",
178
+ description: "Get a summary of latest crypto narratives and discussions from monitored channels.",
179
+ inputSchema: { type: "object", properties: {}, required: [] },
180
+ },
181
+ {
182
+ name: "indicator_guide",
183
+ description:
184
+ "Reference guide for Asrai-specific indicators. FREE — no payment required. " +
185
+ "WHEN TO CALL: only when you encounter an unfamiliar indicator name in tool output " +
186
+ "(e.g. ALSAT, SuperALSAT, AlphaTrend, PMax, MavilimW). " +
187
+ "Standard indicators (RSI, MACD, Ichimoku, Elliott Wave, BB) are well-known — skip them. " +
188
+ "indicator='' or 'list' → compact 1-line summary of all. " +
189
+ "indicator='ALSAT' → full detail. indicator='all' → everything (avoid unless needed).",
190
+ inputSchema: {
191
+ type: "object",
192
+ properties: {
193
+ indicator: {
194
+ type: "string",
195
+ description: "Indicator name e.g. 'ALSAT', 'SuperALSAT', 'PMax'. Empty = compact list.",
196
+ },
197
+ },
198
+ required: [],
199
+ },
200
+ },
201
+ ];
package/src/tools.js CHANGED
@@ -1,29 +1,34 @@
1
1
  /**
2
2
  * Asrai API calls via x402 payment protocol.
3
- * Each call costs $0.001 USDC from the user's wallet on Base mainnet.
3
+ * Each call costs PRICE_PER_CALL USDC from the user's wallet on Base mainnet.
4
4
  *
5
5
  * Supports two modes:
6
- * - stdio (Claude Desktop): reads PRIVATE_KEY from env
6
+ * - stdio (Claude Desktop): reads ASRAI_PRIVATE_KEY from env
7
7
  * - SSE (Docker/n8n): key injected per-connection via AsyncLocalStorage
8
+ *
9
+ * Endpoints and tool handlers imported from tool-endpoints.js (synced from asrai-shared/).
10
+ * Do NOT edit tool handlers here — edit in asrai-shared/tool-endpoints.js and run sync.py.
8
11
  */
9
12
 
10
13
  import { AsyncLocalStorage } from "node:async_hooks";
11
14
  import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
12
15
  import { registerExactEvmScheme } from "@x402/evm/exact/client";
13
16
  import { privateKeyToAccount } from "viem/accounts";
17
+ import { createHandlers } from "./tool-endpoints.js";
14
18
 
15
19
  const BASE_URL = "https://x402.asrai.me";
16
20
  const X402_HEADERS = { "x-coinbase-402": "true", "x-payment-token": "usdc" };
17
21
 
18
22
  // Per-connection state — works for both stdio and multi-user SSE
19
- // In stdio mode: nothing set here, falls back to PRIVATE_KEY env var
20
- // In SSE mode: each connection runs inside connectionStorage.run({ key, spend: 0 }, ...)
21
23
  export const connectionStorage = new AsyncLocalStorage();
22
24
 
23
25
  function getMaxSpend() {
24
26
  return parseFloat(process.env.ASRAI_MAX_SPEND ?? "2.0");
25
27
  }
26
28
 
29
+ // PRICE_PER_CALL — updated by sync.py when pricing.json changes
30
+ const PRICE_PER_CALL = 0.005;
31
+
27
32
  function checkSpend(amount) {
28
33
  const store = connectionStorage.getStore();
29
34
  if (store) {
@@ -38,11 +43,9 @@ function checkSpend(amount) {
38
43
  }
39
44
 
40
45
  function buildFetch() {
41
- // SSE mode: key from per-connection storage
42
- // stdio mode: key from env var
43
46
  const store = connectionStorage.getStore();
44
- const key = store?.key ?? process.env.PRIVATE_KEY;
45
- if (!key) throw new Error("PRIVATE_KEY environment variable is required");
47
+ const key = store?.key ?? process.env.ASRAI_PRIVATE_KEY;
48
+ if (!key) throw new Error("ASRAI_PRIVATE_KEY environment variable is required");
46
49
 
47
50
  const signer = privateKeyToAccount(key);
48
51
  const client = new x402Client();
@@ -51,7 +54,7 @@ function buildFetch() {
51
54
  }
52
55
 
53
56
  async function _get(path) {
54
- checkSpend(0.001);
57
+ checkSpend(PRICE_PER_CALL);
55
58
  const fetchWithPayment = buildFetch();
56
59
  const res = await fetchWithPayment(`${BASE_URL}${path}`, {
57
60
  headers: X402_HEADERS,
@@ -62,7 +65,7 @@ async function _get(path) {
62
65
  }
63
66
 
64
67
  async function _post(path, body) {
65
- checkSpend(0.001);
68
+ checkSpend(PRICE_PER_CALL);
66
69
  const fetchWithPayment = buildFetch();
67
70
  const res = await fetchWithPayment(`${BASE_URL}${path}`, {
68
71
  method: "POST",
@@ -84,147 +87,15 @@ async function _gather(...paths) {
84
87
  return results;
85
88
  }
86
89
 
87
- // ── Symbol normalisation ────────────────────────────────────────────────────
88
-
89
- function sym(symbol) {
90
- return symbol.toLowerCase().replace(/usdt$/, "");
91
- }
92
-
93
- // ── Tool handlers ───────────────────────────────────────────────────────────
94
-
95
- export async function market_overview() {
96
- return JSON.stringify(await _gather(
97
- "/api/trending/",
98
- "/api/gainers-losers/",
99
- "/api/rsi/",
100
- "/api/top-bottom/",
101
- "/api/cmcai/",
102
- "/api/cbbi/",
103
- "/api/channel-summary/",
104
- "/api/cashflow/market",
105
- "/api/cmc-sentiment/",
106
- "/api/socialdominance/",
107
- "/api/ath/",
108
- "/api/ichimoku-trend/",
109
- "/api/bounce-dip/",
110
- "/api/sar-coins/",
111
- "/api/macd-coins/",
112
- "/api/emacross/",
113
- "/api/techrating/",
114
- "/api/volume/",
115
- "/api/highvolumelowcap/",
116
- ), null, 2);
117
- }
118
-
119
- export async function technical_analysis(symbol, timeframe = "1D") {
120
- const s = sym(symbol);
121
- return JSON.stringify(await _gather(
122
- `/api/signal/${s}usdt/${timeframe}`,
123
- `/api/alsat/${s}usdt/${timeframe}`,
124
- `/api/superalsat/${s}usdt`,
125
- `/api/psar/${s}usdt/${timeframe}`,
126
- `/api/macd-dema/${s}usdt/${timeframe}`,
127
- `/api/alphatrend/${s}usdt/${timeframe}`,
128
- `/api/td/${s}usdt/${timeframe}`,
129
- `/api/forecasting/${s}usdt`,
130
- `/api/smartmoney/${s}usdt/${timeframe}`,
131
- `/api/support-resistance/${s}usdt/${timeframe}`,
132
- `/api/ew/${s}usdt/${timeframe}`,
133
- `/api/ichimoku/${s}usdt/${timeframe}`,
134
- ), null, 2);
135
- }
136
-
137
- export async function sentiment() {
138
- return JSON.stringify(await _gather(
139
- "/api/cbbi/",
140
- "/api/cmc-sentiment/",
141
- "/api/cmcai/",
142
- ), null, 2);
143
- }
144
-
145
- export async function forecast(symbol) {
146
- return JSON.stringify(await _get(`/api/forecasting/${sym(symbol)}usdt`), null, 2);
147
- }
148
-
149
- export async function screener(screener_type) {
150
- const valid = [
151
- "ichimoku-trend", "sar-coins", "macd-coins", "emacross",
152
- "techrating", "vwap", "volume", "highvolumelowcap",
153
- "bounce-dip", "galaxyscore", "socialdominance", "late-unlocked-coins",
154
- "ath", "rsi", "rsi-heatmap", "ao",
155
- ];
156
- if (!valid.includes(screener_type)) {
157
- return JSON.stringify({ error: `Invalid screener. Choose from: ${valid.join(", ")}` });
158
- }
159
- return JSON.stringify(await _get(`/api/${screener_type}/`), null, 2);
160
- }
161
-
162
- export async function smart_money(symbol, timeframe = "1D") {
163
- const s = sym(symbol);
164
- return JSON.stringify(await _gather(
165
- `/api/smartmoney/${s}usdt/${timeframe}`,
166
- `/api/support-resistance/${s}usdt/${timeframe}`,
167
- ), null, 2);
168
- }
169
-
170
- export async function elliott_wave(symbol, timeframe = "1D") {
171
- return JSON.stringify(await _get(`/api/ew/${sym(symbol)}usdt/${timeframe}`), null, 2);
172
- }
173
-
174
- export async function ichimoku(symbol, timeframe = "1D") {
175
- return JSON.stringify(await _get(`/api/ichimoku/${sym(symbol)}usdt/${timeframe}`), null, 2);
176
- }
177
-
178
- export async function cashflow(mode, symbol = "") {
179
- if (mode === "market") {
180
- return JSON.stringify(await _get("/api/cashflow/market"), null, 2);
181
- }
182
- if ((mode === "coin" || mode === "group") && symbol) {
183
- return JSON.stringify(await _get(`/api/cashflow/${mode}/${symbol.toLowerCase()}`), null, 2);
184
- }
185
- return JSON.stringify({ error: "mode must be 'market', 'coin', or 'group'. coin/group require symbol." });
186
- }
187
-
188
- export async function coin_info(symbol) {
189
- const s = symbol.toLowerCase();
90
+ // ── Shared handlers (from tool-endpoints.js) ──────────────────────────────────
190
91
 
191
- // Phase 1: sequential (x402 wallet conflict prevents parallel)
192
- const data = await _gather(
193
- `/api/coinstats/${s}`,
194
- `/api/info/${s}`,
195
- `/api/price/${s}`,
196
- `/api/tags/${s}`,
197
- `/api/cmcai/${s}`,
198
- );
199
-
200
- // Phase 2: dexscreener via contract_address if available
201
- const contractAddress = data[`/api/info/${s}`]?.info_data?.contract_address;
202
- if (contractAddress) {
203
- data[`/api/dexscreener/${contractAddress}`] = await _get(`/api/dexscreener/${contractAddress}`);
204
- }
92
+ export const {
93
+ market_overview, technical_analysis, sentiment, forecast, screener,
94
+ smart_money, elliott_wave, ichimoku, cashflow, coin_info,
95
+ dexscreener, chain_tokens, portfolio, channel_summary,
96
+ } = createHandlers(_get, _gather);
205
97
 
206
- return JSON.stringify(data, null, 2);
207
- }
208
-
209
- export async function dexscreener(contract_address, chain = "") {
210
- const path = chain
211
- ? `/api/dexscreener/${chain}/${contract_address}`
212
- : `/api/dexscreener/${contract_address}`;
213
- return JSON.stringify(await _get(path), null, 2);
214
- }
215
-
216
- export async function chain_tokens(chain, max_mcap) {
217
- return JSON.stringify(await _get(`/api/chain/${chain}/${max_mcap}`), null, 2);
218
- }
219
-
220
- export async function portfolio(symbol = "") {
221
- const path = symbol ? `/api/portfolio/${symbol.toLowerCase()}` : "/api/portfolio/";
222
- return JSON.stringify(await _get(path), null, 2);
223
- }
224
-
225
- export async function channel_summary() {
226
- return JSON.stringify(await _get("/api/channel-summary/"), null, 2);
227
- }
98
+ // ── x402-only tools ───────────────────────────────────────────────────────────
228
99
 
229
100
  export async function ask_ai(question) {
230
101
  return JSON.stringify(await _post("/ai", { message: question }), null, 2);