finoptima 1.2.1 → 1.3.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/index.js +91 -4
- package/lib/api-client.js +11 -1
- package/package.json +2 -2
- package/tools/index.js +89 -1
- package/tools/intent-tools.js +1 -1
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* EcoChain MCP Server (DEC-2026-043)
|
|
4
4
|
*
|
|
5
5
|
* Model Context Protocol server for AI agent access to EcoChain.
|
|
6
|
-
* Provides
|
|
6
|
+
* Provides 30 tools: 1 login + 29 platform tools.
|
|
7
7
|
*
|
|
8
8
|
* Authentication: EcoAuth login flow (no hardcoded credentials)
|
|
9
9
|
* 1. AI asks user for their phone number
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Environment variables:
|
|
15
15
|
* ECOCHAIN_API_URL — Optional. API base URL (default: https://api.toutcreer.com)
|
|
16
|
+
* ECOCHAIN_API_KEY — Optional. API key for agent auth (skips phone login)
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
@@ -30,6 +31,14 @@ const POLL_TIMEOUT_MS = 120000;
|
|
|
30
31
|
|
|
31
32
|
let authenticated = false;
|
|
32
33
|
|
|
34
|
+
// API key auth — skip phone login if ECOCHAIN_API_KEY is set (DEC-2026-050)
|
|
35
|
+
const API_KEY = process.env.ECOCHAIN_API_KEY;
|
|
36
|
+
if (API_KEY) {
|
|
37
|
+
apiClient.setApiKey(API_KEY);
|
|
38
|
+
authenticated = true;
|
|
39
|
+
console.error('[EcoChain MCP] Authenticated via API key (no phone login required)');
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
/**
|
|
34
43
|
* EcoAuth login flow
|
|
35
44
|
*/
|
|
@@ -97,7 +106,11 @@ const loginTool = {
|
|
|
97
106
|
},
|
|
98
107
|
handler: async ({ phone_number }) => {
|
|
99
108
|
if (authenticated) {
|
|
100
|
-
return JSON.stringify({
|
|
109
|
+
return JSON.stringify({
|
|
110
|
+
status: 'already_authenticated',
|
|
111
|
+
auth_method: API_KEY ? 'api_key' : 'ecoauth',
|
|
112
|
+
user_id: process.env.ECOCHAIN_USER_ID
|
|
113
|
+
});
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
const result = await loginWithEcoAuth(phone_number);
|
|
@@ -117,16 +130,90 @@ const loginTool = {
|
|
|
117
130
|
// All tools: login first, then platform tools
|
|
118
131
|
const allToolsWithLogin = [loginTool, ...allTools];
|
|
119
132
|
|
|
133
|
+
// Server instructions — shown to the AI to explain how to use FINOPTIMA
|
|
134
|
+
const SERVER_INSTRUCTIONS = `FINOPTIMA — EcoChain AI Finance Agent (v1.3.0, ${allTools.length + 1} tools)
|
|
135
|
+
|
|
136
|
+
## Setup
|
|
137
|
+
If the user doesn't have FINOPTIMA installed, they can add it to their MCP config:
|
|
138
|
+
\`\`\`json
|
|
139
|
+
{
|
|
140
|
+
"mcpServers": {
|
|
141
|
+
"ecochain": {
|
|
142
|
+
"command": "npx",
|
|
143
|
+
"args": ["-y", "finoptima"],
|
|
144
|
+
"env": { "ECOCHAIN_API_URL": "https://api.toutcreer.com" }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
\`\`\`
|
|
149
|
+
|
|
150
|
+
## Authentication
|
|
151
|
+
Two authentication methods are supported:
|
|
152
|
+
|
|
153
|
+
### Method 1: EcoAuth (interactive — for users)
|
|
154
|
+
1. Ask the user for their phone number (format: +33...)
|
|
155
|
+
2. Call the \`login\` tool — a push notification is sent to their EcoAuth mobile app
|
|
156
|
+
3. The user approves on their phone — JWT session is established
|
|
157
|
+
4. All other tools are now active
|
|
158
|
+
|
|
159
|
+
### Method 2: API Key (non-interactive — for agents)
|
|
160
|
+
Set \`ECOCHAIN_API_KEY\` in the MCP config env:
|
|
161
|
+
\`\`\`json
|
|
162
|
+
{
|
|
163
|
+
"env": {
|
|
164
|
+
"ECOCHAIN_API_URL": "https://api.toutcreer.com",
|
|
165
|
+
"ECOCHAIN_API_KEY": "eco_ak_..."
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
\`\`\`
|
|
169
|
+
No login call needed — all tools are immediately available.
|
|
170
|
+
API keys are scoped (read/trade/swap/liquidity/send/strategy) and rate-limited.
|
|
171
|
+
Requires a Pro or API subscription plan.
|
|
172
|
+
|
|
173
|
+
## Available Capabilities
|
|
174
|
+
- **Portfolio**: get_balances, get_portfolio, get_prices, get_lp_positions, get_transaction_history
|
|
175
|
+
- **Market**: get_pools, get_pool_details, get_orderbook, get_ticker, get_candles, get_trading_pairs
|
|
176
|
+
- **Trading**: place_order, cancel_order, get_my_orders, get_trade_history
|
|
177
|
+
- **Swap (AMM)**: get_swap_quote, execute_swap (0.3% fee, multi-hop routing supported)
|
|
178
|
+
- **Liquidity**: add_liquidity, remove_liquidity
|
|
179
|
+
- **Transfer**: send_funds
|
|
180
|
+
- **AI Strategies**: create_strategy, list_strategies, get_strategy, activate_strategy, pause_strategy
|
|
181
|
+
- **Marketplace**: get_templates, subscribe_template, get_leaderboard, follow_strategy, unfollow_strategy
|
|
182
|
+
- **Intents**: create_intent, list_intents
|
|
183
|
+
- **Performance**: get_strategy_performance
|
|
184
|
+
|
|
185
|
+
## Multi-Pool AMM
|
|
186
|
+
Multiple liquidity pools supported (ECO/BTC, ECO/ETH). Swaps between non-direct pairs (e.g., BTC↔ETH) are automatically routed via ECO as a hub token (2-hop swap).
|
|
187
|
+
|
|
188
|
+
## AI Trading Strategies
|
|
189
|
+
4 strategy types: LP Yield, DCA, BTC Hedge, Eco Pilot.
|
|
190
|
+
Pipeline: Create (draft) → Activate → Runner evaluates every 60s → Creates intents → Policy validation (8 rules) → Auto-approve if < 0.01 BTC, else manual approval → Execute.
|
|
191
|
+
Modes: "auto" (auto-approve small trades) or "watch" (all manual approval).
|
|
192
|
+
|
|
193
|
+
## Strategy Marketplace
|
|
194
|
+
- Browse public strategy templates (\`get_templates\`)
|
|
195
|
+
- Subscribe to a template to clone its config (\`subscribe_template\`)
|
|
196
|
+
- Follow a provider's live trades with copy trading (\`follow_strategy\`)
|
|
197
|
+
- View top-performing strategies (\`get_leaderboard\`)
|
|
198
|
+
|
|
199
|
+
## Rules
|
|
200
|
+
- Always call \`login\` first before any other tool (unless using API key auth)
|
|
201
|
+
- Always confirm financial actions (trade, swap, send) with the user before executing
|
|
202
|
+
- Use \`get_swap_quote\` before \`execute_swap\` to show expected outcome
|
|
203
|
+
- Platform: https://www.toutcreer.com/trading
|
|
204
|
+
`;
|
|
205
|
+
|
|
120
206
|
// Create MCP server
|
|
121
207
|
const server = new Server(
|
|
122
208
|
{
|
|
123
209
|
name: 'ecochain',
|
|
124
|
-
version: '1.
|
|
210
|
+
version: '1.3.0',
|
|
125
211
|
},
|
|
126
212
|
{
|
|
127
213
|
capabilities: {
|
|
128
214
|
tools: {},
|
|
129
215
|
},
|
|
216
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
130
217
|
}
|
|
131
218
|
);
|
|
132
219
|
|
|
@@ -156,7 +243,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
156
243
|
// Block all tools except login if not authenticated
|
|
157
244
|
if (!authenticated && name !== 'login') {
|
|
158
245
|
return {
|
|
159
|
-
content: [{ type: 'text', text: 'Not authenticated.
|
|
246
|
+
content: [{ type: 'text', text: 'Not authenticated. Either set ECOCHAIN_API_KEY env var or ask the user for their phone number and call the `login` tool first.' }],
|
|
160
247
|
isError: true,
|
|
161
248
|
};
|
|
162
249
|
}
|
package/lib/api-client.js
CHANGED
|
@@ -12,6 +12,7 @@ class ApiClient {
|
|
|
12
12
|
constructor() {
|
|
13
13
|
this.apiUrl = process.env.ECOCHAIN_API_URL || 'https://api.toutcreer.com';
|
|
14
14
|
this.jwtToken = null; // Set after EcoAuth login
|
|
15
|
+
this.apiKey = null; // Set via ECOCHAIN_API_KEY env var (DEC-2026-050)
|
|
15
16
|
|
|
16
17
|
const parsed = new URL(this.apiUrl);
|
|
17
18
|
this.hostname = parsed.hostname;
|
|
@@ -27,6 +28,13 @@ class ApiClient {
|
|
|
27
28
|
this.jwtToken = token;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Set API key for agent authentication (DEC-2026-050)
|
|
33
|
+
*/
|
|
34
|
+
setApiKey(key) {
|
|
35
|
+
this.apiKey = key;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
/**
|
|
31
39
|
* Make an HTTP request to the backend API
|
|
32
40
|
*/
|
|
@@ -38,7 +46,9 @@ class ApiClient {
|
|
|
38
46
|
'X-Client-Type': 'mcp-agent',
|
|
39
47
|
};
|
|
40
48
|
|
|
41
|
-
if (this.
|
|
49
|
+
if (this.apiKey) {
|
|
50
|
+
headers['X-API-Key'] = this.apiKey;
|
|
51
|
+
} else if (this.jwtToken) {
|
|
42
52
|
headers['Authorization'] = `Bearer ${this.jwtToken}`;
|
|
43
53
|
}
|
|
44
54
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "finoptima",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "FINOPTIMA — EcoChain AI Finance Agent via MCP.
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "FINOPTIMA — EcoChain AI Finance Agent via MCP. Multi-pool AMM (ECO/BTC, ECO/ETH), 4 AI strategies, strategy marketplace, copy trading, leaderboard. 35 tools. API key auth. Secured by EcoAuth 2FA.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"finoptima": "index.js"
|
package/tools/index.js
CHANGED
|
@@ -393,6 +393,92 @@ const sendTools = [
|
|
|
393
393
|
}
|
|
394
394
|
];
|
|
395
395
|
|
|
396
|
+
// ============================================
|
|
397
|
+
// MARKETPLACE TOOLS (DEC-2026-050)
|
|
398
|
+
// ============================================
|
|
399
|
+
|
|
400
|
+
const marketplaceTools = [
|
|
401
|
+
{
|
|
402
|
+
name: 'get_leaderboard',
|
|
403
|
+
description: 'Get top performing public strategies. Returns strategy name, type, total return, max drawdown, follower count, and provider name.',
|
|
404
|
+
inputSchema: {
|
|
405
|
+
type: 'object',
|
|
406
|
+
properties: {
|
|
407
|
+
limit: { type: 'number', description: 'Max results (default 50)' }
|
|
408
|
+
},
|
|
409
|
+
required: []
|
|
410
|
+
},
|
|
411
|
+
handler: withLogging('get_leaderboard', async ({ limit }) => {
|
|
412
|
+
const params = limit ? `?limit=${limit}` : '';
|
|
413
|
+
const data = await apiClient.get(`/performance/leaderboard${params}`);
|
|
414
|
+
return JSON.stringify(data);
|
|
415
|
+
})
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: 'get_templates',
|
|
419
|
+
description: 'List all active strategy templates in the marketplace. Returns template name, type, parameters, return, subscribers, and provider.',
|
|
420
|
+
inputSchema: {
|
|
421
|
+
type: 'object',
|
|
422
|
+
properties: {},
|
|
423
|
+
required: []
|
|
424
|
+
},
|
|
425
|
+
handler: withLogging('get_templates', async () => {
|
|
426
|
+
const data = await apiClient.get('/strategies/templates');
|
|
427
|
+
return JSON.stringify(data);
|
|
428
|
+
})
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'subscribe_template',
|
|
432
|
+
description: 'Subscribe to a strategy template. Clones the template config into a new strategy for the user. Requires pilot+ plan.',
|
|
433
|
+
inputSchema: {
|
|
434
|
+
type: 'object',
|
|
435
|
+
properties: {
|
|
436
|
+
template_id: { type: 'string', description: 'The UUID of the template to subscribe to' }
|
|
437
|
+
},
|
|
438
|
+
required: ['template_id']
|
|
439
|
+
},
|
|
440
|
+
handler: withLogging('subscribe_template', async ({ template_id }) => {
|
|
441
|
+
const data = await apiClient.post(`/strategies/templates/${template_id}/subscribe`);
|
|
442
|
+
return JSON.stringify(data);
|
|
443
|
+
})
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: 'follow_strategy',
|
|
447
|
+
description: 'Start copy trading a public strategy. Creates a shadow strategy that mirrors the provider\'s trades with optional scaling. Requires pilot+ plan.',
|
|
448
|
+
inputSchema: {
|
|
449
|
+
type: 'object',
|
|
450
|
+
properties: {
|
|
451
|
+
strategy_id: { type: 'string', description: 'The UUID of the public strategy to follow' },
|
|
452
|
+
scale_factor: { type: 'number', description: 'Trade size multiplier (0.1-2.0, default 1.0)' },
|
|
453
|
+
max_trade_btc: { type: 'number', description: 'Maximum trade size in BTC (default 0.01)' }
|
|
454
|
+
},
|
|
455
|
+
required: ['strategy_id']
|
|
456
|
+
},
|
|
457
|
+
handler: withLogging('follow_strategy', async ({ strategy_id, scale_factor, max_trade_btc }) => {
|
|
458
|
+
const body = {};
|
|
459
|
+
if (scale_factor !== undefined) body.scale_factor = scale_factor;
|
|
460
|
+
if (max_trade_btc !== undefined) body.max_trade_btc = max_trade_btc;
|
|
461
|
+
const data = await apiClient.post(`/strategies/${strategy_id}/follow`, body);
|
|
462
|
+
return JSON.stringify(data);
|
|
463
|
+
})
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: 'unfollow_strategy',
|
|
467
|
+
description: 'Stop copy trading a strategy. Pauses the shadow strategy and removes the copy trading link.',
|
|
468
|
+
inputSchema: {
|
|
469
|
+
type: 'object',
|
|
470
|
+
properties: {
|
|
471
|
+
strategy_id: { type: 'string', description: 'The UUID of the strategy to unfollow' }
|
|
472
|
+
},
|
|
473
|
+
required: ['strategy_id']
|
|
474
|
+
},
|
|
475
|
+
handler: withLogging('unfollow_strategy', async ({ strategy_id }) => {
|
|
476
|
+
const data = await apiClient.delete(`/strategies/${strategy_id}/follow`);
|
|
477
|
+
return JSON.stringify(data);
|
|
478
|
+
})
|
|
479
|
+
},
|
|
480
|
+
];
|
|
481
|
+
|
|
396
482
|
// Export all tools grouped by scope
|
|
397
483
|
module.exports = {
|
|
398
484
|
readTools,
|
|
@@ -403,6 +489,7 @@ module.exports = {
|
|
|
403
489
|
strategyTools,
|
|
404
490
|
intentTools,
|
|
405
491
|
performanceTools,
|
|
492
|
+
marketplaceTools,
|
|
406
493
|
allTools: [
|
|
407
494
|
...readTools,
|
|
408
495
|
...tradeTools,
|
|
@@ -411,6 +498,7 @@ module.exports = {
|
|
|
411
498
|
...sendTools,
|
|
412
499
|
...strategyTools,
|
|
413
500
|
...intentTools,
|
|
414
|
-
...performanceTools
|
|
501
|
+
...performanceTools,
|
|
502
|
+
...marketplaceTools,
|
|
415
503
|
]
|
|
416
504
|
};
|
package/tools/intent-tools.js
CHANGED
|
@@ -14,7 +14,7 @@ const intentTools = [
|
|
|
14
14
|
type: 'object',
|
|
15
15
|
properties: {
|
|
16
16
|
strategy_id: { type: 'string', description: 'Optional: link to a strategy' },
|
|
17
|
-
action: { type: 'string', description: 'Trading action', enum: ['execute_swap', 'add_liquidity', 'remove_liquidity', 'place_order'] },
|
|
17
|
+
action: { type: 'string', description: 'Trading action', enum: ['execute_swap', 'add_liquidity', 'remove_liquidity', 'zap_liquidity', 'place_order', 'vault_to_hot'] },
|
|
18
18
|
params: {
|
|
19
19
|
type: 'object',
|
|
20
20
|
description: 'Action parameters. Swap: {token_in, token_out, amount_in, max_slippage_bps}. Liquidity: {pool_id, amount_a, amount_b}. Order: {pair_id, side, type, amount, price}.'
|