@parabolicfamily/mcp 0.1.2 → 0.1.3

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/src/server.ts CHANGED
@@ -69,18 +69,23 @@ export function createServer(opts: ServerOptions = {}): McpServer {
69
69
  const whole = z.union([z.string(), z.number()]);
70
70
  const slippageBps = z.number().int().min(0).max(9999).optional().describe("Slippage tolerance in basis points used to derive the minimum output (default 100 = 1%)");
71
71
  const buyer = z.string().optional().describe("Wallet that will send the buy; when given, its snipe-tax exemption is checked on the curve");
72
- const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true } as const;
72
+ // Tool arguments are validated strictly: an argument the tool does not know (a misspelling, or
73
+ // `referral` at the top level instead of inside `memo`) is an error, not something silently dropped
74
+ // and quietly changing what the tool does.
75
+ const strictArgs = <T extends z.ZodRawShape>(shape: T) => z.object(shape).strict();
76
+
77
+ const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true } as const;
73
78
 
74
79
  server.registerTool(
75
80
  "parabolic.list_coins",
76
81
  {
77
82
  title: "List Parabolic coins",
78
- description: "Coins on Parabolic with status, raised / threshold / progress, price and market cap. From the subgraph when PARABOLIC_SUBGRAPH_URL is set (adds holders, 24h volume, 24h change), otherwise from a TokenLaunched log scan of the factory (newest 50).",
79
- inputSchema: {
83
+ description: "Coins on Parabolic with status, raised / threshold / progress, price and market cap. From the subgraph when PARABOLIC_SUBGRAPH_URL is set (adds holders, 24h volume, 24h change), otherwise from Parabolic's public read API (PARABOLIC_API_URL); a TokenLaunched log scan of the factory is the last resort when both are unavailable.",
84
+ inputSchema: strictArgs({
80
85
  status: z.enum(["climbing", "graduated"]).optional().describe("climbing = still on the bonding curve; graduated = in its Uniswap v4 pool"),
81
86
  sort: z.enum(SORT_KEYS).optional().describe("trending (24h volume, then last trade), new (launch time), near_graduation (curve progress, climbing first), market_cap"),
82
87
  limit: z.number().int().min(1).max(100).optional().describe("Max coins to return (default 20)"),
83
- },
88
+ }),
84
89
  annotations: readOnly,
85
90
  },
86
91
  (args) => run(() => api.listCoins(args)),
@@ -91,7 +96,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
91
96
  {
92
97
  title: "Get a Parabolic coin",
93
98
  description: "Everything about one coin, read from the factory, curve and token (plus the subgraph when configured): name, ticker, creator, curve, status/phase, raised/threshold/progress, price, market cap, holders, pool id, fee terms and split, snipe-tax window and the current snipe-tax bps.",
94
- inputSchema: { address },
99
+ inputSchema: strictArgs({ address }),
95
100
  annotations: readOnly,
96
101
  },
97
102
  (args) => run(() => api.getCoin(args.address)),
@@ -102,7 +107,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
102
107
  {
103
108
  title: "Quote a curve buy",
104
109
  description: "Tokens received for `quoteIn` USDC (or EURC) using the curve's exact math on live reserves: fee, creator tax and snipe tax breakdown, spot vs effective price, price impact, whether the buy graduates the coin, and a suggested minTokensOut.",
105
- inputSchema: { address, quoteIn: whole.describe("Quote to spend, whole units (e.g. \"50\" = 50 USDC)"), buyer, slippageBps },
110
+ inputSchema: strictArgs({ address, quoteIn: whole.describe("Quote to spend, whole units (e.g. \"50\" = 50 USDC)"), buyer, slippageBps }),
106
111
  annotations: readOnly,
107
112
  },
108
113
  (args) => run(() => api.quoteBuy(args)),
@@ -113,7 +118,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
113
118
  {
114
119
  title: "Quote a curve sell",
115
120
  description: "Quote received for `tokensIn` coins using the curve's exact math on live reserves: gross, fee and creator tax, effective price, price impact and a suggested minQuoteOut. Sells carry no snipe tax.",
116
- inputSchema: { address, tokensIn: whole.describe("Coins to sell, whole tokens (e.g. \"1000000\")"), slippageBps },
121
+ inputSchema: strictArgs({ address, tokensIn: whole.describe("Coins to sell, whole tokens (e.g. \"1000000\")"), slippageBps }),
117
122
  annotations: readOnly,
118
123
  },
119
124
  (args) => run(() => api.quoteSell(args)),
@@ -124,7 +129,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
124
129
  {
125
130
  title: "Build an unsigned buy",
126
131
  description: "Unsigned `curve.buy(quoteIn, minTokensOut, recipient)` as { to, data, value, chainId } for the caller's wallet to sign. Native-USDC curves take the quote as value; EURC curves return an approval step too. Pass `memo` for the memo-routed variant (referral code in an Arc memo; EOA-only).",
127
- inputSchema: {
132
+ inputSchema: strictArgs({
128
133
  address,
129
134
  quoteIn: whole.describe("Quote to spend, whole units"),
130
135
  minTokensOut: whole.optional().describe("Minimum coins to receive, whole tokens; derived from the live quote and slippageBps when omitted"),
@@ -132,7 +137,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
132
137
  buyer,
133
138
  slippageBps,
134
139
  memo: z.object({ referral: z.string().optional().describe("Referrer's 16-hex referral code or wallet address"), note: z.string().max(280).optional() }).optional().describe("Route through ParabolicMemoRouter via Arc's Memo contract (EOA-only, needs PARABOLIC_MEMO_ROUTER)"),
135
- },
140
+ }),
136
141
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
137
142
  },
138
143
  (args) => run(() => api.buildBuyTx(args)),
@@ -143,13 +148,13 @@ export function createServer(opts: ServerOptions = {}): McpServer {
143
148
  {
144
149
  title: "Build an unsigned sell",
145
150
  description: "Unsigned ERC-20 approval plus `curve.sell(tokensIn, minQuoteOut, recipient)` as { to, data, value, chainId } for the caller's wallet to sign.",
146
- inputSchema: {
151
+ inputSchema: strictArgs({
147
152
  address,
148
153
  tokensIn: whole.describe("Coins to sell, whole tokens"),
149
154
  minQuoteOut: whole.optional().describe("Minimum quote to receive, whole units; derived from the live quote and slippageBps when omitted"),
150
155
  recipient: z.string().describe("Address that receives the quote (normally the signer)"),
151
156
  slippageBps,
152
- },
157
+ }),
153
158
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
154
159
  },
155
160
  (args) => run(() => api.buildSellTx(args)),
@@ -160,7 +165,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
160
165
  {
161
166
  title: "Build an unsigned launch",
162
167
  description: "Unsigned `factory.launchToken(TokenParams, launchConfigId, pairToken)` with the launch fee as value, using the same TokenParams shape as the web Create form (expectedEconomics pinned from the factory, random salt). Optional devBuy returns the follow-up buy calldata for the creator to send once the curve address is known.",
163
- inputSchema: {
168
+ inputSchema: strictArgs({
164
169
  name: z.string().min(1).max(64),
165
170
  ticker: z.string().min(1).max(16).describe("Symbol, letters and digits"),
166
171
  image: z.string().max(512).optional().describe("https:// or ipfs:// pointer to a square image"),
@@ -171,7 +176,7 @@ export function createServer(opts: ServerOptions = {}): McpServer {
171
176
  creatorTaxBps: z.number().int().min(0).max(1000).optional().describe("Creator tax in basis points, 0–500 (5%) on the adopted parameters"),
172
177
  creator: z.string().describe("Wallet that signs the launch; becomes deployer and creatorFeeRecipient"),
173
178
  buybackEnabled: z.boolean().optional().describe("Default true"),
174
- },
179
+ }),
175
180
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
176
181
  },
177
182
  (args) => run(() => api.buildLaunchTx(args)),