@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
@@ -1,48 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { dirname, extname, resolve, sep } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { sendJson } from "./http.js";
5
- export async function serveReviewAsset(response, assetsDir, requestPath) {
6
- let decoded;
7
- try {
8
- decoded = decodeURIComponent(requestPath);
9
- }
10
- catch {
11
- sendJson(response, 404, { error: "not_found" });
12
- return;
13
- }
14
- const root = resolve(assetsDir);
15
- const filePath = resolve(root, decoded);
16
- if (filePath !== root && !filePath.startsWith(`${root}${sep}`)) {
17
- sendJson(response, 404, { error: "not_found" });
18
- return;
19
- }
20
- try {
21
- const content = await readFile(filePath);
22
- response.writeHead(200, {
23
- "content-type": contentTypeForAsset(filePath),
24
- // Entry assets keep stable names across rebuilds; never let the browser
25
- // serve a stale bundle for a loopback dev/review surface.
26
- "cache-control": "no-store"
27
- });
28
- response.end(content);
29
- }
30
- catch {
31
- sendJson(response, 404, { error: "not_found" });
32
- }
33
- }
34
- function contentTypeForAsset(filePath) {
35
- switch (extname(filePath)) {
36
- case ".js":
37
- return "text/javascript; charset=utf-8";
38
- case ".css":
39
- return "text/css; charset=utf-8";
40
- case ".svg":
41
- return "image/svg+xml";
42
- default:
43
- return "application/octet-stream";
44
- }
45
- }
46
- export function defaultReviewAssetsDir() {
47
- return resolve(dirname(fileURLToPath(import.meta.url)), "../review-app");
48
- }
@@ -1,45 +0,0 @@
1
- // Page HTML shells served by the review server. Each page is a minimal document:
2
- // the shared <head> (charset, viewport, title, favicon, the page stylesheet) and
3
- // a mount element plus the page's module script. The shared UI module renders the
4
- // header, navigation, and content into the mount on the public pages it has
5
- // already migrated; pages not yet migrated keep their existing body. `data-theme`
6
- // defaults to light on <html> so there is no unstyled flash before the theme
7
- // helper applies the stored or preferred theme.
8
- function pageDocument(options) {
9
- const uiLink = options.ui ? ` <link rel="stylesheet" href="/review-assets/ui.css">\n` : "";
10
- return `<!doctype html>
11
- <html lang="en" data-theme="light">
12
- <head>
13
- <meta charset="utf-8">
14
- <meta name="viewport" content="width=device-width, initial-scale=1">
15
- <title>${options.title}</title>
16
- <link rel="icon" type="image/svg+xml" href="/review-assets/favicon.svg">
17
- ${uiLink} <link rel="stylesheet" href="/review-assets/${options.css}">
18
- </head>
19
- <body>
20
- ${options.body}
21
- <script type="module" src="/review-assets/${options.js}"></script>
22
- </body>
23
- </html>`;
24
- }
25
- export function settingsHtml(sessionId) {
26
- return pageDocument({
27
- title: "Sui MCP Settings",
28
- css: "settings.css",
29
- js: "settings.js",
30
- ui: true,
31
- body: ` <div id="settings-app" data-settings-session-id="${escapeHtml(sessionId)}"></div>`
32
- });
33
- }
34
- function escapeHtml(value) {
35
- return value.replace(/[&<>"']/g, (char) => {
36
- const replacements = {
37
- "&": "&amp;",
38
- "<": "&lt;",
39
- ">": "&gt;",
40
- '"': "&quot;",
41
- "'": "&#39;"
42
- };
43
- return replacements[char] ?? char;
44
- });
45
- }
@@ -1,47 +0,0 @@
1
- export const MAX_JSON_BODY_BYTES = 64 * 1024;
2
- export class HttpError extends Error {
3
- status;
4
- code;
5
- constructor(status, code) {
6
- super(code);
7
- this.status = status;
8
- this.code = code;
9
- }
10
- }
11
- export function sendHtml(response, body, headers = {}, status = 200) {
12
- response.writeHead(status, { "content-type": "text/html; charset=utf-8", ...headers });
13
- response.end(body);
14
- }
15
- export function sendJson(response, status, body) {
16
- response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
17
- response.end(JSON.stringify(body));
18
- }
19
- export async function readJsonBody(request, maxBytes = MAX_JSON_BODY_BYTES) {
20
- const chunks = [];
21
- let totalBytes = 0;
22
- for await (const chunk of request) {
23
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
24
- totalBytes += buffer.length;
25
- if (totalBytes > maxBytes) {
26
- throw new HttpError(413, "payload_too_large");
27
- }
28
- chunks.push(buffer);
29
- }
30
- if (chunks.length === 0) {
31
- return {};
32
- }
33
- const raw = Buffer.concat(chunks).toString("utf8");
34
- let parsed;
35
- try {
36
- parsed = JSON.parse(raw);
37
- }
38
- catch {
39
- throw new HttpError(400, "invalid_json");
40
- }
41
- return parsed && typeof parsed === "object" && !Array.isArray(parsed)
42
- ? parsed
43
- : raiseHttpError(400, "invalid_body_shape");
44
- }
45
- export function raiseHttpError(status, code) {
46
- throw new HttpError(status, code);
47
- }
@@ -1,7 +0,0 @@
1
- export function readReviewToken(headers) {
2
- const headerValue = headers["x-sui-mcp-token"];
3
- if (typeof headerValue === "string" && headerValue.length > 0) {
4
- return headerValue;
5
- }
6
- return undefined;
7
- }
@@ -1,158 +0,0 @@
1
- import { LocalDataError } from "../core/activity/localDataService.js";
2
- import { LocalSettingsError } from "../core/preferences/preferencesStore.js";
3
- import { readReviewToken } from "./middleware/reviewToken.js";
4
- import { HttpError, MAX_JSON_BODY_BYTES, readJsonBody, sendJson } from "./http.js";
5
- const MAX_SETTINGS_BODY_BYTES = MAX_JSON_BODY_BYTES;
6
- const MAX_IMPORT_BODY_BYTES = 16 * 1024 * 1024;
7
- export async function routeSettingsApi(request, response, options, _url, matches) {
8
- // Host/Origin and a query token are rejected once by the global guards in
9
- // routeRequest before this handler runs (see the "validate once" guards
10
- // there), so this handler does not re-check them.
11
- const settings = requireSettingsDeps(options);
12
- const sessionId = matches.status ??
13
- matches.clearActiveAccount ??
14
- matches.setSuiGrpcUrl ??
15
- matches.restoreDefaultSuiGrpcUrl ??
16
- matches.setSuiGraphqlUrl ??
17
- matches.restoreDefaultSuiGraphqlUrl ??
18
- matches.exportLocalData ??
19
- matches.previewImport ??
20
- matches.importLocalData ??
21
- matches.resetLocalData;
22
- if (!sessionId) {
23
- throw new HttpError(404, "not_found");
24
- }
25
- const token = readReviewToken(request.headers);
26
- if (!token || !(await options.store.validateSettingsToken(sessionId, token))) {
27
- sendJson(response, 401, { error: "invalid_settings_token" });
28
- return;
29
- }
30
- if (request.method === "GET" && matches.status) {
31
- const [localSettings, activeAccount, dataCounts] = await Promise.all([
32
- mapLocalSettingsError(() => settings.localSettings.getLocalSettings()),
33
- settings.activityStore.getActiveAccount(),
34
- settings.localData.getDataCounts()
35
- ]);
36
- sendJson(response, 200, {
37
- settingsSessionId: sessionId,
38
- server: settings.serverInfo,
39
- localSettings,
40
- activeAccount: activeAccount
41
- ? {
42
- account: activeAccount.address,
43
- source: activeAccount.source,
44
- setAt: activeAccount.setAt
45
- }
46
- : undefined,
47
- dataCounts,
48
- restartRequired: localSettings.suiGrpcUrl.appliesAfter === "mcp_server_restart" ||
49
- localSettings.suiGraphqlUrl.appliesAfter === "mcp_server_restart"
50
- });
51
- return;
52
- }
53
- if (request.method === "POST" && matches.clearActiveAccount) {
54
- await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
55
- await settings.activityStore.clearActiveAccount();
56
- sendJson(response, 200, { status: "cleared" });
57
- return;
58
- }
59
- if (request.method === "POST" && matches.setSuiGrpcUrl) {
60
- const body = await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
61
- const urlValue = typeof body.url === "string" ? body.url : undefined;
62
- if (!urlValue) {
63
- sendJson(response, 400, { error: "input_invalid" });
64
- return;
65
- }
66
- const result = await mapLocalSettingsError(() => settings.localSettings.setSuiGrpcUrl(urlValue));
67
- sendJson(response, 200, result);
68
- return;
69
- }
70
- if (request.method === "POST" && matches.restoreDefaultSuiGrpcUrl) {
71
- await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
72
- const result = await mapLocalSettingsError(() => settings.localSettings.resetSuiGrpcUrl());
73
- sendJson(response, 200, result);
74
- return;
75
- }
76
- if (request.method === "POST" && matches.setSuiGraphqlUrl) {
77
- const body = await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
78
- const urlValue = typeof body.url === "string" ? body.url : undefined;
79
- if (!urlValue) {
80
- sendJson(response, 400, { error: "input_invalid" });
81
- return;
82
- }
83
- const result = await mapLocalSettingsError(() => settings.localSettings.setSuiGraphqlUrl(urlValue));
84
- sendJson(response, 200, result);
85
- return;
86
- }
87
- if (request.method === "POST" && matches.restoreDefaultSuiGraphqlUrl) {
88
- await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
89
- const result = await mapLocalSettingsError(() => settings.localSettings.resetSuiGraphqlUrl());
90
- sendJson(response, 200, result);
91
- return;
92
- }
93
- if (request.method === "GET" && matches.exportLocalData) {
94
- const envelope = await mapLocalDataError(() => settings.localData.exportLocalData());
95
- sendJson(response, 200, envelope);
96
- return;
97
- }
98
- if (request.method === "POST" && matches.previewImport) {
99
- const body = await readJsonBody(request, MAX_IMPORT_BODY_BYTES);
100
- const preview = await mapLocalDataError(() => settings.localData.previewImportLocalData(body));
101
- sendJson(response, 200, preview);
102
- return;
103
- }
104
- if (request.method === "POST" && matches.importLocalData) {
105
- const body = await readJsonBody(request, MAX_IMPORT_BODY_BYTES);
106
- const result = await mapLocalDataError(() => settings.localData.importLocalDataReplace(body));
107
- sendJson(response, 200, result);
108
- return;
109
- }
110
- if (request.method === "POST" && matches.resetLocalData) {
111
- await readJsonBody(request, MAX_SETTINGS_BODY_BYTES);
112
- const result = await mapLocalDataError(() => settings.localData.resetLocalData());
113
- sendJson(response, 200, result);
114
- return;
115
- }
116
- sendJson(response, 404, { error: "not_found" });
117
- }
118
- function requireSettingsDeps(options) {
119
- if (!options.activityStore || !options.localSettings || !options.localData || !options.serverInfo) {
120
- throw new HttpError(404, "not_found");
121
- }
122
- return {
123
- activityStore: options.activityStore,
124
- localSettings: options.localSettings,
125
- localData: options.localData,
126
- serverInfo: options.serverInfo
127
- };
128
- }
129
- async function mapLocalSettingsError(operation) {
130
- try {
131
- return await operation();
132
- }
133
- catch (error) {
134
- if (error instanceof LocalSettingsError) {
135
- throw new HttpError(statusForLocalSettingsError(error), error.kind);
136
- }
137
- throw error;
138
- }
139
- }
140
- function statusForLocalSettingsError(error) {
141
- return error.kind === "input_invalid" ? 400 : 500;
142
- }
143
- async function mapLocalDataError(operation) {
144
- try {
145
- return await operation();
146
- }
147
- catch (error) {
148
- if (error instanceof LocalDataError) {
149
- if (error.details.reason === "wallet_request_unsettled")
150
- throw new HttpError(409, "wallet_request_unsettled");
151
- throw new HttpError(statusForLocalDataError(error), error.kind);
152
- }
153
- throw error;
154
- }
155
- }
156
- function statusForLocalDataError(error) {
157
- return error.kind === "input_invalid" ? 400 : 500;
158
- }