finoptima 1.2.2 → 1.3.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/index.js +47 -7
- package/lib/api-client.js +11 -1
- package/mcp-registry-key.pem +3 -0
- package/package.json +3 -2
- package/server.json +58 -0
- package/tools/index.js +89 -1
- package/tools/intent-tools.js +1 -1
package/index.js
CHANGED
|
@@ -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);
|
|
@@ -118,7 +131,7 @@ const loginTool = {
|
|
|
118
131
|
const allToolsWithLogin = [loginTool, ...allTools];
|
|
119
132
|
|
|
120
133
|
// Server instructions — shown to the AI to explain how to use FINOPTIMA
|
|
121
|
-
const SERVER_INSTRUCTIONS = `FINOPTIMA — EcoChain AI Finance Agent (v1.
|
|
134
|
+
const SERVER_INSTRUCTIONS = `FINOPTIMA — EcoChain AI Finance Agent (v1.3.0, ${allTools.length + 1} tools)
|
|
122
135
|
|
|
123
136
|
## Setup
|
|
124
137
|
If the user doesn't have FINOPTIMA installed, they can add it to their MCP config:
|
|
@@ -135,29 +148,56 @@ If the user doesn't have FINOPTIMA installed, they can add it to their MCP confi
|
|
|
135
148
|
\`\`\`
|
|
136
149
|
|
|
137
150
|
## Authentication
|
|
151
|
+
Two authentication methods are supported:
|
|
152
|
+
|
|
153
|
+
### Method 1: EcoAuth (interactive — for users)
|
|
138
154
|
1. Ask the user for their phone number (format: +33...)
|
|
139
155
|
2. Call the \`login\` tool — a push notification is sent to their EcoAuth mobile app
|
|
140
156
|
3. The user approves on their phone — JWT session is established
|
|
141
157
|
4. All other tools are now active
|
|
142
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
|
+
|
|
143
173
|
## Available Capabilities
|
|
144
174
|
- **Portfolio**: get_balances, get_portfolio, get_prices, get_lp_positions, get_transaction_history
|
|
145
175
|
- **Market**: get_pools, get_pool_details, get_orderbook, get_ticker, get_candles, get_trading_pairs
|
|
146
176
|
- **Trading**: place_order, cancel_order, get_my_orders, get_trade_history
|
|
147
|
-
- **Swap (AMM)**: get_swap_quote, execute_swap (0.3% fee)
|
|
177
|
+
- **Swap (AMM)**: get_swap_quote, execute_swap (0.3% fee, multi-hop routing supported)
|
|
148
178
|
- **Liquidity**: add_liquidity, remove_liquidity
|
|
149
179
|
- **Transfer**: send_funds
|
|
150
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
|
|
151
182
|
- **Intents**: create_intent, list_intents
|
|
152
183
|
- **Performance**: get_strategy_performance
|
|
153
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
|
+
|
|
154
188
|
## AI Trading Strategies
|
|
155
|
-
|
|
189
|
+
4 strategy types: LP Yield, DCA, BTC Hedge, Eco Pilot.
|
|
156
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.
|
|
157
191
|
Modes: "auto" (auto-approve small trades) or "watch" (all manual approval).
|
|
158
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
|
+
|
|
159
199
|
## Rules
|
|
160
|
-
- Always call \`login\` first before any other tool
|
|
200
|
+
- Always call \`login\` first before any other tool (unless using API key auth)
|
|
161
201
|
- Always confirm financial actions (trade, swap, send) with the user before executing
|
|
162
202
|
- Use \`get_swap_quote\` before \`execute_swap\` to show expected outcome
|
|
163
203
|
- Platform: https://www.toutcreer.com/trading
|
|
@@ -167,7 +207,7 @@ Modes: "auto" (auto-approve small trades) or "watch" (all manual approval).
|
|
|
167
207
|
const server = new Server(
|
|
168
208
|
{
|
|
169
209
|
name: 'ecochain',
|
|
170
|
-
version: '1.
|
|
210
|
+
version: '1.3.0',
|
|
171
211
|
},
|
|
172
212
|
{
|
|
173
213
|
capabilities: {
|
|
@@ -203,7 +243,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
203
243
|
// Block all tools except login if not authenticated
|
|
204
244
|
if (!authenticated && name !== 'login') {
|
|
205
245
|
return {
|
|
206
|
-
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.' }],
|
|
207
247
|
isError: true,
|
|
208
248
|
};
|
|
209
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,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "finoptima",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"mcpName": "com.toutcreer/finoptima",
|
|
5
|
+
"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
6
|
"main": "index.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"finoptima": "index.js"
|
package/server.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "com.toutcreer/finoptima",
|
|
4
|
+
"title": "FINOPTIMA",
|
|
5
|
+
"description": "AI trading agent: multi-pool AMM, 4 strategies, marketplace, copy trading. 35 tools.",
|
|
6
|
+
"version": "1.3.1",
|
|
7
|
+
"repository": {
|
|
8
|
+
"url": "https://github.com/toutcreer/finoptima",
|
|
9
|
+
"source": "github"
|
|
10
|
+
},
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "npm",
|
|
14
|
+
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
|
+
"identifier": "finoptima",
|
|
16
|
+
"version": "1.3.1",
|
|
17
|
+
"transport": {
|
|
18
|
+
"type": "stdio"
|
|
19
|
+
},
|
|
20
|
+
"environmentVariables": [
|
|
21
|
+
{
|
|
22
|
+
"name": "ECOCHAIN_API_URL",
|
|
23
|
+
"description": "EcoChain API endpoint (default: https://api.toutcreer.com)",
|
|
24
|
+
"isRequired": false,
|
|
25
|
+
"isSecret": false
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "ECOCHAIN_API_KEY",
|
|
29
|
+
"description": "API key for autonomous agent authentication (format: eco_ak_...). Optional — alternative to EcoAuth 2FA login. Available on Pro and API subscription plans.",
|
|
30
|
+
"isRequired": false,
|
|
31
|
+
"isSecret": true
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"arguments": ["-y", "finoptima"]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"_meta": {
|
|
38
|
+
"io.modelcontextprotocol.registry/publisher-provided": {
|
|
39
|
+
"homepage": "https://www.toutcreer.com/finoptima",
|
|
40
|
+
"documentation": "https://www.toutcreer.com/trading/finoptima.md",
|
|
41
|
+
"categories": ["finance", "trading", "defi", "portfolio"],
|
|
42
|
+
"toolCount": 35,
|
|
43
|
+
"features": [
|
|
44
|
+
"Multi-pool AMM (ECO/BTC, ECO/ETH) with smart router",
|
|
45
|
+
"4 AI trading strategies (DCA, BTC Hedge, LP Yield, Eco Pilot)",
|
|
46
|
+
"Strategy marketplace with templates and copy trading",
|
|
47
|
+
"Policy engine with 8 security rules",
|
|
48
|
+
"EcoAuth 2FA + API key authentication"
|
|
49
|
+
],
|
|
50
|
+
"pricing": {
|
|
51
|
+
"free": "0 EUR — browse marketplace",
|
|
52
|
+
"pilot": "9 EUR/mo — 5 strategies, follow",
|
|
53
|
+
"pro": "29 EUR/mo — 20 strategies, publish, 3 API keys",
|
|
54
|
+
"api": "99 EUR/mo — unlimited, 10 API keys"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
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}.'
|