@fidesbuild/mcp-server 0.9.0 → 0.10.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/dist/server.js +138 -17
- package/package.json +3 -2
package/dist/server.js
CHANGED
|
@@ -22,6 +22,7 @@ import { netObligations, settle, netRoots, PUBLISH_SELECTOR, NetError } from '@f
|
|
|
22
22
|
import { createSubClient, SubFidesKit, SubAgent, SubFidesError, subDeployments } from '@fidesbuild/sub';
|
|
23
23
|
import { quoteUniswap, checkMandate, swapActions, verifySwap, describeRoute, mandateOf, uniswap, NATIVE, SwapError } from '@fidesbuild/swap';
|
|
24
24
|
import { route } from '@fidesbuild/router';
|
|
25
|
+
import { LongAgent, LongFides, isLong } from '@fidesbuild/long';
|
|
25
26
|
export function configFromEnv(env = process.env) {
|
|
26
27
|
const home = env.FIDES_HOME ?? path.join(os.homedir(), '.fides');
|
|
27
28
|
return {
|
|
@@ -142,6 +143,23 @@ export function createFidesMcpServer(cfg) {
|
|
|
142
143
|
const subClient = subAddrs ? createSubClient({ chainId: cfg.chainId, rpc: cfg.rpc, base: cfg.addresses, addresses: subAddrs }) : null;
|
|
143
144
|
const subPlans = new Map();
|
|
144
145
|
const subsDir = path.join(cfg.home, 'subs');
|
|
146
|
+
// LONG FIDES: the terms (period, budget per period, calls per period) come with the FIDES from the API,
|
|
147
|
+
// and are kept here so a FIDES loaded from the file can still run.
|
|
148
|
+
const longFile = path.join(cfg.home, 'long.json');
|
|
149
|
+
const longTerms = new Map(Object.entries(fs.existsSync(longFile) ? fromJson(fs.readFileSync(longFile, 'utf8')) : {}));
|
|
150
|
+
const keepTerms = (id, terms) => { longTerms.set(id, terms); fs.mkdirSync(cfg.home, { recursive: true }); fs.writeFileSync(longFile, toJson(Object.fromEntries(longTerms), 2) + '\n'); };
|
|
151
|
+
const longPlans = new Map();
|
|
152
|
+
/** The agent for a LONG FIDES, or a message saying why it cannot run. */
|
|
153
|
+
const longAgentFor = (fides) => {
|
|
154
|
+
const terms = longTerms.get(fides.id);
|
|
155
|
+
if (!subClient)
|
|
156
|
+
return `LONG FIDES is not deployed on chain ${cfg.chainId}.`;
|
|
157
|
+
if (!terms)
|
|
158
|
+
return 'This is a LONG FIDES, but its terms (the budget per period) are not here. The app shares them when the owner signs; ask the user to open it in the FIDES app once, or sign it again.';
|
|
159
|
+
return new LongAgent({ client: subClient, fides, terms, agent, ...(cfg.api ? { api: cfg.api } : { relayer: agent }) });
|
|
160
|
+
};
|
|
161
|
+
const every = (t) => ({ 3600: 'hour', 86400: 'day', 604800: 'week', 2592000: '30 days' }[String(t.period)] ?? `${Number(t.period) / 3600} hours`);
|
|
162
|
+
const noLong = (what) => `This is a LONG FIDES. It swaps, pays paid APIs, sends and calls contracts inside its budget per period, but it does not ${what}. For that, the user signs an ordinary FIDES (fides_setup without keeps_going).`;
|
|
145
163
|
const token = async (asset) => {
|
|
146
164
|
const key = asset.toLowerCase();
|
|
147
165
|
if (!tokens.has(key)) {
|
|
@@ -175,8 +193,11 @@ export function createFidesMcpServer(cfg) {
|
|
|
175
193
|
const res = await fetch(`${cfg.directory}/v1/agents/${agent.address}/fides`);
|
|
176
194
|
if (res.ok)
|
|
177
195
|
for (const x of fromJson(await res.text()).fidesList ?? [])
|
|
178
|
-
if (mine(x.fides) && x.status?.valid)
|
|
196
|
+
if (mine(x.fides) && x.status?.valid) {
|
|
179
197
|
candidates.push(x.fides);
|
|
198
|
+
if (x.long)
|
|
199
|
+
keepTerms(x.fides.id, x.long);
|
|
200
|
+
}
|
|
180
201
|
}
|
|
181
202
|
catch { /* the API is optional: the copy kept here still works */ }
|
|
182
203
|
}
|
|
@@ -203,9 +224,16 @@ export function createFidesMcpServer(cfg) {
|
|
|
203
224
|
const amount = (v) => `${formatUnits(v, t.decimals)} ${t.symbol}`;
|
|
204
225
|
const outside = await stray(fides.spendLimits[0].asset);
|
|
205
226
|
const status = check.revoked ? 'revoked' : check.expired ? 'expired' : check.notYetValid ? 'not valid yet' : !check.validSignature || !check.idMatches ? 'signature does not verify' : 'active';
|
|
227
|
+
const terms = isLong(fides) ? longTerms.get(fides.id) : undefined;
|
|
228
|
+
const period = terms && subClient ? await LongFides.status(subClient, fides, terms).catch(() => null) : null;
|
|
206
229
|
return {
|
|
207
230
|
status,
|
|
208
231
|
fides: fides.id,
|
|
232
|
+
...(isLong(fides) ? { keeps_going: !terms ? 'a LONG FIDES, but its terms are not here yet' : {
|
|
233
|
+
budget_per_period: `${amount(terms.perPeriod[0].amount)} every ${every(terms)}`,
|
|
234
|
+
this_period: period ? { spent: amount(period.thisPeriod[0].spent), left: amount(period.thisPeriod[0].remaining), calls: `${period.actions.used} of ${terms.maxActionsPerPeriod}`, resets_at: new Date(Number(period.resetsAt) * 1000).toISOString() } : 'unknown',
|
|
235
|
+
note: 'The budget per period resets by itself. The total budget is the most it can ever spend.',
|
|
236
|
+
} } : {}),
|
|
209
237
|
owner: fides.owner,
|
|
210
238
|
agent: fides.agent,
|
|
211
239
|
fides_account: fides.account,
|
|
@@ -223,12 +251,13 @@ export function createFidesMcpServer(cfg) {
|
|
|
223
251
|
...(outside > 0n ? { warning: `${amount(outside)} sits on the agent key ${agent.address} outside the FIDES: it was sent to the agent address directly instead of to the FIDES Account. FIDES never spends it. To get it back, the owner can import the agent key from ${path.join(cfg.home, 'agent.json')} into a wallet on this computer, add a little ETH for gas, and send it to themselves. Funds for the agent always go to the FIDES Account address.` } : {}),
|
|
224
252
|
};
|
|
225
253
|
}
|
|
226
|
-
const server = new McpServer({ name: 'fides', version: '0.
|
|
254
|
+
const server = new McpServer({ name: 'fides', version: '0.10.0' }, {
|
|
227
255
|
instructions: 'FIDES lets you act with the owner\'s funds only inside limits the owner signed (a FIDES): a budget, allowed contracts and functions, paid services, and an expiry. ' +
|
|
228
256
|
'Funds move from the owner\'s FIDES Account, never from the owner\'s wallet. If no FIDES is set up yet, call fides_setup and give the user the link. Always call fides_plan first and show the user what is inside the FIDES, what is blocked and why, ' +
|
|
229
257
|
'and ask before calling fides_execute. Actions outside the FIDES are blocked before anything is sent. Amounts are in whole token units, like "1.5". ' +
|
|
230
258
|
'When the user asks what you can do with FIDES, which paid APIs or services there are, or how to start, call fides_help and answer from it: give the paid APIs by name with their price and suggest a next step. ' +
|
|
231
259
|
'A paid service\'s id is a name the user chooses when signing, like "spraay"; nobody issues it. Any API that charges USDG on Robinhood Chain over x402 (exact, EIP-3009) works, not only the ones fides_help lists. ' +
|
|
260
|
+
'For a LONG FIDES (one that keeps going, with a budget per period), plans are checked against what is left this period and in total; if this period\'s budget is used up, say when it resets. ' +
|
|
232
261
|
'When the user has many payments owed between their agent and services (or other parties), fides_net nets them to final positions and builds the FIDES Account\'s side of the settlement, plus the publication of the round\'s roots onchain, into one plan for fides_execute.',
|
|
233
262
|
});
|
|
234
263
|
server.registerTool('fides_help', {
|
|
@@ -250,6 +279,7 @@ export function createFidesMcpServer(cfg) {
|
|
|
250
279
|
'Buy from paid APIs that charge per request over x402, paying from the FIDES budget, each capped per service.',
|
|
251
280
|
'Swap on Uniswap inside a trading mandate the owner signed: tokens it may buy, worst price, most per swap; best route across Robinhood Chain including v4 hook pools, approval and swap as one transaction (fides_swap). The FIDES must allow swaps: fides_setup with swap.',
|
|
252
281
|
'Hand another agent a slice of the FIDES with its own budget and expiry (fides_delegate, SUB-FIDES); or, as the child, act inside a slice handed to you (fides_sub_plan). The FIDES must allow delegation: fides_setup with delegate: true.',
|
|
282
|
+
'Keep going for weeks on one signature (LONG FIDES): a budget per hour, day, week or month that resets by itself, a total it can never pass and an end date. fides_setup with keeps_going; then fides_plan, fides_swap and fides_pay_request work inside this period\'s budget, and fides_status shows what is left this period and when it resets.',
|
|
253
283
|
'Settle with FIDES NET: give the payments owed to and from the FIDES Account, and they are netted to final positions and settled in one transaction, with the round published onchain (fides_net). The FIDES must allow NET: fides_setup with net: true.',
|
|
254
284
|
'Show the budget spent and left, what is allowed, the expiry, and every transaction executed, with explorer links.',
|
|
255
285
|
'Everything is planned and simulated first, and shown to the user before anything is sent. Anything outside the FIDES is blocked. The hosted relayer pays the gas.',
|
|
@@ -302,8 +332,15 @@ export function createFidesMcpServer(cfg) {
|
|
|
302
332
|
max_slippage_percent: z.number().positive().max(50).optional().describe('The worst price it may accept against the quote, in percent. Default 1'),
|
|
303
333
|
max_per_swap: z.string().optional().describe('The most one swap may spend, in the budget token. Default "1"'),
|
|
304
334
|
}).optional().describe('Let the agent swap on Uniswap inside a trading mandate the owner signs; fides_swap needs it'),
|
|
335
|
+
keeps_going: z.object({
|
|
336
|
+
every: z.enum(['hour', 'day', 'week', 'month']).describe('How often the budget resets'),
|
|
337
|
+
per_period: z.string().describe('The most it may spend each period, in USDG, e.g. "5"'),
|
|
338
|
+
days: z.number().positive().max(365).optional().describe('How long it keeps going, in days. Default 30'),
|
|
339
|
+
}).optional().describe('A LONG FIDES: one signature, and the agent keeps going period after period, with a budget per period that resets by itself, a total it can never pass and an end date. It cannot be combined with delegate or net.'),
|
|
305
340
|
},
|
|
306
|
-
}, async ({ services, net, delegate, swap, contracts }) => {
|
|
341
|
+
}, async ({ services, net, delegate, swap, contracts, keeps_going }) => {
|
|
342
|
+
if (keeps_going && (delegate || net))
|
|
343
|
+
return fail('A LONG FIDES cannot hand out slices or settle with NET. Leave out delegate and net, or sign an ordinary FIDES for those.');
|
|
307
344
|
let targets = '';
|
|
308
345
|
const added = [];
|
|
309
346
|
for (const c of contracts ?? []) {
|
|
@@ -335,7 +372,8 @@ export function createFidesMcpServer(cfg) {
|
|
|
335
372
|
}
|
|
336
373
|
return ok({
|
|
337
374
|
agent_address: agent.address,
|
|
338
|
-
link: setupLink + targets + (services ?? []).map((x) => `&service=${encodeURIComponent(x.id)}:${encodeURIComponent(x.max)}`).join('') + (net && netRoots[cfg.chainId] ? `&target=${netRoots[cfg.chainId]}:${PUBLISH_SELECTOR}` : '') + (delegate ? '&delegate=1' : '') + (swap ? `&swap=${encodeURIComponent(swap.buy.map((t) => (/^weth$/i.test(t) ? uniswap[cfg.chainId]?.weth ?? t : t)).join('|'))}:${Math.round((swap.max_slippage_percent ?? 1) * 100)}:${encodeURIComponent(swap.max_per_swap ?? '1')}` : ''),
|
|
375
|
+
link: setupLink + targets + (services ?? []).map((x) => `&service=${encodeURIComponent(x.id)}:${encodeURIComponent(x.max)}`).join('') + (net && netRoots[cfg.chainId] ? `&target=${netRoots[cfg.chainId]}:${PUBLISH_SELECTOR}` : '') + (delegate ? '&delegate=1' : '') + (swap ? `&swap=${encodeURIComponent(swap.buy.map((t) => (/^weth$/i.test(t) ? uniswap[cfg.chainId]?.weth ?? t : t)).join('|'))}:${Math.round((swap.max_slippage_percent ?? 1) * 100)}:${encodeURIComponent(swap.max_per_swap ?? '1')}` : '') + (keeps_going ? `&every=${keeps_going.every}&per=${encodeURIComponent(keeps_going.per_period)}&days=${keeps_going.days ?? 30}` : ''),
|
|
376
|
+
keeps_going: keeps_going ? `${keeps_going.per_period} USDG every ${keeps_going.every}, for ${keeps_going.days ?? 30} days. The budget resets by itself; the total is the most it can ever spend.` : undefined,
|
|
339
377
|
services: services?.length ? services.map((x) => `${x.id}: up to ${x.max} USDG`) : undefined,
|
|
340
378
|
contracts: added.length ? added : undefined,
|
|
341
379
|
steps: [
|
|
@@ -453,6 +491,25 @@ export function createFidesMcpServer(cfg) {
|
|
|
453
491
|
if ('error' in made)
|
|
454
492
|
return fail(made.error);
|
|
455
493
|
const built = made.built;
|
|
494
|
+
if (isLong(fides)) {
|
|
495
|
+
const la = longAgentFor(fides);
|
|
496
|
+
if (typeof la === 'string')
|
|
497
|
+
return fail(la);
|
|
498
|
+
const p = await la.plan(built);
|
|
499
|
+
const sim = p.bundle ? await la.simulate(p) : { success: false, reason: 'NOTHING_TO_SEND' };
|
|
500
|
+
const id = (p.bundleHash ?? '0x').slice(0, 18);
|
|
501
|
+
if (sim.success)
|
|
502
|
+
longPlans.set(id, { agent: la, plan: p, fides });
|
|
503
|
+
return ok({
|
|
504
|
+
plan_id: sim.success ? id : null,
|
|
505
|
+
ready_to_execute: sim.success,
|
|
506
|
+
keeps_going: `period ${p.period}: checked against this period's budget and the total`,
|
|
507
|
+
actions: p.plan.actions.map((a) => ({ action: a.label, decision: a.decision === 'AUTHORIZED' ? 'inside the FIDES' : a.decision === 'REJECTED' ? `blocked: ${REASONS[a.reason ?? ''] ?? a.reason} (${a.reason}), counting this period's budget` : 'duplicate, sent once' })),
|
|
508
|
+
transactions: p.bundle ? `${p.bundle.calls.length} onchain write(s) go out as 1 transaction` : 'nothing to execute',
|
|
509
|
+
simulation: !p.bundle ? 'nothing to execute: no action fits this period\'s budget' : sim.success ? 'simulated against the chain: it would succeed' : `would fail: ${failure(sim.reason)}`,
|
|
510
|
+
sent: 'nothing has been sent',
|
|
511
|
+
});
|
|
512
|
+
}
|
|
456
513
|
const plan = await new FidesBuilder({ client, fides, agent }).add(built).build();
|
|
457
514
|
const savings = plan.estimatedSavings();
|
|
458
515
|
const simulations = savings.estimatedTransactions ? await plan.simulate() : [];
|
|
@@ -490,6 +547,8 @@ export function createFidesMcpServer(cfg) {
|
|
|
490
547
|
return fail(notLoaded());
|
|
491
548
|
if (fides.agent.toLowerCase() !== agent.address.toLowerCase())
|
|
492
549
|
return fail(`The loaded FIDES is for agent ${fides.agent}, not this computer's agent key ${agent.address}.`);
|
|
550
|
+
if (isLong(fides))
|
|
551
|
+
return fail(noLong('settle with FIDES NET'));
|
|
493
552
|
const roots = netRoots[cfg.chainId];
|
|
494
553
|
if (!roots)
|
|
495
554
|
return fail(`FIDES NET is not deployed on chain ${cfg.chainId}.`);
|
|
@@ -657,20 +716,36 @@ export function createFidesMcpServer(cfg) {
|
|
|
657
716
|
let funded;
|
|
658
717
|
if (float < price) {
|
|
659
718
|
const actions = [x402.float(offer.asset, price - float, 'pay-float')];
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
719
|
+
if (isLong(fides)) {
|
|
720
|
+
const la = longAgentFor(fides);
|
|
721
|
+
if (typeof la === 'string')
|
|
722
|
+
return fail(la);
|
|
723
|
+
const lp = await la.plan(actions);
|
|
724
|
+
if (lp.plan.rejected.length)
|
|
725
|
+
return fail(`Blocked by the FIDES: ${REASONS[lp.plan.rejected[0].reason ?? ''] ?? lp.plan.rejected[0].reason}, counting this period's budget. Nothing was paid.`);
|
|
726
|
+
const r = await la.execute(lp);
|
|
727
|
+
if (r.status !== 'EXECUTED')
|
|
728
|
+
return fail('Could not move the price to the agent key: ' + failure(r.error) + ' Nothing was paid.');
|
|
729
|
+
funded = txLink(r.txHash);
|
|
730
|
+
float = price;
|
|
731
|
+
setFloat(fides, offer.asset, float);
|
|
732
|
+
}
|
|
733
|
+
const plan = isLong(fides) ? null : await new FidesBuilder({ client, fides, agent }).add(actions).build();
|
|
734
|
+
if (plan) {
|
|
735
|
+
if (plan.rejected().length) {
|
|
736
|
+
const r = plan.rejected()[0];
|
|
737
|
+
return fail(`Blocked by the FIDES: ${REASONS[r.reason ?? ''] ?? r.reason}. Nothing was paid.`);
|
|
738
|
+
}
|
|
739
|
+
const [sim] = await plan.simulate();
|
|
740
|
+
if (sim && !sim.success)
|
|
741
|
+
return fail('Blocked: ' + failure(sim.revertReason) + ' Nothing was paid.');
|
|
742
|
+
const bundles = await send(plan, actions, fides);
|
|
743
|
+
if (!bundles.length || bundles.some((b) => b.status !== 'EXECUTED'))
|
|
744
|
+
return fail('Could not move the price to the agent key: ' + failure(bundles.find((b) => b.error)?.error) + ' Nothing was paid.');
|
|
745
|
+
funded = txLink(bundles[0].txHash);
|
|
746
|
+
float = price;
|
|
747
|
+
setFloat(fides, offer.asset, float);
|
|
664
748
|
}
|
|
665
|
-
const [sim] = await plan.simulate();
|
|
666
|
-
if (sim && !sim.success)
|
|
667
|
-
return fail('Blocked: ' + failure(sim.revertReason) + ' Nothing was paid.');
|
|
668
|
-
const bundles = await send(plan, actions, fides);
|
|
669
|
-
if (!bundles.length || bundles.some((b) => b.status !== 'EXECUTED'))
|
|
670
|
-
return fail('Could not move the price to the agent key: ' + failure(bundles.find((b) => b.error)?.error) + ' Nothing was paid.');
|
|
671
|
-
funded = txLink(bundles[0].txHash);
|
|
672
|
-
float = price;
|
|
673
|
-
setFloat(fides, offer.asset, float);
|
|
674
749
|
}
|
|
675
750
|
// 3. pay and fetch
|
|
676
751
|
const res = await x402.fetch(url);
|
|
@@ -882,6 +957,28 @@ export function createFidesMcpServer(cfg) {
|
|
|
882
957
|
const mandate = excluded.find((x) => x.why.startsWith('trading mandate'));
|
|
883
958
|
return fail(mandate ? `Blocked by the ${mandate.why} Nothing was sent.` : `Uniswap has no route for this swap: ${excluded.map((x) => `${x.route} (${x.why})`).join('; ')}. Nothing was sent.`);
|
|
884
959
|
}
|
|
960
|
+
if (isLong(fides)) {
|
|
961
|
+
const la = longAgentFor(fides);
|
|
962
|
+
if (typeof la === 'string')
|
|
963
|
+
return fail(la);
|
|
964
|
+
const [bestName, bq] = [...quotes].sort((a, b) => (b[1].quotedOut > a[1].quotedOut ? 1 : -1))[0];
|
|
965
|
+
const outInfo = bq.tokenOut === NATIVE ? { symbol: 'ETH', decimals: 18 } : await token(bq.tokenOut);
|
|
966
|
+
const fmt = (v) => `${formatUnits(v, outInfo.decimals)} ${outInfo.symbol}`;
|
|
967
|
+
const lp = await la.plan(swapActions(bq, `swap ${amount} ${tIn.symbol} to ${outInfo.symbol} on Uniswap`));
|
|
968
|
+
const sim = lp.bundle ? await la.simulate(lp) : { success: false, reason: lp.plan.rejected[0]?.reason ?? 'NOTHING_TO_SEND' };
|
|
969
|
+
const id = (lp.bundleHash ?? '0x').slice(0, 18);
|
|
970
|
+
if (sim.success)
|
|
971
|
+
longPlans.set(id, { agent: la, plan: lp, fides, swap: { quote: bq, outDecimals: outInfo.decimals, outSymbol: outInfo.symbol } });
|
|
972
|
+
return ok({
|
|
973
|
+
plan_id: sim.success ? id : null, ready_to_execute: sim.success,
|
|
974
|
+
routes_compared: [...quotes].map(([name, q]) => ({ route: name, quoted: fmt(q.quotedOut), verdict: name === bestName ? 'best quote, chosen' : 'lower quote' })).concat(excluded.map((x) => ({ route: x.route, quoted: '-', verdict: 'out: ' + x.why }))),
|
|
975
|
+
chosen: bestName, route: describeRoute(bq), spend: `${amount} ${tIn.symbol}`, quoted: fmt(bq.quotedOut),
|
|
976
|
+
worst_accepted: `${fmt(bq.minOut)} (${bq.slippageBps / 100}% below the quote)`,
|
|
977
|
+
keeps_going: `period ${lp.period}: checked against this period's budget and the total`,
|
|
978
|
+
simulation: sim.success ? 'simulated against the chain: it would succeed' : `would fail: ${failure(sim.reason)}`,
|
|
979
|
+
sent: 'nothing has been sent',
|
|
980
|
+
});
|
|
981
|
+
}
|
|
885
982
|
const first = [...quotes.values()][0];
|
|
886
983
|
const outInfo = first.tokenOut === NATIVE ? { symbol: 'ETH', decimals: 18 } : await token(first.tokenOut);
|
|
887
984
|
const fmt = (v) => `${formatUnits(v, outInfo.decimals)} ${outInfo.symbol}`;
|
|
@@ -941,6 +1038,8 @@ export function createFidesMcpServer(cfg) {
|
|
|
941
1038
|
return fail('"agent" must be an address.');
|
|
942
1039
|
if (child.toLowerCase() === agent.address.toLowerCase())
|
|
943
1040
|
return fail('An agent cannot delegate to itself.');
|
|
1041
|
+
if (isLong(parent))
|
|
1042
|
+
return fail(noLong('hand out slices to other agents'));
|
|
944
1043
|
const v2 = await accountOf(subClient, parent.owner);
|
|
945
1044
|
if (v2.toLowerCase() !== parent.account.toLowerCase())
|
|
946
1045
|
return fail('This FIDES cannot delegate: its FIDES Account is not one that supports SUB-FIDES. The user signs a new FIDES that allows delegation: call fides_setup with delegate: true and give them the link.');
|
|
@@ -1078,6 +1177,28 @@ export function createFidesMcpServer(cfg) {
|
|
|
1078
1177
|
return fail('Execution stopped: ' + failure(err?.shortMessage ?? err?.message ?? err));
|
|
1079
1178
|
}
|
|
1080
1179
|
}
|
|
1180
|
+
const longEntry = longPlans.get(plan_id);
|
|
1181
|
+
if (longEntry) {
|
|
1182
|
+
longPlans.delete(plan_id);
|
|
1183
|
+
try {
|
|
1184
|
+
const sw = longEntry.swap, f2 = longEntry.fides;
|
|
1185
|
+
const outBalance = () => !sw ? Promise.resolve(0n) : sw.quote.tokenOut === NATIVE ? client.public.getBalance({ address: f2.account }) : client.public.readContract({ address: sw.quote.tokenOut, abi: erc20Abi, functionName: 'balanceOf', args: [f2.account] });
|
|
1186
|
+
const before = await outBalance();
|
|
1187
|
+
const r = await longEntry.agent.execute(longEntry.plan);
|
|
1188
|
+
const st = await longEntry.agent.status().catch(() => null), t = await token(f2.spendLimits[0].asset);
|
|
1189
|
+
const received = sw && r.status === 'EXECUTED' ? (await outBalance()) - before : 0n;
|
|
1190
|
+
return ok({
|
|
1191
|
+
result: r.status === 'EXECUTED' ? 'executed' : 'not executed',
|
|
1192
|
+
transactions: r.txHash || r.error ? [{ status: r.status.toLowerCase(), transaction: txLink(r.txHash), ...(r.error ? { reason: failure(r.error) } : {}) }] : [],
|
|
1193
|
+
blocked_and_not_sent: longEntry.plan.plan.rejected.map((a) => `${a.label}: ${REASONS[a.reason ?? ''] ?? a.reason}`),
|
|
1194
|
+
...(sw && r.status === 'EXECUTED' ? { best_execution: { quoted: `${formatUnits(sw.quote.quotedOut, sw.outDecimals)} ${sw.outSymbol}`, received: `${formatUnits(received, sw.outDecimals)} ${sw.outSymbol}`, route: describeRoute(sw.quote) } } : {}),
|
|
1195
|
+
...(st ? { this_period: { spent: `${formatUnits(st.thisPeriod[0].spent, t.decimals)} ${t.symbol}`, left: `${formatUnits(st.thisPeriod[0].remaining, t.decimals)} ${t.symbol}`, resets_at: new Date(Number(st.resetsAt) * 1000).toISOString() }, total_left: `${formatUnits(st.total[0].remaining, t.decimals)} of ${formatUnits(st.total[0].limit, t.decimals)} ${t.symbol}` } : {}),
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
catch (err) {
|
|
1199
|
+
return fail('Execution stopped: ' + failure(err?.shortMessage ?? err?.message ?? err));
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1081
1202
|
const entry = plans.get(plan_id);
|
|
1082
1203
|
if (!entry)
|
|
1083
1204
|
return fail('No plan with that id. Make one with fides_plan first (plans are kept until they are executed or the server restarts).');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidesbuild/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "FIDES as tools for Claude and any MCP client: plan and execute actions inside a signed FIDES, gas paid by the hosted relayer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fides",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@fidesbuild/net": "0.2.1",
|
|
44
44
|
"@fidesbuild/sub": "0.2.0",
|
|
45
45
|
"@fidesbuild/swap": "0.2.1",
|
|
46
|
-
"@fidesbuild/router": "0.2.0"
|
|
46
|
+
"@fidesbuild/router": "0.2.0",
|
|
47
|
+
"@fidesbuild/long": "0.2.0"
|
|
47
48
|
}
|
|
48
49
|
}
|