@zktx.io/sui-mcp 0.6.0 → 0.6.1

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.
Files changed (103) hide show
  1. package/README.md +18 -18
  2. package/dist/adapters/adapterPromptSurfaces.js +0 -20
  3. package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
  4. package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
  5. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
  6. package/dist/adapters/intentPlanFactories.js +0 -6
  7. package/dist/core/action/contractNameRegistry.js +0 -53
  8. package/dist/core/action/forbiddenFields.js +0 -4
  9. package/dist/core/action/ptbVisualizationProducer.js +0 -3
  10. package/dist/core/action/receiptPtbGraph.js +0 -17
  11. package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
  12. package/dist/core/action/signableAdapterContract.js +0 -5
  13. package/dist/core/action/suiChainReceiptMapping.js +0 -18
  14. package/dist/core/action/suiChainReceiptReader.js +1 -21
  15. package/dist/core/action/suiChainReceiptVerifier.js +0 -10
  16. package/dist/core/action/transactionFunding.js +0 -7
  17. package/dist/core/action/types.js +0 -1
  18. package/dist/core/action/walletReviewContractAssembler.js +0 -2
  19. package/dist/core/activity/localDataService.js +0 -6
  20. package/dist/core/activity/sqliteActivityStore.js +0 -8
  21. package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
  22. package/dist/core/activity/sqliteActivityStoreSchema.js +0 -4
  23. package/dist/core/read/deepbookReadHelpers.js +0 -2
  24. package/dist/core/read/deepbookRegistry.js +0 -19
  25. package/dist/core/read/deepbookUsdcChartService.js +0 -2
  26. package/dist/core/read/readCardResult.js +0 -2
  27. package/dist/core/read/readService.js +0 -19
  28. package/dist/core/read/readServiceTypes.js +0 -2
  29. package/dist/core/session/cardSession.js +0 -4
  30. package/dist/core/session/cardSessionStore.js +1 -4
  31. package/dist/core/session/reviewValidity.js +0 -3
  32. package/dist/core/session/sessionRecordStore.js +0 -1
  33. package/dist/core/session/sessionStore.js +0 -15
  34. package/dist/core/session/settingsFileIdentity.js +0 -2
  35. package/dist/core/session/settingsFileProtocol.js +0 -5
  36. package/dist/core/session/settingsFileStore.js +1 -10
  37. package/dist/core/session/settingsInputError.js +0 -2
  38. package/dist/core/session/settingsOperation.js +0 -4
  39. package/dist/core/session/settingsPreparationSql.js +0 -2
  40. package/dist/core/session/settingsWorkflow.js +1 -37
  41. package/dist/core/session/sqliteCardStore.js +0 -2
  42. package/dist/core/session/sqliteSessionStore.js +0 -11
  43. package/dist/core/session/sqliteSettingsWorkflowStore.js +0 -10
  44. package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
  45. package/dist/core/session/sqliteWalletWorkflowStore.js +0 -29
  46. package/dist/core/session/status.js +0 -2
  47. package/dist/core/session/transactionMaterialStore.js +0 -4
  48. package/dist/core/session/transactionRequest.js +0 -4
  49. package/dist/core/session/walletConnection.js +0 -3
  50. package/dist/core/session/walletWorkflow.js +3 -21
  51. package/dist/core/session/workflowState.js +0 -8
  52. package/dist/core/session/workflowView.js +0 -6
  53. package/dist/core/suiAddress.js +0 -5
  54. package/dist/core/suiEndpoint.js +0 -3
  55. package/dist/mcp/prompts.js +0 -12
  56. package/dist/mcp/schemas.js +0 -3
  57. package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
  58. package/dist/mcp/tools/read/serverStatusTools.js +0 -1
  59. package/dist/mcp/tools/session/shared.js +0 -2
  60. package/dist/mcp/tools/session/statusTools.js +0 -2
  61. package/dist/mcp-app/account.html +1 -1
  62. package/dist/mcp-app/chart.html +6 -6
  63. package/dist/mcp-app/connect.html +6 -6
  64. package/dist/mcp-app/receipt.html +298 -298
  65. package/dist/mcp-app/review.html +237 -237
  66. package/dist/mcp-app/settings.html +1 -1
  67. package/dist/mcp-ui/tools.js +3 -4
  68. package/dist/review-server/server.js +0 -2
  69. package/dist/runtime/application.js +1 -1
  70. package/dist/runtime/config.js +0 -2
  71. package/dist/runtime/localSettingsService.js +0 -3
  72. package/dist/runtime/logger.js +0 -2
  73. package/dist/runtime/reviewServerAcquire.js +1 -18
  74. package/dist/runtime/runtimeTermination.js +1 -4
  75. package/dist/runtime/shared/authenticatedFetch.js +0 -6
  76. package/dist/runtime/shared/control.js +0 -2
  77. package/dist/runtime/shared/dataAccess.js +0 -2
  78. package/dist/runtime/shared/mcpHttp.js +0 -5
  79. package/dist/runtime/shared/ownerLease.js +0 -4
  80. package/dist/runtime/shared/server.js +0 -1
  81. package/dist/runtime/shared/stdio.js +2 -5
  82. package/dist/runtime/smokeMainnetRead.js +0 -2
  83. package/dist/runtime/smokeReview.js +0 -4
  84. package/dist/runtime/start.js +0 -2
  85. package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
  86. package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
  87. package/dist/runtime/walletConnectConfig.js +0 -2
  88. package/dist/runtime/walletConnectStorage.js +0 -6
  89. package/dist/runtime/walletConnectTransport.js +0 -11
  90. package/dist/runtime/walletSdkChildRuntime.js +0 -5
  91. package/dist/runtime/walletSdkProcess.js +0 -6
  92. package/docs/AGENT_BEHAVIOR.md +1 -5
  93. package/docs/LOCAL_DB_ARCHITECTURE.md +3 -3
  94. package/docs/MCP_SETUP.md +14 -14
  95. package/docs/MCP_TOOLS.md +15 -0
  96. package/docs/SDK_API.md +12 -0
  97. package/docs/WALLET_CONNECTION.md +2 -1
  98. package/package.json +10 -2
  99. package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -819
  100. package/docs/FRONTEND_POLICY.md +0 -611
  101. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
  102. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
  103. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
@@ -21,8 +21,9 @@ export function supportsCards(server) {
21
21
  const capabilities = server.server.getClientCapabilities();
22
22
  const { extensions, ...standard } = capabilities ?? {};
23
23
  const current = capabilities === undefined ? undefined : { ...standard, ...(extensions === undefined ? {} : { extensions }) };
24
- return getUiCapability(current)?.mimeTypes?.includes(RESOURCE_MIME_TYPE) === true ||
25
- server.server.getClientVersion()?.name === "codex-mcp-client";
24
+ const ui = getUiCapability(current);
25
+ return ui !== null && typeof ui === "object" && !Array.isArray(ui) && Array.isArray(ui.mimeTypes) &&
26
+ ui.mimeTypes.every((mimeType) => typeof mimeType === "string") && ui.mimeTypes.includes(RESOURCE_MIME_TYPE);
26
27
  }
27
28
  export function cardToolResult(response, savedLink = false) {
28
29
  const snapshot = response.snapshot;
@@ -141,8 +142,6 @@ export function registerReadCards(server, deps) {
141
142
  try {
142
143
  if (!deps.cards)
143
144
  throw new CardError("Card service is unavailable.");
144
- // Measure the actual response shape, including its text fallback and UI
145
- // metadata. Reserve maximum safe-integer digit widths for size fields.
146
145
  const base = okToolResult({ status: "read", fileId: input.fileId, offset: input.offset, rangeSize: Number.MAX_SAFE_INTEGER });
147
146
  const metadata = { ...(input.attemptId ? { attemptId: input.attemptId } : {}), cardId: input.cardId, operationId: input.operationId, fileId: input.fileId, offset: input.offset, totalSize: Number.MAX_SAFE_INTEGER,
148
147
  sha256: "0".repeat(64), chunkBase64: "" };
@@ -1,8 +1,6 @@
1
1
  import { validateHostOrigin } from "./middleware/hostOrigin.js";
2
2
  import { sendJson } from "../runtime/shared/http.js";
3
3
  import { ALLOWED_HOSTNAMES } from "./reviewServerPolicy.js";
4
- // The shared runtime serves authenticated private MCP/control routes itself.
5
- // There are no public browser pages, Settings APIs or static-asset fallbacks.
6
4
  export function createReviewRequestHandler(options) {
7
5
  return async (request, response) => {
8
6
  try {
@@ -184,7 +184,7 @@ export async function createRuntimeApplication(bootConfig, logger, ownerId = ran
184
184
  try {
185
185
  logger.error("Temporary settings file cleanup is incomplete", { stage: "settings_file_cleanup" });
186
186
  }
187
- catch { /* Stored cleanup facts remain authoritative. */ }
187
+ catch { }
188
188
  });
189
189
  return {
190
190
  handleMcp: (request, response) => access.run(() => mcp.handle(request, response)),
@@ -17,8 +17,6 @@ export function loadBootConfig(env = process.env) {
17
17
  activityDatabasePath: resolveActivityDatabasePath(env)
18
18
  };
19
19
  }
20
- // Existing shared-backend port: clients using the same data directory must
21
- // agree on this port and configuration. It is not browser-wallet authority.
22
20
  export const DEFAULT_REVIEW_PORT = 8765;
23
21
  function parseReviewPort(value) {
24
22
  if (value === undefined || value === "") {
@@ -57,8 +57,6 @@ export class RuntimeLocalSettingsService {
57
57
  suiGraphqlUrl: withBootEffectiveEndpoint(currentStoredGraphqlView, this.bootSuiGraphqlUrl)
58
58
  };
59
59
  }
60
- // The same side-effect-free target resolution serves display and final
61
- // verification. Default URLs belong to this service, never to the View.
62
60
  resolveEndpointChange(input) {
63
61
  const value = input.kind === "restore_endpoint"
64
62
  ? input.endpoint === "grpc" ? this.defaultSuiGrpcUrl : this.defaultSuiGraphqlUrl
@@ -66,7 +64,6 @@ export class RuntimeLocalSettingsService {
66
64
  return { key: input.endpoint === "grpc" ? "suiGrpcUrl" : "suiGraphqlUrl", value,
67
65
  status: input.kind === "restore_endpoint" ? "reset" : "saved" };
68
66
  }
69
- // Verification does not store a value or change this parent's effective URL.
70
67
  async prepareEndpointChange(input) {
71
68
  const target = this.resolveEndpointChange(input);
72
69
  await this.verifyEndpointForTool(target.value, input.endpoint);
@@ -3,8 +3,6 @@ export function flushStderr() {
3
3
  const failed = (error) => { process.stderr.off("error", failed); reject(error); };
4
4
  process.stderr.once("error", failed);
5
5
  process.stderr.write("", (error) => {
6
- // A failed stream write also emits error; keep its handler through that
7
- // event instead of allowing EPIPE to replace the intended exit code.
8
6
  if (error) {
9
7
  reject(error);
10
8
  return;
@@ -4,8 +4,6 @@ function errorCode(error) {
4
4
  ? error.code
5
5
  : undefined;
6
6
  }
7
- // Bind the port: returns the server on success, undefined when the port is already
8
- // in use, and rethrows any other (non-EADDRINUSE) startup error.
9
7
  async function tryStart(start, port) {
10
8
  try {
11
9
  return await start(port);
@@ -17,17 +15,6 @@ async function tryStart(start, port) {
17
15
  throw error;
18
16
  }
19
17
  }
20
- /**
21
- * Bind the review server to its fixed port, or defer to a healthy peer already
22
- * serving it.
23
- *
24
- * - Port free → bind and own the single review origin.
25
- * - Port held by a separate healthy instance of our review server → defer (run no
26
- * local server; the peer serves the shared database for every client) and watch for
27
- * the owner to exit, then take the origin over. No peer process is signalled.
28
- * - Port held by anything else (foreign, no identity answer, or our own pid) → clear
29
- * error; the origin is never silently reassigned.
30
- */
31
18
  export async function startOrDeferReviewServer(start, port, deps) {
32
19
  const owned = await tryStart(start, port);
33
20
  if (owned) {
@@ -58,14 +45,12 @@ export async function startOrDeferReviewServer(start, port, deps) {
58
45
  next = await tryStart(start, port);
59
46
  }
60
47
  catch (error) {
61
- // An authenticated prior owner may have closed its port while its SDK
62
- // still owns private storage. Reuse the existing takeover observation.
63
48
  if (typeof error === "object" && error !== null && "code" in error && error.code === "DATA_DIRECTORY_OWNED")
64
49
  continue;
65
50
  throw error;
66
51
  }
67
52
  if (!next) {
68
- continue; // a peer still owns the port; keep deferring
53
+ continue;
69
54
  }
70
55
  if (stopped) {
71
56
  await next.close();
@@ -82,8 +67,6 @@ export async function startOrDeferReviewServer(start, port, deps) {
82
67
  return {
83
68
  deferred: true,
84
69
  close: async () => {
85
- // Stop watching; the loop's stopped-check closes any bind that lands in-flight,
86
- // so we never await the (possibly mid-delay) watch loop here.
87
70
  stopped = true;
88
71
  if (acquired) {
89
72
  await acquired.close();
@@ -1,5 +1,4 @@
1
1
  import { SuiEndpointError } from "../core/suiEndpoint.js";
2
- // Keep arbitrary SDK/server exception text, endpoint values and stacks private.
3
2
  export function runtimeFailureReason(error, stage) {
4
3
  if (error instanceof SuiEndpointError)
5
4
  return `Sui mainnet endpoint verification failed (${error.kind}). Check the configured endpoint.`;
@@ -16,8 +15,6 @@ export function runtimeFailureReason(error, stage) {
16
15
  return "The private runtime control identity could not be loaded. Check data directory ownership and permissions.";
17
16
  return "The local runtime could not complete this stage. Check local data permissions and runtime configuration.";
18
17
  }
19
- // Bootstrap alone decides exit intent. Cleanup-induced onclose notifications
20
- // join the same promise; they cannot replace a fatal or signal exit with EOF.
21
18
  export class RuntimeTermination {
22
19
  options;
23
20
  closing;
@@ -34,7 +31,7 @@ export class RuntimeTermination {
34
31
  try {
35
32
  this.options.logger.error(message, { stage, reason: runtimeFailureReason(error, stage) });
36
33
  }
37
- catch { /* Preserve failure intent even if stderr is unavailable. */ }
34
+ catch { }
38
35
  };
39
36
  if (cause.kind === "failure")
40
37
  report("fatal runtime error", cause.stage, cause.error);
@@ -4,8 +4,6 @@ import { randomBytes } from "node:crypto";
4
4
  import { Readable } from "node:stream";
5
5
  import { MAX_JSON_BODY_BYTES } from "./http.js";
6
6
  import { IDENTITY_CHALLENGE_HEADER, IDENTITY_PATH, IDENTITY_TIMEOUT_MS, INTERNAL_MCP_PATH, SERVER_INSTANCE_HEADER, verifyServerIdentity } from "./control.js";
7
- // Proof and authenticated request use the same TCP socket. A closed socket is never
8
- // transparently replaced: no credential or UI permission can reach a new listener.
9
7
  async function authenticatedChannel(port, control, signal) {
10
8
  signal?.throwIfAborted();
11
9
  const socket = connect({ host: "127.0.0.1", port });
@@ -86,10 +84,6 @@ export function createAuthenticatedFetch(port, control, expectedInstanceId) {
86
84
  headers.set("host", url.host);
87
85
  headers.set("authorization", `Bearer ${control.key}`);
88
86
  headers.set(SERVER_INSTANCE_HEADER, channel.identity.instanceId);
89
- // An early rejection (for example the SDK's body-size guard) must reach
90
- // the caller while the request is still being written. Server-side
91
- // Connection: close can race that response with EPIPE. This channel is
92
- // still destroyed after the response and never reused or replaced.
93
87
  headers.set("connection", "keep-alive");
94
88
  const body = init?.body;
95
89
  if (body !== undefined && body !== null && typeof body !== "string") {
@@ -3,12 +3,10 @@ import { link, lstat, mkdir, readFile, realpath, unlink, writeFile } from "node:
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { z } from "zod";
5
5
  import { SERVER_VERSION } from "../../mcp/serverInfo.js";
6
- // Peers use the one package release identity, with no separate API counter.
7
6
  export const IDENTITY_PATH = "/__runtime/identity";
8
7
  export const INTERNAL_MCP_PATH = "/__runtime/mcp";
9
8
  export const IDENTITY_CHALLENGE_HEADER = "x-sui-mcp-identity-challenge";
10
9
  export const SERVER_INSTANCE_HEADER = "x-sui-mcp-server-instance";
11
- // The existing review-server identity probe allows one second for a local response.
12
10
  export const IDENTITY_TIMEOUT_MS = 1000;
13
11
  const secretSchema = z.string().regex(/^[A-Za-z0-9_-]{43}$/);
14
12
  export const serverIdentitySchema = z.object({
@@ -1,6 +1,4 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- // An asynchronous read may finish after import/reset. Its original generation
3
- // remains attached to the continuation and cannot read or write the replacement DB.
4
2
  export class RuntimeDataAccess {
5
3
  context = new AsyncLocalStorage();
6
4
  generation = 0;
@@ -9,8 +9,6 @@ export function createInternalMcpHandler(createServer) {
9
9
  const connections = new Set();
10
10
  return {
11
11
  async handle(request, response) {
12
- // This product has no unsolicited server requests. POST returns JSON, and
13
- // clients do not create a reconnecting SSE channel or replay requests.
14
12
  if (request.method === "GET") {
15
13
  response.setHeader("Allow", "POST, DELETE");
16
14
  sendJson(response, 405, { error: "method_not_allowed" });
@@ -49,9 +47,6 @@ export function createInternalMcpHandler(createServer) {
49
47
  close: () => transport.close()
50
48
  };
51
49
  transport.onmessage = (message, extra) => {
52
- // The SDK enforces its 4 MiB HTTP body limit before this callback.
53
- // Card calls additionally have a 64 KiB JSON-RPC envelope budget;
54
- // ordinary MCP calls do not inherit that smaller card limit.
55
50
  if (isJSONRPCRequest(message) && message.method === "tools/call" &&
56
51
  Object.values(CARD_TOOLS).some((name) => name === message.params?.name) &&
57
52
  Buffer.byteLength(JSON.stringify(message)) > MAX_JSON_BODY_BYTES) {
@@ -4,8 +4,6 @@ import { dirname, join } from "node:path";
4
4
  export class DataDirectoryOwnedError extends Error {
5
5
  code = "DATA_DIRECTORY_OWNED";
6
6
  }
7
- /** An OS-released ownership lock, separate from the product database. No
8
- * business transaction is held while waiting for a wallet or network reply. */
9
7
  export function acquireDataDirectoryOwner(databasePath) {
10
8
  const directory = dirname(databasePath);
11
9
  mkdirSync(directory, { recursive: true, mode: 0o700 });
@@ -31,8 +29,6 @@ export function acquireDataDirectoryOwner(databasePath) {
31
29
  process.umask(mask);
32
30
  }
33
31
  try {
34
- // A second owner is refused immediately, including one using another port.
35
- // SQLite retains the EXCLUSIVE lock after commit until this handle closes.
36
32
  lock.pragma("locking_mode=EXCLUSIVE");
37
33
  lock.exec("BEGIN EXCLUSIVE");
38
34
  lock.exec("CREATE TABLE IF NOT EXISTS owner_lock (singleton INTEGER PRIMARY KEY CHECK (singleton=1))");
@@ -64,7 +64,6 @@ export async function startSharedServer(input) {
64
64
  server.listen(port, "127.0.0.1");
65
65
  });
66
66
  port = server.address().port;
67
- // Only the process that owns the port opens SQLite or initializes services.
68
67
  application = Promise.resolve().then(() => input.createApplication(instanceId));
69
68
  const closeListener = async () => {
70
69
  if (!server.listening)
@@ -5,9 +5,6 @@ import { CallToolRequestSchema, CallToolResultSchema, CompleteRequestSchema, Get
5
5
  import { SERVER_INSTRUCTIONS, SERVER_NAME, SERVER_VERSION } from "../../mcp/serverInfo.js";
6
6
  import { createAuthenticatedFetch, probeAuthenticatedServer } from "./authenticatedFetch.js";
7
7
  import { INTERNAL_MCP_PATH } from "./control.js";
8
- // The SDK owns request IDs, initialization, cancellation and response parsing.
9
- // This proxy has no domain services or writable stores. A new request may open a
10
- // new MCP session after ownership changes; an already dispatched call is never replayed.
11
8
  export async function startSharedStdio(input) {
12
9
  const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, {
13
10
  capabilities: { tools: {}, resources: {}, prompts: {}, completions: {} }, instructions: SERVER_INSTRUCTIONS
@@ -56,7 +53,7 @@ export async function startSharedStdio(input) {
56
53
  close: async () => { try {
57
54
  await http.terminateSession();
58
55
  }
59
- catch { /* The prior owner may already be gone. */ } await http.close(); }
56
+ catch { } await http.close(); }
60
57
  };
61
58
  http.onmessage = (message) => { if (http.sessionId !== undefined)
62
59
  transport.sessionId = http.sessionId; transport.onmessage?.(message); };
@@ -105,7 +102,7 @@ export async function startSharedStdio(input) {
105
102
  try {
106
103
  await connecting;
107
104
  }
108
- catch { /* Initialization was cancelled with the stdio connection. */ }
105
+ catch { }
109
106
  await connection?.client.close();
110
107
  connection = undefined;
111
108
  }
@@ -125,8 +125,6 @@ async function main() {
125
125
  });
126
126
  const reviewDependencies = createRuntimeReviewDependencies({ client: suiClient, chainIdentifier,
127
127
  expectedChainIdentifier: config.expectedChainIdentifier, materialStore: transactionMaterialStore, readService });
128
- // Utility-only read context in this script's temporary DB. This does not
129
- // create a live wallet session or prove address ownership.
130
128
  await activityStore.setActiveAccount(smokeAddress, "wallet_connection", new Date(), { name: "Explicit smoke read fixture" });
131
129
  server = createMcpServer({
132
130
  sessions,
@@ -3,8 +3,6 @@ import { computeReviewStateWithPrivateArtifacts } from "../core/review/reviewCom
3
3
  export class SmokeReviewConfigError extends Error {
4
4
  }
5
5
  const requiredSwapInputs = ["SMOKE_SWAP_PROTOCOL", "SMOKE_SWAP_FROM_SYMBOL", "SMOKE_SWAP_TO_SYMBOL", "SMOKE_SWAP_AMOUNT_DISPLAY"];
6
- /** Optional read-only review computation. This never establishes a wallet
7
- * connection; the caller owns the explicit temporary read-account fixture. */
8
6
  export async function computeSmokeReview(options) {
9
7
  const { env, sessions, computation, account } = options;
10
8
  const rawSlippage = env.SMOKE_SWAP_MAX_SLIPPAGE_BPS;
@@ -16,8 +14,6 @@ export async function computeSmokeReview(options) {
16
14
  return { status: "not_run", notRunReason: "missing_env", requiredEnv: [...requiredSwapInputs] };
17
15
  if (missing.length)
18
16
  throw new SmokeReviewConfigError(`Partial account-bound swap smoke configuration; missing: ${missing.join(", ")}`);
19
- // Preserve the utility's existing 50 bps CLI default; it is not a product
20
- // recommendation or a silently selected protocol/settlement asset.
21
17
  const parsed = swapIntentInputSchema.safeParse({ type: "swap", protocol: env.SMOKE_SWAP_PROTOCOL,
22
18
  from: { symbol: env.SMOKE_SWAP_FROM_SYMBOL, amount: env.SMOKE_SWAP_AMOUNT_DISPLAY },
23
19
  to: { symbol: env.SMOKE_SWAP_TO_SYMBOL }, maxSlippageBps: Number(env.SMOKE_SWAP_MAX_SLIPPAGE_BPS ?? "50") });
@@ -43,8 +43,6 @@ async function main() {
43
43
  grpcOverride: process.env.SUI_GRPC_URL ?? null, graphqlOverride: process.env.SUI_GRAPHQL_URL ?? null
44
44
  });
45
45
  void controlReady.catch(() => { });
46
- // The SDK owns stdin from the start, including fragmented initialization
47
- // and EOF while backend startup is pending. No second reader buffers bytes.
48
46
  stage = "stdio_start";
49
47
  bridge = await startSharedStdio({ stdio, port: bootConfig.reviewPort, control: controlReady, ready: backendReady,
50
48
  onError: (error) => logger.error("MCP transport failed", { error: error.message }) });
@@ -187,8 +187,6 @@ function executionStatusFromGraphql(value) {
187
187
  return "success";
188
188
  if (value === "FAILURE")
189
189
  return "failure";
190
- // Null status can occur before checkpointed effects are available; unmapped
191
- // provider enum values stay unknown until explicitly mapped.
192
190
  return "unknown";
193
191
  }
194
192
  function normalizeGraphqlTimestamp(value) {
@@ -211,9 +211,6 @@ async function queryGraphql(client, options) {
211
211
  const result = await client.query({ query: options.query, variables: options.variables ?? {} });
212
212
  if (result.errors && result.errors.length > 0) {
213
213
  const message = result.errors.map((error) => error.message).join("; ");
214
- // The pinned SDK exposes provider GraphQL errors as messages here. Cursor
215
- // rejection is intentionally detected conservatively and otherwise falls
216
- // back to provider_error.
217
214
  throw new TransactionActivitySourceError(message.toLowerCase().includes("cursor") ? "cursor_invalid" : "provider_error", "Sui GraphQL query returned errors", { message });
218
215
  }
219
216
  if (!result.data) {
@@ -1,3 +1 @@
1
- // Product-owned public dApp identifier. All installations share this Relay project.
2
- // This is not a wallet credential and has no user configuration override.
3
1
  export const WALLETCONNECT_PROJECT_ID = "62c5f7d7917d3936258d4b0746c99cab";
@@ -2,9 +2,6 @@ import { existsSync, lstatSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { serialize, deserialize } from "node:v8";
4
4
  import Database from "better-sqlite3";
5
- // Restoration namespaces of pinned SignClient/Core 2.23.10. Request history,
6
- // pending requests, messages, unacknowledged envelopes and new namespaces are
7
- // intentionally volatile; they may contain transaction or signature material.
8
5
  const persistentKeys = new Set([
9
6
  "wc@2:core:0.3//keychain", "wc@2:core:0.3//pairing",
10
7
  "wc@2:core:0.3//subscription", "wc@2:core:0.3//expirer",
@@ -60,8 +57,6 @@ export function openWalletConnectStorage(dataDirectory) {
60
57
  db.exec(storageSchema);
61
58
  })();
62
59
  }
63
- // There is exactly one SDK owner. FULL protects the session material that
64
- // the SDK needs to resume an acknowledged connection after process exit.
65
60
  db.pragma("journal_mode = DELETE");
66
61
  db.pragma("synchronous = FULL");
67
62
  }
@@ -92,6 +87,5 @@ export function openWalletConnectStorage(dataDirectory) {
92
87
  volatile.delete(key);
93
88
  }
94
89
  };
95
- // Once injected, SDK timers own the storage until the owner process exits.
96
90
  return { storage, closeBeforeSdkUse: () => { volatile.clear(); db.close(); } };
97
91
  }
@@ -27,8 +27,6 @@ export async function createWalletConnectTransport(options) {
27
27
  if (!/^[0-9a-f]{32}$/i.test(options.projectId))
28
28
  throw new Error("WalletConnect project ID format is invalid.");
29
29
  const owner = openWalletConnectStorage(options.dataDirectory);
30
- // SDK log bodies can contain pairing credentials and serialized requests.
31
- // Product failure stages are recorded by the caller without these values.
32
30
  options.onSdkStart?.();
33
31
  const client = await (options.initialize ?? SignClient.init.bind(SignClient))({ projectId: options.projectId, metadata: { ...options.metadata, icons: [] },
34
32
  storage: owner.storage, logger: "silent", telemetryEnabled: false });
@@ -37,8 +35,6 @@ export async function createWalletConnectTransport(options) {
37
35
  const changed = (event) => { if (!stopped)
38
36
  for (const listener of listeners)
39
37
  listener(event.topic); };
40
- // Pinned SignClient emits session_event without changing session.namespaces.
41
- // Treat active account/chain changes as invalidation, not new authorization.
42
38
  const selectionChanged = (event) => {
43
39
  if (!stopped && ["accountsChanged", "chainChanged"].includes(event.params.event.name)) {
44
40
  for (const listener of listeners)
@@ -60,7 +56,6 @@ export async function createWalletConnectTransport(options) {
60
56
  const inspect = (topic) => {
61
57
  if (stopped)
62
58
  throw new Error("WalletConnect owner stopped.");
63
- // A store failure is not evidence that the session disappeared.
64
59
  const session = client.session.getAll().find((item) => item.topic === topic);
65
60
  return session ? inspectValue(session) : { topic, status: "absent" };
66
61
  };
@@ -75,8 +70,6 @@ export async function createWalletConnectTransport(options) {
75
70
  return {
76
71
  async restore() {
77
72
  requireActive();
78
- // A pending proposal is not an approved session and must not reappear as
79
- // a new approval ceremony after owner replacement.
80
73
  for (const pairing of client.core.pairing.getPairings()) {
81
74
  if (!pairing.active)
82
75
  await client.core.pairing.disconnect({ topic: pairing.topic });
@@ -89,8 +82,6 @@ export async function createWalletConnectTransport(options) {
89
82
  chains: [SUI_MAINNET_WALLET_CHAIN], methods: [SUI_SIGN_TRANSACTION_METHOD], events: ["accountsChanged", "chainChanged"]
90
83
  } } });
91
84
  const approval = connecting.approval().then(sessionValue).catch((error) => { throw transportError(error); });
92
- // Attach a rejection handler immediately; the backend records the same
93
- // promise's outcome once the private QR has been associated with its row.
94
85
  void approval.catch(() => undefined);
95
86
  if (!connecting.uri)
96
87
  throw new Error("The wallet pairing could not be prepared.");
@@ -121,8 +112,6 @@ export async function createWalletConnectTransport(options) {
121
112
  client.off("session_delete", changed);
122
113
  client.off("session_expire", changed);
123
114
  client.off("session_event", selectionChanged);
124
- // SignClient has no supported complete in-process disposal. The runtime
125
- // ends the owner process; it must not close injected storage under timers.
126
115
  }
127
116
  };
128
117
  }
@@ -4,8 +4,6 @@ import { isDeepStrictEqual } from "node:util";
4
4
  import { WalletUserRejectedError } from "../core/session/walletConnection.js";
5
5
  import { acquireDataDirectoryOwner } from "./shared/ownerLease.js";
6
6
  import { walletCommandSchema, walletEventSchema } from "./walletSdkIpc.js";
7
- // Production and the real-SDK wire fixture run this same process lifecycle.
8
- // The factory is a code dependency, never a serialized command or setting.
9
7
  export function runWalletSdkChild(createTransport) {
10
8
  let init, transport;
11
9
  let sequence = 0, lastRequest = 0, ready = false, exiting = false;
@@ -30,7 +28,6 @@ export function runWalletSdkChild(createTransport) {
30
28
  ready = false;
31
29
  if (init && !exiting)
32
30
  send({ ...envelope(), type: "failure", reason });
33
- // Neither SDK cleanup nor a callback is a prerequisite for process exit.
34
31
  if (!exiting) {
35
32
  exiting = true;
36
33
  process.exit(1);
@@ -75,7 +72,6 @@ export function runWalletSdkChild(createTransport) {
75
72
  };
76
73
  const start = async (input) => {
77
74
  init = input;
78
- // The child alone opens the SDK store; OS exit releases this separate lock.
79
75
  ownership = acquireDataDirectoryOwner(join(input.dataDirectory, "walletconnect", "sessions.sqlite"));
80
76
  send({ ...envelope(), type: "stage", stage: "sdk_start" });
81
77
  transport = await createTransport(input);
@@ -154,7 +150,6 @@ export function runWalletSdkChild(createTransport) {
154
150
  }
155
151
  if (!init || input.runId !== init.runId || input.requestId <= lastRequest)
156
152
  return;
157
- // Reserve before awaiting anything, including a connect approval.
158
153
  lastRequest = input.requestId;
159
154
  void command(input).catch(() => fail("wallet_state_unavailable"));
160
155
  });
@@ -5,8 +5,6 @@ import { isDeepStrictEqual } from "node:util";
5
5
  import { walletUnavailable, WalletUnavailableError, WalletUserRejectedError } from "../core/session/walletConnection.js";
6
6
  import { WalletRunInterruptedError, WalletReplacementFailedError } from "../core/session/walletRuntime.js";
7
7
  import { walletCommandSchema, walletEventSchema } from "./walletSdkIpc.js";
8
- /** Owns one SDK process. Business authority and all database transitions remain
9
- * in the workflow; a process event is never transaction approval. */
10
8
  export class WalletSdkProcess {
11
9
  options;
12
10
  run = this.newRun();
@@ -75,7 +73,6 @@ export class WalletSdkProcess {
75
73
  child.once("exit", () => this.exited(run));
76
74
  child.once("error", () => {
77
75
  this.lose(run, this.failureReason());
78
- // Failed spawn has no process and therefore cannot emit exit.
79
76
  if (child.pid === undefined)
80
77
  this.exited(run);
81
78
  });
@@ -93,7 +90,6 @@ export class WalletSdkProcess {
93
90
  send(run, message) {
94
91
  if (run !== this.run || run.fenced || this.closed || !run.child?.connected)
95
92
  throw new WalletRunInterruptedError(run.id);
96
- // A false return means backpressure, not permission to repeat the command.
97
93
  run.child.send(walletCommandSchema.parse(message), (error) => { if (error)
98
94
  this.lose(run, "wallet_state_unavailable"); });
99
95
  }
@@ -271,8 +267,6 @@ export class WalletSdkProcess {
271
267
  if (this.closed || this.run !== old || this.replacement !== replacement)
272
268
  throw new WalletRunInterruptedError(runId);
273
269
  const next = this.newRun();
274
- // A failed reservation ends this attempt. Reads may save its failure,
275
- // but only a new explicit admission may start another SDK process.
276
270
  beforeStart(next.id);
277
271
  this.run = next;
278
272
  this.state = this.starting(next.id, "process_start");
@@ -4,7 +4,7 @@ This document is the MCP-exposed answer playbook for AI clients. It owns user-qu
4
4
 
5
5
  It does not define tool schemas or field contracts. Use `docs/MCP_TOOLS.md` for the MCP API reference, response fields, statuses, and follow-up fields.
6
6
 
7
- It is not the contributor rulebook and it is not enforcement. Development rules live in `AGENTS.md` and `docs/AGENT_DEVELOPMENT_POLICY.md`. Hard product boundaries live in code, schemas, allowlists, mainnet guards, and the local review layer.
7
+ It is not the contributor rulebook and it is not enforcement. Development rules live in `AGENTS.md` and `docs/AGENT_DEVELOPMENT_POLICY.md` in the [source repository](https://github.com/zktx-mcp/sui-mcp). They are not installed MCP resources. Hard product boundaries live in code, schemas, allowlists, mainnet guards, and the local review layer.
8
8
 
9
9
  ## Support Matrix
10
10
 
@@ -552,7 +552,3 @@ Avoid unqualified words such as best, recommended, guaranteed, or safe. Prefer:
552
552
  ## Language
553
553
 
554
554
  This reference document stays in English. Reply in the user's language. Translate response patterns as needed, but keep SDK names, tool names, object IDs, package IDs, and token symbols in their original form.
555
-
556
- ## Golden Scenarios
557
-
558
- Detailed expected response classes live in `docs/golden-scenarios/BEHAVIOR_MATRIX.md`. They are documentation for release review, not automated cross-client results.
@@ -178,7 +178,7 @@ target must name the recorded file and pass size/SHA checks before `saved` is
178
178
  committed. Existing destinations are never overwritten or removed. Private
179
179
  unlink follows that result and actual descriptor closure; the final file has
180
180
  no automatic expiry. Historical export path validation remains distinct from
181
- the private `.download` row identity. No table, column or schema version changes.
181
+ the private `.download` row identity.
182
182
 
183
183
  Unknown file effects remain `saving`, retaining their source and reservations.
184
184
  A failed DB result write retains the ended physical outcome for settlement.
@@ -476,7 +476,7 @@ The runtime accepts one current SQLite structure, checked against its table, col
476
476
 
477
477
  A current backup carries `format`, `network`, `exportedAt` and its product data. It has no numeric schema version. Import rejects a different format, unknown or missing fields, invalid references or invalid raw quantities. Missing settings or activity arrays are not filled from defaults. Supported `function_scan` provenance remains part of the current format. Validation failure leaves current data intact.
478
478
 
479
- New installations do not inherit older local review records, activity scans, known/active accounts or stored endpoints. Users configure the required context again; environment overrides retain their existing precedence. Empty local history does not imply an absence of on-chain transactions. Existing DB and backup files are not deleted by startup.
479
+ With a new empty data directory, users configure account context and endpoints; environment overrides retain their existing precedence. Empty local history does not imply an absence of on-chain transactions. Existing DB and backup files are not deleted by startup.
480
480
 
481
481
  ## Boundaries
482
482
 
@@ -496,7 +496,7 @@ Ownership and simulation private artifacts use their v2 evidence formats to
496
496
  bind transaction funding to the stored material. They cover real coin objects,
497
497
  address withdrawals, gas reservations, and their observed balance/epoch facts.
498
498
  They are deeply copied and revalidated before admission, and are excluded from
499
- public backup. They introduce no table, column, or DB format migration.
499
+ public backup.
500
500
 
501
501
  Old private evidence is not upgraded into funding proof. Existing owner recovery
502
502
  removes private material; a new review recomputes it. Historical public reviews,
package/docs/MCP_SETUP.md CHANGED
@@ -6,7 +6,7 @@ It does not define tool field contracts or response wording. Use `docs/MCP_TOOLS
6
6
 
7
7
  The README keeps only the short entry path; client-specific setup, restart behavior, and troubleshooting live here.
8
8
 
9
- These instructions target `@zktx.io/sui-mcp@0.6.0`. Package installation requires
9
+ These instructions target `@zktx.io/sui-mcp@0.6.1`. Package installation requires
10
10
  that exact version to be published; use the developer checkout instructions when
11
11
  it is not available. A repository rename or local build does not publish the
12
12
  package. Changing client settings or rebuilding files does not replace an
@@ -130,7 +130,7 @@ per-client configuration below, which give the exact `command` and `args`.
130
130
 
131
131
  The npm package for this release is `@zktx.io/sui-mcp`. The commands below
132
132
  require the requested version to be published; confirm it with
133
- `npm view @zktx.io/sui-mcp@0.6.0 version` before switching. Both installation
133
+ `npm view @zktx.io/sui-mcp@0.6.1 version` before switching. Both installation
134
134
  methods start the same `sui-mcp` stdio MCP server. Use a local checkout when
135
135
  working on a version that has not been published.
136
136
 
@@ -193,20 +193,20 @@ command, for example `"command": "cmd", "args": ["/c", "npx", "-y", "@zktx.io/su
193
193
  ### Updating an Existing Sui MCP Installation
194
194
 
195
195
  Confirm that the requested package version is available before changing every
196
- participating client's configuration. This release requires a new empty data
197
- directory when replacing a 0.5.x installation. Preserve the previous data,
198
- backups and private WalletConnect store; they are not converted or imported.
199
- Configure endpoints and read context as needed and connect through a new card.
200
- Use the same new data directory and review port in every participating client.
201
- Fully quit the participating apps, update
196
+ participating client's configuration. A compatible update can retain the current
197
+ data directory, backups and private WalletConnect store. Use the same directory
198
+ and review port in every participating client. Stop the participating MCP
199
+ connections and wait for the shared backend to exit, update
202
200
  the installed package or configured version, then reopen them and check
203
201
  `read.get_server_status` in each client. The package name, version and server
204
202
  name must match the intended installation. Updating the package does not
205
203
  replace a backend that is already running.
206
204
 
207
- A compatible update that retains the current database structure can use existing
208
- data. Incompatible databases and backups are rejected without migration;
209
- see [Local Database Architecture](LOCAL_DB_ARCHITECTURE.md#current-format-and-backups).
205
+ Incompatible databases and backups are rejected without migration. To use a
206
+ different database structure, choose a new empty directory and preserve the
207
+ existing data and backups. Configure endpoints and read context as needed and
208
+ connect through a new card. See
209
+ [Local Database Architecture](LOCAL_DB_ARCHITECTURE.md#current-format-and-backups).
210
210
  Do not reset data or resend a transaction as an update step. Coordinate a safe
211
211
  restart if a transaction request is still in progress.
212
212
 
@@ -229,7 +229,7 @@ restart first. A previous wallet pairing can remain in the wallet app; remove
229
229
  an unwanted pairing there rather than assuming new local storage disconnected it.
230
230
 
231
231
  Replace the old registration instead of adding a second one. Use exactly
232
- `sui-mcp` for the registration name and select `@zktx.io/sui-mcp@0.6.0` in its
232
+ `sui-mcp` for the registration name and select `@zktx.io/sui-mcp@0.6.1` in its
233
233
  command arguments. Do not append an environment label, version or numeric
234
234
  suffix. Remove obsolete runtime environment overrides. If you set
235
235
  `SUI_MCP_DATA_DIR`, choose a new empty directory; never point it at another
@@ -534,7 +534,7 @@ A card accepts one read selection. Moving between chats or recreating its frame
534
534
 
535
535
  ## Local data format
536
536
 
537
- Start this runtime with an empty `SUI_MCP_DATA_DIR` or a database already in its current format. A mismatched existing database is refused before writes. There is no older DB or backup migration. A checkout with different table, constraint or field definitions needs a new empty data folder, preserving the previous folder. Databases and backups do not carry a separate numeric schema version. Older local records, known/active accounts and stored endpoints are not inherited; set the needed account context and endpoints again. Existing files are left in place, and environment overrides keep their existing precedence. See [Local DB Architecture](LOCAL_DB_ARCHITECTURE.md) for the current format, backup scope and card-result retention. Card results do not expire with the input period and can increase DB size. Reset/import removes them along with the affected local data; it is not a card-only space cleanup operation.
537
+ Start this runtime with an empty `SUI_MCP_DATA_DIR` or a database already in its current format. A mismatched existing database is refused before writes; incompatible databases and backups are not migrated or converted. A database with different table, constraint or field definitions requires a new empty data folder, preserving the existing folder. Databases and backups do not carry a separate numeric schema version. With a new empty folder, configure the required account context and endpoints. Existing files are left in place, and environment overrides keep their existing precedence. See [Local DB Architecture](LOCAL_DB_ARCHITECTURE.md) for the current format, backup scope and card-result retention. Card results do not expire with the input period and can increase DB size. Reset/import removes them along with the affected local data; it is not a card-only space cleanup operation.
538
538
 
539
539
  ## Local Settings
540
540
 
@@ -776,7 +776,7 @@ If the user supplied a specific Sui address for `read.summarize_wallet_assets` o
776
776
 
777
777
  A 404 means the requested package or version could not be retrieved from the
778
778
  selected registry. Confirm the exact name and version, for example with
779
- `npm view @zktx.io/sui-mcp@0.6.0 version --registry=https://registry.npmjs.org`.
779
+ `npm view @zktx.io/sui-mcp@0.6.1 version --registry=https://registry.npmjs.org`.
780
780
  A new version may not have been published or become visible yet; a mistyped name
781
781
  or an alternate/private registry can also explain the response. Check the
782
782
  publication result and registry configuration before retrying. Do not assume