@potswap_io/mcp 0.5.0 → 0.7.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/README.md +3 -1
- package/dist/observationTools.d.ts +8 -0
- package/dist/observationTools.js +72 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.js +26 -9
- package/dist/strategies/tools.js +4 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -73,11 +73,13 @@ Use a dedicated agent wallet with only the funds you want the agent to control.
|
|
|
73
73
|
| `get_token {mint}` | Price, mcap, liquidity, holders, audit data, **risk badges** (`riskLevel` 0 safe → 3 danger) |
|
|
74
74
|
| `get_trending {kind}` | `new`, `trending`, `top`, `graduating` lists |
|
|
75
75
|
| `get_quote {inputMint, outputMint, amount}` | Best route, fee, impact, router. `amount` is in UI units (1 = 1 SOL) |
|
|
76
|
-
| `swap {…, confirm: true}` | Executes at the best route and returns the signature, amounts, fee and POT points |
|
|
76
|
+
| `swap {…, confirm: true, route?, opId?}` | Executes at the best route and returns the signature, amounts, fee and POT points. `route: direct` swaps on a Raydium CPMM pool built and validated locally (same fee); `opId` makes retries safe |
|
|
77
|
+
| `get_operation {id?}` | State of an idempotent swap (pending / sent / ok / failed / unknown) |
|
|
77
78
|
| `get_balances {wallet?}` | SOL + SPL balances with USD |
|
|
78
79
|
| `get_positions {wallet?}` | Holdings with USD value, average cost, unrealised and realised PnL (from swap history) |
|
|
79
80
|
| `get_points {wallet?}` | Season 1 points, POT allocated / claimable, next unlock |
|
|
80
81
|
| `get_leaderboard` | Top wallets by fee paid |
|
|
82
|
+
| `observe_publish`, `observe_query`, `observe_verify`, `observe_consume` | Signed, slot-anchored market observations on the open index: publish with your wallet, read everyone's with a per-publisher median, verify locally, record that you used one (earns the publisher Season 2 rewards). `limit_order_create` can trigger on the observation median (`priceSource: observations`) |
|
|
81
83
|
| `get_launch_status` | Launch-pool meter (SOL / 500 target, from chain), season and day, fee split, unclaimed reward balances and the public claim ladder |
|
|
82
84
|
| `get_price_history {mint, timeframe, limit}` | OHLCV candles from the token's top pool (`1m`…`1d`) |
|
|
83
85
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type Config, type TradeContext } from '@potswap_io/sdk';
|
|
3
|
+
export interface ObservationToolDeps {
|
|
4
|
+
trade: TradeContext;
|
|
5
|
+
config: Config;
|
|
6
|
+
getSlot?: () => Promise<number>;
|
|
7
|
+
}
|
|
8
|
+
export declare function registerObservationTools(server: McpServer, deps: ObservationToolDeps): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Connection } from '@solana/web3.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { NO_WALLET, OBSERVATION_KINDS, aggregate, redact, redactValue, signConsumption, signObservation, verifyObservation } from '@potswap_io/sdk';
|
|
4
|
+
const ok = (data, summary) => ({ content: [{ type: 'text', text: redact(summary ?? JSON.stringify(data, null, 2)) }], structuredContent: redactValue(data) });
|
|
5
|
+
const fail = (message, extra = {}) => ({ content: [{ type: 'text', text: redact(message) }], structuredContent: redactValue({ error: message, ...extra }), isError: true });
|
|
6
|
+
const MINT = z.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
|
|
7
|
+
const KIND = z.enum(OBSERVATION_KINDS);
|
|
8
|
+
export function registerObservationTools(server, deps) {
|
|
9
|
+
const { trade, config } = deps;
|
|
10
|
+
const api = trade.api;
|
|
11
|
+
const getSlot = deps.getSlot ?? (() => new Connection(config.rpcUrl, 'confirmed').getSlot());
|
|
12
|
+
server.registerTool('observe_publish', {
|
|
13
|
+
title: 'Publish a signed observation',
|
|
14
|
+
description: 'Signs a market observation (price, liquidity, volume24h, holders, risk, route) with the agent wallet, anchored to the current slot, and posts it to the open Potswap index. Free; no funds move. Other agents can verify and consume it; verified consumption earns the publisher Season 2 rewards.',
|
|
15
|
+
inputSchema: { kind: KIND, mint: MINT, value: z.number().finite(), unit: z.string().min(1).max(16).optional(), source: z.string().min(1).max(64), ttlSec: z.number().int().min(10).max(86400).default(300), note: z.string().max(280).optional() },
|
|
16
|
+
}, async (a) => {
|
|
17
|
+
if (!trade.signer)
|
|
18
|
+
return fail(NO_WALLET);
|
|
19
|
+
try {
|
|
20
|
+
const slot = await getSlot();
|
|
21
|
+
const obs = signObservation(trade.signer, { ...a, slot });
|
|
22
|
+
const r = await api.publishObservation(obs);
|
|
23
|
+
return ok({ ...obs, ...r }, `Published ${a.kind} for ${a.mint.slice(0, 4)}… = ${a.value} ${obs.unit} (id ${obs.id}, slot ${slot}, ttl ${obs.ttlSec}s)${r.duplicate ? ' — already indexed' : ''}.`);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return fail(e instanceof Error ? e.message : String(e));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.registerTool('observe_query', {
|
|
30
|
+
title: 'Query observations',
|
|
31
|
+
description: 'Fresh observations from the open index, newest first, with a per-publisher median aggregate when mint and kind are given. Free, no wallet needed.',
|
|
32
|
+
inputSchema: { mint: MINT.optional(), kind: KIND.optional(), publisher: MINT.optional(), limit: z.number().int().min(1).max(200).default(50), includeStale: z.boolean().default(false) },
|
|
33
|
+
}, async (a) => {
|
|
34
|
+
try {
|
|
35
|
+
const r = await api.observations({ mint: a.mint, kind: a.kind, publisher: a.publisher, limit: a.limit, fresh: !a.includeStale });
|
|
36
|
+
const agg = a.mint && a.kind ? aggregate(r.items, a.kind, a.mint, r.now ?? Date.now()) : undefined;
|
|
37
|
+
return ok({ ...r, ...(agg ? { aggregate: agg } : {}) }, agg
|
|
38
|
+
? `${agg.count} fresh ${a.kind} observation(s) from ${agg.publishers} publisher(s)${agg.median !== null ? ` · median ${agg.median} · spread ${agg.spreadPct?.toFixed(2)}%` : ''}`
|
|
39
|
+
: `${r.items.length} observation(s)` + (r.items.length ? `\n` + r.items.slice(0, 10).map((o) => `${new Date(o.observedAt).toISOString()} ${o.kind} ${o.mint.slice(0, 4)}… = ${o.value} ${o.unit} by ${o.publisher.slice(0, 4)}… (${o.source})`).join('\n') : ''));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
return fail(e instanceof Error ? e.message : String(e));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
server.registerTool('observe_verify', {
|
|
46
|
+
title: 'Verify an observation',
|
|
47
|
+
description: 'Checks an observation locally: schema, id, Ed25519 signature by its publisher, freshness; for price observations also the deviation from the current Potswap price.',
|
|
48
|
+
inputSchema: { observation: z.record(z.string(), z.unknown()) },
|
|
49
|
+
}, async ({ observation }) => {
|
|
50
|
+
const o = observation;
|
|
51
|
+
let referencePrice = null;
|
|
52
|
+
if (o.kind === 'price' && o.mint)
|
|
53
|
+
referencePrice = (await api.prices([o.mint]).catch(() => ({})))[o.mint] ?? null;
|
|
54
|
+
const v = verifyObservation(observation, { referencePrice });
|
|
55
|
+
return ok({ ...v, referencePrice }, v.ok ? `Valid${v.fresh ? ', fresh' : ', expired'}${v.deviationPct !== undefined && v.deviationPct !== null ? ` · ${v.deviationPct >= 0 ? '+' : ''}${v.deviationPct.toFixed(2)}% vs current price` : ''}.` : `Invalid: ${v.reason}`);
|
|
56
|
+
});
|
|
57
|
+
server.registerTool('observe_consume', {
|
|
58
|
+
title: 'Record that you used an observation',
|
|
59
|
+
description: 'Signs a receipt with the agent wallet saying this observation informed a decision. Receipts weight the publisher\'s Season 2 rewards. Free.',
|
|
60
|
+
inputSchema: { id: z.string().regex(/^[0-9a-f]{32}$/) },
|
|
61
|
+
}, async ({ id }) => {
|
|
62
|
+
if (!trade.signer)
|
|
63
|
+
return fail(NO_WALLET);
|
|
64
|
+
try {
|
|
65
|
+
const r = await api.consumeObservation(signConsumption(trade.signer, id));
|
|
66
|
+
return ok(r, `Receipt recorded for ${id} (publisher ${String(r.publisher).slice(0, 4)}…)${r.duplicate ? ' — already recorded' : ''}.`);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return fail(e instanceof Error ? e.message : String(e));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
package/dist/server.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ import { type BalancesFn } from '@potswap_io/sdk';
|
|
|
5
5
|
import { type Signer } from '@potswap_io/sdk';
|
|
6
6
|
import { type TxRpc } from './agentTools.js';
|
|
7
7
|
import type { Rpc } from '@potswap_io/sdk';
|
|
8
|
+
import { type OpsStore } from '@potswap_io/sdk';
|
|
8
9
|
import type { StrategyStore } from '@potswap_io/sdk';
|
|
9
10
|
import type { Scheduler } from '@potswap_io/sdk';
|
|
10
|
-
export declare const VERSION = "0.
|
|
11
|
+
export declare const VERSION = "0.7.0";
|
|
11
12
|
export interface ServerDeps {
|
|
12
13
|
config?: Config;
|
|
13
14
|
signer?: Signer | null;
|
|
@@ -20,6 +21,10 @@ export interface ServerDeps {
|
|
|
20
21
|
/** Transfer / transaction RPC surfaces; injectable for tests (P13). */
|
|
21
22
|
rpc?: Rpc;
|
|
22
23
|
txRpc?: TxRpc;
|
|
24
|
+
/** Current slot for observation anchoring; injectable for tests (P15). */
|
|
25
|
+
getSlot?: () => Promise<number>;
|
|
26
|
+
/** Idempotent operation records (P14.3); defaults to a file store in the state dir. */
|
|
27
|
+
ops?: OpsStore;
|
|
23
28
|
}
|
|
24
29
|
export declare function createServer(deps?: ServerDeps): McpServer;
|
|
25
30
|
export { SOL_MINT, USDC_MINT };
|
package/dist/server.js
CHANGED
|
@@ -10,7 +10,9 @@ import { loadSigner } from '@potswap_io/sdk';
|
|
|
10
10
|
import { riskBadges, riskLevel } from '@potswap_io/sdk';
|
|
11
11
|
import { registerStrategyTools } from './strategies/tools.js';
|
|
12
12
|
import { registerAgentTools } from './agentTools.js';
|
|
13
|
-
|
|
13
|
+
import { registerObservationTools } from './observationTools.js';
|
|
14
|
+
import { FileOpsStore, rpcFromConnection } from '@potswap_io/sdk';
|
|
15
|
+
export const VERSION = '0.7.0';
|
|
14
16
|
const ok = (data, summary) => ({
|
|
15
17
|
content: [{ type: 'text', text: redact(summary ?? JSON.stringify(data, null, 2)) }], structuredContent: redactValue(data),
|
|
16
18
|
});
|
|
@@ -22,7 +24,7 @@ export function createServer(deps = {}) {
|
|
|
22
24
|
const signer = deps.signer !== undefined ? deps.signer : loadSigner({ privateKey: config.privateKey, keypairPath: config.keypairPath, stateDir: config.stateDir });
|
|
23
25
|
const api = new PotswapApi(config.apiBase, deps.fetchFn);
|
|
24
26
|
const readBalances = deps.balances ?? rpcBalances(config.rpcUrl);
|
|
25
|
-
const trade = { api, config, signer, balances: readBalances };
|
|
27
|
+
const trade = { api, config, signer, balances: readBalances, rpc: deps.rpc ?? rpcFromConnection(new Connection(config.rpcUrl, 'confirmed')), ops: deps.ops ?? new FileOpsStore(config.stateDir) };
|
|
26
28
|
const server = new McpServer({ name: 'potswap', version: VERSION });
|
|
27
29
|
const walletOr = (w) => w ?? signer?.publicKey ?? null;
|
|
28
30
|
server.registerTool('search_tokens', {
|
|
@@ -69,12 +71,12 @@ export function createServer(deps = {}) {
|
|
|
69
71
|
});
|
|
70
72
|
server.registerTool('get_quote', {
|
|
71
73
|
title: 'Get a swap quote',
|
|
72
|
-
description: 'Best route across Metis, JupiterZ, DFlow and OKX. Amount is in UI units of the input token (1 = 1 SOL). Includes the Potswap fee (returned to the wallet as POT points).',
|
|
74
|
+
description: 'Best route across Metis, JupiterZ, DFlow and OKX, plus `direct` when a Raydium CPMM pool exists (same Potswap fee, compared net). Amount is in UI units of the input token (1 = 1 SOL). Includes the Potswap fee (returned to the wallet as POT points).',
|
|
73
75
|
inputSchema: quoteShape,
|
|
74
76
|
}, async (a) => {
|
|
75
77
|
try {
|
|
76
|
-
const q = await buildQuote(api, a);
|
|
77
|
-
return ok({ ...q }, `${a.amount} ${q.inputSymbol} → ${q.outAmount} ${q.outputSymbol} via ${q.router} (fee ${q.feeLabel ?? `${q.feeBps} bps`}, impact ${q.priceImpactPct}%)`);
|
|
78
|
+
const q = await buildQuote(api, a, { direct: trade.rpc });
|
|
79
|
+
return ok({ ...q }, `${a.amount} ${q.inputSymbol} → ${q.outAmount} ${q.outputSymbol} via ${q.router} (fee ${q.feeLabel ?? `${q.feeBps} bps`}, impact ${q.priceImpactPct}%)${q.direct ? `\nDirect Raydium CPMM: ${q.direct.outAmount} ${q.outputSymbol} (${q.direct.savedVsAggregatorPct >= 0 ? '+' : ''}${q.direct.savedVsAggregatorPct.toFixed(2)}% vs aggregator, pool fee ${q.direct.poolTradeFeeBps} bps)` : ''}`);
|
|
78
80
|
}
|
|
79
81
|
catch (e) {
|
|
80
82
|
return fail(msg(e));
|
|
@@ -82,19 +84,33 @@ export function createServer(deps = {}) {
|
|
|
82
84
|
});
|
|
83
85
|
server.registerTool('swap', {
|
|
84
86
|
title: 'Swap tokens (signs with the agent wallet)',
|
|
85
|
-
description: 'Executes a swap at the best route. Requires confirm=true; otherwise returns the quote only. Protected by POTSWAP_MAX_TRADE_SOL and
|
|
86
|
-
inputSchema: { ...quoteShape, confirm: z.boolean().default(false), maxSlippageBps: z.number().int().min(1).max(5000).optional() },
|
|
87
|
+
description: 'Executes a swap at the best route. Requires confirm=true; otherwise returns the quote only. Protected by POTSWAP_MAX_TRADE_SOL, POTSWAP_MAX_SLIPPAGE_BPS and the transaction validator. route=direct uses a Raydium CPMM pool built locally (same fee); route=auto picks the better net output. opId makes the call safe to retry (a repeat returns the stored result). Every error states whether funds were debited.',
|
|
88
|
+
inputSchema: { ...quoteShape, confirm: z.boolean().default(false), maxSlippageBps: z.number().int().min(1).max(5000).optional(), route: z.enum(['jupiter', 'direct', 'auto']).default('jupiter'), opId: z.string().min(4).max(64).optional() },
|
|
87
89
|
}, async (a) => {
|
|
88
|
-
const out = await executeSwap(trade, { inputMint: a.inputMint, outputMint: a.outputMint, amount: a.amount, slippageBps: a.slippageBps ?? a.maxSlippageBps, confirm: a.confirm });
|
|
90
|
+
const out = await executeSwap(trade, { inputMint: a.inputMint, outputMint: a.outputMint, amount: a.amount, slippageBps: a.slippageBps ?? a.maxSlippageBps, confirm: a.confirm, route: a.route, opId: a.opId });
|
|
89
91
|
switch (out.kind) {
|
|
90
92
|
case 'no-wallet': return fail(out.message);
|
|
91
93
|
case 'refused': return fail(out.message, { quote: out.quote });
|
|
92
94
|
case 'quote-only': return ok({ confirmRequired: true, quote: out.quote }, `Quote only (confirm=true to execute): ${a.amount} ${out.quote.inputSymbol} → ${out.quote.outAmount} ${out.quote.outputSymbol} via ${out.quote.router}`);
|
|
93
95
|
case 'dry-run': return ok({ dryRun: true, quote: out.quote }, `DRY RUN, nothing signed: ${a.amount} ${out.quote.inputSymbol} → ${out.quote.outAmount} ${out.quote.outputSymbol}`);
|
|
94
96
|
case 'failed': return fail(out.message, { signature: out.signature, explorerUrl: out.explorerUrl, quote: out.quote });
|
|
95
|
-
case 'ok': return ok({ ...out.result },
|
|
97
|
+
case 'ok': return ok({ ...out.result }, `${out.result.replayed ? 'Already done (replayed): ' : 'Swapped '}${a.amount} ${out.result.inputSymbol} → ${out.result.outAmount} ${out.result.outputSymbol} via ${out.result.router}. ${out.result.explorerUrl}`);
|
|
96
98
|
}
|
|
97
99
|
});
|
|
100
|
+
server.registerTool('get_operation', {
|
|
101
|
+
title: 'Operation record',
|
|
102
|
+
description: 'State of an idempotent swap by opId: pending, sent, ok, failed or unknown, with the signature and result when known. Lists recent operations without an id.',
|
|
103
|
+
inputSchema: { id: z.string().min(4).max(64).optional(), limit: z.number().int().min(1).max(100).default(20) },
|
|
104
|
+
}, async ({ id, limit }) => {
|
|
105
|
+
if (!trade.ops)
|
|
106
|
+
return fail('No operation store configured.');
|
|
107
|
+
if (id) {
|
|
108
|
+
const r = trade.ops.get(id);
|
|
109
|
+
return r ? ok({ ...r }, `${r.id}: ${r.state}${r.signature ? ` · ${r.signature}` : ''}${r.error ? ` · ${r.error}` : ''}`) : fail(`No operation ${id}.`);
|
|
110
|
+
}
|
|
111
|
+
const rows = trade.ops.list(limit);
|
|
112
|
+
return ok({ operations: rows }, rows.map((r) => `${r.updatedAt} ${r.id} ${r.state}${r.signature ? ` ${r.signature.slice(0, 8)}…` : ''}`).join('\n') || 'No operations yet.');
|
|
113
|
+
});
|
|
98
114
|
server.registerTool('get_balances', {
|
|
99
115
|
title: 'Wallet balances',
|
|
100
116
|
description: 'SOL and SPL token balances with USD values. Defaults to the agent wallet.',
|
|
@@ -148,6 +164,7 @@ export function createServer(deps = {}) {
|
|
|
148
164
|
});
|
|
149
165
|
registerAgentTools(server, { trade, config, rpc: deps.rpc, txRpc: deps.txRpc, schedulerActive: () => !!deps.scheduler?.active });
|
|
150
166
|
registerStrategyTools(server, { trade, store: deps.strategyStore, scheduler: deps.scheduler });
|
|
167
|
+
registerObservationTools(server, { trade, config, getSlot: deps.getSlot });
|
|
151
168
|
return server;
|
|
152
169
|
}
|
|
153
170
|
const quoteShape = {
|
package/dist/strategies/tools.js
CHANGED
|
@@ -112,12 +112,14 @@ export function registerStrategyTools(server, deps) {
|
|
|
112
112
|
});
|
|
113
113
|
server.registerTool('limit_order_create', {
|
|
114
114
|
title: 'Create a limit order (price-triggered swap)',
|
|
115
|
-
description: 'Buy or sell a token when its USD price crosses a level. Runs through the strategy scheduler (needs this host open or the daemon). Fills once at the best route with the normal fee and points, then completes.',
|
|
115
|
+
description: 'Buy or sell a token when its USD price crosses a level. Runs through the strategy scheduler (needs this host open or the daemon). Fills once at the best route with the normal fee and points, then completes. priceSource=observations uses the median of fresh signed observations instead of the price API.',
|
|
116
116
|
inputSchema: {
|
|
117
117
|
side: z.enum(['buy', 'sell']), mint: MINT, quoteMint: MINT.default(SOL_MINT),
|
|
118
118
|
amount: z.number().positive().describe('Input amount in UI units: quote (SOL) amount for buys, token amount for sells'),
|
|
119
119
|
triggerPriceUsd: z.number().positive(), condition: z.enum(['above', 'below']),
|
|
120
120
|
expiresAt: z.string().datetime().optional(), maxSlippageBps: z.number().int().min(1).max(5000).optional(),
|
|
121
|
+
priceSource: z.enum(['potswap', 'observations']).default('potswap').describe('observations = median of fresh signed observations from at least minPublishers publishers (PRD P15)'),
|
|
122
|
+
minPublishers: z.number().int().min(1).max(50).default(2),
|
|
121
123
|
confirm: z.boolean().default(false),
|
|
122
124
|
},
|
|
123
125
|
}, async (a) => {
|
|
@@ -126,7 +128,7 @@ export function registerStrategyTools(server, deps) {
|
|
|
126
128
|
return g;
|
|
127
129
|
if (a.mint === a.quoteMint)
|
|
128
130
|
return fail('mint and quoteMint must differ. Nothing was created.');
|
|
129
|
-
const s = { ...base('limit', a.maxSlippageBps), kind: 'limit', side: a.side, mint: a.mint, quoteMint: a.quoteMint, amount: a.amount, triggerPriceUsd: a.triggerPriceUsd, condition: a.condition, expiresAt: a.expiresAt, nextRunAt: null };
|
|
131
|
+
const s = { ...base('limit', a.maxSlippageBps), kind: 'limit', side: a.side, mint: a.mint, quoteMint: a.quoteMint, amount: a.amount, triggerPriceUsd: a.triggerPriceUsd, condition: a.condition, expiresAt: a.expiresAt, nextRunAt: null, ...(a.priceSource === 'observations' ? { priceSource: 'observations', minPublishers: a.minPublishers } : {}) };
|
|
130
132
|
return created(s, `Limit ${a.side} created: ${a.amount} ${a.side === 'buy' ? 'quote' : 'tokens'} when price ${a.condition} $${a.triggerPriceUsd}${a.expiresAt ? ` until ${a.expiresAt}` : ''}.`);
|
|
131
133
|
});
|
|
132
134
|
server.registerTool('strategy_list', { title: 'List strategies', description: 'All strategies with status, next run, totals and last result.', inputSchema: {} }, async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@potswap_io/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Potswap MCP server: best-route Solana swaps, wallet, transfers, staking, DCA / rebalance / take-profit / limit strategies and POT rewards for AI agents. Non-custodial; built on @potswap_io/sdk.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
43
|
-
"@potswap_io/sdk": "^0.
|
|
43
|
+
"@potswap_io/sdk": "^0.3.0",
|
|
44
44
|
"@solana/web3.js": "^1.99.0",
|
|
45
45
|
"zod": "^4.6.7"
|
|
46
46
|
},
|