@zktx.io/sui-mcp 0.5.0 → 0.6.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.
Files changed (87) hide show
  1. package/README.md +6 -6
  2. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +3 -3
  3. package/dist/core/activity/localDataService.js +71 -52
  4. package/dist/core/activity/localDataValidation.js +1 -3
  5. package/dist/core/activity/sqliteActivityStore.js +24 -16
  6. package/dist/core/activity/sqliteActivityStoreSchema.js +83 -12
  7. package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
  8. package/dist/core/session/cardSession.js +1 -1
  9. package/dist/core/session/cardSessionStore.js +53 -3
  10. package/dist/core/session/sessionStore.js +0 -17
  11. package/dist/core/session/settingsFileIdentity.js +11 -0
  12. package/dist/core/session/settingsFileProtocol.js +25 -0
  13. package/dist/core/session/settingsFileStore.js +346 -0
  14. package/dist/core/session/settingsInputError.js +34 -0
  15. package/dist/core/session/settingsOperation.js +224 -0
  16. package/dist/core/session/settingsPreparationSql.js +15 -0
  17. package/dist/core/session/settingsWorkflow.js +1219 -0
  18. package/dist/core/session/sqliteSessionStore.js +0 -40
  19. package/dist/core/session/sqliteSettingsWorkflowStore.js +463 -0
  20. package/dist/core/session/sqliteWalletWorkflowStore.js +164 -41
  21. package/dist/core/session/status.js +2 -2
  22. package/dist/core/session/walletConnection.js +27 -8
  23. package/dist/core/session/walletWorkflow.js +73 -15
  24. package/dist/core/session/workflowState.js +16 -8
  25. package/dist/core/session/workflowView.js +18 -2
  26. package/dist/mcp/responseGuidance.js +10 -5
  27. package/dist/mcp/serverInfo.js +3 -4
  28. package/dist/mcp/toolNames.js +2 -2
  29. package/dist/mcp/tools/account/index.js +1 -19
  30. package/dist/mcp/tools/read/readToolHelpers.js +5 -4
  31. package/dist/mcp/tools/session/executionResultTools.js +2 -2
  32. package/dist/mcp/tools/session/shared.js +1 -0
  33. package/dist/mcp/tools/session/statusTools.js +4 -3
  34. package/dist/mcp/tools/settings/index.js +8 -28
  35. package/dist/mcp-app/account.html +14 -14
  36. package/dist/mcp-app/account.notices.txt +1 -1
  37. package/dist/mcp-app/chart.html +9 -9
  38. package/dist/mcp-app/chart.notices.txt +1 -1
  39. package/dist/mcp-app/connect.html +14 -14
  40. package/dist/mcp-app/connect.notices.txt +1 -1
  41. package/dist/mcp-app/receipt.html +1298 -1463
  42. package/dist/mcp-app/receipt.notices.txt +226 -1204
  43. package/dist/mcp-app/review.html +1298 -1463
  44. package/dist/mcp-app/review.notices.txt +226 -1204
  45. package/dist/mcp-app/settings.html +3133 -0
  46. package/dist/mcp-app/settings.notices.txt +973 -0
  47. package/dist/mcp-ui/contracts.js +8 -2
  48. package/dist/mcp-ui/readCards.js +1 -1
  49. package/dist/mcp-ui/tools.js +33 -2
  50. package/dist/review-server/server.js +5 -51
  51. package/dist/runtime/application.js +41 -14
  52. package/dist/runtime/localSettingsService.js +26 -2
  53. package/dist/runtime/shared/authenticatedFetch.js +6 -2
  54. package/dist/runtime/shared/control.js +5 -6
  55. package/dist/runtime/shared/http.js +14 -0
  56. package/dist/runtime/shared/mcpHttp.js +9 -4
  57. package/dist/runtime/shared/server.js +1 -1
  58. package/dist/runtime/smokeMainnetRead.js +0 -1
  59. package/dist/runtime/walletConnectStorage.js +2 -4
  60. package/dist/runtime/walletSdkChildRuntime.js +2 -1
  61. package/dist/runtime/walletSdkIpc.js +2 -1
  62. package/dist/runtime/walletSdkProcess.js +3 -2
  63. package/docs/AGENT_BEHAVIOR.md +44 -10
  64. package/docs/AGENT_DEVELOPMENT_POLICY.md +16 -12
  65. package/docs/FRONTEND_POLICY.md +105 -28
  66. package/docs/LOCAL_DB_ARCHITECTURE.md +143 -20
  67. package/docs/MCP_SETUP.md +99 -39
  68. package/docs/MCP_TOOLS.md +273 -24
  69. package/docs/SDK_API.md +51 -10
  70. package/docs/UTILITY_INDEX.md +9 -6
  71. package/docs/WALLET_CONNECTION.md +30 -9
  72. package/package.json +13 -7
  73. package/dist/core/activity/schemaVersion.js +0 -2
  74. package/dist/core/session/keyedRecordStore.js +0 -18
  75. package/dist/core/session/settingsSession.js +0 -1
  76. package/dist/core/session/settingsSessions.js +0 -45
  77. package/dist/review-app/brand-dark.svg +0 -1
  78. package/dist/review-app/brand-light.svg +0 -1
  79. package/dist/review-app/favicon.svg +0 -1
  80. package/dist/review-app/settings.css +0 -1
  81. package/dist/review-app/settings.js +0 -1
  82. package/dist/review-app/ui.css +0 -809
  83. package/dist/review-server/assets.js +0 -48
  84. package/dist/review-server/html.js +0 -45
  85. package/dist/review-server/http.js +0 -47
  86. package/dist/review-server/middleware/reviewToken.js +0 -7
  87. package/dist/review-server/settingsApi.js +0 -158
package/docs/SDK_API.md CHANGED
@@ -1,23 +1,62 @@
1
1
  # SDK API Verification
2
2
 
3
- This document records the pinned SDK APIs used by the current runtime. The source of truth is the installed package source in `node_modules`, not memory or external summaries.
3
+ This document records the pinned SDK APIs used by the current runtime. Backend
4
+ API facts come from the installed SDK source in `node_modules`. Browser
5
+ libraries are embedded during the build; their packaged versions are recorded
6
+ in the card dependency notices described below.
4
7
 
5
8
  ## Package Versions
6
9
 
7
10
  - `@modelcontextprotocol/ext-apps`: `1.7.5`
8
- - `@modelcontextprotocol/sdk`: `1.29.0`
11
+ - `@modelcontextprotocol/sdk`: `1.31.0`
9
12
  - `@mysten/sui`: `2.17.0`
10
13
  - `@mysten/deepbook-v3`: `1.3.6`
11
14
  - `@walletconnect/sign-client`, `@walletconnect/types`, `@walletconnect/utils`: `2.23.10`
12
15
  - `qrcode`: `1.5.4`
13
16
  - `@zktx.io/ptb-model`: `0.5.0`
14
- - `mermaid`: `11.12.0`
17
+ - `mermaid`: `11.16.1`
18
+ - `katex`: `0.18.2`, pinned by an override scoped to `mermaid@11.16.1`
19
+
20
+ Mermaid and its KaTeX dependency are build dependencies for the embedded card
21
+ resources, not Node server runtime dependencies. Their versions are fixed by
22
+ the source-checkout lockfile and included in the generated HTML. Installed npm
23
+ dependencies do not apply a dependency package's own overrides.
24
+
25
+ ## Bundled Transaction Diagrams
26
+
27
+ The backend uses `@zktx.io/ptb-model` to generate a flowchart from verified
28
+ review material or a transaction read from Sui. The shared browser renderer
29
+ uses Mermaid to display that flowchart in the Review and Receipt cards.
30
+ The diagram is display evidence; it does not authorize signing or execution.
31
+
32
+ `scripts/build-mcp-ui.ts` embeds each card's script and styles in one HTML
33
+ resource and records the modules included in that resource. Check
34
+ `dist/mcp-app/review.notices.txt` and `receipt.notices.txt`, or the dependency
35
+ notices embedded in their HTML, for the actual browser-library versions.
36
+ The dependency versions in a new source-checkout build come from
37
+ `package-lock.json`. Reinstalling a published package's runtime dependencies
38
+ does not replace the libraries already embedded in its card HTML. A browser
39
+ dependency update requires rebuilding and distributing the card resources.
15
40
 
16
41
  ## MCP Apps
17
42
 
18
- `@modelcontextprotocol/ext-apps` is pinned to `1.7.5`, with MCP SDK `1.29.0`. The server uses `registerAppTool`, `registerAppResource` and `getUiCapability`; the view uses `App` for host-mediated calls and teardown. Account, Receipt, Chart, Connect and Review are separate self-contained resources, so an account card does not load the PTB or chart renderer. The server keeps app-only permissions out of model content.
43
+ `@modelcontextprotocol/ext-apps` is pinned to `1.7.5`, with MCP SDK `1.31.0`. The server uses `registerAppTool`, `registerAppResource` and `getUiCapability`; the view uses `App` for host-mediated calls and teardown. Account, Receipt, Chart, Connect, Review and Settings are separate self-contained resources, so an account card does not load the PTB or chart renderer. Settings uses scoped app-only inputs for local actions and file transfer. The server keeps app-only permissions out of model content.
19
44
 
20
- The shared server uses the pinned MCP SDK Streamable HTTP transport behind authenticated loopback access. Stdio clients forward tool, resource and prompt requests with their original client identity and capabilities. Ordinary MCP input schemas remain unchanged by the transport; the 64 KiB card/HTTP input limit includes the card call envelope.
45
+ Settings completes a selected upload's verification and restore preparation on
46
+ the backend. The card renders the stored `restoreCheck` result and collects one
47
+ final restore confirmation; it does not parse backup contents, decide backend
48
+ eligibility or apply data automatically. The complete file, preparation and
49
+ final input remain bound to their card/operation identity and current revision.
50
+ The backend distinguishes unsealed receiving, active checking, a sealed file
51
+ needing a new check, a current confirmation and actual rejection. Definite
52
+ file/mainnet rejection is independent of local-data confirmation revision and
53
+ survives Back until explicit file selection changes. The View's current file-job
54
+ progress describes communication only; it cannot grant restoration authority.
55
+
56
+ Recorded backup locations use backend-native folder and file-name fields;
57
+ optional `App.downloadFile` delivery remains separate from local publication.
58
+
59
+ The shared server uses the pinned MCP SDK Streamable HTTP transport behind authenticated loopback access. Stdio clients forward tool, resource and prompt requests with their original client identity and capabilities. SDK 1.31.0 limits each internal HTTP request body to 4 MiB, including its complete MCP JSON-RPC envelope and metadata. Oversized bodies are refused before tool or card dispatch. Card calls additionally have a 64 KiB envelope budget; ordinary MCP calls do not inherit that smaller limit. Restore uploads retain their 16 MiB file limit and are split into messages within the card budget. The SDK's internal HTTP batch guard does not expose a stdio batch feature.
21
60
 
22
61
  ## WalletConnect Runtime
23
62
 
@@ -167,13 +206,15 @@ facts; its absence/timeout does not prove execution failure.
167
206
  SDK session storage persists only the pinned connection namespaces. History,
168
207
  requests and unknown queue keys are volatile. The SDK has no supported complete
169
208
  in-process disposal; its owner process lifetime bounds callbacks and storage.
170
- The parent uses a fixed packaged Node child entrypoint and private versioned IPC.
209
+ The parent uses a fixed packaged Node child entrypoint and private package-bound IPC.
171
210
  Only the SDK child owns SDK storage. The supervisor fences a terminating run,
172
211
  sends SIGKILL to its owned child and waits for actual exit. Its explicit
173
- replacement primitive remains private infrastructure; Connect and Settings do
174
- not expose a restart command. Closing relay transport or receiving an IPC disconnection is not
175
- evidence that the SDK operation ended. No SDK private teardown, source patch,
176
- automatic financial retry or overlapping storage writer is used.
212
+ replacement primitive is private infrastructure. Settings/Troubleshooting
213
+ owns explicit run/impact preparation, atomic input revocation/admission,
214
+ old-child exit, next-run reservation and ready/failure recording. Connect does
215
+ not own restart. A new explicit Settings operation can supersede a startup
216
+ still pending; known failure and its first timestamp remain immutable.
217
+ Late callbacks cannot reopen old authority or submit another transaction.
177
218
 
178
219
  Snapshots and per-session versions distinguish present, absent and unusable
179
220
  sessions. Source inspection failure disables the service instead of becoming an
@@ -43,7 +43,7 @@ Source-checkout scripts are not packaged product commands, MCP tools, review-tim
43
43
  | Live Sui sent-function activity summary | `read.summarize_sui_function_activity_scan` | Implemented | Summarizes bounded sent-function rows. Not P&L, route recommendation, or signing readiness. |
44
44
  | Stored Sui activity summary | `read.summarize_sui_account_activity` | Implemented | Summarizes stored normalized Sui activity facts, including stored Move call, balance change, object/event, gas, execution error details, and optional protocol activity labels when evidence is available. |
45
45
  | Stored account asset timeline | `read.get_account_asset_timeline` | Implemented | Builds stored local account asset net-flow bars and optional DeepBook USDC token-denominated candle references. Not held balances, complete wallet history, USD value, P&L, cost basis, route advice, transaction building, or signing readiness. |
46
- | Local settings | `settings.create_local_settings_session`, `settings.get_local_settings` | Implemented | MCP creates or reads local settings sessions. Settings mutations happen in the local settings page after token validation and apply after MCP server restart when they affect the endpoint. |
46
+ | Local settings | `settings.create_local_settings_session`, `settings.get_local_settings` | Implemented | MCP opens an internal Settings card or reads settings. App-only preparation/confirmation owns changes, files and explicit connection-service recovery. Stored endpoints apply after all participating apps restart. |
47
47
  | Release package check | `npm run release:check` | Maintainer, source checkout | Verifies metadata, tests/build, tarball assets/licenses and installed MCP/Settings startup. Requires network and loopback access; no wallet pairing, signing or publishing. |
48
48
  | Mainnet read smoke | `npm run smoke:mainnet` | Manual | Runs selected mainnet read paths from built `dist/`. Build first. Not part of CI or `release:check`; see notes below. |
49
49
  | Sui GraphQL function filter probe | `npm exec -- tsx scripts/sui-graphql-function-filter-probe.ts [--endpoint <mainnet-graphql-url>] [--sample-size <1-50>] [--timeout-ms <ms>]` | Manual, source evidence only | Runs a read-only Sui mainnet GraphQL source-shape probe for function-filter diagnostics; see notes below. |
@@ -51,19 +51,22 @@ Source-checkout scripts are not packaged product commands, MCP tools, review-tim
51
51
 
52
52
  ## Release Package Check
53
53
 
54
- `npm run release:check` is a source-checkout maintainer command. Node 22 or newer,
54
+ `npm run release:check` is a source-checkout maintainer command. Node 22.12+ within
55
+ 22.x, or Node 24+,
55
56
  installed development dependencies, npm registry access for a temporary tarball
56
57
  installation, local loopback sockets, and reachable Sui mainnet endpoints are
57
58
  required. Native SQLite must work both in the checkout and the installed package.
59
+ The source build and tests use Vite, Rolldown and Vitest's common Node ranges; the
60
+ installed package's Node runtime requirement is documented in MCP Setup.
58
61
  The runtime uses its built-in endpoints unless `SUI_GRPC_URL` or `SUI_GRAPHQL_URL`
59
62
  is supplied; the actual mainnet checks remain enabled.
60
63
 
61
64
  The command checks metadata, types, tests, builds, package contents and licenses,
62
65
  then launches the installed package through stdio MCP with a new data directory
63
66
  and an independent port. It reads the registered tools and resources, creates a
64
- Settings session only in that temporary database, and checks the Settings HTML,
65
- assets and authenticated status. A scoped test card verifies
66
- that restart input is rejected and opening manage does not pair. The check
67
+ Settings card only in that temporary database, and checks the six internal
68
+ HTML resources, app-only tools, scoped status and retired-route rejection. A scoped test card verifies
69
+ that Connect rejects retired restart input and opening manage does not pair. The check
67
70
  verifies the isolated SDK retains exclusive storage ownership until shutdown. It waits
68
71
  for process termination before removing its installation. A termination failure preserves the temporary files for cleanup
69
72
  after the process stops. It does not pair, sign, submit, publish, or mutate the
@@ -259,7 +262,7 @@ It is not an MCP tool, not a CI check, not packaged product functionality, not r
259
262
  - Local replay output is debug evidence only. The utility is not an MCP tool, review-time simulation, wallet authorization, signing readiness, or onchain transaction submission/execution.
260
263
  - When a review activity output has `lowSampleWarning: true`, report raw counts and avoid inferring behavior patterns.
261
264
  - For review activity list output, `dataScope.recordCount` is the full matching local review count. The returned `activities` array can be shorter when `truncated.activities: true`.
262
- - Local settings page actions mutate local settings or logical local data only. They do not execute transactions, sign, or create custody. MCP settings tools create a settings page session or read current settings.
265
+ - Settings card actions require scoped permission and explicit confirmation. They change local settings/data or recover the connection service without authorizing a new transaction, signing or custody. Ordinary MCP opens the card or reads settings; app-only file transfer never exposes raw backup bytes to the model.
263
266
 
264
267
  ### Optional account-bound review smoke
265
268
 
@@ -19,9 +19,23 @@ an explicit retry even if the other connection later ends. If multiple valid con
19
19
  review preparation or signing. The Connect card shows each exact target for
20
20
  user-confirmed disconnection. After recorded disconnections leave one valid
21
21
  connection and no pending connection operation, that connection becomes usable;
22
- with none remaining, a new pairing is allowed. Terminal history alone does not
22
+ with no recorded or SDK-present connection and no pending operation, a new pairing is allowed. Terminal history alone does not
23
23
  block it. A failed disconnect remains unconfirmed in the wallet app; its SDK
24
24
  session cannot revive the failed product record.
25
+ The current SDK snapshot also gates pairing and account use. A known failed or
26
+ disconnected record whose SDK session remains present is a disconnect target
27
+ on a new card, not a usable wallet. `manageableConnectionIds` identifies those
28
+ exact targets and ordinary connected targets, including expired records.
29
+ An unmapped SDK session in a published available run sets
30
+ `connectionReconciliationRequired: true` after pending registration/cleanup
31
+ ends. An initializing or state_sync memory snapshot does not establish this
32
+ diagnosis, and an absent flag does not prove session absence. Pairing, account use, preparation and signing
33
+ remain blocked. Remove the retained connection in the wallet app, then confirm
34
+ its absence through the current connection service. Settings/Troubleshooting
35
+ can explicitly restart the service to inspect it again, but restarting never
36
+ promotes an unrecorded or failed session into a usable connection. The card does
37
+ not invent a connection ID or report physical SDK failure.
38
+ The SDK's availability and a recorded terminal outcome alone do not prove absence.
25
39
 
26
40
  The backend validates accounts, Sui mainnet namespace, methods and expiry before
27
41
  recording a connection. A single wallet-approved account can become read context
@@ -80,14 +94,21 @@ stored results. Normal startup is shown as initialization, not as a wallet
80
94
  rejection. No implicit asset account is offered until the current service run
81
95
  confirms a usable connection.
82
96
 
83
- There is no card or Settings control for restarting the connection service.
84
- If the service stops or a disconnection remains unresponsive, fully quit all
85
- apps using Sui MCP, then reopen them. Closing a non-owning app or only a
86
- window does not stop the shared backend; another open client may take over.
87
- Confirm the new service is available before starting a new wallet operation.
88
- This does not guarantee that a relay responds or that a connection is removed
89
- in your wallet app. Multiple saved connections are resolved through their
90
- individual Disconnect controls, not by assuming an app restart clears them.
97
+ Settings owns explicit connection-service restart. Open its Troubleshooting
98
+ section, inspect the current run and affected requests, then confirm the
99
+ restart. Connect manages connections/approved addresses and retains individual
100
+ Disconnect controls for conflicting saved sessions; it does not own global
101
+ service controls. A restart waits for old child exit, new run reservation and
102
+ complete ready observation. Unsubmitted signing authority is revoked at
103
+ admission; late signatures cannot submit. Already submitted transactions keep
104
+ their original digest and observation. A new explicit Settings operation may
105
+ supersede a still-pending startup without erasing a known failure.
106
+
107
+ Restart completion does not confirm an interrupted disconnect or remove a
108
+ remote session. Check retained connections in the wallet app. If Settings or
109
+ the parent is unavailable, fully quit all apps using Sui MCP and reopen them;
110
+ closing a peer/window can leave the owner running. Full app restart, rather
111
+ than child restart, applies stored endpoints and installation changes.
91
112
 
92
113
  A new owner records interrupted connection approval as stopped and unconfirmed
93
114
  disconnection as failed. It does not repeat either request or restore their
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zktx.io/sui-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "mcpName": "io.github.zktx-mcp/sui-mcp",
5
5
  "description": "Sui MCP is a local-first toolkit for helping AI applications and users inspect Sui DeFi actions before execution.",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "scripts": {
41
41
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
42
- "build": "npm run clean && tsc -p tsconfig.build.json && vite build && tsx scripts/build-mcp-ui.ts",
42
+ "build": "npm run clean && tsc -p tsconfig.build.json && tsx scripts/build-mcp-ui.ts",
43
43
  "prepublishOnly": "npm run release:check",
44
44
  "release:check": "tsx scripts/release-check.ts",
45
45
  "smoke:mainnet": "node dist/runtime/smokeMainnetRead.js",
@@ -49,9 +49,10 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/ext-apps": "1.7.5",
52
- "@modelcontextprotocol/sdk": "1.29.0",
52
+ "@modelcontextprotocol/sdk": "1.31.0",
53
53
  "@mysten/deepbook-v3": "1.3.6",
54
54
  "@mysten/sui": "2.17.0",
55
+ "@noble/hashes": "2.2.0",
55
56
  "@scure/bip39": "2.2.0",
56
57
  "@walletconnect/sign-client": "2.23.10",
57
58
  "@walletconnect/types": "2.23.10",
@@ -59,19 +60,24 @@
59
60
  "@zktx.io/ptb-model": "0.5.0",
60
61
  "better-sqlite3": "12.9.0",
61
62
  "lightweight-charts": "5.2.0",
62
- "mermaid": "11.12.0",
63
63
  "qrcode": "1.5.4",
64
64
  "zod": "4.4.3"
65
65
  },
66
+ "overrides": {
67
+ "mermaid@11.16.1": {
68
+ "katex": "0.18.2"
69
+ }
70
+ },
66
71
  "devDependencies": {
67
72
  "@types/better-sqlite3": "7.6.13",
68
73
  "@types/node": "25.6.2",
69
74
  "@types/qrcode": "1.5.6",
70
75
  "marked": "16.4.2",
71
- "tsx": "4.21.0",
76
+ "mermaid": "11.16.1",
77
+ "tsx": "4.22.0",
72
78
  "typescript": "6.0.3",
73
- "vite": "8.0.11",
74
- "vitest": "4.1.5"
79
+ "vite": "8.0.16",
80
+ "vitest": "4.1.11"
75
81
  },
76
82
  "engines": {
77
83
  "node": ">=22"
@@ -1,2 +0,0 @@
1
- // Current-format-only storage; existing databases are never migrated.
2
- export const DB_USER_VERSION = 9;
@@ -1,18 +0,0 @@
1
- export class InMemoryKeyedRecordStore {
2
- records = new Map();
3
- get(id) {
4
- return this.records.get(id);
5
- }
6
- set(id, value) {
7
- this.records.set(id, value);
8
- }
9
- delete(id) {
10
- this.records.delete(id);
11
- }
12
- ids() {
13
- return [...this.records.keys()];
14
- }
15
- clear() {
16
- this.records.clear();
17
- }
18
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,45 +0,0 @@
1
- import { cloneLocalSession, createLocalSessionBase, isLocalSessionExpired, tokenMatchesHash } from "./localSession.js";
2
- import { InMemoryKeyedRecordStore } from "./keyedRecordStore.js";
3
- export class SettingsSessionManager {
4
- options;
5
- sessions;
6
- constructor(options) {
7
- this.options = options;
8
- this.sessions = options.recordStore ?? new InMemoryKeyedRecordStore();
9
- }
10
- async create(now) {
11
- const { base, token } = createLocalSessionBase(now, this.options.ttlMs);
12
- const session = {
13
- ...base,
14
- type: "local_settings"
15
- };
16
- this.sessions.set(session.id, session);
17
- await this.options.appendEventLog({
18
- type: "settings_session.created",
19
- sessionId: session.id,
20
- at: now.toISOString()
21
- });
22
- return { session: cloneLocalSession(session), token };
23
- }
24
- async get(id, now) {
25
- const session = this.sessions.get(id);
26
- if (!session) {
27
- return undefined;
28
- }
29
- if (isLocalSessionExpired(session, now)) {
30
- this.sessions.delete(id);
31
- return undefined;
32
- }
33
- return cloneLocalSession(session);
34
- }
35
- async validateToken(id, token, now) {
36
- const session = await this.get(id, now);
37
- if (!session) {
38
- return false;
39
- }
40
- return tokenMatchesHash(session.tokenHash, token);
41
- }
42
- clear() {
43
- this.sessions.clear();
44
- }
45
- }
@@ -1 +0,0 @@
1
- <svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#4DA2FF"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
@@ -1 +0,0 @@
1
- <svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#1565C0"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
@@ -1 +0,0 @@
1
- <svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#1565C0"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
@@ -1 +0,0 @@
1
- .settings-title{margin:8px 0 6px;font-size:22px;line-height:1.2}.settings-file{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
@@ -1 +0,0 @@
1
- function e(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function t(e,t,n=`primary`){let r=document.createElement(`button`);return r.type=`button`,r.className=n===`primary`?`ui-btn ui-btn--primary`:n===`danger`?`ui-btn ui-btn--danger`:`ui-btn`,r.textContent=e,r.addEventListener(`click`,t),r}function n(e,t,n){let r=document.createElement(`button`);return r.type=`button`,r.className=`ui-icon-btn`,r.setAttribute(`aria-label`,t),r.innerHTML=e,r.addEventListener(`click`,n),r}function r(...t){let n=e(`div`,`ui-btn-row`);return n.append(...t),n}function i(e){let t=r(e);return t.classList.add(`ui-btn-row--end`),t}function a(e){let t=document.createElement(`input`);return t.className=`ui-input`,t.type=e.type??`text`,e.value!==void 0&&(t.value=e.value),e.placeholder!==void 0&&(t.placeholder=e.placeholder),e.id&&(t.id=e.id),e.name&&(t.name=e.name),t.autocomplete=`off`,t}function o(t){let n=e(`section`,`ui-card`);return t&&n.append(e(`h2`,`ui-card-head`,t)),n}function s(t,n){let r=e(`div`,`ui-row`);r.append(e(`span`,`ui-row-label`,t));let i=e(`span`,`ui-row-value`);return typeof n==`string`?i.textContent=n:i.append(n),r.append(i),r}function c(t,n,r){let i=e(`div`,t===`error`?`ui-feedback ui-feedback--error`:`ui-feedback ui-feedback--ok`);return i.setAttribute(`role`,`status`),i.setAttribute(`aria-live`,`polite`),i.append(e(`span`,void 0,n)),r&&i.append(e(`span`,`ui-feedback-detail`,r)),i}function l(t){return e(`p`,`ui-note`,t)}var u=`sui-mcp-theme`;function d(){try{let e=localStorage.getItem(u);return e===`light`||e===`dark`?e:void 0}catch{return}}function f(e){try{localStorage.setItem(u,e)}catch{}}function p(){try{return window.matchMedia(`(prefers-color-scheme: dark)`).matches}catch{return!1}}function m(){return document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`}function h(e){document.documentElement.setAttribute(`data-theme`,e)}function g(){let e=d()??(p()?`dark`:`light`);return h(e),e}function _(e){h(e),f(e)}function v(){let e=m()===`dark`?`light`:`dark`;return _(e),e}var y={brand:`Sui MCP`,shell:{toggleTheme:`Toggle theme`},account:{name:`Name`,address:`Account address`,balances:`Balances`,noBalances:`No token balances were returned.`,nfts:`NFTs`,noNfts:`No NFTs in the scanned objects.`,noImage:`No image`,objectsTruncated:`Some objects beyond the scanned range are omitted.`},receipt:{success:`Transaction succeeded on Sui`,failure:`Transaction failed on Sui`,sender:`Sender`,account:`Affected account`,object:`Object`,noBalanceChanges:`No balance changes were recorded.`,gasComputation:`Computation cost`,gasStorage:`Storage cost`,gasRebate:`Storage rebate`,gasBudget:`Network fee limit (gas budget)`,netFeeExplanation:`The network fee shown above includes the storage rebate. If it is negative, the rebate exceeds the computation and storage charges.`,estimatedNetFeeExplanation:`The estimated network fee shown above includes the storage rebate. If it is negative, the rebate exceeds the estimated computation and storage charges.`,gasLimitExplanation:`The gas budget limits network fee spending. It is not an estimate of the amount charged.`,gasPrice:`Gas price`,gasPayment:`Gas payment object`,graph:`Transaction graph`,graphTip:`A diagram of this transaction's programmable transaction block (PTB), derived from public on-chain data. It is a diagnostic view only, not transaction input, signing, or a safety verdict.`,graphReviewTip:`A diagram of the transaction prepared for this review and simulation. It shows what is being reviewed, not an actual result, permission to sign, or a guarantee of safety.`,graphRendering:`Rendering the transaction graph…`,graphFailed:`The transaction graph could not be rendered`,graphZoomIn:`Zoom in`,graphZoomOut:`Zoom out`,graphCenter:`Center`,graphShowAddresses:`Show full addresses`,graphShowNames:`Show readable labels`,moveCalls:`Move calls`,noMoveCalls:`No Move calls were recorded.`,inputs:`Inputs`,noInputs:`No inputs were recorded.`,inputKinds:{object:`Owned object`,shared_object:`Shared object`,receiving:`Receiving object`,pure:`Pure value`,withdrawal:`Balance withdrawal`,unknown:`Input`},bytes:`bytes`,events:`Events`,noEvents:`No events were recorded.`,objectChanges:`Object changes`,noObjectChanges:`No object changes were recorded.`},chart:{boundaryUsdc:`Prices are in USDC, not USD; dollar parity is not guaranteed.`,boundaryScope:`Historical prices, not live quotes.`,source:`Source: DeepBookV3 Indexer.`,renderedWith:`Rendered with`,library:`TradingView Lightweight Charts`},common:{walletStatusRecovery:`To restart the connection service, fully quit all apps using Sui MCP, then reopen them. This does not confirm removal of a connection in your wallet app.`,mainnet:`Sui mainnet`,retrievedAt:`Data retrieved`}},b={en:y};function ee(){try{return(navigator.language||`en`).slice(0,2).toLowerCase()}catch{return`en`}}function te(e=ee()){return b[e]??y}var x=te(),S=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>`,ne=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>`;function C(e){return e===`dark`?ne:S}function w(e){let t=document.createElement(`img`);return t.className=e,t.src=`/review-assets/${e===`ui-brand-light`?`brand-light`:`brand-dark`}.svg`,t.alt=``,t.width=24,t.height=24,t}function T(){let t=e(`span`,`ui-brand`);return t.append(w(`ui-brand-light`),w(`ui-brand-dark`),e(`span`,void 0,x.brand)),t}function E(t){g();let r=e(`div`,`ui-shell`),i=e(`header`,`ui-header`);i.append(T()),i.append(e(`span`,`ui-header-spacer`));let a=n(C(m()),x.shell.toggleTheme,()=>{a.innerHTML=C(v())});i.append(a);let o=e(`main`,`ui-main`),s=e(`div`,`ui-overlay`);s.setAttribute(`role`,`status`),s.setAttribute(`aria-live`,`polite`);let c=e(`div`,`ui-overlay-box`);c.append(e(`div`,`ui-spinner`));let l=e(`span`,`ui-overlay-label`,`Processing…`);return c.append(l),s.append(c),r.append(i),r.append(o,s),t.replaceChildren(r),{root:r,main:o,setBusy(e,t){t&&(l.textContent=t),e?r.setAttribute(`aria-busy`,`true`):r.removeAttribute(`aria-busy`)}}}var D=class extends Error{constructor(e,t){super(`HTTP JSON request failed: ${e} ${t}`),this.status=e,this.code=t}};async function O(e){try{let t=await e.json();if(typeof t.error==`string`&&t.error.length>0)return t.error}catch{}return`http_${e.status}`}function k(e,t){return e instanceof D?e.code===`wallet_request_unsettled`?`A wallet request is still being settled. Local data cannot be replaced yet.`:`${t} (${e.code})`:t}var A=`x-sui-mcp-token`;function j(){let e=window.location.hash;return e.startsWith(`#`)?e.slice(1):``}function re(e,t){return{...t,[A]:e}}var M=16*1024*1024,N=document.querySelector(`#settings-app`);if(!N)throw Error(`settings app root missing`);var P=N,F=P.dataset.settingsSessionId??``,I=j(),L=E(P).main,R,z,B,V,H,U;function W(e,n,r=`primary`){let i=t(e,n,r);return i.disabled=H!==void 0,i}function G(e){if(H?.scope===e)return l(H.text);if(U?.scope===e)return c(U.kind,U.text)}function K(e,t){let n=G(t);n&&e.append(n)}F&&I?X():q();function q(){let t=[];if(t.push(e(`h1`,`settings-title`,`Sui MCP Settings`)),t.push(l(`Use this local page to manage the default address for reads, local data, and Sui read endpoints. Settings changes do not sign transactions or grant custody.`)),V&&t.push(c(`error`,V)),!F||!I){t.push(c(`error`,`Missing settings session id or token. Open the settings URL from your AI client again.`)),L.replaceChildren(...t);return}t.push(J(),Y(),ie(),ae(),oe()),L.replaceChildren(...t)}function J(){let e=o(`Status`);return R?(e.append(s(`Server`,`${R.server.name} ${R.server.version}`)),e.append(s(`Network`,R.server.network)),e.append(s(`Endpoint source`,R.localSettings.suiGrpcUrl.source)),e.append(s(`Effective endpoint`,R.localSettings.suiGrpcUrl.effectiveValue)),e.append(s(`Stored endpoint`,R.localSettings.suiGrpcUrl.storedValue)),e.append(s(`GraphQL endpoint source`,R.localSettings.suiGraphqlUrl.source)),e.append(s(`Effective GraphQL endpoint`,R.localSettings.suiGraphqlUrl.effectiveValue)),e.append(s(`Stored GraphQL endpoint`,R.localSettings.suiGraphqlUrl.storedValue)),e.append(s(`Restart required`,R.restartRequired?`yes`:`no`)),e.append(s(`Default address`,R.activeAccount?.account??`none`)),e.append(s(`Data counts`,Object.entries(R.dataCounts).map(([e,t])=>`${e}: ${t}`).join(`, `))),e):(e.append(l(`Loading…`)),e)}function Y(){let e=o(`Default address for reads`);return e.append(l(`Clearing the default address does not disconnect your wallet or revoke onchain permission. Choose an approved address or disconnect in the wallet connection card.`)),R?.activeAccount?e.append(i(W(`Clear default address`,()=>void se(),`secondary`))):e.append(l(R?`No default address is selected.`:`Loading…`)),K(e,`wallet`),e}function ie(){let t=o(`Sui gRPC endpoint`),n=a({type:`url`,value:R?.localSettings.suiGrpcUrl.storedValue??``,placeholder:`https://fullnode.mainnet.sui.io:443`});n.maxLength=512,n.setAttribute(`aria-label`,`Sui gRPC endpoint URL`),t.append(n),t.append(r(W(`Save endpoint`,()=>void ce(n.value)),W(`Restore default Sui gRPC URL`,()=>void le(),`secondary`))),K(t,`grpc`);let i=o(`Sui GraphQL endpoint`),s=a({type:`url`,value:R?.localSettings.suiGraphqlUrl.storedValue??``,placeholder:`https://graphql.mainnet.sui.io/graphql`});s.maxLength=512,s.setAttribute(`aria-label`,`Sui GraphQL endpoint URL`),i.append(s),i.append(r(W(`Save GraphQL endpoint`,()=>void ue(s.value)),W(`Restore default Sui GraphQL URL`,()=>void de(),`secondary`))),K(i,`graphql`);let c=e(`div`,`settings-endpoints`);return c.append(t,i),c}function ae(){let e=o(`Local data`);e.append(l(`Export downloads a backup of your local data. Import previews a chosen backup file (shape only); endpoint verification runs when you confirm, and importing replaces current local data and invalidates open cards and settings sessions.`));let t=document.createElement(`input`);return t.type=`file`,t.accept=`application/json,.json`,t.className=`settings-file`,t.setAttribute(`aria-label`,`Choose a Sui MCP backup file to import`),t.onchange=()=>void pe(t),e.append(r(W(`Export local data`,()=>void fe()),W(`Choose backup file…`,()=>t.click(),`secondary`))),e.append(t),B&&(e.append(c(`ok`,`Import preview ready. Incoming accounts: ${B.incomingCounts.accounts}. Active account change: ${B.activeAccountChange}. Endpoint verification runs before replacement. This import replaces current local data.`)),e.append(l($())),e.append(i(W(`Import and replace local data`,()=>void me(),`danger`)))),K(e,`localData`),e}function oe(){let e=o(`Danger zone`);return e.append(l(`Reset permanently clears all local Sui MCP data and invalidates every open card and settings session. This cannot be undone.`)),e.append(l($())),e.append(i(W(`Reset local data`,()=>void he(),`danger`))),K(e,`danger`),e}async function X(){try{R=await Q(`/api/settings/${encodeURIComponent(F)}`,{method:`GET`}),V=void 0}catch(e){V=k(e,`The local server did not accept this settings session.`)}q()}async function se(){window.confirm(`Clear the default address for reads? This does not disconnect your wallet or revoke onchain permission.`)&&await Z(`wallet`,`clear-active-account`,`Default address cleared.`)}async function ce(e){if(e.length>512){U={scope:`grpc`,kind:`error`,text:`Sui gRPC endpoint must be 512 characters or fewer.`},q();return}await Z(`grpc`,`sui-grpc-url`,`Sui gRPC endpoint saved. Restart the MCP server for the stored value to apply.`,{url:e},`Verifying and saving the Sui gRPC endpoint...`)}async function le(){window.confirm(`Restore the built-in default Sui gRPC URL? Restart the MCP server for the restored value to apply.`)&&await Z(`grpc`,`sui-grpc-url/restore-default`,`Default Sui gRPC URL restored. Restart the MCP server for it to apply.`)}async function ue(e){if(e.length>512){U={scope:`graphql`,kind:`error`,text:`Sui GraphQL endpoint must be 512 characters or fewer.`},q();return}await Z(`graphql`,`sui-graphql-url`,`Sui GraphQL endpoint saved. Restart the MCP server for the stored value to apply.`,{url:e},`Verifying and saving the Sui GraphQL endpoint...`)}async function de(){window.confirm(`Restore the built-in default Sui GraphQL URL? Restart the MCP server for the restored value to apply.`)&&await Z(`graphql`,`sui-graphql-url/restore-default`,`Default Sui GraphQL URL restored. Restart the MCP server for it to apply.`)}async function fe(){try{H={scope:`localData`,text:`Preparing local data export...`},U=void 0,q();let e=await Q(`/api/settings/${encodeURIComponent(F)}/local-data/export`,{method:`GET`}),t=new Blob([JSON.stringify(e,null,2)],{type:`application/json`}),n=document.createElement(`a`),r=URL.createObjectURL(t);n.href=r,n.download=`sui-mcp-local-data-${new Date().toISOString().replaceAll(`:`,`-`)}.json`,n.click(),window.setTimeout(()=>URL.revokeObjectURL(r),0),H=void 0,U={scope:`localData`,kind:`ok`,text:`Local data export prepared.`}}catch(e){H=void 0,U={scope:`localData`,kind:`error`,text:k(e,`Could not export local data.`)}}q()}async function pe(e){let t=e.files?.[0];if(t){if(t.size>M){z=void 0,B=void 0,U={scope:`localData`,kind:`error`,text:`Import file is too large. Maximum size is ${ge(M)}.`},e.value=``,q();return}try{H={scope:`localData`,text:`Loading import preview...`},U=void 0,q(),z=JSON.parse(await t.text()),B=await Q(`/api/settings/${encodeURIComponent(F)}/local-data/import/preview`,{method:`POST`,body:JSON.stringify(z)}),H=void 0,U={scope:`localData`,kind:`ok`,text:`Import preview loaded. Endpoint verification will run only if you confirm import.`}}catch(t){z=void 0,B=void 0,e.value=``,H=void 0,U={scope:`localData`,kind:`error`,text:k(t,`Import preview failed. Check that the file is a Sui MCP local data export.`)}}q()}}async function me(){if(z&&window.confirm(`Import and replace all local data? This invalidates all open cards and settings sessions. `+$())){try{H={scope:`localData`,text:`Importing local data...`},U=void 0,q(),await Q(`/api/settings/${encodeURIComponent(F)}/local-data/import`,{method:`POST`,body:JSON.stringify(z)}),H=void 0,U={scope:`localData`,kind:`ok`,text:`Local data imported. This settings page is now invalid; create a new settings session to continue.`},z=void 0,B=void 0}catch(e){H=void 0,U={scope:`localData`,kind:`error`,text:k(e,`Could not import local data.`)}}q()}}async function he(){if(window.confirm(`Reset all local Sui MCP data? This invalidates all open cards and settings sessions. `+$())){try{H={scope:`danger`,text:`Resetting local data...`},U=void 0,q(),await Q(`/api/settings/${encodeURIComponent(F)}/local-data/reset`,{method:`POST`,body:`{}`}),H=void 0,U={scope:`danger`,kind:`ok`,text:`Local data reset. This settings page is now invalid; create a new settings session to continue.`}}catch(e){H=void 0,U={scope:`danger`,kind:`error`,text:k(e,`Could not reset local data.`)}}q()}}async function Z(e,t,n,r={},i=`Sending local settings request...`){H={scope:e,text:i},U=void 0,q();try{await Q(`/api/settings/${encodeURIComponent(F)}/${t}`,{method:`POST`,body:JSON.stringify(r)}),H=void 0,U={scope:e,kind:`ok`,text:n},await X()}catch(t){H=void 0,U={scope:e,kind:`error`,text:k(t,`The local settings request failed.`)},q()}}async function Q(e,t){let n=t.body===void 0?void 0:{"content-type":`application/json`},r=await fetch(e,{...t,headers:re(I,n)});if(!r.ok)throw new D(r.status,await O(r));return await r.json()}function ge(e){let t=e/(1024*1024);return Number.isInteger(t)?`${t} MiB`:`${e} bytes`}function $(){return`Replacement removes stored requests, including any with an unconfirmed chain outcome. Removing local records cannot cancel or reverse a transaction on Sui. Export the records before replacement if you need to investigate them later.`}