@cmdoss/suipay-mcp 0.2.2-dev.7 → 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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "../chunk-KR6WRUDY.js";
5
- import "../chunk-6ZSOXII5.js";
4
+ } from "../chunk-KW3ZECP6.js";
5
+ import "../chunk-K4C64BXE.js";
6
6
 
7
7
  // src/bin/suipay.ts
8
8
  main().catch((err) => {
@@ -5675,6 +5675,35 @@ function loadMcpConfig(env = process.env, argv = []) {
5675
5675
  exclusionDirectoryObjectId: rawDir || null
5676
5676
  };
5677
5677
  }
5678
+ var OBJECT_ID = /^0x(?:[0-9a-fA-F]{1,64})$/;
5679
+ async function publishedExclusionDirectoryId(gatewayUrl, fetchImpl = fetch) {
5680
+ let url;
5681
+ try {
5682
+ url = new URL("/v1/delegate/deployment", gatewayUrl).toString();
5683
+ } catch {
5684
+ return null;
5685
+ }
5686
+ let response;
5687
+ try {
5688
+ response = await fetchImpl(url, {
5689
+ method: "GET",
5690
+ redirect: "manual",
5691
+ signal: AbortSignal.timeout(1500)
5692
+ });
5693
+ } catch {
5694
+ return null;
5695
+ }
5696
+ if (response.status !== 200) return null;
5697
+ let body;
5698
+ try {
5699
+ body = await response.json();
5700
+ } catch {
5701
+ return null;
5702
+ }
5703
+ const id = body && typeof body === "object" && "exclusionDirectoryObjectId" in body ? body.exclusionDirectoryObjectId : void 0;
5704
+ if (typeof id !== "string" || !OBJECT_ID.test(id.trim())) return null;
5705
+ return id.trim();
5706
+ }
5678
5707
  function rpcClient(cfg) {
5679
5708
  return new SuiGrpcClient2({ network: cfg.network, baseUrl: cfg.rpcUrl });
5680
5709
  }
@@ -6938,7 +6967,23 @@ async function fetchGatewayDeployment(options) {
6938
6967
  }
6939
6968
  };
6940
6969
  }
6941
- return { ok: true, value: { network: d.network, packageId: d.packageId } };
6970
+ if (d.exclusionDirectoryObjectId !== void 0 && !isNonEmptyString2(d.exclusionDirectoryObjectId)) {
6971
+ return {
6972
+ ok: false,
6973
+ error: {
6974
+ code: MANIFEST_ERROR_CODES.MALFORMED_MANIFEST,
6975
+ detail: "gateway deployment identity carries an unusable exclusionDirectoryObjectId"
6976
+ }
6977
+ };
6978
+ }
6979
+ return {
6980
+ ok: true,
6981
+ value: {
6982
+ network: d.network,
6983
+ packageId: d.packageId,
6984
+ ...isNonEmptyString2(d.exclusionDirectoryObjectId) ? { exclusionDirectoryObjectId: d.exclusionDirectoryObjectId } : {}
6985
+ }
6986
+ };
6942
6987
  }
6943
6988
  function explainBlockedGrant(raw, error2) {
6944
6989
  if (error2.code !== MANIFEST_ERROR_CODES.MISSING_POLICY_BINDING)
@@ -7030,7 +7075,7 @@ async function resolvePayerSession(options) {
7030
7075
  ...options.select ? { select: options.select } : {}
7031
7076
  });
7032
7077
  if (whole.ok)
7033
- return whole;
7078
+ return { ok: true, value: { ...whole.value, deployment } };
7034
7079
  const data = bootstrap.data;
7035
7080
  const rawGrants = Array.isArray(data.grants) ? data.grants : [];
7036
7081
  const usable = [];
@@ -7065,11 +7110,12 @@ async function resolvePayerSession(options) {
7065
7110
  const pick = scoped ?? blocked[0];
7066
7111
  return pick ? { ok: false, error: explainBlockedGrant(pick.raw, pick.error) } : whole;
7067
7112
  }
7068
- return manifestToSession({
7113
+ const resolved = manifestToSession({
7069
7114
  manifest: { ...data, grants: usable },
7070
7115
  signer: options.signer,
7071
7116
  ...options.select ? { select: options.select } : {}
7072
7117
  });
7118
+ return resolved.ok ? { ok: true, value: { ...resolved.value, deployment } } : resolved;
7073
7119
  }
7074
7120
 
7075
7121
  // ../sdk/dist/buyer/transport.js
@@ -7099,6 +7145,9 @@ function mergeObjectEnvelope(object) {
7099
7145
  var SECRET_KEY_SET = new Set(FORBIDDEN_SECRET_KEYS.map((k) => k.toLowerCase()));
7100
7146
 
7101
7147
  // ../sdk/dist/agent-sdk/delegate-agent.js
7148
+ function resolveExclusionDirectoryId(explicit, discovered) {
7149
+ return explicit ?? discovered;
7150
+ }
7102
7151
  function createExclusionRegistryResolver(client, exclusionDirectoryObjectId) {
7103
7152
  if (!exclusionDirectoryObjectId || !exclusionDirectoryObjectId.trim())
7104
7153
  return void 0;
@@ -7169,6 +7218,7 @@ async function createPayer(options) {
7169
7218
  network: session.network,
7170
7219
  packageId: session.packageId
7171
7220
  };
7221
+ const exclusionDirectoryObjectId = resolveExclusionDirectoryId(options.exclusionDirectoryObjectId, resolved.value.deployment.exclusionDirectoryObjectId);
7172
7222
  let network;
7173
7223
  try {
7174
7224
  network = toGrpcNetwork(deployment.network);
@@ -7227,7 +7277,7 @@ async function createPayer(options) {
7227
7277
  // directory id was configured, so such an offer fails closed (BUILD_FAILED)
7228
7278
  // rather than building an unguarded settle. Unguarded offers never call it.
7229
7279
  ...(() => {
7230
- const resolve = createExclusionRegistryResolver(chain.client, options.exclusionDirectoryObjectId);
7280
+ const resolve = createExclusionRegistryResolver(chain.client, exclusionDirectoryObjectId);
7231
7281
  return resolve ? { resolveExclusionRegistry: resolve } : {};
7232
7282
  })(),
7233
7283
  // x402 bounds the transaction's expiration by the current epoch, read from
@@ -7591,6 +7641,22 @@ import { fromBase64 as fromBase6410, toBase64 as toBase646 } from "@mysten/sui/u
7591
7641
 
7592
7642
  // src/spend-account-pay.ts
7593
7643
  var SUI_CLOCK3 = "0x6";
7644
+ var sharedObjectReaderForTests = null;
7645
+ function chainSharedObjectReader(cfg, packageId) {
7646
+ const chain = createSpendAccountChain({
7647
+ packageId,
7648
+ network: cfg.network,
7649
+ url: cfg.rpcUrl
7650
+ });
7651
+ return async (objectId) => {
7652
+ const { object } = await chain.client.core.getObject({
7653
+ objectId,
7654
+ include: { json: true }
7655
+ });
7656
+ if (!object?.owner) return null;
7657
+ return { owner: object.owner };
7658
+ };
7659
+ }
7594
7660
  function traceEmitter(trace) {
7595
7661
  if (!trace) return () => void 0;
7596
7662
  const now = trace.now ?? Date.now;
@@ -7650,12 +7716,12 @@ async function buildSpendAccountPayKind2(input) {
7650
7716
  }),
7651
7717
  tx.sharedObjectRef({
7652
7718
  objectId: input.poolObjectId,
7653
- initialSharedVersion: "1",
7719
+ initialSharedVersion: input.poolInitialSharedVersion?.trim() || "1",
7654
7720
  mutable: true
7655
7721
  }),
7656
7722
  tx.sharedObjectRef({
7657
7723
  objectId: input.grantObjectId,
7658
- initialSharedVersion: "1",
7724
+ initialSharedVersion: input.grantInitialSharedVersion?.trim() || "1",
7659
7725
  mutable: true
7660
7726
  }),
7661
7727
  tx.pure.u64(BigInt(input.policyId)),
@@ -7666,7 +7732,7 @@ async function buildSpendAccountPayKind2(input) {
7666
7732
  tx.pure.u64(BigInt(guarded.expiresAtMs)),
7667
7733
  tx.sharedObjectRef({
7668
7734
  objectId: SUI_CLOCK3,
7669
- initialSharedVersion: "1",
7735
+ initialSharedVersion: input.clockInitialSharedVersion?.trim() || "1",
7670
7736
  mutable: false
7671
7737
  })
7672
7738
  ]
@@ -7754,6 +7820,12 @@ async function prepareSpendAccountPayment(args) {
7754
7820
  ...guardedFields
7755
7821
  });
7756
7822
  }
7823
+ const readSharedObject = deps.readSharedObject ?? sharedObjectReaderForTests ?? chainSharedObjectReader(cfg, input.packageId);
7824
+ const versions = await resolveSharedObjectVersionsFromGetObject(readSharedObject, {
7825
+ poolObjectId: input.poolObjectId,
7826
+ grantObjectId: input.grantObjectId,
7827
+ clockObjectId: SUI_CLOCK3
7828
+ });
7757
7829
  if (isOpen) {
7758
7830
  return buildSpendAccountSettleOpenRecipientKind({
7759
7831
  packageId: input.packageId,
@@ -7761,8 +7833,9 @@ async function prepareSpendAccountPayment(args) {
7761
7833
  coinType: input.coinType,
7762
7834
  poolObjectId: input.poolObjectId,
7763
7835
  grantObjectId: input.grantObjectId,
7764
- poolInitialSharedVersion: "1",
7765
- grantInitialSharedVersion: "1",
7836
+ poolInitialSharedVersion: versions.poolInitialSharedVersion,
7837
+ grantInitialSharedVersion: versions.grantInitialSharedVersion,
7838
+ clockInitialSharedVersion: versions.clockInitialSharedVersion,
7766
7839
  recipient: input.recipient,
7767
7840
  targetHash: input.targetHash,
7768
7841
  amount: input.amount,
@@ -7782,6 +7855,9 @@ async function prepareSpendAccountPayment(args) {
7782
7855
  amount: input.amount,
7783
7856
  paymentIdHash: input.paymentIdHash,
7784
7857
  termsHash: input.termsHash,
7858
+ poolInitialSharedVersion: versions.poolInitialSharedVersion,
7859
+ grantInitialSharedVersion: versions.grantInitialSharedVersion,
7860
+ clockInitialSharedVersion: versions.clockInitialSharedVersion,
7785
7861
  ...guardedFields
7786
7862
  });
7787
7863
  };
@@ -7802,7 +7878,8 @@ async function prepareSpendAccountPayment(args) {
7802
7878
  authority: session.snapshot,
7803
7879
  assertLive: session.assertLive
7804
7880
  };
7805
- const resolveExclusionRegistry2 = deps.resolveExclusionRegistry ?? (cfg.exclusionDirectoryObjectId ? (() => {
7881
+ const directorySwitch = cfg.exclusionDirectoryObjectId ? cfg.exclusionDirectoryObjectId : deps.resolveExclusionRegistry ? null : await publishedExclusionDirectoryId(cfg.gatewayUrl);
7882
+ const resolveExclusionRegistry2 = deps.resolveExclusionRegistry ?? (directorySwitch ? (() => {
7806
7883
  const reader = createGrpcExclusionRegistryReader(rpcClient(cfg));
7807
7884
  return async ({ directoryId, recipient }) => resolveExclusionRegistry({
7808
7885
  directoryObjectId: directoryId,
@@ -9035,13 +9112,13 @@ async function suipayLogin(args, cfg, deps = {}) {
9035
9112
  identity,
9036
9113
  label,
9037
9114
  timeoutMs: LOGIN_BG_TIMEOUT_MS,
9038
- openBrowser: false,
9115
+ openBrowser: true,
9039
9116
  onUrl: deps.onUrl
9040
9117
  });
9041
9118
  const url = started.connectUrl;
9042
9119
  return text(
9043
9120
  [
9044
- `## ACTION REQUIRED: User must click this URL to sign in`,
9121
+ `## ACTION REQUIRED: The sign-in page was opened in the browser`,
9045
9122
  ``,
9046
9123
  `**Target:** ${target ?? cfg.target ?? "custom"} \xB7 ${endpoints.gatewayUrl}`,
9047
9124
  ``,
@@ -9053,11 +9130,13 @@ async function suipayLogin(args, cfg, deps = {}) {
9053
9130
  ``,
9054
9131
  `[Click here to open SuiPay sign-in](${url})`,
9055
9132
  ``,
9056
- `**IMPORTANT for the assistant**: do NOT summarize or omit the URL above.`,
9057
- `The user CANNOT proceed without seeing the exact URL. Surface it verbatim.`,
9133
+ `**IMPORTANT for the assistant**: the browser was already opened to this exact URL.`,
9134
+ `Do NOT navigate to /connect/agent yourself. That path without these query`,
9135
+ `parameters shows "Authorization link incomplete". If the page did not`,
9136
+ `appear, surface the URL above verbatim and do not shorten it.`,
9058
9137
  ``,
9059
- `1. Open the URL in any browser`,
9060
- `2. Connect the owner wallet and approve the spend-account grant (Access keys / policies)`,
9138
+ `1. Use the browser window that just opened, or open the URL above`,
9139
+ `2. Connect the owner wallet, choose or create a key, then Authorize on the review step`,
9061
9140
  `3. Once Connected appears, retry the original request \u2014 credentials at \`~/.suipay/credentials.json\` are picked up on the next tool call`,
9062
9141
  ``,
9063
9142
  `_The login link stays valid for 5 minutes. If it expires, call \`suipay_login\` again._`,
@@ -4,7 +4,7 @@ import {
4
4
  loadLocalSigningKeyRecord,
5
5
  loadMcpConfig,
6
6
  loadPaymentProfile
7
- } from "./chunk-6ZSOXII5.js";
7
+ } from "./chunk-K4C64BXE.js";
8
8
 
9
9
  // src/index.ts
10
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -83,11 +83,10 @@ interface McpConfig {
83
83
  minMaxPerPayment: bigint;
84
84
  label: string;
85
85
  /**
86
- * The canonical `ExclusionDirectory` object id (the same
87
- * `SUIPAY_EXCLUSION_DIRECTORY_OBJECT_ID` the gateway reads). Present enables the
88
- * non-custodial pay path to resolve the per-recipient `RecipientRegistry` and
89
- * build a GUARDED delegate settle for a guarded offer. `null` when
90
- * unset — a guarded offer then FAILS CLOSED (no unguarded settle).
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.
91
90
  */
92
91
  exclusionDirectoryObjectId: string | null;
93
92
  }
@@ -304,6 +303,9 @@ type PayResult = {
304
303
  * See docs/design/mcp-non-custodial-signing.md and ADR-0013 §3.
305
304
  */
306
305
 
306
+ type SharedObjectReader = (objectId: string) => Promise<{
307
+ owner?: unknown;
308
+ } | null | undefined>;
307
309
  /**
308
310
  * Read/prepare-only session for the non-custodial MCP pay tool. It carries the
309
311
  * immutable grant snapshot and an optional prepare-time live gate — and no key,
@@ -355,6 +357,11 @@ interface SpendAccountPayDeps {
355
357
  * configured `ExclusionDirectory` id when present, else fails closed.
356
358
  */
357
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;
358
365
  /**
359
366
  * Live trace for this prepare. Purely observational: emission records what the
360
367
  * canonical read decisions already returned — an event is written after a check
@@ -437,6 +444,9 @@ declare function buildSpendAccountPayKind(input: {
437
444
  amount: string;
438
445
  paymentIdHash: Uint8Array;
439
446
  termsHash: Uint8Array;
447
+ poolInitialSharedVersion?: string;
448
+ grantInitialSharedVersion?: string;
449
+ clockInitialSharedVersion?: string;
440
450
  } & GuardedSettleFields): Promise<{
441
451
  kindBytes: string;
442
452
  }>;
package/dist/http.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { r as handleSuipayMcpHttpRequest } from './http-DggnqM91.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';
package/dist/http.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleSuipayMcpHttpRequest
3
- } from "./chunk-6ZSOXII5.js";
3
+ } from "./chunk-K4C64BXE.js";
4
4
  export {
5
5
  handleSuipayMcpHttpRequest
6
6
  };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Wallet } from '@cmdoss/suipay-core/chain/types';
2
- import { S as SpendAccountSigningRequest, M as McpConfig, a as McpAuthContext, b as McpTraceHooks, c as SpendAccountPayDeps } from './http-DggnqM91.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-DggnqM91.js';
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';
4
4
  import { GetObjectJson, createReader } from '@cmdoss/suipay-sdk/agent-sdk/reads';
5
5
  import { DelegatedKeySigner, DelegatedPayerSession, DelegatedPayerConfig, DelegatedPayerDeps, SpendIntent, DelegatedPayResult } from '@cmdoss/suipay-sdk/delegated-signer/payer';
6
6
  import { PaidHttpRequest } from '@cmdoss/suipay-sdk/protocol/paid-http-request';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadBootProfile,
3
3
  main
4
- } from "./chunk-KR6WRUDY.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-6ZSOXII5.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.7",
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": {
@@ -25,11 +25,6 @@
25
25
  "engines": {
26
26
  "node": ">=20"
27
27
  },
28
- "scripts": {
29
- "build": "tsup",
30
- "test": "vitest run",
31
- "typecheck": "tsc -p tsconfig.json --noEmit"
32
- },
33
28
  "dependencies": {
34
29
  "@modelcontextprotocol/sdk": "^1.29.0",
35
30
  "@mysten/sui": "2.24.0",
@@ -38,11 +33,14 @@
38
33
  "open": "^11.0.0"
39
34
  },
40
35
  "devDependencies": {
41
- "@cmdoss/suipay-core": "workspace:*",
42
- "@cmdoss/suipay-sdk": "workspace:*",
43
- "@types/node": "catalog:",
44
- "tsup": "catalog:",
45
- "typescript": "catalog:",
46
- "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"
47
45
  }
48
- }
46
+ }