@cmdoss/suipay-mcp 0.2.2-dev.9 → 0.3.0-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.
@@ -1,645 +0,0 @@
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, GuardedSettleFields } from '@cmdoss/suipay-sdk/delegated-signer/payer';
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';
11
-
12
- /**
13
- * Authenticated MCP request context (remote Streamable HTTP).
14
- * Bearer is required on /mcp; tools enforce scope from the resolved grant.
15
- */
16
-
17
- /**
18
- * Read/prepare-only shared-pool context for an authenticated MCP bearer.
19
- *
20
- * Non-custodial: the gateway holds NO key that can spend. There is deliberately
21
- * no `decryptDelegateSeed` and no forwarded `sponsorAuthorization` here — the
22
- * `pay` tool only PREPARES an unsigned `spend_account::settle_policy` / `settle_any_recipient` transaction from the
23
- * immutable grant snapshot (`prepareSpendAccountPayment`), and the client that
24
- * holds the delegate key signs locally and submits. Only signatures ever reach
25
- * the gateway. See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
26
- */
27
- interface McpSpendAccountPayAuth {
28
- snapshot: GrantSnapshot;
29
- packageId: string;
30
- /**
31
- * Re-verify the paying authority against a FRESH graph + injected clock at
32
- * prepare time. Throws on drift/expiry so the pay tool refuses to hand back a
33
- * signing request for a grant that just went paused/revoked/expired. Optional:
34
- * absent for legacy/local paths.
35
- */
36
- assertLive?: () => Promise<void>;
37
- }
38
- interface McpAuthContext {
39
- grantId: string;
40
- buyerAccountId: string;
41
- /** Space-separated scopes from the access token. */
42
- scope: string;
43
- /** Public, descriptive policy context. Never used as payment authority. */
44
- access?: McpAccessContext;
45
- /**
46
- * When present, `pay` prepares unsigned spend_account::settle_policy / settle_any_recipient bytes for the
47
- * client-held delegate key to sign locally — no server-side decrypt or
48
- * signing. Stdio local V1 profile path remains when absent.
49
- */
50
- spendAccountPay?: McpSpendAccountPayAuth;
51
- /**
52
- * Server-scoped receipts reader, built by the /mcp route from the verified
53
- * bearer's connection. The `receipts` tool calls this instead of hitting the
54
- * global settlement feed with a forwarded credential — the bearer never
55
- * enters the tool surface, and scoping is enforced server-side. Absent for
56
- * local stdio (no connection, no settlement source).
57
- */
58
- listReceipts?: (challengeId?: string) => Promise<SettlementDTO[]>;
59
- }
60
-
61
- type McpTarget = 'dev' | 'local';
62
- /**
63
- * Public suipay-dev origins (Railway). `local` is the laptop stack from
64
- * `pnpm demo:testnet`. Explicit `SUIPAY_GATEWAY_URL` / `SUIPAY_CONSOLE_URL`
65
- * still win over the preset.
66
- */
67
- declare const MCP_TARGET_PRESETS: Record<McpTarget, {
68
- consoleUrl: string;
69
- gatewayUrl: string;
70
- }>;
71
- interface McpConfig {
72
- /** Preset used when console/gateway env is unset. Set by `loadMcpConfig`. */
73
- target?: McpTarget;
74
- consoleUrl: string;
75
- gatewayUrl: string;
76
- rpcUrl: string;
77
- network: 'testnet' | 'mainnet' | 'devnet' | 'localnet';
78
- packageId: string;
79
- recipient: string;
80
- coinType: string;
81
- sponsorUrl: string;
82
- minFunded: bigint;
83
- minMaxPerPayment: bigint;
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;
92
- }
93
- /**
94
- * CLI flags: `--local` / `-local`, `--dev` / `-dev`. Unknown args are ignored
95
- * so an MCP host can pass extra argv without breaking stdio.
96
- */
97
- declare function parseMcpTargetFlag(argv: readonly string[]): McpTarget | undefined;
98
- declare function resolveMcpTarget(env?: NodeJS.ProcessEnv, argv?: readonly string[]): McpTarget;
99
- /** Same public origin, or the same laptop loopback port (`localhost` ≡ `127.0.0.1`). */
100
- declare function sameMcpGateway(a: string, b: string): boolean;
101
- declare function loadMcpConfig(env?: NodeJS.ProcessEnv, argv?: readonly string[]): McpConfig;
102
-
103
- /**
104
- * Agent-held settlement wiring for the `pay` tool.
105
- *
106
- * The gateway prepares (it holds no key). Settlement needs the delegate key, so
107
- * it can only happen in the process that holds one. This module answers a single
108
- * question for a given grant: *is this process that grant's agent, and is it
109
- * configured to settle?* Any "no" returns null and `pay` falls back to handing
110
- * back unsigned bytes — the pre-existing behavior.
111
- *
112
- * Two conditions gate it, and the first is the security one:
113
- *
114
- * 1. **The persisted key must be the key the grant names.** A hosted gateway
115
- * running this same tool code either holds no credentials file (null) or
116
- * holds one for some unrelated key (address mismatch → null). It can only
117
- * settle by already possessing the grant's delegate seed — which is exactly
118
- * the custody ADR-0015 removed, not a state a flag protects against.
119
- * 2. **The settlement dependencies must all be present.** A half-configured
120
- * settler is worse than none: it would sign and then fail to confirm. Missing
121
- * anything → null, prepare-only.
122
- *
123
- * The key is read, never minted. See `loadLocalSigner`.
124
- */
125
-
126
- /** Everything `pay` needs to settle rather than prepare. */
127
- interface AgentHeldSettlement {
128
- signer: DelegatedKeySigner;
129
- getObjectJson: GetObjectJson;
130
- payerDeps: DelegatedPayerDeps;
131
- }
132
- /**
133
- * Resolve agent-held settlement for one grant, or null to stay prepare-only.
134
- * Injectable so `createServer` can be driven without touching disk,
135
- * chain, or environment.
136
- */
137
- type AgentHeldResolver = (cfg: McpConfig, spendAccountPay: McpSpendAccountPayAuth) => AgentHeldSettlement | null;
138
- /** Test seam: drop the process ledger so suites do not share reservations. */
139
- declare function resetAgentHeldProcessLedger(): void;
140
- declare function resolveAgentHeldSettlement(cfg: McpConfig, spendAccountPay: McpSpendAccountPayAuth, env?: NodeJS.ProcessEnv): AgentHeldSettlement | null;
141
-
142
- /**
143
- * Transparent MCP wire proxy.
144
- *
145
- * This module is the *only* place in the system that parses MCP methods and
146
- * JSON-RPC ids. The gateway authenticates and hands over a run manager and a
147
- * sink; it never sniffs an MCP body a second time. Payment code emits domain
148
- * facts, never wire facts. That single-owner rule is what stops two subsystems
149
- * from disagreeing about which tool call a payment belongs to.
150
- *
151
- * Transparency is the top invariant, and it is structural rather than
152
- * best-effort:
153
- *
154
- * - With no runtime, this file is a pass-through and does not touch the
155
- * request or the response at all.
156
- * - With a runtime, observation reads `Request.clone()` and `Response.clone()`
157
- * only. The exact `Response` instance the handler produced is what the caller
158
- * receives, so byte equality is by construction, not by careful copying.
159
- * - Response observation is started and deliberately not awaited. A trace
160
- * store that hangs cannot hold an MCP response open.
161
- * - The request side cannot be fire-and-forget — the proxy must know the run
162
- * and the JSON-RPC ids before the handler runs — so it is bounded instead:
163
- * one deadline over envelope parsing plus run selection, and the same byte
164
- * cap the response observer uses. A hung store or an oversized body costs
165
- * the trace, never the request.
166
- * - Every observation path is wrapped: a hostile body, a failing run manager or
167
- * a throwing sink degrades to "no trace", never to a changed response.
168
- *
169
- * Volume control lives here too. One `tool_called` per JSON-RPC id and one
170
- * terminal `tool_result` / `tool_error` per id — never one row per SSE frame.
171
- */
172
-
173
- /** Everything the proxy needs to attribute wire facts to one owned run. */
174
- interface McpTraceRuntime {
175
- buyerAccountId: string;
176
- connectionId: string;
177
- runs: TraceRunResolver;
178
- sink: TraceSink;
179
- /**
180
- * HMAC secret for signed cross-request context. Supplied by the composition
181
- * root so a published SDK never reads a gateway-only environment variable.
182
- * Absent means internal hops carry no correlation and the gateway simply
183
- * emits no linked events.
184
- */
185
- secret?: string | null;
186
- /** Injected clock; tests pin it, production leaves it out. */
187
- now?: () => number;
188
- }
189
- /** The proxy-resolved context handed down to payment code (Task 5 seams). */
190
- interface McpTraceContext {
191
- traceId: string;
192
- requestId: string | null;
193
- }
194
- interface McpTraceHooks {
195
- runtime: McpTraceRuntime;
196
- context: McpTraceContext;
197
- sink: TraceSink;
198
- }
199
- /** One JSON-RPC message read off the request side. */
200
- interface McpEnvelopeEntry {
201
- method: string | null;
202
- jsonRpcId: string | number | null;
203
- /** Canonical, header-safe request id. `null` for notifications. */
204
- requestId: string | null;
205
- toolName: string | null;
206
- toolArguments: unknown;
207
- clientName: string | null;
208
- clientVersion: string | null;
209
- protocolVersion: string | null;
210
- }
211
- interface McpEnvelope {
212
- batch: boolean;
213
- entries: McpEnvelopeEntry[];
214
- /** First entry's method — what run selection keys on. */
215
- method: string | null;
216
- requestId: string | null;
217
- clientName: string | null;
218
- }
219
- /**
220
- * Canonical MCP request id.
221
- *
222
- * JSON-RPC allows both `7` and `"7"` as distinct ids, and they must stay
223
- * distinct: correlating a payment to the wrong tool call is worse than not
224
- * correlating it. Numbers keep their JSON form under `n:`; strings are
225
- * base64url-encoded under `s:` so an id containing newlines, colons or quotes
226
- * survives being carried in an HTTP header without escaping games.
227
- */
228
- declare function canonicalMcpRequestId(id: unknown): string | null;
229
- /**
230
- * Read the JSON-RPC envelope from a request **clone**.
231
- *
232
- * The caller owns cloning; this consumes the body it is given. An unparsable,
233
- * bodiless or over-cap request yields an empty envelope rather than an error — a
234
- * malformed request is the transport's problem to answer, not tracing's problem
235
- * to block.
236
- */
237
- declare function inspectMcpEnvelope(request: Request, maxBytes?: number): Promise<McpEnvelope>;
238
- interface TraceMcpHttpRequestInput {
239
- request: Request;
240
- /**
241
- * The real MCP handler. Receives the untouched original request, plus the
242
- * resolved trace context when tracing is live.
243
- */
244
- handle: (request: Request, hooks?: McpTraceHooks) => Promise<Response>;
245
- runtime?: McpTraceRuntime | null;
246
- /** Cap on bytes read for observation, request and response. Never unbounded. */
247
- maxObservedBytes?: number;
248
- /**
249
- * Deadline for envelope parsing plus run selection, the only trace work that
250
- * happens before the handler. Past it the exchange proceeds untraced.
251
- */
252
- runSetupTimeoutMs?: number;
253
- }
254
- /**
255
- * Wrap one MCP HTTP exchange in observation.
256
- *
257
- * The response returned here is the exact object `handle` produced. Nothing on
258
- * the observation path can change it, delay it, or prevent it.
259
- */
260
- declare function traceMcpHttpRequest(input: TraceMcpHttpRequestInput): Promise<Response>;
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
-
490
- /**
491
- * Transport-neutral SuiPay MCP server + tool registry.
492
- * Used by stdio (index.ts) and Streamable HTTP (http.ts).
493
- * Remote HTTP passes McpAuthContext so tools enforce OAuth scope + shared-pool pay.
494
- */
495
-
496
- declare const PACKAGE_NAME = "@cmdoss/suipay-mcp";
497
- declare const TOOLS: readonly [{
498
- readonly name: "access_context";
499
- 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.";
500
- readonly inputSchema: {
501
- readonly type: "object";
502
- readonly properties: {};
503
- };
504
- }, {
505
- readonly name: "pay";
506
- readonly description: string;
507
- readonly inputSchema: {
508
- readonly type: "object";
509
- readonly properties: {
510
- readonly url: {
511
- readonly type: "string";
512
- readonly description: "Full gateway resource URL.";
513
- };
514
- readonly method: {
515
- readonly type: "string";
516
- readonly enum: readonly ["GET", "POST"];
517
- readonly description: "HTTP method of the paid request. Defaults to GET.";
518
- };
519
- readonly json: {
520
- readonly type: "object";
521
- readonly description: "JSON request body for a POST. Sent as application/json and bound to the payment.";
522
- };
523
- readonly file: {
524
- readonly type: "string";
525
- readonly description: "Absolute path under SUIPAY_MCP_FILE_ROOT (unset disables attach). Stdio POST only; requires fileField. Regular non-empty file whose decoded size fits the paid-body ceiling. Not a URL, FIFO, device, or path under ~/.suipay. Bytes fill json[fileField] as canonical base64.";
526
- };
527
- readonly fileField: {
528
- readonly type: "string";
529
- readonly description: "JSON key to set from file (e.g. contentBase64). Required with file. Pattern [A-Za-z][A-Za-z0-9_]{0,63}. Must not already be present on json.";
530
- };
531
- readonly recipient: {
532
- readonly type: "string";
533
- readonly description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else.";
534
- };
535
- readonly maxAmount: {
536
- readonly type: "string";
537
- readonly description: "Atomic ceiling you will spend on this call. The payment is refused, unsigned, if the offer costs more.";
538
- };
539
- };
540
- readonly required: readonly ["url"];
541
- };
542
- }, {
543
- readonly name: "discover";
544
- 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).";
545
- readonly inputSchema: {
546
- readonly type: "object";
547
- readonly properties: {
548
- readonly query: {
549
- readonly type: "string";
550
- readonly description: "Optional service search terms.";
551
- };
552
- };
553
- };
554
- }, {
555
- readonly name: "receipts";
556
- readonly description: "List SuiPay settlement receipts, optionally filtered by challenge id.";
557
- readonly inputSchema: {
558
- readonly type: "object";
559
- readonly properties: {
560
- readonly challengeId: {
561
- readonly type: "string";
562
- readonly description: "Optional payment challenge id.";
563
- };
564
- };
565
- };
566
- }, {
567
- readonly name: "suipay_login";
568
- 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.";
569
- readonly inputSchema: {
570
- readonly type: "object";
571
- readonly properties: {
572
- readonly label: {
573
- readonly type: "string";
574
- readonly description: "Label shown in SuiPay console.";
575
- };
576
- readonly target: {
577
- readonly type: "string";
578
- readonly enum: readonly ["dev", "local"];
579
- readonly description: "Where to bind the grant. Default is Railway suipay-dev. Pass \"local\" for localhost:3000 / 127.0.0.1:4340.";
580
- };
581
- readonly gatewayResourceUrl: {
582
- readonly type: "string";
583
- readonly description: "Gateway resource URL whose 402 defines recipient, asset, and package.";
584
- };
585
- };
586
- };
587
- }, {
588
- readonly name: "suipay_logout";
589
- readonly description: "Remove the local delegate-key credentials file. Missing file is success. Force clears a corrupt file without printing the seed.";
590
- readonly inputSchema: {
591
- readonly type: "object";
592
- readonly properties: {
593
- readonly force: {
594
- readonly type: "boolean";
595
- readonly description: "Remove local credentials even if the file is unreadable.";
596
- };
597
- };
598
- };
599
- }];
600
- /**
601
- * Create a Server with the public SuiPay tool surface registered.
602
- *
603
- * `trace` is the proxy-resolved run for this HTTP exchange. It is threaded into
604
- * tool dependencies so payment code can emit *domain* events, and nothing else:
605
- * this wrapper deliberately emits no tool lifecycle of its own, because the
606
- * proxy observes the actual JSON-RPC wire and a second source would double-count
607
- * every call and disagree on ids.
608
- */
609
- interface McpServerOptions {
610
- /**
611
- * How `pay` finds an agent-held delegate key for the authenticated grant.
612
- * Defaults to the persisted local key (`resolveAgentHeldSettlement`), which
613
- * returns null on any process that does not hold the key the grant names — so
614
- * the default is prepare-only everywhere except the agent's own machine.
615
- * Injectable for tests; there is no way to make it hand over a key the grant
616
- * does not already authorize.
617
- */
618
- resolveAgentHeld?: AgentHeldResolver;
619
- /** Override the exclusion-registry resolver (tests / offline). */
620
- resolveExclusionRegistry?: SpendAccountPayDeps['resolveExclusionRegistry'];
621
- /**
622
- * Wire that created this server. Hosted HTTP never forwards `file` into `pay`.
623
- * Stdio is the default so existing tests stay local.
624
- */
625
- transport?: 'stdio' | 'http';
626
- }
627
- declare function createServer(cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceHooks, options?: McpServerOptions): Server;
628
-
629
- /**
630
- * Streamable HTTP transport for SuiPay MCP.
631
- * Stateless per-request (sessionIdGenerator undefined) — same public tools as stdio.
632
- * Auth context is required for remote gateway /mcp (bearer resolved upstream).
633
- *
634
- * The optional trace runtime turns on live observation. It is a pure wrapper:
635
- * `traceMcpHttpRequest` reads clones and returns the transport's own Response,
636
- * so passing a runtime cannot change what an MCP client sees.
637
- */
638
-
639
- /**
640
- * Handle one MCP Streamable HTTP request (initialize, tools/list, tools/call, …).
641
- * Creates a fresh transport+server pair per request (stateless mode).
642
- */
643
- declare function handleSuipayMcpHttpRequest(req: Request, cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceRuntime | null, options?: McpServerOptions): Promise<Response>;
644
-
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 };