@cmdoss/suipay-mcp 0.2.0 → 0.2.2-dev.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.
@@ -0,0 +1,392 @@
1
+ import { GrantSnapshot } from '@cmdoss/suipay-core/policy/grant-target';
2
+ import { McpAccessContext } from '@cmdoss/suipay-sdk/mcp/access-context';
3
+ import { SettlementDTO } from '@cmdoss/suipay-sdk/buyer/settlement-dto';
4
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
+ import { GetObjectJson } from '@cmdoss/suipay-sdk/agent-sdk/reads';
6
+ import { DelegatedKeySigner, DelegatedPayerDeps } from '@cmdoss/suipay-sdk/delegated-signer/payer';
7
+ import { TraceRunResolver, TraceSink } from '@cmdoss/suipay-core/trace/types';
8
+
9
+ /**
10
+ * Authenticated MCP request context (remote Streamable HTTP).
11
+ * Bearer is required on /mcp; tools enforce scope from the resolved grant.
12
+ */
13
+
14
+ /**
15
+ * Read/prepare-only shared-pool context for an authenticated MCP bearer.
16
+ *
17
+ * Non-custodial: the gateway holds NO key that can spend. There is deliberately
18
+ * no `decryptDelegateSeed` and no forwarded `sponsorAuthorization` here — the
19
+ * `pay` tool only PREPARES an unsigned `spend_account::settle_policy_payment` transaction from the
20
+ * immutable grant snapshot (`prepareSpendAccountPayment`), and the client that
21
+ * holds the delegate key signs locally and submits. Only signatures ever reach
22
+ * the gateway. See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
23
+ */
24
+ interface McpSpendAccountPayAuth {
25
+ snapshot: GrantSnapshot;
26
+ packageId: string;
27
+ /**
28
+ * Re-verify the paying authority against a FRESH graph + injected clock at
29
+ * prepare time. Throws on drift/expiry so the pay tool refuses to hand back a
30
+ * signing request for a grant that just went paused/revoked/expired. Optional:
31
+ * absent for legacy/local paths.
32
+ */
33
+ assertLive?: () => Promise<void>;
34
+ }
35
+ interface McpAuthContext {
36
+ grantId: string;
37
+ buyerAccountId: string;
38
+ /** Space-separated scopes from the access token. */
39
+ scope: string;
40
+ /** Public, descriptive policy context. Never used as payment authority. */
41
+ access?: McpAccessContext;
42
+ /**
43
+ * When present, `pay` prepares unsigned spend_account::settle_policy_payment bytes for the
44
+ * client-held delegate key to sign locally — no server-side decrypt or
45
+ * signing. Stdio local V1 profile path remains when absent.
46
+ */
47
+ spendAccountPay?: McpSpendAccountPayAuth;
48
+ /**
49
+ * Server-scoped receipts reader, built by the /mcp route from the verified
50
+ * bearer's connection. The `receipts` tool calls this instead of hitting the
51
+ * global settlement feed with a forwarded credential — the bearer never
52
+ * enters the tool surface, and scoping is enforced server-side. Absent for
53
+ * local stdio (no connection, no settlement source).
54
+ */
55
+ listReceipts?: (challengeId?: string) => Promise<SettlementDTO[]>;
56
+ }
57
+
58
+ type McpTarget = 'dev' | 'local';
59
+ /**
60
+ * Public suipay-dev origins (Railway). `local` is the laptop stack from
61
+ * `pnpm demo:testnet`. Explicit `SUIPAY_GATEWAY_URL` / `SUIPAY_CONSOLE_URL`
62
+ * still win over the preset.
63
+ */
64
+ declare const MCP_TARGET_PRESETS: Record<McpTarget, {
65
+ consoleUrl: string;
66
+ gatewayUrl: string;
67
+ }>;
68
+ interface McpConfig {
69
+ /** Preset used when console/gateway env is unset. Set by `loadMcpConfig`. */
70
+ target?: McpTarget;
71
+ consoleUrl: string;
72
+ gatewayUrl: string;
73
+ rpcUrl: string;
74
+ network: 'testnet' | 'mainnet' | 'devnet' | 'localnet';
75
+ packageId: string;
76
+ recipient: string;
77
+ coinType: string;
78
+ sponsorUrl: string;
79
+ minFunded: bigint;
80
+ minMaxPerPayment: bigint;
81
+ label: string;
82
+ }
83
+ /**
84
+ * CLI flags: `--local` / `-local`, `--dev` / `-dev`. Unknown args are ignored
85
+ * so an MCP host can pass extra argv without breaking stdio.
86
+ */
87
+ declare function parseMcpTargetFlag(argv: readonly string[]): McpTarget | undefined;
88
+ declare function resolveMcpTarget(env?: NodeJS.ProcessEnv, argv?: readonly string[]): McpTarget;
89
+ /** Same public origin, or the same laptop loopback port (`localhost` ≡ `127.0.0.1`). */
90
+ declare function sameMcpGateway(a: string, b: string): boolean;
91
+ declare function loadMcpConfig(env?: NodeJS.ProcessEnv, argv?: readonly string[]): McpConfig;
92
+
93
+ /**
94
+ * Agent-held settlement wiring for the `pay` tool.
95
+ *
96
+ * The gateway prepares (it holds no key). Settlement needs the delegate key, so
97
+ * it can only happen in the process that holds one. This module answers a single
98
+ * question for a given grant: *is this process that grant's agent, and is it
99
+ * configured to settle?* Any "no" returns null and `pay` falls back to handing
100
+ * back unsigned bytes — the pre-existing behavior.
101
+ *
102
+ * Two conditions gate it, and the first is the security one:
103
+ *
104
+ * 1. **The persisted key must be the key the grant names.** A hosted gateway
105
+ * running this same tool code either holds no credentials file (null) or
106
+ * holds one for some unrelated key (address mismatch → null). It can only
107
+ * settle by already possessing the grant's delegate seed — which is exactly
108
+ * the custody ADR-0015 removed, not a state a flag protects against.
109
+ * 2. **The settlement dependencies must all be present.** A half-configured
110
+ * settler is worse than none: it would sign and then fail to confirm. Missing
111
+ * anything → null, prepare-only.
112
+ *
113
+ * The key is read, never minted. See `loadLocalSigner`.
114
+ */
115
+
116
+ /** Everything `pay` needs to settle rather than prepare. */
117
+ interface AgentHeldSettlement {
118
+ signer: DelegatedKeySigner;
119
+ getObjectJson: GetObjectJson;
120
+ payerDeps: DelegatedPayerDeps;
121
+ }
122
+ /**
123
+ * Resolve agent-held settlement for one grant, or null to stay prepare-only.
124
+ * Injectable so `createServer` can be driven without touching disk,
125
+ * chain, or environment.
126
+ */
127
+ type AgentHeldResolver = (cfg: McpConfig, spendAccountPay: McpSpendAccountPayAuth) => AgentHeldSettlement | null;
128
+ /** Test seam: drop the process ledger so suites do not share reservations. */
129
+ declare function resetAgentHeldProcessLedger(): void;
130
+ declare function resolveAgentHeldSettlement(cfg: McpConfig, spendAccountPay: McpSpendAccountPayAuth, env?: NodeJS.ProcessEnv): AgentHeldSettlement | null;
131
+
132
+ /**
133
+ * Transparent MCP wire proxy.
134
+ *
135
+ * This module is the *only* place in the system that parses MCP methods and
136
+ * JSON-RPC ids. The gateway authenticates and hands over a run manager and a
137
+ * sink; it never sniffs an MCP body a second time. Payment code emits domain
138
+ * facts, never wire facts. That single-owner rule is what stops two subsystems
139
+ * from disagreeing about which tool call a payment belongs to.
140
+ *
141
+ * Transparency is the top invariant, and it is structural rather than
142
+ * best-effort:
143
+ *
144
+ * - With no runtime, this file is a pass-through and does not touch the
145
+ * request or the response at all.
146
+ * - With a runtime, observation reads `Request.clone()` and `Response.clone()`
147
+ * only. The exact `Response` instance the handler produced is what the caller
148
+ * receives, so byte equality is by construction, not by careful copying.
149
+ * - Response observation is started and deliberately not awaited. A trace
150
+ * store that hangs cannot hold an MCP response open.
151
+ * - The request side cannot be fire-and-forget — the proxy must know the run
152
+ * and the JSON-RPC ids before the handler runs — so it is bounded instead:
153
+ * one deadline over envelope parsing plus run selection, and the same byte
154
+ * cap the response observer uses. A hung store or an oversized body costs
155
+ * the trace, never the request.
156
+ * - Every observation path is wrapped: a hostile body, a failing run manager or
157
+ * a throwing sink degrades to "no trace", never to a changed response.
158
+ *
159
+ * Volume control lives here too. One `tool_called` per JSON-RPC id and one
160
+ * terminal `tool_result` / `tool_error` per id — never one row per SSE frame.
161
+ */
162
+
163
+ /** Everything the proxy needs to attribute wire facts to one owned run. */
164
+ interface McpTraceRuntime {
165
+ buyerAccountId: string;
166
+ connectionId: string;
167
+ runs: TraceRunResolver;
168
+ sink: TraceSink;
169
+ /**
170
+ * HMAC secret for signed cross-request context. Supplied by the composition
171
+ * root so a published SDK never reads a gateway-only environment variable.
172
+ * Absent means internal hops carry no correlation and the gateway simply
173
+ * emits no linked events.
174
+ */
175
+ secret?: string | null;
176
+ /** Injected clock; tests pin it, production leaves it out. */
177
+ now?: () => number;
178
+ }
179
+ /** The proxy-resolved context handed down to payment code (Task 5 seams). */
180
+ interface McpTraceContext {
181
+ traceId: string;
182
+ requestId: string | null;
183
+ }
184
+ interface McpTraceHooks {
185
+ runtime: McpTraceRuntime;
186
+ context: McpTraceContext;
187
+ sink: TraceSink;
188
+ }
189
+ /** One JSON-RPC message read off the request side. */
190
+ interface McpEnvelopeEntry {
191
+ method: string | null;
192
+ jsonRpcId: string | number | null;
193
+ /** Canonical, header-safe request id. `null` for notifications. */
194
+ requestId: string | null;
195
+ toolName: string | null;
196
+ toolArguments: unknown;
197
+ clientName: string | null;
198
+ clientVersion: string | null;
199
+ protocolVersion: string | null;
200
+ }
201
+ interface McpEnvelope {
202
+ batch: boolean;
203
+ entries: McpEnvelopeEntry[];
204
+ /** First entry's method — what run selection keys on. */
205
+ method: string | null;
206
+ requestId: string | null;
207
+ clientName: string | null;
208
+ }
209
+ /**
210
+ * Canonical MCP request id.
211
+ *
212
+ * JSON-RPC allows both `7` and `"7"` as distinct ids, and they must stay
213
+ * distinct: correlating a payment to the wrong tool call is worse than not
214
+ * correlating it. Numbers keep their JSON form under `n:`; strings are
215
+ * base64url-encoded under `s:` so an id containing newlines, colons or quotes
216
+ * survives being carried in an HTTP header without escaping games.
217
+ */
218
+ declare function canonicalMcpRequestId(id: unknown): string | null;
219
+ /**
220
+ * Read the JSON-RPC envelope from a request **clone**.
221
+ *
222
+ * The caller owns cloning; this consumes the body it is given. An unparsable,
223
+ * bodiless or over-cap request yields an empty envelope rather than an error — a
224
+ * malformed request is the transport's problem to answer, not tracing's problem
225
+ * to block.
226
+ */
227
+ declare function inspectMcpEnvelope(request: Request, maxBytes?: number): Promise<McpEnvelope>;
228
+ interface TraceMcpHttpRequestInput {
229
+ request: Request;
230
+ /**
231
+ * The real MCP handler. Receives the untouched original request, plus the
232
+ * resolved trace context when tracing is live.
233
+ */
234
+ handle: (request: Request, hooks?: McpTraceHooks) => Promise<Response>;
235
+ runtime?: McpTraceRuntime | null;
236
+ /** Cap on bytes read for observation, request and response. Never unbounded. */
237
+ maxObservedBytes?: number;
238
+ /**
239
+ * Deadline for envelope parsing plus run selection, the only trace work that
240
+ * happens before the handler. Past it the exchange proceeds untraced.
241
+ */
242
+ runSetupTimeoutMs?: number;
243
+ }
244
+ /**
245
+ * Wrap one MCP HTTP exchange in observation.
246
+ *
247
+ * The response returned here is the exact object `handle` produced. Nothing on
248
+ * the observation path can change it, delay it, or prevent it.
249
+ */
250
+ declare function traceMcpHttpRequest(input: TraceMcpHttpRequestInput): Promise<Response>;
251
+
252
+ /**
253
+ * Transport-neutral SuiPay MCP server + tool registry.
254
+ * Used by stdio (index.ts) and Streamable HTTP (http.ts).
255
+ * Remote HTTP passes McpAuthContext so tools enforce OAuth scope + shared-pool pay.
256
+ */
257
+
258
+ declare const PACKAGE_NAME = "@cmdoss/suipay-mcp";
259
+ declare const TOOLS: readonly [{
260
+ readonly name: "access_context";
261
+ readonly description: "Inspect profiles, policies, service targets, spend caps, usage, and lifecycle state enforced for this authenticated SuiPay session. Call before choosing a paid service.";
262
+ readonly inputSchema: {
263
+ readonly type: "object";
264
+ readonly properties: {};
265
+ };
266
+ }, {
267
+ readonly name: "pay";
268
+ readonly description: string;
269
+ readonly inputSchema: {
270
+ readonly type: "object";
271
+ readonly properties: {
272
+ readonly url: {
273
+ readonly type: "string";
274
+ readonly description: "Full gateway resource URL.";
275
+ };
276
+ readonly method: {
277
+ readonly type: "string";
278
+ readonly enum: readonly ["GET", "POST"];
279
+ readonly description: "HTTP method of the paid request. Defaults to GET.";
280
+ };
281
+ readonly json: {
282
+ readonly type: "object";
283
+ readonly description: "JSON request body for a POST. Sent as application/json and bound to the payment.";
284
+ };
285
+ readonly recipient: {
286
+ readonly type: "string";
287
+ readonly description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else.";
288
+ };
289
+ readonly maxAmount: {
290
+ readonly type: "string";
291
+ readonly description: "Atomic ceiling you will spend on this call. The payment is refused, unsigned, if the offer costs more.";
292
+ };
293
+ };
294
+ readonly required: readonly ["url"];
295
+ };
296
+ }, {
297
+ readonly name: "discover";
298
+ readonly description: "Search SuiPay gateway resources. After login, each row includes name, path, url, catalog price, and whether the current session can cover one call at that price (descriptive only — pay still enforces the grant).";
299
+ readonly inputSchema: {
300
+ readonly type: "object";
301
+ readonly properties: {
302
+ readonly query: {
303
+ readonly type: "string";
304
+ readonly description: "Optional service search terms.";
305
+ };
306
+ };
307
+ };
308
+ }, {
309
+ readonly name: "receipts";
310
+ readonly description: "List SuiPay settlement receipts, optionally filtered by challenge id.";
311
+ readonly inputSchema: {
312
+ readonly type: "object";
313
+ readonly properties: {
314
+ readonly challengeId: {
315
+ readonly type: "string";
316
+ readonly description: "Optional payment challenge id.";
317
+ };
318
+ };
319
+ };
320
+ }, {
321
+ readonly name: "suipay_login";
322
+ readonly description: "Mint or reuse the local delegate key (version-2, mode 0600) and return a clickable console URL immediately so the owner can bind a spend-account grant. Does not wait for the wallet. Default target is Railway suipay-dev; pass target=\"local\" (or start the binary with --local) for the laptop stack. Stdio only; hosted /mcp never holds a spendable key.";
323
+ readonly inputSchema: {
324
+ readonly type: "object";
325
+ readonly properties: {
326
+ readonly label: {
327
+ readonly type: "string";
328
+ readonly description: "Label shown in SuiPay console.";
329
+ };
330
+ readonly target: {
331
+ readonly type: "string";
332
+ readonly enum: readonly ["dev", "local"];
333
+ readonly description: "Where to bind the grant. Default is Railway suipay-dev. Pass \"local\" for localhost:3000 / 127.0.0.1:4340.";
334
+ };
335
+ readonly gatewayResourceUrl: {
336
+ readonly type: "string";
337
+ readonly description: "Gateway resource URL whose 402 defines recipient, asset, and package.";
338
+ };
339
+ };
340
+ };
341
+ }, {
342
+ readonly name: "suipay_logout";
343
+ readonly description: "Remove the local delegate-key credentials file. Missing file is success. Force clears a corrupt file without printing the seed.";
344
+ readonly inputSchema: {
345
+ readonly type: "object";
346
+ readonly properties: {
347
+ readonly force: {
348
+ readonly type: "boolean";
349
+ readonly description: "Remove local credentials even if the file is unreadable.";
350
+ };
351
+ };
352
+ };
353
+ }];
354
+ /**
355
+ * Create a Server with the public SuiPay tool surface registered.
356
+ *
357
+ * `trace` is the proxy-resolved run for this HTTP exchange. It is threaded into
358
+ * tool dependencies so payment code can emit *domain* events, and nothing else:
359
+ * this wrapper deliberately emits no tool lifecycle of its own, because the
360
+ * proxy observes the actual JSON-RPC wire and a second source would double-count
361
+ * every call and disagree on ids.
362
+ */
363
+ interface McpServerOptions {
364
+ /**
365
+ * How `pay` finds an agent-held delegate key for the authenticated grant.
366
+ * Defaults to the persisted local key (`resolveAgentHeldSettlement`), which
367
+ * returns null on any process that does not hold the key the grant names — so
368
+ * the default is prepare-only everywhere except the agent's own machine.
369
+ * Injectable for tests; there is no way to make it hand over a key the grant
370
+ * does not already authorize.
371
+ */
372
+ resolveAgentHeld?: AgentHeldResolver;
373
+ }
374
+ declare function createServer(cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceHooks, options?: McpServerOptions): Server;
375
+
376
+ /**
377
+ * Streamable HTTP transport for SuiPay MCP.
378
+ * Stateless per-request (sessionIdGenerator undefined) — same public tools as stdio.
379
+ * Auth context is required for remote gateway /mcp (bearer resolved upstream).
380
+ *
381
+ * The optional trace runtime turns on live observation. It is a pure wrapper:
382
+ * `traceMcpHttpRequest` reads clones and returns the transport's own Response,
383
+ * so passing a runtime cannot change what an MCP client sees.
384
+ */
385
+
386
+ /**
387
+ * Handle one MCP Streamable HTTP request (initialize, tools/list, tools/call, …).
388
+ * Creates a fresh transport+server pair per request (stateless mode).
389
+ */
390
+ declare function handleSuipayMcpHttpRequest(req: Request, cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceRuntime | null, options?: McpServerOptions): Promise<Response>;
391
+
392
+ 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 };
package/dist/http.d.ts CHANGED
@@ -1,3 +1,8 @@
1
- export { q as handleSuipayMcpHttpRequest } from './http-BnIjiwcR.js';
1
+ export { m as handleSuipayMcpHttpRequest } from './http-B4YTVw0k.js';
2
+ import '@cmdoss/suipay-core/policy/grant-target';
3
+ import '@cmdoss/suipay-sdk/mcp/access-context';
4
+ import '@cmdoss/suipay-sdk/buyer/settlement-dto';
2
5
  import '@modelcontextprotocol/sdk/server/index.js';
3
- import '@mysten/sui/keypairs/ed25519';
6
+ import '@cmdoss/suipay-sdk/agent-sdk/reads';
7
+ import '@cmdoss/suipay-sdk/delegated-signer/payer';
8
+ import '@cmdoss/suipay-core/trace/types';
package/dist/http.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  handleSuipayMcpHttpRequest
3
- } from "./chunk-XZQOOY66.js";
4
- import "./chunk-A3JIIDYT.js";
3
+ } from "./chunk-NVTYOOMY.js";
5
4
  export {
6
5
  handleSuipayMcpHttpRequest
7
6
  };
package/dist/index.d.ts CHANGED
@@ -1,25 +1,18 @@
1
- import { D as Dialect, G as GrantSnapshot, M as McpConfig, T as TraceSink, a as DelegatedKeySigner, b as GetObjectJson, c as DelegatedPayerSession, d as DelegatedPayerConfig, e as DelegatedPayerDeps, S as SpendIntent, f as DelegatedPayResult } from './http-BnIjiwcR.js';
2
- export { A as AgentHeldResolver, g as AgentHeldSettlement, h as McpEnvelope, i as McpEnvelopeEntry, j as McpServerOptions, k as McpTraceContext, l as McpTraceHooks, m as McpTraceRuntime, P as PACKAGE_NAME, n as TOOLS, o as canonicalMcpRequestId, p as createServer, q as handleSuipayMcpHttpRequest, r as inspectMcpEnvelope, s as loadMcpConfig, t as resetAgentHeldProcessLedger, u as resolveAgentHeldSettlement, v as resolveGrantTarget, w as traceMcpHttpRequest } from './http-BnIjiwcR.js';
1
+ import { Wallet } from '@cmdoss/suipay-core/chain/types';
2
+ import { M as McpConfig, a as McpAuthContext, b as McpTraceHooks } from './http-B4YTVw0k.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-B4YTVw0k.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';
9
+ import { GetObjectJson, createReader } from '@cmdoss/suipay-sdk/agent-sdk/reads';
10
+ import { DelegatedKeySigner, DelegatedPayerSession, DelegatedPayerConfig, DelegatedPayerDeps, SpendIntent, DelegatedPayResult } from '@cmdoss/suipay-sdk/delegated-signer/payer';
11
+ import { createPayer } from '@cmdoss/suipay-sdk/agent-sdk/delegate-agent';
12
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
13
+ import '@cmdoss/suipay-sdk/mcp/access-context';
14
+ import '@cmdoss/suipay-sdk/buyer/settlement-dto';
3
15
  import '@modelcontextprotocol/sdk/server/index.js';
4
- import '@mysten/sui/keypairs/ed25519';
5
-
6
- type PaidHttpMethod = 'GET' | 'POST';
7
- interface PaidHttpRequest {
8
- url: string;
9
- method: PaidHttpMethod;
10
- /** Exact wire bytes. Empty for GET. */
11
- body: Uint8Array;
12
- /** Normalized media type, or null when no body is sent. */
13
- contentType: string | null;
14
- /** `hashRequestBody(body)` — the single canonical body hash. */
15
- bodyHash: string;
16
- }
17
-
18
- /** Holds a key and signs transaction bytes. Never leaves the payer. */
19
- interface Wallet {
20
- readonly address: string;
21
- signTransaction(txBytes: string): Promise<string>;
22
- }
23
16
 
24
17
  interface SuiPayCredentials {
25
18
  /** 64-hex Ed25519 seed. NEVER logged. */
@@ -53,14 +46,9 @@ interface PaymentProfile {
53
46
  /**
54
47
  * Load a historical V1 allowance payment profile, if present.
55
48
  * ADR-0010: not for new settlement. Callers that pay must refuse this profile
56
- * and point operators at shared_pool OAuth / delegated-signer flows.
49
+ * and point operators at delegate OAuth / delegated-signer flows.
57
50
  */
58
51
  declare function loadPaymentProfile(): PaymentProfile | null;
59
- /**
60
- * First authorization generates a local keypair and persists it at 0600.
61
- * Re-authorization reuses the stored key so a second key cannot orphan the grant.
62
- * Returns only the public identity.
63
- */
64
52
  declare function ensureLocalSigningKey(opts?: {
65
53
  label?: string;
66
54
  }): Promise<LocalSigningKeyIdentity>;
@@ -85,8 +73,8 @@ declare function loadLocalSigner(): {
85
73
  *
86
74
  * The V1 allowance sponsored payer (ADR-0010) is retired; only the
87
75
  * rail-neutral request surface the SpendAccount payer depends on survives here:
88
- * the `PayResult` shape every payer returns and `paidRequestInit`, which builds
89
- * the one RequestInit replayed byte for byte on the unpaid and the paid call.
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.
90
78
  */
91
79
 
92
80
  type PayResult = {
@@ -215,11 +203,11 @@ interface SpendAccountSigningRequest {
215
203
  moveCallTarget: string;
216
204
  };
217
205
  /**
218
- * Non-secret wallet/console handoff for hosts that cannot run a local signer:
219
- * the delegate key signs and submits out of band there. Parameterized only by
220
- * public Connection-manifest fields never any secret.
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.
221
209
  */
222
- handoffUrl: string;
210
+ expiresAt: string;
223
211
  /** Restated hard rule for the agent host. */
224
212
  note: string;
225
213
  }
@@ -259,11 +247,17 @@ declare function verifySpendAccountPayReconstructs(bytes: string, expected: {
259
247
  amount: string;
260
248
  }): void;
261
249
  /**
262
- * Prepare a shared_pool payment WITHOUT signing. Reads the 402 offer, binds it,
250
+ * Prepare a delegate payment WITHOUT signing. Reads the 402 offer, binds it,
263
251
  * gates on the immutable grant snapshot + a fresh authority check, builds the
264
252
  * unsigned transaction, and returns a signing request. The client holding the
265
253
  * delegate key signs locally and submits. This function never decrypts, signs,
266
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.
267
261
  */
268
262
  declare function prepareSpendAccountPayment(args: {
269
263
  url: string;
@@ -292,6 +286,12 @@ declare function prepareSpendAccountPayment(args: {
292
286
 
293
287
  declare function settlePreparedMcpPayment(args: {
294
288
  signingRequest: SpendAccountSigningRequest;
289
+ /**
290
+ * The same paid request that minted the prepare hop. Reconstructing from
291
+ * url+method alone drops a POST body and the settlement hop then prices
292
+ * (or 400s) a different request than the one the tool was called with.
293
+ */
294
+ request: PaidHttpRequest;
295
295
  /**
296
296
  * What this payment is supposed to move, sourced from the caller's intent —
297
297
  * never re-read from the object being verified. Comparing a prepared kind
@@ -315,10 +315,84 @@ declare function settlePreparedMcpPayment(args: {
315
315
  intent?: SpendIntent;
316
316
  }): Promise<DelegatedPayResult>;
317
317
 
318
+ interface StdioLoginOptions {
319
+ consoleUrl: string;
320
+ gatewayUrl: string;
321
+ identity: {
322
+ delegateAddress: string;
323
+ publicKeyHex: string;
324
+ };
325
+ label?: string;
326
+ timeoutMs?: number;
327
+ openBrowser?: boolean;
328
+ onUrl?: (connectUrl: string) => void;
329
+ }
330
+ interface StartedStdioLogin {
331
+ connectUrl: string;
332
+ port: number;
333
+ connectState: string;
334
+ done: Promise<void>;
335
+ close: () => void;
336
+ }
337
+ /**
338
+ * Bind the loopback listener and return the console URL immediately.
339
+ * The wallet callback is awaited on `done`, not on the MCP tool call.
340
+ */
341
+ declare function startStdioLogin(opts: StdioLoginOptions): Promise<StartedStdioLogin>;
342
+
343
+ /**
344
+ * MCP tool payloads. Settlement honesty matches the playground stamp:
345
+ * money that moved is never reported as a tool error (that invites a second pay).
346
+ */
347
+
348
+ type ToolResult = CallToolResult;
349
+
350
+ interface ToolDeps {
351
+ fetchImpl?: typeof fetch;
352
+ profileLoader?: () => PaymentProfile | null;
353
+ /** Remote HTTP auth (grant + scope + optional shared-pool pay session). */
354
+ auth?: McpAuthContext;
355
+ /**
356
+ * Proxy-resolved live trace for this MCP request. Observation only: it never
357
+ * changes which rail runs, what is paid, or whether a payment is allowed.
358
+ */
359
+ trace?: McpTraceHooks;
360
+ /**
361
+ * Agent-held delegate key. Present only in the process that stores the
362
+ * key — never on the gateway. When set after a server-side prepare, `pay`
363
+ * verifies the prepared kind and settles through `payDelegatedSpendAccount`.
364
+ */
365
+ agentHeld?: {
366
+ signer: DelegatedKeySigner;
367
+ getObjectJson: GetObjectJson;
368
+ payerDeps: DelegatedPayerDeps;
369
+ };
370
+ /** Override the prepare-time kind builder (tests / offline). */
371
+ buildPayKind?: SpendAccountPayDeps['buildPayKind'];
372
+ /**
373
+ * Stdio login surfaces the console URL (MCP progress notification). The URL
374
+ * carries the delegate address only — never the seed.
375
+ */
376
+ onUrl?: (url: string) => void;
377
+ createPayer?: typeof createPayer;
378
+ createReader?: typeof createReader;
379
+ startStdioLogin?: typeof startStdioLogin;
380
+ }
381
+ declare function pay(args: {
382
+ url: string;
383
+ method?: unknown;
384
+ json?: unknown;
385
+ recipient?: unknown;
386
+ maxAmount?: unknown;
387
+ }, cfg: McpConfig, deps?: ToolDeps): Promise<ToolResult>;
388
+ declare function receipts(args: {
389
+ challengeId?: string;
390
+ }, cfg: McpConfig, deps?: ToolDeps): Promise<ToolResult>;
391
+
318
392
  declare function loadBootProfile(load?: typeof loadPaymentProfile): {
319
393
  profile: ReturnType<typeof loadPaymentProfile>;
320
394
  unreadable: boolean;
321
395
  };
322
396
  declare function main(): Promise<void>;
323
397
 
324
- export { type PreparedPayment, type SpendAccountPrepareResult, type SpendAccountPrepareSession, type SpendAccountSigningRequest, buildSpendAccountPayKind, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, prepareSpendAccountPayment, settlePreparedMcpPayment, verifySpendAccountPayReconstructs };
398
+ export { McpAuthContext, McpConfig, McpTraceHooks, type PreparedPayment, type SpendAccountPrepareResult, type SpendAccountPrepareSession, type SpendAccountSigningRequest, buildSpendAccountPayKind, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, pay, prepareSpendAccountPayment, receipts, settlePreparedMcpPayment, verifySpendAccountPayReconstructs };
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  loadBootProfile,
3
3
  main
4
- } from "./chunk-TPW6S2K4.js";
4
+ } from "./chunk-K6WM4Z7H.js";
5
5
  import {
6
+ MCP_TARGET_PRESETS,
6
7
  PACKAGE_NAME,
7
8
  TOOLS,
8
9
  buildSpendAccountPayKind,
@@ -13,16 +14,21 @@ import {
13
14
  inspectMcpEnvelope,
14
15
  loadLocalSigner,
15
16
  loadMcpConfig,
17
+ parseMcpTargetFlag,
18
+ pay,
16
19
  prepareSpendAccountPayment,
20
+ receipts,
17
21
  resetAgentHeldProcessLedger,
18
22
  resolveAgentHeldSettlement,
19
23
  resolveGrantTarget,
24
+ resolveMcpTarget,
25
+ sameMcpGateway,
20
26
  settlePreparedMcpPayment,
21
27
  traceMcpHttpRequest,
22
28
  verifySpendAccountPayReconstructs
23
- } from "./chunk-XZQOOY66.js";
24
- import "./chunk-A3JIIDYT.js";
29
+ } from "./chunk-NVTYOOMY.js";
25
30
  export {
31
+ MCP_TARGET_PRESETS,
26
32
  PACKAGE_NAME,
27
33
  TOOLS,
28
34
  buildSpendAccountPayKind,
@@ -35,10 +41,15 @@ export {
35
41
  loadLocalSigner,
36
42
  loadMcpConfig,
37
43
  main,
44
+ parseMcpTargetFlag,
45
+ pay,
38
46
  prepareSpendAccountPayment,
47
+ receipts,
39
48
  resetAgentHeldProcessLedger,
40
49
  resolveAgentHeldSettlement,
41
50
  resolveGrantTarget,
51
+ resolveMcpTarget,
52
+ sameMcpGateway,
42
53
  settlePreparedMcpPayment,
43
54
  traceMcpHttpRequest,
44
55
  verifySpendAccountPayReconstructs