@cleave-protocol/ai-sdk 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cleave
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,340 @@
1
- # Temporary Holding Version
1
+ # @cleave-protocol/ai-sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Cleave swap tools for [Vercel AI SDK](https://ai-sdk.dev) agents.
4
+
5
+ Cleave is a DEX aggregator on Ethereum, Arbitrum, Base, BNB Chain, Polygon
6
+ and Optimism. It splits a trade across pools and paths, and swaps between
7
+ chains in one step. Quotes include Cleave's fees in the price.
8
+
9
+ These tools plan swaps and return unsigned calls. The user's own wallet
10
+ signs and sends them. They sign nothing; the only thing they send is a gas
11
+ order the wallet has signed.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install @cleave-protocol/ai-sdk ai zod
17
+ ```
18
+
19
+ No API key is needed.
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { generateText, isStepCount } from 'ai';
25
+ import { cleaveTools } from '@cleave-protocol/ai-sdk';
26
+
27
+ const { text } = await generateText({
28
+ model: 'openai/gpt-5-mini',
29
+ prompt:
30
+ 'Plan a swap of 25 USDC to WETH on Base for wallet 0x00000000000000000000000000000000000A11cE.',
31
+ tools: cleaveTools(),
32
+ stopWhen: isStepCount(5),
33
+ });
34
+
35
+ console.log(text);
36
+ ```
37
+
38
+ On `ai` 6, use `stepCountIs` in place of `isStepCount`.
39
+
40
+ `cleaveTools()` includes `getGas`. Its second call passes a gas order the
41
+ wallet has signed to Cleave's relayer. That call waits for your approval
42
+ by default; see [Approving a gas order](#approving-a-gas-order).
43
+
44
+ Each tool can also be added on its own:
45
+
46
+ ```ts
47
+ import { findToken, planSwap } from '@cleave-protocol/ai-sdk';
48
+
49
+ const tools = { planSwap: planSwap(), findToken: findToken() };
50
+ ```
51
+
52
+ ## Tools
53
+
54
+ The tools match Cleave's MCP server tools and call the public
55
+ [Agent REST API](https://cleave.fi/docs/agents/rest).
56
+
57
+ | Tool | MCP tool | What it does |
58
+ | --- | --- | --- |
59
+ | `planSwap` | `cleave_plan_swap` | Plans a swap on one chain, a cross-chain swap or a bridge, and returns unsigned calls in signing order |
60
+ | `trackSwap` | `cleave_track_swap` | Reports the state of a sent plan or gas order, and what arrived |
61
+ | `findToken` | `cleave_find_token` | Finds a token by symbol or address and flags look-alikes |
62
+ | `getGas` | `cleave_get_gas` | Offers native gas for USDC, then passes the order the wallet signed to Cleave's relayer |
63
+
64
+ The usual flow:
65
+
66
+ 1. `findToken`, when a symbol is ambiguous.
67
+ 2. `planSwap`, with the wallet, the tokens and the amount.
68
+ 3. The wallet signs the returned `calls` in order, before `validUntil`.
69
+ 4. `trackSwap`, with the chain, the transaction hash and the plan's
70
+ `requestId`.
71
+
72
+ Check with `trackSwap` before sending a plan again: a plan still in flight
73
+ that is sent twice can pay twice.
74
+
75
+ A plan states its notices first, then the outcome kind, the costs charged
76
+ by others, the expected time and `validUntil`. A `block` notice means no
77
+ calls. A `warn` notice, or a recipient other than the wallet, sets
78
+ `requiresConfirmation`. The token to receive must be on Cleave's token
79
+ list.
80
+
81
+ Before any wallet signs, show the user the warnings, recipient, amounts
82
+ and slippage, and get their confirmation. Do not raise slippage above the
83
+ default 50 bps without the user's consent. Your signer enforces this: the
84
+ tools only plan.
85
+
86
+ `findToken` returns up to ten candidates for a symbol. When the user can
87
+ give a verified contract address, prefer it to a symbol.
88
+
89
+ Field-by-field answers are documented at
90
+ [cleave.fi/docs/agents/mcp](https://cleave.fi/docs/agents/mcp).
91
+
92
+ ## Keys and signing
93
+
94
+ - The tools take no private keys.
95
+ - They sign nothing; the only thing they send is a gas order the wallet
96
+ has signed.
97
+ - `planSwap` returns unsigned calls as `{ chainId, from, to, data, value }`.
98
+ - `getGas` returns an offer with one USDC authorization to sign. Called
99
+ again with that offer, the wallet and the wallet's signature, it passes
100
+ the signed order to Cleave's relayer. That second call has
101
+ `needsApproval` set, so the AI SDK asks for your approval before it runs.
102
+
103
+ ## Address checks
104
+
105
+ Before a `planSwap` plan or a `getGas` offer reaches the model, the package
106
+ checks it against per-chain contract addresses, rails and tokens pinned in
107
+ this package:
108
+
109
+ - `planSwap`: every call is on the planned chain and from the wallet. The
110
+ last call goes to a listed contract and function that fits the route,
111
+ sends to the planned chain and pays the requested recipient where its
112
+ calldata names one; refunds go to the wallet. Any call before it is an
113
+ approve of the sell token to that contract for exactly what the last
114
+ call pulls. For USDC and the chain's native or wrapped native coin, the
115
+ amount sold is the requested amount. The native value is what the
116
+ native amount sold and the Stargate native messaging fee need.
117
+ - `getGas`: the authorization is a USDC `ReceiveWithAuthorization` from
118
+ the wallet on the paying chain. The order carries the requested amount,
119
+ recipient and refund address, the pinned contracts, rail and fee
120
+ recipient, and the fee and amounts the offer shows. The order hash and
121
+ the payment address are recomputed and must match what is signed.
122
+
123
+ A plan or offer that fails a check is not passed on; the tool returns only
124
+ this:
125
+
126
+ ```json
127
+ {
128
+ "error": {
129
+ "code": "address_not_allowed",
130
+ "message": "P2: the main call's target is not on the allowlist",
131
+ "retryable": false
132
+ }
133
+ }
134
+ ```
135
+
136
+ A plan or offer that passes is returned with only its documented
137
+ top-level fields.
138
+
139
+ What these checks do not cover:
140
+
141
+ - The price, the route and the amount received.
142
+ - On routes that swap on arrival or pay a fee through the bridge message,
143
+ the funds go to the Across handler or Cleave's composer, and that
144
+ message decides who receives them. These checks do not read it, so a
145
+ compromised API could redirect those funds. Have a person check such a
146
+ plan before signing.
147
+ - When `sellToken` is a symbol other than USDC or the chain's native or
148
+ wrapped native coin, the API resolves it to an address. Check
149
+ `send.token` before signing.
150
+ - For any other sell token, given by symbol or by address, the package
151
+ does not know its decimals, so the amount in the approve and the
152
+ calldata is not checked against the request. Check the approve amount
153
+ and `pullsAtMost` before signing.
154
+ - The Stargate native messaging fee is the API's figure. The checks only
155
+ match it with the call's value; nothing bounds it.
156
+ - `next` and the notices are text from the API.
157
+
158
+ After Cleave redeploys a contract, plans that use it fail these checks
159
+ until a package update pins the new address.
160
+
161
+ Agents that call the API directly should pass each answer through
162
+ `screenPlanSwap(request, body)` or `screenGetGas(request, body)` and use
163
+ only what they return: the refusal, or the answer cut to its documented
164
+ fields. `checkPlanSwap` and `checkGetGas` return only a verdict; a pass
165
+ from them does not make the original answer safe to forward, since it
166
+ can carry fields the checks do not read.
167
+
168
+ Do not pair these tools with a wallet that signs without a human looking.
169
+
170
+ ## Gas top-ups
171
+
172
+ `getGas` takes two calls.
173
+
174
+ 1. The offer: `payChainId`, `chainId`, `recipient` and `amount`.
175
+ - `typedData`, the USDC authorization to sign, comes back only when
176
+ `wallet` is passed.
177
+ - An order from another chain needs `wallet` or `refundTo`. On one
178
+ chain, a refund goes to `refundTo`, else `wallet`, else the recipient.
179
+ - `amount` is USDC with at most 6 decimals, from 2 to 50 USDC; gas
180
+ delivered on Ethereum needs at least 5 USDC.
181
+ 2. The order: `offer`, `wallet` and `signature`. The tool passes the
182
+ signed order to Cleave's relayer.
183
+
184
+ An authorization that is never used never debits the wallet. Once an
185
+ order is funded, follow `trackSwap` with its `requestId`
186
+ (`g:<payChainId>:<orderHash>`) and report the status it gives; do not
187
+ assume a refund.
188
+
189
+ ### Approving a gas order
190
+
191
+ The second `getGas` call has `needsApproval` set, so `generateText` stops
192
+ with a `tool-approval-request` instead of running it. Your app shows the
193
+ request to the user and resumes with their answer:
194
+
195
+ ```ts
196
+ import { generateText, isStepCount } from 'ai';
197
+ import type { ModelMessage, ToolApprovalResponse } from 'ai';
198
+ import { cleaveTools } from '@cleave-protocol/ai-sdk';
199
+
200
+ const tools = cleaveTools();
201
+ const messages: ModelMessage[] = [
202
+ { role: 'user', content: 'Submit the gas order my wallet just signed.' },
203
+ ];
204
+
205
+ const first = await generateText({
206
+ model: 'openai/gpt-5-mini',
207
+ tools,
208
+ messages,
209
+ stopWhen: isStepCount(5),
210
+ });
211
+ messages.push(...first.response.messages);
212
+
213
+ const approvals: ToolApprovalResponse[] = [];
214
+ for (const part of first.content) {
215
+ if (part.type === 'tool-approval-request') {
216
+ approvals.push({
217
+ type: 'tool-approval-response',
218
+ approvalId: part.approvalId,
219
+ approved: await confirmWithUser(part.toolCall.input),
220
+ });
221
+ }
222
+ }
223
+
224
+ if (approvals.length > 0) {
225
+ messages.push({ role: 'tool', content: approvals });
226
+ const second = await generateText({
227
+ model: 'openai/gpt-5-mini',
228
+ tools,
229
+ messages,
230
+ stopWhen: isStepCount(5),
231
+ });
232
+ console.log(second.text);
233
+ }
234
+ ```
235
+
236
+ `confirmWithUser` stands for your own prompt to the user.
237
+
238
+ - This approval comes after the wallet has signed. Before the wallet
239
+ signs, show the user the order from the offer: the payer, the
240
+ recipient, the refund address, the amount and the fee.
241
+ - Calling a tool's `.execute()` directly skips the AI SDK's approval step.
242
+ - `getGasApproval: false` turns the approval off.
243
+
244
+ ## Options
245
+
246
+ Every factory, and `cleaveTools`, takes the same optional settings:
247
+
248
+ ```ts
249
+ cleaveTools({
250
+ baseUrl: 'https://api.cleave.fi',
251
+ apiKey: process.env.CLEAVE_API_KEY,
252
+ timeoutMs: 30000,
253
+ fetch: globalThis.fetch,
254
+ getGasApproval: true,
255
+ });
256
+ ```
257
+
258
+ | Option | Default | Meaning |
259
+ | --- | --- | --- |
260
+ | `baseUrl` | `https://api.cleave.fi` | API origin |
261
+ | `apiKey` | none | Optional partner key, sent to `baseUrl` as `Authorization: Bearer`. An unknown or revoked key fails every call with `unauthorized`; there is no keyless fallback |
262
+ | `timeoutMs` | `30000` | Time limit for each call, combined with the AI SDK's abort signal |
263
+ | `fetch` | `globalThis.fetch` | A custom fetch |
264
+ | `getGasApproval` | `true` | Whether `getGas` asks for approval before passing a signed order |
265
+
266
+ ## Errors
267
+
268
+ From `planSwap` and `getGas`, a refusal from the API comes back as the
269
+ tool's result with only its `apiVersion`, `requestId` and `error` fields,
270
+ so the model can read it. `trackSwap` and `findToken` return it unchanged:
271
+
272
+ ```json
273
+ {
274
+ "apiVersion": "1",
275
+ "requestId": "...",
276
+ "error": {
277
+ "code": "not_found",
278
+ "reason": "unknown_order",
279
+ "message": "...",
280
+ "detail": "...",
281
+ "retryable": false
282
+ }
283
+ }
284
+ ```
285
+
286
+ `retryable: true` means the same call can succeed later; `retryAfterSec`,
287
+ when present, says when.
288
+
289
+ `CleaveApiError` is thrown when there is no API answer to return:
290
+
291
+ - a network error, a timeout or an abort: `status` is `0` and `cause`
292
+ holds the original error;
293
+ - a successful answer that is not a JSON object: `status` is the HTTP
294
+ status.
295
+
296
+ An error status whose body has no `error` object comes back as the tool's
297
+ result `{ "error": { "code": "http_error", "message": "HTTP 502", "retryable": true } }`,
298
+ with nothing else from the body; `retryable` is true for 5xx and 429.
299
+
300
+ `requestId` carries the `x-request-id` header when the API sent one.
301
+
302
+ Aborting a call, or a timeout, does not cancel a gas order the API has
303
+ already accepted. If a `getGas` submission fails this way, track
304
+ `g:<payChainId>:<orderHash>` from the first offer before signing a new
305
+ one.
306
+
307
+ ## Server side only
308
+
309
+ Run these tools on a server, in a bot or in agent code. The Agent REST API
310
+ refuses requests that carry an `Origin` header, as browsers send, with
311
+ `403` and code `forbidden_origin`.
312
+
313
+ ## Limits
314
+
315
+ - Without a key, each client IP can make 40 requests per 10 seconds and
316
+ 120 per minute.
317
+ - From a recognised hosted platform, where many agents share one address,
318
+ a request with a valid `wallet` in its body is counted by that wallet
319
+ instead. `findToken` and `trackSwap` take no `wallet`, so they stay
320
+ counted by IP.
321
+ - With `apiKey`, calls count against the partner key's own limits.
322
+ - All keyless agents share one planning capacity, used by `planSwap` and
323
+ by `getGas` offers; a cross-chain plan or offer counts twice. When it is
324
+ busy, the answer is `rate_limited` with reason `agent_limit` and a short
325
+ `retryAfterSec`.
326
+
327
+ ## Links
328
+
329
+ - [Cleave for agents](https://cleave.fi/docs/agents)
330
+ - [Agent REST API](https://cleave.fi/docs/agents/rest)
331
+ - [Gas top-up](https://cleave.fi/docs/agents/gas)
332
+ - [MCP server](https://cleave.fi/docs/agents/mcp): `https://api.cleave.fi/mcp`
333
+
334
+ Use of the API is subject to Cleave's
335
+ [Terms of Use](https://cleave.fi/terms) and
336
+ [Privacy Policy](https://cleave.fi/privacy).
337
+
338
+ ## License
339
+
340
+ MIT