@haven_ai/mcp 0.4.0-alpha.0 → 0.6.0-alpha.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 -0
- package/dist/cli.cjs +178 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +179 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +178 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.js +179 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { HavenClient } from '@haven_ai/sdk';
|
|
2
|
+
import { HavenClientUpdate, HavenClient } from '@haven_ai/sdk';
|
|
3
3
|
import { z } from 'zod/v3';
|
|
4
4
|
|
|
5
5
|
interface HavenCredentialFile {
|
|
@@ -66,7 +66,7 @@ declare function loadCredentials(source?: string | HavenCredentialSource | undef
|
|
|
66
66
|
* which is where URL-hardening would matter.)
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
|
-
type HavenMcpToolName = 'haven_send' | 'haven_pay_mcp_tool' | 'haven_quote_x402' | 'haven_pay_x402_quote' | 'haven_pay_x402' | 'haven_resume_x402_payment' | 'haven_get_payment_status' | 'haven_get_resume_state' | 'haven_get_agent' | 'haven_get_allowances' | 'haven_list_receipts' | 'haven_verify_receipt' | 'haven_sweep_delegate' | 'haven_discover_tools' | 'haven_submit_catalog_entry';
|
|
69
|
+
type HavenMcpToolName = 'haven_send' | 'haven_pay_mcp_tool' | 'haven_quote_x402' | 'haven_pay_x402_quote' | 'haven_pay_x402' | 'haven_resume_x402_payment' | 'haven_get_payment_status' | 'haven_get_resume_state' | 'haven_get_agent' | 'haven_get_allowances' | 'haven_list_receipts' | 'haven_verify_receipt' | 'haven_sweep_delegate' | 'haven_discover_tools' | 'haven_submit_catalog_entry' | 'haven_open_task_budget' | 'haven_close_task_budget' | 'haven_submit';
|
|
70
70
|
declare const toolSchemas: {
|
|
71
71
|
readonly haven_send: {
|
|
72
72
|
readonly asset: z.ZodEnum<["ETH", "USDC"]>;
|
|
@@ -75,6 +75,8 @@ declare const toolSchemas: {
|
|
|
75
75
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
76
76
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
77
77
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
78
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
79
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
78
80
|
};
|
|
79
81
|
readonly haven_pay_mcp_tool: {
|
|
80
82
|
readonly merchant_url: z.ZodString;
|
|
@@ -98,6 +100,8 @@ declare const toolSchemas: {
|
|
|
98
100
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
99
101
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
100
102
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
103
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
104
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
101
105
|
};
|
|
102
106
|
readonly haven_pay_x402: {
|
|
103
107
|
readonly url: z.ZodString;
|
|
@@ -107,6 +111,8 @@ declare const toolSchemas: {
|
|
|
107
111
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
108
112
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
109
113
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
114
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
115
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
110
116
|
};
|
|
111
117
|
readonly haven_resume_x402_payment: {
|
|
112
118
|
readonly payment_id: z.ZodOptional<z.ZodString>;
|
|
@@ -139,6 +145,21 @@ declare const toolSchemas: {
|
|
|
139
145
|
readonly haven_verify_receipt: {
|
|
140
146
|
readonly receipt: z.ZodUnknown;
|
|
141
147
|
};
|
|
148
|
+
readonly haven_open_task_budget: {
|
|
149
|
+
readonly max_amount_human: z.ZodString;
|
|
150
|
+
readonly ttl_minutes: z.ZodNumber;
|
|
151
|
+
readonly recipient: z.ZodOptional<z.ZodString>;
|
|
152
|
+
readonly label: z.ZodOptional<z.ZodString>;
|
|
153
|
+
readonly token: z.ZodOptional<z.ZodString>;
|
|
154
|
+
};
|
|
155
|
+
readonly haven_close_task_budget: {
|
|
156
|
+
readonly task_budget_id: z.ZodString;
|
|
157
|
+
};
|
|
158
|
+
readonly haven_submit: {
|
|
159
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
160
|
+
readonly payment_id: z.ZodOptional<z.ZodString>;
|
|
161
|
+
readonly signature: z.ZodString;
|
|
162
|
+
};
|
|
142
163
|
};
|
|
143
164
|
/**
|
|
144
165
|
* MCP tool descriptions, composed from the shared semantic source in
|
|
@@ -159,6 +180,12 @@ interface ToolSuccess<T> {
|
|
|
159
180
|
* leave, which is how `idempotencyKey` survived #2312 and #2348.
|
|
160
181
|
*/
|
|
161
182
|
warnings?: string[];
|
|
183
|
+
/**
|
|
184
|
+
* #3303: the backend's `client_update` hint when this package is behind the
|
|
185
|
+
* version the deployment recommends (`required: false`) or below its
|
|
186
|
+
* minimum (`required: true`). Carries the exact command that updates it.
|
|
187
|
+
*/
|
|
188
|
+
client_update?: HavenClientUpdate;
|
|
162
189
|
}
|
|
163
190
|
interface ToolFailure {
|
|
164
191
|
success: false;
|
|
@@ -198,6 +225,8 @@ interface ToolFailure {
|
|
|
198
225
|
* own wallet needs to recover first.
|
|
199
226
|
*/
|
|
200
227
|
retry_with_new_quote?: boolean;
|
|
228
|
+
/** #3303: as on {@link ToolSuccess}; on a 426 `client_outdated` refusal it is always `required: true`. */
|
|
229
|
+
client_update?: HavenClientUpdate;
|
|
201
230
|
}
|
|
202
231
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
203
232
|
declare function createToolHandlers(haven: HavenClient): Record<HavenMcpToolName, (input: unknown) => Promise<ToolPayload>>;
|
|
@@ -239,7 +268,7 @@ interface ConsentInput {
|
|
|
239
268
|
apiUrl?: string;
|
|
240
269
|
/** Agent identity from the credential file, when present. */
|
|
241
270
|
agentId?: string;
|
|
242
|
-
/** Haven wallet
|
|
271
|
+
/** Haven wallet the agent spends from. */
|
|
243
272
|
accountAddress?: string;
|
|
244
273
|
/** Agent's delegate EOA — the local signer. */
|
|
245
274
|
delegateAddress?: string;
|
|
@@ -345,7 +374,7 @@ declare function createHavenMcpServer(options?: HavenMcpServerOptions): Promise<
|
|
|
345
374
|
* `agent_tool_invocations` rows are always attributed to the right tool.
|
|
346
375
|
*/
|
|
347
376
|
declare const MCP_NAME = "@haven_ai/mcp";
|
|
348
|
-
declare const MCP_VERSION = "0.
|
|
377
|
+
declare const MCP_VERSION = "0.6.0-alpha.0";
|
|
349
378
|
/**
|
|
350
379
|
* Refuse to start on an unsupported Node (#1161).
|
|
351
380
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { HavenClient } from '@haven_ai/sdk';
|
|
2
|
+
import { HavenClientUpdate, HavenClient } from '@haven_ai/sdk';
|
|
3
3
|
import { z } from 'zod/v3';
|
|
4
4
|
|
|
5
5
|
interface HavenCredentialFile {
|
|
@@ -66,7 +66,7 @@ declare function loadCredentials(source?: string | HavenCredentialSource | undef
|
|
|
66
66
|
* which is where URL-hardening would matter.)
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
|
-
type HavenMcpToolName = 'haven_send' | 'haven_pay_mcp_tool' | 'haven_quote_x402' | 'haven_pay_x402_quote' | 'haven_pay_x402' | 'haven_resume_x402_payment' | 'haven_get_payment_status' | 'haven_get_resume_state' | 'haven_get_agent' | 'haven_get_allowances' | 'haven_list_receipts' | 'haven_verify_receipt' | 'haven_sweep_delegate' | 'haven_discover_tools' | 'haven_submit_catalog_entry';
|
|
69
|
+
type HavenMcpToolName = 'haven_send' | 'haven_pay_mcp_tool' | 'haven_quote_x402' | 'haven_pay_x402_quote' | 'haven_pay_x402' | 'haven_resume_x402_payment' | 'haven_get_payment_status' | 'haven_get_resume_state' | 'haven_get_agent' | 'haven_get_allowances' | 'haven_list_receipts' | 'haven_verify_receipt' | 'haven_sweep_delegate' | 'haven_discover_tools' | 'haven_submit_catalog_entry' | 'haven_open_task_budget' | 'haven_close_task_budget' | 'haven_submit';
|
|
70
70
|
declare const toolSchemas: {
|
|
71
71
|
readonly haven_send: {
|
|
72
72
|
readonly asset: z.ZodEnum<["ETH", "USDC"]>;
|
|
@@ -75,6 +75,8 @@ declare const toolSchemas: {
|
|
|
75
75
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
76
76
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
77
77
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
78
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
79
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
78
80
|
};
|
|
79
81
|
readonly haven_pay_mcp_tool: {
|
|
80
82
|
readonly merchant_url: z.ZodString;
|
|
@@ -98,6 +100,8 @@ declare const toolSchemas: {
|
|
|
98
100
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
99
101
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
100
102
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
103
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
104
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
101
105
|
};
|
|
102
106
|
readonly haven_pay_x402: {
|
|
103
107
|
readonly url: z.ZodString;
|
|
@@ -107,6 +111,8 @@ declare const toolSchemas: {
|
|
|
107
111
|
readonly idempotency_key: z.ZodOptional<z.ZodString>;
|
|
108
112
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
109
113
|
readonly idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
114
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
115
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
110
116
|
};
|
|
111
117
|
readonly haven_resume_x402_payment: {
|
|
112
118
|
readonly payment_id: z.ZodOptional<z.ZodString>;
|
|
@@ -139,6 +145,21 @@ declare const toolSchemas: {
|
|
|
139
145
|
readonly haven_verify_receipt: {
|
|
140
146
|
readonly receipt: z.ZodUnknown;
|
|
141
147
|
};
|
|
148
|
+
readonly haven_open_task_budget: {
|
|
149
|
+
readonly max_amount_human: z.ZodString;
|
|
150
|
+
readonly ttl_minutes: z.ZodNumber;
|
|
151
|
+
readonly recipient: z.ZodOptional<z.ZodString>;
|
|
152
|
+
readonly label: z.ZodOptional<z.ZodString>;
|
|
153
|
+
readonly token: z.ZodOptional<z.ZodString>;
|
|
154
|
+
};
|
|
155
|
+
readonly haven_close_task_budget: {
|
|
156
|
+
readonly task_budget_id: z.ZodString;
|
|
157
|
+
};
|
|
158
|
+
readonly haven_submit: {
|
|
159
|
+
readonly task_budget_id: z.ZodOptional<z.ZodString>;
|
|
160
|
+
readonly payment_id: z.ZodOptional<z.ZodString>;
|
|
161
|
+
readonly signature: z.ZodString;
|
|
162
|
+
};
|
|
142
163
|
};
|
|
143
164
|
/**
|
|
144
165
|
* MCP tool descriptions, composed from the shared semantic source in
|
|
@@ -159,6 +180,12 @@ interface ToolSuccess<T> {
|
|
|
159
180
|
* leave, which is how `idempotencyKey` survived #2312 and #2348.
|
|
160
181
|
*/
|
|
161
182
|
warnings?: string[];
|
|
183
|
+
/**
|
|
184
|
+
* #3303: the backend's `client_update` hint when this package is behind the
|
|
185
|
+
* version the deployment recommends (`required: false`) or below its
|
|
186
|
+
* minimum (`required: true`). Carries the exact command that updates it.
|
|
187
|
+
*/
|
|
188
|
+
client_update?: HavenClientUpdate;
|
|
162
189
|
}
|
|
163
190
|
interface ToolFailure {
|
|
164
191
|
success: false;
|
|
@@ -198,6 +225,8 @@ interface ToolFailure {
|
|
|
198
225
|
* own wallet needs to recover first.
|
|
199
226
|
*/
|
|
200
227
|
retry_with_new_quote?: boolean;
|
|
228
|
+
/** #3303: as on {@link ToolSuccess}; on a 426 `client_outdated` refusal it is always `required: true`. */
|
|
229
|
+
client_update?: HavenClientUpdate;
|
|
201
230
|
}
|
|
202
231
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
203
232
|
declare function createToolHandlers(haven: HavenClient): Record<HavenMcpToolName, (input: unknown) => Promise<ToolPayload>>;
|
|
@@ -239,7 +268,7 @@ interface ConsentInput {
|
|
|
239
268
|
apiUrl?: string;
|
|
240
269
|
/** Agent identity from the credential file, when present. */
|
|
241
270
|
agentId?: string;
|
|
242
|
-
/** Haven wallet
|
|
271
|
+
/** Haven wallet the agent spends from. */
|
|
243
272
|
accountAddress?: string;
|
|
244
273
|
/** Agent's delegate EOA — the local signer. */
|
|
245
274
|
delegateAddress?: string;
|
|
@@ -345,7 +374,7 @@ declare function createHavenMcpServer(options?: HavenMcpServerOptions): Promise<
|
|
|
345
374
|
* `agent_tool_invocations` rows are always attributed to the right tool.
|
|
346
375
|
*/
|
|
347
376
|
declare const MCP_NAME = "@haven_ai/mcp";
|
|
348
|
-
declare const MCP_VERSION = "0.
|
|
377
|
+
declare const MCP_VERSION = "0.6.0-alpha.0";
|
|
349
378
|
/**
|
|
350
379
|
* Refuse to start on an unsupported Node (#1161).
|
|
351
380
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenPaymentStateError, HavenSigningError, HavenApiError, AgentPaymentNextAction, HavenError, HavenClient, isSupportedNodeVersion, unsupportedNodeVersionMessage, verifyPaymentReceipt, discoverMerchantMcpUrl, sameUrl, AgentPaymentFailureCode } from '@haven_ai/sdk';
|
|
3
|
+
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenPaymentStateError, HavenSigningError, HavenApiError, AgentPaymentNextAction, HavenError, HavenClient, havenClientIdentity, isSupportedNodeVersion, unsupportedNodeVersionMessage, verifyPaymentReceipt, discoverMerchantMcpUrl, sameUrl, AgentPaymentFailureCode, resolveTokenFromAddress } from '@haven_ai/sdk';
|
|
4
4
|
import { readFile, stat, mkdir, writeFile } from 'fs/promises';
|
|
5
5
|
import { z } from 'zod/v3';
|
|
6
6
|
import { createHash } from 'crypto';
|
|
@@ -244,7 +244,9 @@ var toolSchemas = {
|
|
|
244
244
|
amount: z.string().min(1),
|
|
245
245
|
idempotency_key: z.string().optional(),
|
|
246
246
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
247
|
-
idempotencyKey: z.string().optional()
|
|
247
|
+
idempotencyKey: z.string().optional(),
|
|
248
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
249
|
+
task_budget_id: z.string().min(1).optional()
|
|
248
250
|
},
|
|
249
251
|
haven_pay_mcp_tool: {
|
|
250
252
|
merchant_url: z.string().url(),
|
|
@@ -267,7 +269,9 @@ var toolSchemas = {
|
|
|
267
269
|
quote: z.unknown(),
|
|
268
270
|
idempotency_key: z.string().optional(),
|
|
269
271
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
270
|
-
idempotencyKey: z.string().optional()
|
|
272
|
+
idempotencyKey: z.string().optional(),
|
|
273
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
274
|
+
task_budget_id: z.string().min(1).optional()
|
|
271
275
|
},
|
|
272
276
|
haven_pay_x402: {
|
|
273
277
|
url: z.string().url(),
|
|
@@ -276,7 +280,9 @@ var toolSchemas = {
|
|
|
276
280
|
body: z.string().optional(),
|
|
277
281
|
idempotency_key: z.string().optional(),
|
|
278
282
|
/** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
|
|
279
|
-
idempotencyKey: z.string().optional()
|
|
283
|
+
idempotencyKey: z.string().optional(),
|
|
284
|
+
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
285
|
+
task_budget_id: z.string().min(1).optional()
|
|
280
286
|
},
|
|
281
287
|
haven_resume_x402_payment: {
|
|
282
288
|
payment_id: z.string().optional(),
|
|
@@ -308,9 +314,59 @@ var toolSchemas = {
|
|
|
308
314
|
},
|
|
309
315
|
haven_verify_receipt: {
|
|
310
316
|
receipt: z.unknown()
|
|
317
|
+
},
|
|
318
|
+
// #3329: a budget for one task that ends by itself — a short-lived child of
|
|
319
|
+
// the agent's own budget, capped and time-boxed independently of the period
|
|
320
|
+
// reset.
|
|
321
|
+
haven_open_task_budget: {
|
|
322
|
+
max_amount_human: z.string().min(1),
|
|
323
|
+
ttl_minutes: z.number().int().min(1).max(1440),
|
|
324
|
+
recipient: z.string().optional(),
|
|
325
|
+
label: z.string().max(120).optional(),
|
|
326
|
+
token: z.string().optional()
|
|
327
|
+
},
|
|
328
|
+
haven_close_task_budget: {
|
|
329
|
+
task_budget_id: z.string().min(1)
|
|
330
|
+
},
|
|
331
|
+
// #3329: relays a signature from the local signer — either the open/close
|
|
332
|
+
// signature for a task budget, or (schema parity with the hosted surface)
|
|
333
|
+
// a direct-payment signature by payment_id. Exactly one of task_budget_id /
|
|
334
|
+
// payment_id, never both or neither.
|
|
335
|
+
haven_submit: {
|
|
336
|
+
task_budget_id: z.string().min(1).optional(),
|
|
337
|
+
payment_id: z.string().min(1).optional(),
|
|
338
|
+
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string")
|
|
311
339
|
}
|
|
312
340
|
// #3101: keys survive on the type (see the hosted server's contracts.ts).
|
|
313
341
|
};
|
|
342
|
+
var OPEN_TASK_BUDGET_DESCRIPTION = [
|
|
343
|
+
"Open a budget for one task that ends by itself: a spending cap, good for at most ttl_minutes,",
|
|
344
|
+
"reserved out of the agent's own budget and separate from its period reset.",
|
|
345
|
+
'Pass max_amount_human (whole tokens, e.g. "5" for 5 USDC), ttl_minutes (1-1440), and optionally',
|
|
346
|
+
"recipient (pins every payment against this task budget to one address), label, and token",
|
|
347
|
+
'(defaults to USDC). Returns { task_budget, next_action: "sign", next_tool, next_arguments } \u2014',
|
|
348
|
+
"call next_tool with next_arguments EXACTLY as given to get a signature, then relay it with",
|
|
349
|
+
"haven_submit. Spending anything above the reserved amount, past the deadline, or to a",
|
|
350
|
+
"different recipient than the one pinned here is declined on the spot \u2014 nothing is queued.",
|
|
351
|
+
"A recipient pin is checked against where each payment first goes: haven_pay_x402 and",
|
|
352
|
+
"haven_pay_x402_quote first move funds into the agent's own wallet, so a budget pinned to a",
|
|
353
|
+
"merchant is declined there \u2014 pin only budgets meant for haven_send."
|
|
354
|
+
].join(" ");
|
|
355
|
+
var CLOSE_TASK_BUDGET_DESCRIPTION = [
|
|
356
|
+
"End a task budget early, before its deadline, releasing whatever of its cap was unspent back",
|
|
357
|
+
"to the agent's own budget. Pass task_budget_id. If the budget was never signed (still pending),",
|
|
358
|
+
'this ends it immediately with { task_budget, status: "closed" } and nothing was ever reserved',
|
|
359
|
+
'on-chain. Otherwise it returns a signature request: { task_budget, next_action: "sign",',
|
|
360
|
+
"next_tool, next_arguments } \u2014 call next_tool with next_arguments EXACTLY as given, then relay",
|
|
361
|
+
"the signature with haven_submit. A task budget past its own deadline closes immediately, the",
|
|
362
|
+
"same as the pending case."
|
|
363
|
+
].join(" ");
|
|
364
|
+
var SUBMIT_DESCRIPTION = [
|
|
365
|
+
"Relay a signature from the local signer. Pass exactly one of task_budget_id (from",
|
|
366
|
+
"haven_open_task_budget or haven_close_task_budget) or payment_id \u2014 never both, never neither \u2014",
|
|
367
|
+
"plus signature. For a task budget this opens or closes it on-chain and returns { task_budget,",
|
|
368
|
+
'status }; a close in progress may return { task_budget, status: "closed", close_tx_hash }.'
|
|
369
|
+
].join(" ");
|
|
314
370
|
var toolDescriptions = {
|
|
315
371
|
haven_send: composeDescription(toolDescriptions$1.send),
|
|
316
372
|
haven_pay_mcp_tool: composeDescription(toolDescriptions$1.payMcpTool),
|
|
@@ -326,7 +382,10 @@ var toolDescriptions = {
|
|
|
326
382
|
haven_discover_tools: composeDescription(toolDescriptions$1.discoverTools),
|
|
327
383
|
haven_submit_catalog_entry: composeDescription(toolDescriptions$1.submitCatalogEntry),
|
|
328
384
|
haven_list_receipts: composeDescription(toolDescriptions$1.listReceipts),
|
|
329
|
-
haven_verify_receipt: composeDescription(toolDescriptions$1.verifyReceipt)
|
|
385
|
+
haven_verify_receipt: composeDescription(toolDescriptions$1.verifyReceipt),
|
|
386
|
+
haven_open_task_budget: OPEN_TASK_BUDGET_DESCRIPTION,
|
|
387
|
+
haven_close_task_budget: CLOSE_TASK_BUDGET_DESCRIPTION,
|
|
388
|
+
haven_submit: SUBMIT_DESCRIPTION
|
|
330
389
|
};
|
|
331
390
|
function createToolHandlers(haven) {
|
|
332
391
|
return {
|
|
@@ -342,7 +401,10 @@ function createToolHandlers(haven) {
|
|
|
342
401
|
to: args.recipient,
|
|
343
402
|
// #1207: was accepted by the schema but silently dropped — now
|
|
344
403
|
// carried to the backend's replay contract.
|
|
345
|
-
idempotencyKey: args.idempotencyKey
|
|
404
|
+
idempotencyKey: args.idempotencyKey,
|
|
405
|
+
// #3329: spend against an open task budget instead of the
|
|
406
|
+
// agent's period budget, when the caller names one.
|
|
407
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
346
408
|
});
|
|
347
409
|
return {
|
|
348
410
|
payment_id: result.paymentId,
|
|
@@ -440,7 +502,10 @@ function createToolHandlers(haven) {
|
|
|
440
502
|
);
|
|
441
503
|
}
|
|
442
504
|
return runTool(async () => {
|
|
443
|
-
const response = await haven.payX402Quote(args.quote, {
|
|
505
|
+
const response = await haven.payX402Quote(args.quote, {
|
|
506
|
+
idempotencyKey: args.idempotencyKey,
|
|
507
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
508
|
+
});
|
|
444
509
|
return responsePayload(response);
|
|
445
510
|
}, warnings);
|
|
446
511
|
},
|
|
@@ -449,7 +514,10 @@ function createToolHandlers(haven) {
|
|
|
449
514
|
if ("success" in pf) return pf;
|
|
450
515
|
const { args, warnings } = pf;
|
|
451
516
|
return runTool(async () => {
|
|
452
|
-
const response = await haven.fetch(args.url, requestInit(args), {
|
|
517
|
+
const response = await haven.fetch(args.url, requestInit(args), {
|
|
518
|
+
idempotencyKey: args.idempotencyKey,
|
|
519
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
520
|
+
});
|
|
453
521
|
return responsePayload(response);
|
|
454
522
|
}, warnings);
|
|
455
523
|
},
|
|
@@ -550,6 +618,73 @@ function createToolHandlers(haven) {
|
|
|
550
618
|
haven_verify_receipt: async (input) => {
|
|
551
619
|
const args = objectInput("haven_verify_receipt", input);
|
|
552
620
|
return runTool(async () => verifyPaymentReceipt(args.receipt));
|
|
621
|
+
},
|
|
622
|
+
haven_open_task_budget: async (input) => {
|
|
623
|
+
const args = objectInput("haven_open_task_budget", input);
|
|
624
|
+
return runTool(async () => {
|
|
625
|
+
const token = await resolveTaskBudgetToken(haven, typeof args.token === "string" ? args.token : void 0);
|
|
626
|
+
const maxAmountAtomic = humanToAtomicOrNull(args.max_amount_human, token.decimals);
|
|
627
|
+
if (maxAmountAtomic === null) {
|
|
628
|
+
throw new HavenApiError(
|
|
629
|
+
`max_amount_human ("${args.max_amount_human}") is not a valid decimal amount for ${token.symbol} (${token.decimals} decimal places). Nothing was reserved.`,
|
|
630
|
+
400
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
const result = await haven.openTaskBudget({
|
|
634
|
+
tokenAddress: token.address,
|
|
635
|
+
maxAmountAtomic,
|
|
636
|
+
ttlSeconds: Number(args.ttl_minutes) * 60,
|
|
637
|
+
recipientAddress: typeof args.recipient === "string" ? args.recipient : void 0,
|
|
638
|
+
label: typeof args.label === "string" ? args.label : void 0
|
|
639
|
+
});
|
|
640
|
+
return {
|
|
641
|
+
task_budget: result.taskBudget,
|
|
642
|
+
next_action: "sign",
|
|
643
|
+
next_tool: "mcp__haven-signer__haven_sign",
|
|
644
|
+
next_arguments: { task_budget_id: result.taskBudget.id }
|
|
645
|
+
};
|
|
646
|
+
});
|
|
647
|
+
},
|
|
648
|
+
haven_close_task_budget: async (input) => {
|
|
649
|
+
const args = objectInput("haven_close_task_budget", input);
|
|
650
|
+
return runTool(async () => {
|
|
651
|
+
const result = await haven.closeTaskBudget(args.task_budget_id);
|
|
652
|
+
if (result.status === "closed") {
|
|
653
|
+
return { task_budget: result.taskBudget, status: "closed" };
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
task_budget: result.taskBudget,
|
|
657
|
+
next_action: "sign",
|
|
658
|
+
next_tool: "mcp__haven-signer__haven_sign",
|
|
659
|
+
next_arguments: { task_budget_id: args.task_budget_id }
|
|
660
|
+
};
|
|
661
|
+
});
|
|
662
|
+
},
|
|
663
|
+
haven_submit: async (input) => {
|
|
664
|
+
const args = objectInput("haven_submit", input);
|
|
665
|
+
const hasTaskBudget = typeof args.task_budget_id === "string" && args.task_budget_id.length > 0;
|
|
666
|
+
const hasPayment = typeof args.payment_id === "string" && args.payment_id.length > 0;
|
|
667
|
+
if (hasTaskBudget === hasPayment) {
|
|
668
|
+
return {
|
|
669
|
+
success: false,
|
|
670
|
+
code: "INVALID_INPUT",
|
|
671
|
+
message: "haven_submit takes exactly one of task_budget_id or payment_id, never both or neither. Nothing was relayed."
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
return runTool(async () => {
|
|
675
|
+
if (hasTaskBudget) {
|
|
676
|
+
const result = await haven.submitTaskBudget(args.task_budget_id, args.signature);
|
|
677
|
+
return {
|
|
678
|
+
task_budget: result.taskBudget,
|
|
679
|
+
status: result.status,
|
|
680
|
+
...result.closeTxHash !== void 0 ? { close_tx_hash: result.closeTxHash } : {}
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
throw new HavenApiError(
|
|
684
|
+
"haven_submit with payment_id is not supported on the local MCP surface: haven_send signs and submits a direct payment in one call, so there is no separate relay step. Use haven_submit with task_budget_id to relay a task-budget open/close signature.",
|
|
685
|
+
400
|
|
686
|
+
);
|
|
687
|
+
});
|
|
553
688
|
}
|
|
554
689
|
};
|
|
555
690
|
async function resumeState(args, rail) {
|
|
@@ -641,6 +776,28 @@ async function responsePayload(response) {
|
|
|
641
776
|
body: parseMaybeJson(text)
|
|
642
777
|
};
|
|
643
778
|
}
|
|
779
|
+
async function resolveTaskBudgetToken(haven, symbolOrAddress) {
|
|
780
|
+
const wanted = (symbolOrAddress ?? "USDC").toLowerCase();
|
|
781
|
+
const { allowances } = await haven.getAllowances();
|
|
782
|
+
const isAddress = /^0x[0-9a-fA-F]{40}$/.test(wanted);
|
|
783
|
+
const match = isAddress ? allowances.find((a) => a.tokenAddress.toLowerCase() === wanted) : allowances.find((a) => (a.tokenSymbol ?? "").toLowerCase() === wanted);
|
|
784
|
+
if (!match) {
|
|
785
|
+
const known = allowances.map((a) => `${a.tokenSymbol} (${a.tokenAddress})`);
|
|
786
|
+
throw new HavenApiError(
|
|
787
|
+
`token "${symbolOrAddress ?? "USDC"}" is not the symbol or address of any allowance this agent holds${known.length > 0 ? ` (it holds: ${known.join(", ")})` : " (it holds none)"}. Nothing was reserved.`,
|
|
788
|
+
400
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const resolved = resolveTokenFromAddress(match.tokenAddress);
|
|
792
|
+
return { address: match.tokenAddress, symbol: match.tokenSymbol, decimals: resolved?.decimals ?? 6 };
|
|
793
|
+
}
|
|
794
|
+
function humanToAtomicOrNull(human, decimals) {
|
|
795
|
+
if (!/^[0-9]+(\.[0-9]+)?$/.test(human)) return null;
|
|
796
|
+
const [whole, frac = ""] = human.split(".");
|
|
797
|
+
if (frac.length > decimals) return null;
|
|
798
|
+
const atomic = BigInt(whole || "0") * 10n ** BigInt(decimals) + BigInt(frac.padEnd(decimals, "0") || "0");
|
|
799
|
+
return atomic.toString();
|
|
800
|
+
}
|
|
644
801
|
function parseMaybeJson(text) {
|
|
645
802
|
if (!text) return null;
|
|
646
803
|
try {
|
|
@@ -730,6 +887,9 @@ function normalizeError(err) {
|
|
|
730
887
|
phase: stringOrUndefined(body?.phase),
|
|
731
888
|
nextAction: stringOrUndefined(body?.nextAction) ?? stringOrUndefined(body?.next_action) ?? AgentPaymentNextAction.StopAndTellUser,
|
|
732
889
|
next_action: stringOrUndefined(body?.nextAction) ?? stringOrUndefined(body?.next_action) ?? AgentPaymentNextAction.StopAndTellUser,
|
|
890
|
+
// #3303: a backend refusal that already names why no tool follows (the
|
|
891
|
+
// 426 `client_outdated` does) keeps that reason at the top level.
|
|
892
|
+
...typeof body?.next_tool_omitted_reason === "string" ? { next_tool_omitted_reason: body.next_tool_omitted_reason } : {},
|
|
733
893
|
body: err.body
|
|
734
894
|
};
|
|
735
895
|
}
|
|
@@ -782,7 +942,7 @@ function renderConsentBlock(input, hash) {
|
|
|
782
942
|
];
|
|
783
943
|
if (input.apiUrl) lines.push(`Haven API: ${input.apiUrl}`);
|
|
784
944
|
if (input.agentId) lines.push(`Agent ID: ${input.agentId}`);
|
|
785
|
-
if (input.accountAddress) lines.push(`Haven wallet
|
|
945
|
+
if (input.accountAddress) lines.push(`Haven wallet: ${input.accountAddress}`);
|
|
786
946
|
if (input.delegateAddress) lines.push(`Delegate (local signer): ${input.delegateAddress}`);
|
|
787
947
|
if (typeof input.chainId === "number") lines.push(`Chain ID: ${input.chainId}`);
|
|
788
948
|
lines.push("");
|
|
@@ -945,7 +1105,10 @@ async function resolveHavenClient(options = {}) {
|
|
|
945
1105
|
const client = new HavenClient({
|
|
946
1106
|
apiKey: credentials.apiKey,
|
|
947
1107
|
delegateKey: credentials.delegateKey,
|
|
948
|
-
baseUrl: credentials.apiUrl
|
|
1108
|
+
baseUrl: credentials.apiUrl,
|
|
1109
|
+
// #3303: name this package, not the SDK inside it, so the backend's
|
|
1110
|
+
// update hint and refusal speak about what the user actually installed.
|
|
1111
|
+
clientIdentity: havenClientIdentity(MCP_NAME, MCP_VERSION)
|
|
949
1112
|
});
|
|
950
1113
|
return { client, credentials };
|
|
951
1114
|
}
|
|
@@ -954,7 +1117,7 @@ async function createHavenMcpServer(options = {}) {
|
|
|
954
1117
|
return buildMcpServer(haven);
|
|
955
1118
|
}
|
|
956
1119
|
var MCP_NAME = "@haven_ai/mcp";
|
|
957
|
-
var MCP_VERSION = "0.
|
|
1120
|
+
var MCP_VERSION = "0.6.0-alpha.0";
|
|
958
1121
|
var MCP_INSTRUCTIONS = [
|
|
959
1122
|
"Haven local MCP server: signs in-process with the delegate key it holds on",
|
|
960
1123
|
"this machine \u2014 the key never leaves this process. Call haven_get_agent",
|
|
@@ -991,7 +1154,7 @@ function buildMcpServer(haven) {
|
|
|
991
1154
|
toolSchemas[name],
|
|
992
1155
|
async (args) => haven.withRequestContext(
|
|
993
1156
|
{ "X-Haven-MCP-Tool": name },
|
|
994
|
-
async () => toMcpResult(await handlers[name](args))
|
|
1157
|
+
async () => toMcpResult(withClientUpdate(await handlers[name](args), haven.clientUpdate()))
|
|
995
1158
|
)
|
|
996
1159
|
);
|
|
997
1160
|
}
|
|
@@ -1041,6 +1204,10 @@ async function runConsentGate(haven, credentials, options) {
|
|
|
1041
1204
|
writeAck: options.writeAck
|
|
1042
1205
|
});
|
|
1043
1206
|
}
|
|
1207
|
+
function withClientUpdate(payload, hint) {
|
|
1208
|
+
if (!hint || payload.client_update) return payload;
|
|
1209
|
+
return { ...payload, client_update: hint };
|
|
1210
|
+
}
|
|
1044
1211
|
function toMcpResult(payload) {
|
|
1045
1212
|
return {
|
|
1046
1213
|
isError: !payload.success,
|