@cmdoss/suipay-mcp 0.2.2-dev.6 → 0.2.2-dev.8

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.
@@ -4,7 +4,7 @@ import {
4
4
  loadLocalSigningKeyRecord,
5
5
  loadMcpConfig,
6
6
  loadPaymentProfile
7
- } from "./chunk-UTKVBAEZ.js";
7
+ } from "./chunk-K4C64BXE.js";
8
8
 
9
9
  // src/index.ts
10
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -3,8 +3,11 @@ import { McpAccessContext } from '@cmdoss/suipay-sdk/mcp/access-context';
3
3
  import { SettlementDTO } from '@cmdoss/suipay-sdk/buyer/settlement-dto';
4
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
5
  import { GetObjectJson } from '@cmdoss/suipay-sdk/agent-sdk/reads';
6
- import { DelegatedKeySigner, DelegatedPayerDeps } from '@cmdoss/suipay-sdk/delegated-signer/payer';
6
+ import { DelegatedKeySigner, DelegatedPayerDeps, GuardedSettleFields } from '@cmdoss/suipay-sdk/delegated-signer/payer';
7
7
  import { TraceRunResolver, TraceSink } from '@cmdoss/suipay-core/trace/types';
8
+ import { PaidHttpRequest } from '@cmdoss/suipay-sdk/protocol/paid-http-request';
9
+ import { Dialect } from '@cmdoss/suipay-sdk/protocol/types';
10
+ import { PayOperationDeps } from '@cmdoss/suipay-sdk/pay-operation';
8
11
 
9
12
  /**
10
13
  * Authenticated MCP request context (remote Streamable HTTP).
@@ -16,7 +19,7 @@ import { TraceRunResolver, TraceSink } from '@cmdoss/suipay-core/trace/types';
16
19
  *
17
20
  * Non-custodial: the gateway holds NO key that can spend. There is deliberately
18
21
  * no `decryptDelegateSeed` and no forwarded `sponsorAuthorization` here — the
19
- * `pay` tool only PREPARES an unsigned `spend_account::settle_policy_payment` transaction from the
22
+ * `pay` tool only PREPARES an unsigned `spend_account::settle_policy` / `settle_any_recipient` transaction from the
20
23
  * immutable grant snapshot (`prepareSpendAccountPayment`), and the client that
21
24
  * holds the delegate key signs locally and submits. Only signatures ever reach
22
25
  * the gateway. See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
@@ -40,7 +43,7 @@ interface McpAuthContext {
40
43
  /** Public, descriptive policy context. Never used as payment authority. */
41
44
  access?: McpAccessContext;
42
45
  /**
43
- * When present, `pay` prepares unsigned spend_account::settle_policy_payment bytes for the
46
+ * When present, `pay` prepares unsigned spend_account::settle_policy / settle_any_recipient bytes for the
44
47
  * client-held delegate key to sign locally — no server-side decrypt or
45
48
  * signing. Stdio local V1 profile path remains when absent.
46
49
  */
@@ -79,6 +82,13 @@ interface McpConfig {
79
82
  minFunded: bigint;
80
83
  minMaxPerPayment: bigint;
81
84
  label: string;
85
+ /**
86
+ * Optional override of the gateway's published `ExclusionDirectory` id.
87
+ * When null, pay reads `GET /v1/delegate/deployment` and turns the resolver
88
+ * on only if that response names one. The id here does not choose the
89
+ * directory a settle uses — the offer's `registryIdentity` does.
90
+ */
91
+ exclusionDirectoryObjectId: string | null;
82
92
  }
83
93
  /**
84
94
  * CLI flags: `--local` / `-local`, `--dev` / `-dev`. Unknown args are ignored
@@ -249,6 +259,234 @@ interface TraceMcpHttpRequestInput {
249
259
  */
250
260
  declare function traceMcpHttpRequest(input: TraceMcpHttpRequestInput): Promise<Response>;
251
261
 
262
+ /**
263
+ * Shared pay-request helpers.
264
+ *
265
+ * The V1 allowance sponsored payer (ADR-0010) is retired; only the
266
+ * rail-neutral request surface the SpendAccount payer depends on survives here:
267
+ * the `PayResult` shape every payer returns. `paidRequestInit` is the SDK helper
268
+ * that builds the one RequestInit replayed byte for byte on both calls.
269
+ */
270
+
271
+ type PayResult = {
272
+ status: 'ok' | 'failed' | 'ambiguous';
273
+ /**
274
+ * `true` — settlement confirmed. `false` — money did not leave the
275
+ * account. `'unknown'` — execute/finality did not resolve; a retry can
276
+ * pay twice.
277
+ */
278
+ paid: boolean | 'unknown';
279
+ body?: unknown;
280
+ receipt?: unknown;
281
+ code?: string;
282
+ detail?: string;
283
+ txDigest?: string;
284
+ };
285
+
286
+ /**
287
+ * SpendAccount PREPARE-only client (non-custodial MCP).
288
+ *
289
+ * The remote MCP `pay` tool runs inside the gateway, which holds NO key that can
290
+ * spend. So this module does not sign or submit: it PREPARES a payment and hands
291
+ * back an unsigned `spend_account::settle_policy` / `settle_any_recipient` transaction plus everything the client
292
+ * needs. The client — which holds the delegate key — sponsors, signs locally,
293
+ * and submits through the same personal-message challenge → local-sign → submit flow the
294
+ * REST/SDK delegate path uses. Only signatures ever reach the gateway; the
295
+ * private key is never placed in an HTTP body, a bearer, or any header.
296
+ *
297
+ * Flow: 402 Offer → request-binding → package/network SSOT →
298
+ * resolveGrantTarget(snapshot, offer.targetHash) (pause/revoke gate) → cap/coin
299
+ * checks → assertLive (fresh authority) → build unsigned kind bytes → return the
300
+ * signing request. There is deliberately no sponsor call, no decrypt, no
301
+ * signature, and no forwarded bearer here.
302
+ *
303
+ * See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
304
+ */
305
+
306
+ type SharedObjectReader = (objectId: string) => Promise<{
307
+ owner?: unknown;
308
+ } | null | undefined>;
309
+ /**
310
+ * Read/prepare-only session for the non-custodial MCP pay tool. It carries the
311
+ * immutable grant snapshot and an optional prepare-time live gate — and no key,
312
+ * by construction. There is no `decryptDelegateSeed` and no `sponsorAuthorization`.
313
+ */
314
+ interface SpendAccountPrepareSession {
315
+ snapshot: GrantSnapshot;
316
+ /** SpendAccount package id (V2). */
317
+ packageId: string;
318
+ /**
319
+ * Re-assert the grant is still live (not paused/revoked/expired) against fresh
320
+ * canonical state, invoked immediately before a signing request is handed back.
321
+ * Throw to abort. Optional; when absent, only the request-time snapshot gates
322
+ * the prepare (on-chain `spend_account::settle_policy` remains the final backstop for any
323
+ * spend the client later submits).
324
+ */
325
+ assertLive?: () => Promise<void>;
326
+ }
327
+ interface SpendAccountPayDeps {
328
+ fetchImpl?: typeof fetch;
329
+ /**
330
+ * Build only-transaction-kind bytes for the canonical guarded debit
331
+ * (`spend_account::settle_policy` / `settle_any_recipient`).
332
+ *
333
+ * The guarded-settle fields ({@link GuardedSettleFields}) are required on the
334
+ * fresh lineage: every delegate settle consumes the shared exclusion marker.
335
+ * Dropping them fails closed rather than building an unguarded call that no
336
+ * longer exists on chain.
337
+ */
338
+ buildPayKind?: (input: {
339
+ delegate: string;
340
+ coinType: string;
341
+ poolObjectId: string;
342
+ grantObjectId: string;
343
+ policyId: number | string;
344
+ targetHash: string;
345
+ amount: string;
346
+ paymentIdHash: Uint8Array;
347
+ termsHash: Uint8Array;
348
+ recipient?: string;
349
+ accessMode?: 'scoped' | 'open';
350
+ } & GuardedSettleFields) => Promise<{
351
+ kindBytes: string;
352
+ }>;
353
+ /**
354
+ * Resolve the per-recipient `RecipientRegistry` bound in the offer's exclusion
355
+ * directory (guarded offers only). Injected seam for tests; production
356
+ * leaves it unset and the prepare wires a live gRPC-backed resolver from the
357
+ * configured `ExclusionDirectory` id when present, else fails closed.
358
+ */
359
+ resolveExclusionRegistry?: PayOperationDeps['resolveExclusionRegistry'];
360
+ /**
361
+ * Read a shared object's owner so prepare binds the real initial version.
362
+ * Missing owner fails the prepare; version 1 is not a substitute.
363
+ */
364
+ readSharedObject?: SharedObjectReader;
365
+ /**
366
+ * Live trace for this prepare. Purely observational: emission records what the
367
+ * canonical read decisions already returned — an event is written after a check
368
+ * passes and can never be the thing that lets the next step run.
369
+ */
370
+ trace?: SpendAccountPayTrace;
371
+ }
372
+ interface SpendAccountPayTrace {
373
+ sink: TraceSink;
374
+ traceId: string;
375
+ requestId: string | null;
376
+ buyerAccountId: string;
377
+ connectionId: string;
378
+ /** HMAC secret for signed cross-request context. Absent disables signing. */
379
+ secret?: string | null;
380
+ /** Injected clock, for tests. */
381
+ now?: () => number;
382
+ }
383
+ /** The unsigned payment the client signs locally and submits. */
384
+ interface SpendAccountSigningRequest {
385
+ /**
386
+ * Unsigned `spend_account::settle_policy` / `settle_any_recipient` transaction-kind bytes (base64), sender set to
387
+ * `delegateAddress`. The client sponsors these for gas, signs with the delegate
388
+ * key, and submits — the gateway never receives the key.
389
+ */
390
+ unsignedKindBytes: string;
391
+ delegateAddress: string;
392
+ packageId: string;
393
+ poolObjectId: string;
394
+ grantObjectId: string;
395
+ /** On-chain policy id the pay is bound to. */
396
+ policyId: number | string;
397
+ coinType: string;
398
+ targetHash: string;
399
+ amount: string;
400
+ /** Hex (no 0x) of the payment-id hash bound into the transaction. */
401
+ paymentIdHashHex: string;
402
+ /** Hex (no 0x) of the terms hash bound into the transaction. */
403
+ termsHashHex: string;
404
+ challengeId: string;
405
+ /** The exact paid request to replay after the client submits. */
406
+ resource: {
407
+ url: string;
408
+ method: string;
409
+ };
410
+ dialect: Dialect;
411
+ /** Recipient the agent requested (offer.payTo). */
412
+ recipient: string;
413
+ /** Where the client (holding the key) sponsors and submits. */
414
+ submit: {
415
+ sponsorUrl: string;
416
+ executeUrl: string;
417
+ moveCallTarget: string;
418
+ };
419
+ /**
420
+ * ISO-8601 timestamp when this prepared operation expires. After this time the
421
+ * unsigned bytes are no longer valid and the client must re-call pay() for a
422
+ * fresh prepare. Derived from the offer TTL.
423
+ */
424
+ expiresAt: string;
425
+ /** Restated hard rule for the agent host. */
426
+ note: string;
427
+ }
428
+ interface PreparedPayment {
429
+ status: 'prepared';
430
+ paid: false;
431
+ signingRequest: SpendAccountSigningRequest;
432
+ }
433
+ /** Prepared signing request, or a canonical failure (never a spend). */
434
+ type SpendAccountPrepareResult = PreparedPayment | PayResult;
435
+ /** Default kind builder (no chain client — pure PTB). */
436
+ declare function buildSpendAccountPayKind(input: {
437
+ packageId: string;
438
+ delegate: string;
439
+ coinType: string;
440
+ poolObjectId: string;
441
+ grantObjectId: string;
442
+ policyId: number | string;
443
+ targetHash: string;
444
+ amount: string;
445
+ paymentIdHash: Uint8Array;
446
+ termsHash: Uint8Array;
447
+ poolInitialSharedVersion?: string;
448
+ grantInitialSharedVersion?: string;
449
+ clockInitialSharedVersion?: string;
450
+ } & GuardedSettleFields): Promise<{
451
+ kindBytes: string;
452
+ }>;
453
+ /**
454
+ * Refuse blind signing: a client validating a sponsored transaction must be able
455
+ * to prove the sponsored bytes reconstruct the intended spend_account::settle_policy. Kept
456
+ * here (and exported) so both the client signer and its tests share one check.
457
+ */
458
+ declare function verifySpendAccountPayReconstructs(bytes: string, expected: {
459
+ kindBytes: string;
460
+ packageId: string;
461
+ delegate: string;
462
+ poolObjectId: string;
463
+ grantObjectId: string;
464
+ amount: string;
465
+ }): void;
466
+ /**
467
+ * Prepare a delegate payment WITHOUT signing. Reads the 402 offer, binds it,
468
+ * gates on the immutable grant snapshot + a fresh authority check, builds the
469
+ * unsigned transaction, and returns a signing request. The client holding the
470
+ * delegate key signs locally and submits. This function never decrypts, signs,
471
+ * sponsors, or submits.
472
+ *
473
+ * Uses `resolvePayment` from the SDK pay-operation module for the
474
+ * shared offer-acquisition + authority-resolution pipeline (steps 1–5 + assertLive
475
+ * + hash derivation + buildPayKind). This module wraps that with MCP-specific
476
+ * concerns: trace events, dialect-mode ceiling check, and signing-request
477
+ * construction.
478
+ */
479
+ declare function prepareSpendAccountPayment(args: {
480
+ url: string;
481
+ /** The one normalized paid request, replayed byte for byte after signing. */
482
+ request?: PaidHttpRequest;
483
+ session: SpendAccountPrepareSession;
484
+ cfg: McpConfig;
485
+ deps?: SpendAccountPayDeps;
486
+ /** Preferred dialect when the 402 advertises both (default mpp). */
487
+ preferredDialect?: Dialect;
488
+ }): Promise<SpendAccountPrepareResult>;
489
+
252
490
  /**
253
491
  * Transport-neutral SuiPay MCP server + tool registry.
254
492
  * Used by stdio (index.ts) and Streamable HTTP (http.ts).
@@ -378,6 +616,8 @@ interface McpServerOptions {
378
616
  * does not already authorize.
379
617
  */
380
618
  resolveAgentHeld?: AgentHeldResolver;
619
+ /** Override the exclusion-registry resolver (tests / offline). */
620
+ resolveExclusionRegistry?: SpendAccountPayDeps['resolveExclusionRegistry'];
381
621
  /**
382
622
  * Wire that created this server. Hosted HTTP never forwards `file` into `pay`.
383
623
  * Stdio is the default so existing tests stay local.
@@ -402,4 +642,4 @@ declare function createServer(cfg: McpConfig, auth?: McpAuthContext, trace?: Mcp
402
642
  */
403
643
  declare function handleSuipayMcpHttpRequest(req: Request, cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceRuntime | null, options?: McpServerOptions): Promise<Response>;
404
644
 
405
- export { type AgentHeldResolver as A, type McpConfig as M, PACKAGE_NAME as P, TOOLS as T, type McpAuthContext as a, type McpTraceHooks as b, type AgentHeldSettlement as c, MCP_TARGET_PRESETS as d, type McpEnvelope as e, type McpEnvelopeEntry as f, type McpServerOptions as g, type McpTarget as h, type McpTraceContext as i, type McpTraceRuntime as j, canonicalMcpRequestId as k, createServer as l, handleSuipayMcpHttpRequest as m, inspectMcpEnvelope as n, loadMcpConfig as o, parseMcpTargetFlag as p, resolveAgentHeldSettlement as q, resetAgentHeldProcessLedger as r, resolveMcpTarget as s, sameMcpGateway as t, traceMcpHttpRequest as u };
645
+ export { type AgentHeldResolver as A, traceMcpHttpRequest as B, verifySpendAccountPayReconstructs as C, type McpConfig as M, PACKAGE_NAME as P, type SpendAccountSigningRequest as S, TOOLS as T, type McpAuthContext as a, type McpTraceHooks as b, type SpendAccountPayDeps as c, type AgentHeldSettlement as d, MCP_TARGET_PRESETS as e, type McpEnvelope as f, type McpEnvelopeEntry as g, type McpServerOptions as h, type McpTarget as i, type McpTraceContext as j, type McpTraceRuntime as k, type PreparedPayment as l, type SpendAccountPrepareResult as m, type SpendAccountPrepareSession as n, buildSpendAccountPayKind as o, canonicalMcpRequestId as p, createServer as q, handleSuipayMcpHttpRequest as r, inspectMcpEnvelope as s, loadMcpConfig as t, parseMcpTargetFlag as u, prepareSpendAccountPayment as v, resetAgentHeldProcessLedger as w, resolveAgentHeldSettlement as x, resolveMcpTarget as y, sameMcpGateway as z };
package/dist/http.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { m as handleSuipayMcpHttpRequest } from './http-DBP9qgVw.js';
1
+ export { r as handleSuipayMcpHttpRequest } from './http-RP-Xlg60.js';
2
2
  import '@cmdoss/suipay-core/policy/grant-target';
3
3
  import '@cmdoss/suipay-sdk/mcp/access-context';
4
4
  import '@cmdoss/suipay-sdk/buyer/settlement-dto';
@@ -6,3 +6,6 @@ import '@modelcontextprotocol/sdk/server/index.js';
6
6
  import '@cmdoss/suipay-sdk/agent-sdk/reads';
7
7
  import '@cmdoss/suipay-sdk/delegated-signer/payer';
8
8
  import '@cmdoss/suipay-core/trace/types';
9
+ import '@cmdoss/suipay-sdk/protocol/paid-http-request';
10
+ import '@cmdoss/suipay-sdk/protocol/types';
11
+ import '@cmdoss/suipay-sdk/pay-operation';
package/dist/http.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleSuipayMcpHttpRequest
3
- } from "./chunk-UTKVBAEZ.js";
3
+ } from "./chunk-K4C64BXE.js";
4
4
  export {
5
5
  handleSuipayMcpHttpRequest
6
6
  };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Wallet } from '@cmdoss/suipay-core/chain/types';
2
- import { M as McpConfig, a as McpAuthContext, b as McpTraceHooks } from './http-DBP9qgVw.js';
3
- export { A as AgentHeldResolver, c as AgentHeldSettlement, d as MCP_TARGET_PRESETS, e as McpEnvelope, f as McpEnvelopeEntry, g as McpServerOptions, h as McpTarget, i as McpTraceContext, j as McpTraceRuntime, P as PACKAGE_NAME, T as TOOLS, k as canonicalMcpRequestId, l as createServer, m as handleSuipayMcpHttpRequest, n as inspectMcpEnvelope, o as loadMcpConfig, p as parseMcpTargetFlag, r as resetAgentHeldProcessLedger, q as resolveAgentHeldSettlement, s as resolveMcpTarget, t as sameMcpGateway, u as traceMcpHttpRequest } from './http-DBP9qgVw.js';
4
- import { GrantSnapshot } from '@cmdoss/suipay-core/policy/grant-target';
5
- export { resolveGrantTarget } from '@cmdoss/suipay-core/policy/grant-target';
6
- import { PaidHttpRequest } from '@cmdoss/suipay-sdk/protocol/paid-http-request';
7
- import { TraceSink } from '@cmdoss/suipay-core/trace/types';
8
- import { Dialect } from '@cmdoss/suipay-sdk/protocol/types';
2
+ import { S as SpendAccountSigningRequest, M as McpConfig, a as McpAuthContext, b as McpTraceHooks, c as SpendAccountPayDeps } from './http-RP-Xlg60.js';
3
+ export { A as AgentHeldResolver, d as AgentHeldSettlement, e as MCP_TARGET_PRESETS, f as McpEnvelope, g as McpEnvelopeEntry, h as McpServerOptions, i as McpTarget, j as McpTraceContext, k as McpTraceRuntime, P as PACKAGE_NAME, l as PreparedPayment, m as SpendAccountPrepareResult, n as SpendAccountPrepareSession, T as TOOLS, o as buildSpendAccountPayKind, p as canonicalMcpRequestId, q as createServer, r as handleSuipayMcpHttpRequest, s as inspectMcpEnvelope, t as loadMcpConfig, u as parseMcpTargetFlag, v as prepareSpendAccountPayment, w as resetAgentHeldProcessLedger, x as resolveAgentHeldSettlement, y as resolveMcpTarget, z as sameMcpGateway, B as traceMcpHttpRequest, C as verifySpendAccountPayReconstructs } from './http-RP-Xlg60.js';
9
4
  import { GetObjectJson, createReader } from '@cmdoss/suipay-sdk/agent-sdk/reads';
10
5
  import { DelegatedKeySigner, DelegatedPayerSession, DelegatedPayerConfig, DelegatedPayerDeps, SpendIntent, DelegatedPayResult } from '@cmdoss/suipay-sdk/delegated-signer/payer';
6
+ import { PaidHttpRequest } from '@cmdoss/suipay-sdk/protocol/paid-http-request';
11
7
  import { createPayer } from '@cmdoss/suipay-sdk/agent-sdk/delegate-agent';
12
8
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
9
+ export { resolveGrantTarget } from '@cmdoss/suipay-core/policy/grant-target';
13
10
  import '@cmdoss/suipay-sdk/mcp/access-context';
14
11
  import '@cmdoss/suipay-sdk/buyer/settlement-dto';
15
12
  import '@modelcontextprotocol/sdk/server/index.js';
13
+ import '@cmdoss/suipay-core/trace/types';
14
+ import '@cmdoss/suipay-sdk/protocol/types';
15
+ import '@cmdoss/suipay-sdk/pay-operation';
16
16
 
17
17
  interface SuiPayCredentials {
18
18
  /** 64-hex Ed25519 seed. NEVER logged. */
@@ -68,208 +68,6 @@ declare function loadLocalSigner(): {
68
68
  signPersonalMessage: (message: Uint8Array) => Promise<string>;
69
69
  } | null;
70
70
 
71
- /**
72
- * Shared pay-request helpers.
73
- *
74
- * The V1 allowance sponsored payer (ADR-0010) is retired; only the
75
- * rail-neutral request surface the SpendAccount payer depends on survives here:
76
- * the `PayResult` shape every payer returns. `paidRequestInit` is the SDK helper
77
- * that builds the one RequestInit replayed byte for byte on both calls.
78
- */
79
-
80
- type PayResult = {
81
- status: 'ok' | 'failed' | 'ambiguous';
82
- /**
83
- * `true` — settlement confirmed. `false` — money did not leave the
84
- * account. `'unknown'` — execute/finality did not resolve; a retry can
85
- * pay twice.
86
- */
87
- paid: boolean | 'unknown';
88
- body?: unknown;
89
- receipt?: unknown;
90
- code?: string;
91
- detail?: string;
92
- txDigest?: string;
93
- };
94
-
95
- /**
96
- * SpendAccount PREPARE-only client (non-custodial MCP).
97
- *
98
- * The remote MCP `pay` tool runs inside the gateway, which holds NO key that can
99
- * spend. So this module does not sign or submit: it PREPARES a payment and hands
100
- * back an unsigned `spend_account::settle_policy_payment` transaction plus everything the client
101
- * needs. The client — which holds the delegate key — sponsors, signs locally,
102
- * and submits through the same personal-message challenge → local-sign → submit flow the
103
- * REST/SDK delegate path uses. Only signatures ever reach the gateway; the
104
- * private key is never placed in an HTTP body, a bearer, or any header.
105
- *
106
- * Flow: 402 Offer → request-binding → package/network SSOT →
107
- * resolveGrantTarget(snapshot, offer.targetHash) (pause/revoke gate) → cap/coin
108
- * checks → assertLive (fresh authority) → build unsigned kind bytes → return the
109
- * signing request. There is deliberately no sponsor call, no decrypt, no
110
- * signature, and no forwarded bearer here.
111
- *
112
- * See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
113
- */
114
-
115
- /**
116
- * Read/prepare-only session for the non-custodial MCP pay tool. It carries the
117
- * immutable grant snapshot and an optional prepare-time live gate — and no key,
118
- * by construction. There is no `decryptDelegateSeed` and no `sponsorAuthorization`.
119
- */
120
- interface SpendAccountPrepareSession {
121
- snapshot: GrantSnapshot;
122
- /** SpendAccount package id (V2). */
123
- packageId: string;
124
- /**
125
- * Re-assert the grant is still live (not paused/revoked/expired) against fresh
126
- * canonical state, invoked immediately before a signing request is handed back.
127
- * Throw to abort. Optional; when absent, only the request-time snapshot gates
128
- * the prepare (on-chain `spend_account::settle_policy_payment` remains the final backstop for any
129
- * spend the client later submits).
130
- */
131
- assertLive?: () => Promise<void>;
132
- }
133
- interface SpendAccountPayDeps {
134
- fetchImpl?: typeof fetch;
135
- /** Build only-transaction-kind bytes for spend_account::settle_policy_payment. */
136
- buildPayKind?: (input: {
137
- delegate: string;
138
- coinType: string;
139
- poolObjectId: string;
140
- grantObjectId: string;
141
- policyId: number | string;
142
- targetHash: string;
143
- amount: string;
144
- paymentIdHash: Uint8Array;
145
- termsHash: Uint8Array;
146
- recipient?: string;
147
- accessMode?: 'scoped' | 'open';
148
- }) => Promise<{
149
- kindBytes: string;
150
- }>;
151
- /**
152
- * Live trace for this prepare. Purely observational: emission records what the
153
- * canonical read decisions already returned — an event is written after a check
154
- * passes and can never be the thing that lets the next step run.
155
- */
156
- trace?: SpendAccountPayTrace;
157
- }
158
- interface SpendAccountPayTrace {
159
- sink: TraceSink;
160
- traceId: string;
161
- requestId: string | null;
162
- buyerAccountId: string;
163
- connectionId: string;
164
- /** HMAC secret for signed cross-request context. Absent disables signing. */
165
- secret?: string | null;
166
- /** Injected clock, for tests. */
167
- now?: () => number;
168
- }
169
- /** The unsigned payment the client signs locally and submits. */
170
- interface SpendAccountSigningRequest {
171
- /**
172
- * Unsigned `spend_account::settle_policy_payment` transaction-kind bytes (base64), sender set to
173
- * `delegateAddress`. The client sponsors these for gas, signs with the delegate
174
- * key, and submits — the gateway never receives the key.
175
- */
176
- unsignedKindBytes: string;
177
- delegateAddress: string;
178
- packageId: string;
179
- poolObjectId: string;
180
- grantObjectId: string;
181
- /** On-chain policy id the pay is bound to. */
182
- policyId: number | string;
183
- coinType: string;
184
- targetHash: string;
185
- amount: string;
186
- /** Hex (no 0x) of the payment-id hash bound into the transaction. */
187
- paymentIdHashHex: string;
188
- /** Hex (no 0x) of the terms hash bound into the transaction. */
189
- termsHashHex: string;
190
- challengeId: string;
191
- /** The exact paid request to replay after the client submits. */
192
- resource: {
193
- url: string;
194
- method: string;
195
- };
196
- dialect: Dialect;
197
- /** Recipient the agent requested (offer.payTo). */
198
- recipient: string;
199
- /** Where the client (holding the key) sponsors and submits. */
200
- submit: {
201
- sponsorUrl: string;
202
- executeUrl: string;
203
- moveCallTarget: string;
204
- };
205
- /**
206
- * ISO-8601 timestamp when this prepared operation expires. After this time the
207
- * unsigned bytes are no longer valid and the client must re-call pay() for a
208
- * fresh prepare. Derived from the offer TTL.
209
- */
210
- expiresAt: string;
211
- /** Restated hard rule for the agent host. */
212
- note: string;
213
- }
214
- interface PreparedPayment {
215
- status: 'prepared';
216
- paid: false;
217
- signingRequest: SpendAccountSigningRequest;
218
- }
219
- /** Prepared signing request, or a canonical failure (never a spend). */
220
- type SpendAccountPrepareResult = PreparedPayment | PayResult;
221
- /** Default kind builder (no chain client — pure PTB). */
222
- declare function buildSpendAccountPayKind(input: {
223
- packageId: string;
224
- delegate: string;
225
- coinType: string;
226
- poolObjectId: string;
227
- grantObjectId: string;
228
- policyId: number | string;
229
- targetHash: string;
230
- amount: string;
231
- paymentIdHash: Uint8Array;
232
- termsHash: Uint8Array;
233
- }): Promise<{
234
- kindBytes: string;
235
- }>;
236
- /**
237
- * Refuse blind signing: a client validating a sponsored transaction must be able
238
- * to prove the sponsored bytes reconstruct the intended spend_account::settle_policy_payment. Kept
239
- * here (and exported) so both the client signer and its tests share one check.
240
- */
241
- declare function verifySpendAccountPayReconstructs(bytes: string, expected: {
242
- kindBytes: string;
243
- packageId: string;
244
- delegate: string;
245
- poolObjectId: string;
246
- grantObjectId: string;
247
- amount: string;
248
- }): void;
249
- /**
250
- * Prepare a delegate payment WITHOUT signing. Reads the 402 offer, binds it,
251
- * gates on the immutable grant snapshot + a fresh authority check, builds the
252
- * unsigned transaction, and returns a signing request. The client holding the
253
- * delegate key signs locally and submits. This function never decrypts, signs,
254
- * sponsors, or submits.
255
- *
256
- * Uses `resolvePayment` from the SDK pay-operation module (SAIP-170) for the
257
- * shared offer-acquisition + authority-resolution pipeline (steps 1–5 + assertLive
258
- * + hash derivation + buildPayKind). This module wraps that with MCP-specific
259
- * concerns: trace events, dialect-mode ceiling check, and signing-request
260
- * construction.
261
- */
262
- declare function prepareSpendAccountPayment(args: {
263
- url: string;
264
- /** The one normalized paid request, replayed byte for byte after signing. */
265
- request?: PaidHttpRequest;
266
- session: SpendAccountPrepareSession;
267
- cfg: McpConfig;
268
- deps?: SpendAccountPayDeps;
269
- /** Preferred dialect when the 402 advertises both (default mpp). */
270
- preferredDialect?: Dialect;
271
- }): Promise<SpendAccountPrepareResult>;
272
-
273
71
  /**
274
72
  * Client-side completion of an MCP prepared payment.
275
73
  *
@@ -366,6 +164,8 @@ interface ToolDeps {
366
164
  };
367
165
  /** Override the prepare-time kind builder (tests / offline). */
368
166
  buildPayKind?: SpendAccountPayDeps['buildPayKind'];
167
+ /** Override the exclusion-registry resolver (tests / offline). */
168
+ resolveExclusionRegistry?: SpendAccountPayDeps['resolveExclusionRegistry'];
369
169
  /**
370
170
  * Stdio login surfaces the console URL (MCP progress notification). The URL
371
171
  * carries the delegate address only — never the seed.
@@ -394,4 +194,4 @@ declare function loadBootProfile(load?: typeof loadPaymentProfile): {
394
194
  };
395
195
  declare function main(): Promise<void>;
396
196
 
397
- export { McpAuthContext, McpConfig, McpTraceHooks, type PreparedPayment, type SpendAccountPrepareResult, type SpendAccountPrepareSession, type SpendAccountSigningRequest, buildSpendAccountPayKind, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, pay, prepareSpendAccountPayment, receipts, settlePreparedMcpPayment, verifySpendAccountPayReconstructs };
197
+ export { McpAuthContext, McpConfig, McpTraceHooks, SpendAccountSigningRequest, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, pay, receipts, settlePreparedMcpPayment };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadBootProfile,
3
3
  main
4
- } from "./chunk-AEV6Q6JC.js";
4
+ } from "./chunk-KW3ZECP6.js";
5
5
  import {
6
6
  MCP_TARGET_PRESETS,
7
7
  PACKAGE_NAME,
@@ -26,7 +26,7 @@ import {
26
26
  settlePreparedMcpPayment,
27
27
  traceMcpHttpRequest,
28
28
  verifySpendAccountPayReconstructs
29
- } from "./chunk-UTKVBAEZ.js";
29
+ } from "./chunk-K4C64BXE.js";
30
30
  export {
31
31
  MCP_TARGET_PRESETS,
32
32
  PACKAGE_NAME,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@cmdoss/suipay-mcp",
3
- "version": "0.2.2-dev.6",
3
+ "version": "0.2.2-dev.8",
4
4
  "type": "module",
5
- "description": "SuiPay MCP \u2014 pay MPP-gated APIs from a spend-account grant with an agent-held delegate key.",
5
+ "description": "SuiPay MCP — pay MPP-gated APIs from a spend-account grant with an agent-held delegate key.",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
@@ -19,16 +19,12 @@
19
19
  "README.md"
20
20
  ],
21
21
  "publishConfig": {
22
- "access": "public"
22
+ "access": "public",
23
+ "provenance": true
23
24
  },
24
25
  "engines": {
25
26
  "node": ">=20"
26
27
  },
27
- "scripts": {
28
- "build": "tsup",
29
- "test": "vitest run",
30
- "typecheck": "tsc -p tsconfig.json --noEmit"
31
- },
32
28
  "dependencies": {
33
29
  "@modelcontextprotocol/sdk": "^1.29.0",
34
30
  "@mysten/sui": "2.24.0",
@@ -37,11 +33,14 @@
37
33
  "open": "^11.0.0"
38
34
  },
39
35
  "devDependencies": {
40
- "@cmdoss/suipay-core": "workspace:*",
41
- "@cmdoss/suipay-sdk": "workspace:*",
42
- "@types/node": "catalog:",
43
- "tsup": "catalog:",
44
- "typescript": "catalog:",
45
- "vitest": "catalog:"
36
+ "@types/node": "^26",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^6.0.3",
39
+ "vitest": "^4.1.10"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit"
46
45
  }
47
- }
46
+ }