@cmdoss/suipay-mcp 0.2.2-dev.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/http.d.ts CHANGED
@@ -1,8 +1,3 @@
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';
1
+ export { q as handleSuipayMcpHttpRequest } from './http-BnIjiwcR.js';
5
2
  import '@modelcontextprotocol/sdk/server/index.js';
6
- import '@cmdoss/suipay-sdk/agent-sdk/reads';
7
- import '@cmdoss/suipay-sdk/delegated-signer/payer';
8
- import '@cmdoss/suipay-core/trace/types';
3
+ import '@mysten/sui/keypairs/ed25519';
package/dist/http.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  handleSuipayMcpHttpRequest
3
- } from "./chunk-NVTYOOMY.js";
3
+ } from "./chunk-26NKQ37M.js";
4
+ import "./chunk-A3JIIDYT.js";
4
5
  export {
5
6
  handleSuipayMcpHttpRequest
6
7
  };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,25 @@
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';
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';
15
3
  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
+ }
16
23
 
17
24
  interface SuiPayCredentials {
18
25
  /** 64-hex Ed25519 seed. NEVER logged. */
@@ -46,9 +53,14 @@ interface PaymentProfile {
46
53
  /**
47
54
  * Load a historical V1 allowance payment profile, if present.
48
55
  * ADR-0010: not for new settlement. Callers that pay must refuse this profile
49
- * and point operators at delegate OAuth / delegated-signer flows.
56
+ * and point operators at shared_pool OAuth / delegated-signer flows.
50
57
  */
51
58
  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
+ */
52
64
  declare function ensureLocalSigningKey(opts?: {
53
65
  label?: string;
54
66
  }): Promise<LocalSigningKeyIdentity>;
@@ -73,8 +85,8 @@ declare function loadLocalSigner(): {
73
85
  *
74
86
  * The V1 allowance sponsored payer (ADR-0010) is retired; only the
75
87
  * 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.
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.
78
90
  */
79
91
 
80
92
  type PayResult = {
@@ -203,11 +215,11 @@ interface SpendAccountSigningRequest {
203
215
  moveCallTarget: string;
204
216
  };
205
217
  /**
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.
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.
209
221
  */
210
- expiresAt: string;
222
+ handoffUrl: string;
211
223
  /** Restated hard rule for the agent host. */
212
224
  note: string;
213
225
  }
@@ -247,17 +259,11 @@ declare function verifySpendAccountPayReconstructs(bytes: string, expected: {
247
259
  amount: string;
248
260
  }): void;
249
261
  /**
250
- * Prepare a delegate payment WITHOUT signing. Reads the 402 offer, binds it,
262
+ * Prepare a shared_pool payment WITHOUT signing. Reads the 402 offer, binds it,
251
263
  * gates on the immutable grant snapshot + a fresh authority check, builds the
252
264
  * unsigned transaction, and returns a signing request. The client holding the
253
265
  * delegate key signs locally and submits. This function never decrypts, signs,
254
266
  * 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
267
  */
262
268
  declare function prepareSpendAccountPayment(args: {
263
269
  url: string;
@@ -286,12 +292,6 @@ declare function prepareSpendAccountPayment(args: {
286
292
 
287
293
  declare function settlePreparedMcpPayment(args: {
288
294
  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,84 +315,10 @@ 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
-
392
318
  declare function loadBootProfile(load?: typeof loadPaymentProfile): {
393
319
  profile: ReturnType<typeof loadPaymentProfile>;
394
320
  unreadable: boolean;
395
321
  };
396
322
  declare function main(): Promise<void>;
397
323
 
398
- export { McpAuthContext, McpConfig, McpTraceHooks, type PreparedPayment, type SpendAccountPrepareResult, type SpendAccountPrepareSession, type SpendAccountSigningRequest, buildSpendAccountPayKind, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, pay, prepareSpendAccountPayment, receipts, settlePreparedMcpPayment, verifySpendAccountPayReconstructs };
324
+ export { type PreparedPayment, type SpendAccountPrepareResult, type SpendAccountPrepareSession, type SpendAccountSigningRequest, buildSpendAccountPayKind, ensureLocalSigningKey, loadBootProfile, loadLocalSigner, main, prepareSpendAccountPayment, settlePreparedMcpPayment, verifySpendAccountPayReconstructs };
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  loadBootProfile,
3
3
  main
4
- } from "./chunk-K6WM4Z7H.js";
4
+ } from "./chunk-EM7PKWVL.js";
5
5
  import {
6
- MCP_TARGET_PRESETS,
7
6
  PACKAGE_NAME,
8
7
  TOOLS,
9
8
  buildSpendAccountPayKind,
@@ -14,21 +13,16 @@ import {
14
13
  inspectMcpEnvelope,
15
14
  loadLocalSigner,
16
15
  loadMcpConfig,
17
- parseMcpTargetFlag,
18
- pay,
19
16
  prepareSpendAccountPayment,
20
- receipts,
21
17
  resetAgentHeldProcessLedger,
22
18
  resolveAgentHeldSettlement,
23
19
  resolveGrantTarget,
24
- resolveMcpTarget,
25
- sameMcpGateway,
26
20
  settlePreparedMcpPayment,
27
21
  traceMcpHttpRequest,
28
22
  verifySpendAccountPayReconstructs
29
- } from "./chunk-NVTYOOMY.js";
23
+ } from "./chunk-26NKQ37M.js";
24
+ import "./chunk-A3JIIDYT.js";
30
25
  export {
31
- MCP_TARGET_PRESETS,
32
26
  PACKAGE_NAME,
33
27
  TOOLS,
34
28
  buildSpendAccountPayKind,
@@ -41,15 +35,10 @@ export {
41
35
  loadLocalSigner,
42
36
  loadMcpConfig,
43
37
  main,
44
- parseMcpTargetFlag,
45
- pay,
46
38
  prepareSpendAccountPayment,
47
- receipts,
48
39
  resetAgentHeldProcessLedger,
49
40
  resolveAgentHeldSettlement,
50
41
  resolveGrantTarget,
51
- resolveMcpTarget,
52
- sameMcpGateway,
53
42
  settlePreparedMcpPayment,
54
43
  traceMcpHttpRequest,
55
44
  verifySpendAccountPayReconstructs
@@ -0,0 +1,284 @@
1
+ import {
2
+ SPEND_ACCOUNT_DEBIT_FUNCTIONS
3
+ } from "./chunk-A3JIIDYT.js";
4
+
5
+ // ../../src/gateway/sponsor/personal-message.ts
6
+ import { createHash, randomBytes } from "crypto";
7
+ import { bcs } from "@mysten/sui/bcs";
8
+ import {
9
+ isValidPersonalMessageSignature,
10
+ verifyPersonalMessageSignature
11
+ } from "@mysten/sui/verify";
12
+ import {
13
+ fromBase64,
14
+ normalizeSuiAddress,
15
+ toBase64
16
+ } from "@mysten/sui/utils";
17
+ import { Transaction } from "@mysten/sui/transactions";
18
+ var SPONSOR_PERSONAL_MESSAGE_DOMAIN = "suipay:sponsor-personal-message:v1";
19
+ var SPONSOR_PERSONAL_MESSAGE_DEFAULT_TTL_MS = 6e4;
20
+ var SPONSOR_PERSONAL_MESSAGE_DEFAULT_GAS_BUDGET_MIST = 50000000n;
21
+ function encodeSponsorPersonalMessagePayload(payload) {
22
+ if (payload.domain !== SPONSOR_PERSONAL_MESSAGE_DOMAIN) {
23
+ throw new Error(`domain must be ${SPONSOR_PERSONAL_MESSAGE_DOMAIN}`);
24
+ }
25
+ if (payload.txKindHash.length !== 32) {
26
+ throw new Error("txKindHash must be 32 bytes");
27
+ }
28
+ if (!payload.nonce.trim()) throw new Error("nonce is required");
29
+ if (!Number.isFinite(payload.expiryMs) || payload.expiryMs <= 0) {
30
+ throw new Error("expiryMs must be a positive finite number");
31
+ }
32
+ if (!payload.requestId.trim()) throw new Error("requestId is required");
33
+ if (!payload.network.trim()) throw new Error("network is required");
34
+ const parts = [];
35
+ const push = (u) => parts.push(u);
36
+ push(
37
+ bcs.vector(bcs.u8()).serialize([...Buffer.from(payload.domain, "utf8")]).toBytes()
38
+ );
39
+ push(
40
+ bcs.vector(bcs.u8()).serialize([...Buffer.from(payload.nonce, "utf8")]).toBytes()
41
+ );
42
+ push(bcs.u64().serialize(BigInt(payload.expiryMs)).toBytes());
43
+ push(bcs.Address.serialize(normalizeSuiAddress(payload.delegate)).toBytes());
44
+ push(bcs.Address.serialize(normalizeSuiAddress(payload.poolId)).toBytes());
45
+ push(bcs.Address.serialize(normalizeSuiAddress(payload.grantId)).toBytes());
46
+ push(
47
+ bcs.vector(bcs.u8()).serialize([...Buffer.from(payload.network, "utf8")]).toBytes()
48
+ );
49
+ push(bcs.Address.serialize(normalizeSuiAddress(payload.packageId)).toBytes());
50
+ push(
51
+ bcs.vector(bcs.u8()).serialize([...Buffer.from(payload.requestId, "utf8")]).toBytes()
52
+ );
53
+ push(bcs.vector(bcs.u8()).serialize([...payload.txKindHash]).toBytes());
54
+ return Buffer.concat(parts.map((p) => Buffer.from(p)));
55
+ }
56
+ function hashTransactionKindBytes(kindBytes) {
57
+ const raw = typeof kindBytes === "string" ? fromBase64(kindBytes) : kindBytes;
58
+ return createHash("sha256").update(raw).digest();
59
+ }
60
+ function txKindHashHex(kindBytes) {
61
+ return Buffer.from(hashTransactionKindBytes(kindBytes)).toString("hex");
62
+ }
63
+ function buildSponsorPersonalMessagePayload(input) {
64
+ return {
65
+ domain: SPONSOR_PERSONAL_MESSAGE_DOMAIN,
66
+ nonce: input.nonce,
67
+ expiryMs: input.expiryMs,
68
+ delegate: normalizeSuiAddress(input.delegate),
69
+ poolId: normalizeSuiAddress(input.poolId),
70
+ grantId: normalizeSuiAddress(input.grantId),
71
+ network: input.network,
72
+ packageId: normalizeSuiAddress(input.packageId),
73
+ requestId: input.requestId,
74
+ txKindHash: hashTransactionKindBytes(input.transactionKindBytes)
75
+ };
76
+ }
77
+ async function verifySponsorPersonalMessageSignature(input) {
78
+ try {
79
+ const expected = normalizeSuiAddress(input.expectedDelegate);
80
+ const encoded = encodeSponsorPersonalMessagePayload(input.payload);
81
+ const addressMatches = normalizeSuiAddress(input.payload.delegate) === expected;
82
+ if (!addressMatches) {
83
+ return { ok: false, reason: "payload_delegate_mismatch" };
84
+ }
85
+ try {
86
+ const publicKey = await verifyPersonalMessageSignature(
87
+ encoded,
88
+ input.signature,
89
+ { address: expected }
90
+ );
91
+ const derived = publicKey.toSuiAddress();
92
+ if (normalizeSuiAddress(derived) !== expected) {
93
+ return { ok: false, reason: "address_derivation_mismatch" };
94
+ }
95
+ return { ok: true, address: expected };
96
+ } catch {
97
+ const ok = await isValidPersonalMessageSignature(
98
+ encoded,
99
+ input.signature,
100
+ { address: expected }
101
+ );
102
+ if (!ok) return { ok: false, reason: "bad_signature" };
103
+ return { ok: true, address: expected };
104
+ }
105
+ } catch {
106
+ return { ok: false, reason: "bad_signature" };
107
+ }
108
+ }
109
+ function assertPersonalMessageKindIsPermittedPay(input) {
110
+ const allowed = new Set(
111
+ input.allowedFunctions ?? SPEND_ACCOUNT_DEBIT_FUNCTIONS
112
+ );
113
+ let data;
114
+ try {
115
+ data = Transaction.fromKind(
116
+ fromBase64(input.transactionKindBytes)
117
+ ).getData();
118
+ } catch {
119
+ return { ok: false, reason: "kind_invalid" };
120
+ }
121
+ if (JSON.stringify(data.commands).includes('"GasCoin"')) {
122
+ return { ok: false, reason: "kind_gas_coin" };
123
+ }
124
+ const commands = data.commands ?? [];
125
+ if (commands.length !== 1) {
126
+ return { ok: false, reason: "kind_extra_commands" };
127
+ }
128
+ const cmd = commands[0];
129
+ if (cmd.$kind !== "MoveCall") {
130
+ return { ok: false, reason: "kind_not_move_call" };
131
+ }
132
+ const move = cmd.MoveCall;
133
+ if (!move) return { ok: false, reason: "kind_missing_move_call" };
134
+ const pkg = normalizeSuiAddress(String(move.package));
135
+ const expectedPkg = normalizeSuiAddress(input.packageId);
136
+ if (pkg !== expectedPkg) {
137
+ return { ok: false, reason: "kind_package_mismatch" };
138
+ }
139
+ if (String(move.module) !== "spend_account") {
140
+ return { ok: false, reason: "kind_module_not_shared_pool" };
141
+ }
142
+ const fn = String(move.function);
143
+ if (!allowed.has(fn)) {
144
+ return { ok: false, reason: "kind_function_not_permitted" };
145
+ }
146
+ const args = move.arguments ?? [];
147
+ if (args.length < 2) {
148
+ return { ok: false, reason: "kind_args_incomplete" };
149
+ }
150
+ const poolObj = objectIdFromArg(args[0], data.inputs);
151
+ const grantObj = objectIdFromArg(args[1], data.inputs);
152
+ if (!poolObj || !grantObj) {
153
+ return { ok: false, reason: "kind_object_unresolved" };
154
+ }
155
+ if (normalizeSuiAddress(poolObj) !== normalizeSuiAddress(input.poolObjectId)) {
156
+ return { ok: false, reason: "kind_pool_mismatch" };
157
+ }
158
+ if (normalizeSuiAddress(grantObj) !== normalizeSuiAddress(input.grantObjectId)) {
159
+ return { ok: false, reason: "kind_grant_mismatch" };
160
+ }
161
+ return { ok: true, functionName: String(move.function) };
162
+ }
163
+ function assertPersonalMessageDeploymentBinding(input) {
164
+ const wantNet = input.expectedNetwork.trim().toLowerCase();
165
+ const gotNet = input.network.trim().toLowerCase();
166
+ if (!wantNet || gotNet !== wantNet) {
167
+ return { ok: false, reason: "network_mismatch" };
168
+ }
169
+ try {
170
+ if (normalizeSuiAddress(input.packageId) !== normalizeSuiAddress(input.expectedPackageId)) {
171
+ return { ok: false, reason: "package_mismatch" };
172
+ }
173
+ } catch {
174
+ return { ok: false, reason: "package_mismatch" };
175
+ }
176
+ return { ok: true };
177
+ }
178
+ function publicSponsorGrantDenyReason(reason) {
179
+ const r = reason.toLowerCase();
180
+ if (r.includes("expired")) return "grant_expired";
181
+ if (r.includes("revok")) return "grant_revoked";
182
+ if (r.includes("paus")) return "grant_paused";
183
+ if (r.includes("not active") || r.includes("not_active")) {
184
+ return "grant_not_active";
185
+ }
186
+ if (r.includes("no active") || r.includes("not found") || r.includes("missing")) {
187
+ return "grant_not_found";
188
+ }
189
+ return "grant_not_authorized";
190
+ }
191
+ function objectIdFromArg(arg, inputs) {
192
+ if (!arg || typeof arg !== "object") return null;
193
+ const a = arg;
194
+ if (a.$kind !== "Input" || !Number.isInteger(a.Input)) return null;
195
+ const input = inputs?.[Number(a.Input)];
196
+ if (!input || typeof input !== "object") return null;
197
+ const value = input;
198
+ if (value.$kind !== "Object" || !value.Object) return null;
199
+ const object = value.Object;
200
+ const id = object.$kind === "SharedObject" ? object.SharedObject?.objectId : object.$kind === "ImmOrOwnedObject" ? object.ImmOrOwnedObject?.objectId : object.$kind === "Receiving" ? object.Receiving?.objectId : void 0;
201
+ return typeof id === "string" ? id : null;
202
+ }
203
+ function createResolveSponsorGrant(store) {
204
+ return async (query) => {
205
+ const found = await store.findActiveGrantForPersonalMessage({
206
+ grantObjectId: query.grantObjectId,
207
+ poolObjectId: query.poolObjectId,
208
+ delegateAddress: query.delegateAddress
209
+ });
210
+ if (!found) {
211
+ return { ok: false, reason: "grant_not_found" };
212
+ }
213
+ const expiresAtMs = Number(found.grant.expiresAtMs);
214
+ if (Number.isFinite(expiresAtMs) && expiresAtMs > 0 && expiresAtMs <= query.nowMs) {
215
+ return { ok: false, reason: "grant_expired" };
216
+ }
217
+ if (found.grant.status === "revoked") {
218
+ return { ok: false, reason: "grant_revoked" };
219
+ }
220
+ if (found.grant.status !== "active") {
221
+ return { ok: false, reason: "grant_not_active" };
222
+ }
223
+ if (found.aggregateStatus === "paused" || found.aggregateStatus === "pausing") {
224
+ return { ok: false, reason: "grant_paused" };
225
+ }
226
+ if (found.aggregateStatus === "revoking" || found.aggregateStatus === "partially_revoked" || found.aggregateStatus === "revoked") {
227
+ return { ok: false, reason: "grant_revoked" };
228
+ }
229
+ if (!found.grant.grantObjectId) {
230
+ return { ok: false, reason: "grant_not_found" };
231
+ }
232
+ return {
233
+ ok: true,
234
+ grant: {
235
+ delegateAddress: normalizeSuiAddress(found.agentAddress),
236
+ poolObjectId: normalizeSuiAddress(found.grant.poolObjectId),
237
+ grantObjectId: normalizeSuiAddress(found.grant.grantObjectId),
238
+ expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : 0,
239
+ status: "active"
240
+ }
241
+ };
242
+ };
243
+ }
244
+ function mintSponsorChallengeNonce() {
245
+ return randomBytes(24).toString("base64url");
246
+ }
247
+ function mintSponsorExecuteToken() {
248
+ return randomBytes(24).toString("base64url");
249
+ }
250
+ async function signSponsorPersonalMessagePayload(input) {
251
+ const encoded = encodeSponsorPersonalMessagePayload(input.payload);
252
+ return input.signPersonalMessage(encoded);
253
+ }
254
+ function bytesToHex(bytes) {
255
+ return Buffer.from(bytes).toString("hex");
256
+ }
257
+ function hexToBytes32(hex) {
258
+ const h = hex.replace(/^0x/i, "").toLowerCase();
259
+ if (!/^[0-9a-f]{64}$/.test(h)) {
260
+ throw new Error(`expected 32-byte hex, got ${hex}`);
261
+ }
262
+ return Buffer.from(h, "hex");
263
+ }
264
+ export {
265
+ SPONSOR_PERSONAL_MESSAGE_DEFAULT_GAS_BUDGET_MIST,
266
+ SPONSOR_PERSONAL_MESSAGE_DEFAULT_TTL_MS,
267
+ SPONSOR_PERSONAL_MESSAGE_DOMAIN,
268
+ assertPersonalMessageDeploymentBinding,
269
+ assertPersonalMessageKindIsPermittedPay,
270
+ buildSponsorPersonalMessagePayload,
271
+ bytesToHex,
272
+ createResolveSponsorGrant,
273
+ encodeSponsorPersonalMessagePayload,
274
+ fromBase64,
275
+ hashTransactionKindBytes,
276
+ hexToBytes32,
277
+ mintSponsorChallengeNonce,
278
+ mintSponsorExecuteToken,
279
+ publicSponsorGrantDenyReason,
280
+ signSponsorPersonalMessagePayload,
281
+ toBase64,
282
+ txKindHashHex,
283
+ verifySponsorPersonalMessageSignature
284
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmdoss/suipay-mcp",
3
- "version": "0.2.2-dev.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
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",
@@ -19,7 +19,8 @@
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"
@@ -31,17 +32,14 @@
31
32
  },
32
33
  "dependencies": {
33
34
  "@modelcontextprotocol/sdk": "^1.29.0",
34
- "@mysten/sui": "catalog:pinned",
35
+ "@mysten/sui": "2.24.0",
35
36
  "@noble/ed25519": "^2.1.0",
36
37
  "@noble/hashes": "^2.3.0",
37
38
  "open": "^11.0.0"
38
39
  },
39
40
  "devDependencies": {
40
- "@cmdoss/suipay-core": "workspace:*",
41
- "@cmdoss/suipay-sdk": "workspace:*",
42
- "@types/node": "catalog:",
43
- "tsup": "catalog:",
44
- "typescript": "catalog:",
45
- "vitest": "catalog:"
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^6.0.3",
43
+ "vitest": "^3.0.0"
46
44
  }
47
45
  }
@@ -1,46 +0,0 @@
1
- import {
2
- createServer,
3
- loadLocalSigner,
4
- loadLocalSigningKeyRecord,
5
- loadMcpConfig,
6
- loadPaymentProfile
7
- } from "./chunk-NVTYOOMY.js";
8
-
9
- // src/index.ts
10
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
- function loadBootProfile(load = loadPaymentProfile) {
12
- try {
13
- return { profile: load(), unreadable: false };
14
- } catch {
15
- return { profile: null, unreadable: true };
16
- }
17
- }
18
- async function main() {
19
- const argv = process.argv.slice(2);
20
- const cfg = loadMcpConfig(process.env, argv);
21
- const target = cfg.target ?? "dev";
22
- const server = createServer(cfg);
23
- await server.connect(new StdioServerTransport());
24
- try {
25
- const boundGateway = loadLocalSigningKeyRecord()?.gatewayUrl ?? cfg.gatewayUrl;
26
- const local = loadLocalSigner();
27
- if (local) {
28
- console.error(
29
- `[suipay-mcp] ${target} ${boundGateway} \xB7 delegate ${local.address} \xB7 stdio ready`
30
- );
31
- } else {
32
- console.error(
33
- `[suipay-mcp] ${target} ${boundGateway} \xB7 not connected \xB7 run suipay_login \xB7 stdio ready`
34
- );
35
- }
36
- } catch {
37
- console.error(
38
- `[suipay-mcp] ${target} ${cfg.gatewayUrl} \xB7 credentials unreadable: run suipay_login or suipay_logout force=true \xB7 stdio ready`
39
- );
40
- }
41
- }
42
-
43
- export {
44
- loadBootProfile,
45
- main
46
- };