@zktx.io/sui-mcp 0.5.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.
- package/README.md +21 -21
- package/dist/adapters/adapterPromptSurfaces.js +0 -20
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
- package/dist/adapters/intentPlanFactories.js +0 -6
- package/dist/core/action/contractNameRegistry.js +0 -53
- package/dist/core/action/forbiddenFields.js +0 -4
- package/dist/core/action/ptbVisualizationProducer.js +0 -3
- package/dist/core/action/receiptPtbGraph.js +0 -17
- package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
- package/dist/core/action/signableAdapterContract.js +0 -5
- package/dist/core/action/suiChainReceiptMapping.js +0 -18
- package/dist/core/action/suiChainReceiptReader.js +1 -21
- package/dist/core/action/suiChainReceiptVerifier.js +0 -10
- package/dist/core/action/transactionFunding.js +0 -7
- package/dist/core/action/types.js +0 -1
- package/dist/core/action/walletReviewContractAssembler.js +0 -2
- package/dist/core/activity/localDataService.js +65 -52
- package/dist/core/activity/localDataValidation.js +1 -3
- package/dist/core/activity/sqliteActivityStore.js +24 -24
- package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
- package/dist/core/activity/sqliteActivityStoreSchema.js +81 -14
- package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
- package/dist/core/read/deepbookReadHelpers.js +0 -2
- package/dist/core/read/deepbookRegistry.js +0 -19
- package/dist/core/read/deepbookUsdcChartService.js +0 -2
- package/dist/core/read/readCardResult.js +0 -2
- package/dist/core/read/readService.js +0 -19
- package/dist/core/read/readServiceTypes.js +0 -2
- package/dist/core/session/cardSession.js +1 -5
- package/dist/core/session/cardSessionStore.js +54 -7
- package/dist/core/session/reviewValidity.js +0 -3
- package/dist/core/session/sessionRecordStore.js +0 -1
- package/dist/core/session/sessionStore.js +0 -32
- package/dist/core/session/settingsFileIdentity.js +9 -0
- package/dist/core/session/settingsFileProtocol.js +20 -0
- package/dist/core/session/settingsFileStore.js +337 -0
- package/dist/core/session/settingsInputError.js +32 -0
- package/dist/core/session/settingsOperation.js +220 -0
- package/dist/core/session/settingsPreparationSql.js +13 -0
- package/dist/core/session/settingsWorkflow.js +1183 -0
- package/dist/core/session/sqliteCardStore.js +0 -2
- package/dist/core/session/sqliteSessionStore.js +0 -51
- package/dist/core/session/sqliteSettingsWorkflowStore.js +453 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
- package/dist/core/session/sqliteWalletWorkflowStore.js +160 -66
- package/dist/core/session/status.js +2 -4
- package/dist/core/session/transactionMaterialStore.js +0 -4
- package/dist/core/session/transactionRequest.js +0 -4
- package/dist/core/session/walletConnection.js +26 -10
- package/dist/core/session/walletWorkflow.js +70 -30
- package/dist/core/session/workflowState.js +14 -14
- package/dist/core/session/workflowView.js +18 -8
- package/dist/core/suiAddress.js +0 -5
- package/dist/core/suiEndpoint.js +0 -3
- package/dist/mcp/prompts.js +0 -12
- package/dist/mcp/responseGuidance.js +10 -5
- package/dist/mcp/schemas.js +0 -3
- package/dist/mcp/serverInfo.js +3 -4
- package/dist/mcp/toolNames.js +2 -2
- package/dist/mcp/tools/account/index.js +1 -19
- package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
- package/dist/mcp/tools/read/readToolHelpers.js +5 -4
- package/dist/mcp/tools/read/serverStatusTools.js +0 -1
- package/dist/mcp/tools/session/executionResultTools.js +2 -2
- package/dist/mcp/tools/session/shared.js +1 -2
- package/dist/mcp/tools/session/statusTools.js +4 -5
- package/dist/mcp/tools/settings/index.js +8 -28
- package/dist/mcp-app/account.html +14 -14
- package/dist/mcp-app/account.notices.txt +1 -1
- package/dist/mcp-app/chart.html +9 -9
- package/dist/mcp-app/chart.notices.txt +1 -1
- package/dist/mcp-app/connect.html +14 -14
- package/dist/mcp-app/connect.notices.txt +1 -1
- package/dist/mcp-app/receipt.html +1298 -1463
- package/dist/mcp-app/receipt.notices.txt +226 -1204
- package/dist/mcp-app/review.html +1298 -1463
- package/dist/mcp-app/review.notices.txt +226 -1204
- package/dist/mcp-app/settings.html +3133 -0
- package/dist/mcp-app/settings.notices.txt +973 -0
- package/dist/mcp-ui/contracts.js +8 -2
- package/dist/mcp-ui/readCards.js +1 -1
- package/dist/mcp-ui/tools.js +34 -4
- package/dist/review-server/server.js +3 -51
- package/dist/runtime/application.js +41 -14
- package/dist/runtime/config.js +0 -2
- package/dist/runtime/localSettingsService.js +23 -2
- package/dist/runtime/logger.js +0 -2
- package/dist/runtime/reviewServerAcquire.js +1 -18
- package/dist/runtime/runtimeTermination.js +1 -4
- package/dist/runtime/shared/authenticatedFetch.js +2 -4
- package/dist/runtime/shared/control.js +4 -7
- package/dist/runtime/shared/dataAccess.js +0 -2
- package/dist/runtime/shared/http.js +14 -0
- package/dist/runtime/shared/mcpHttp.js +6 -6
- package/dist/runtime/shared/ownerLease.js +0 -4
- package/dist/runtime/shared/server.js +1 -2
- package/dist/runtime/shared/stdio.js +2 -5
- package/dist/runtime/smokeMainnetRead.js +0 -3
- package/dist/runtime/smokeReview.js +0 -4
- package/dist/runtime/start.js +0 -2
- package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
- package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
- package/dist/runtime/walletConnectConfig.js +0 -2
- package/dist/runtime/walletConnectStorage.js +2 -10
- package/dist/runtime/walletConnectTransport.js +0 -11
- package/dist/runtime/walletSdkChildRuntime.js +2 -6
- package/dist/runtime/walletSdkIpc.js +2 -1
- package/dist/runtime/walletSdkProcess.js +3 -8
- package/docs/AGENT_BEHAVIOR.md +45 -15
- package/docs/LOCAL_DB_ARCHITECTURE.md +145 -22
- package/docs/MCP_SETUP.md +99 -39
- package/docs/MCP_TOOLS.md +288 -24
- package/docs/SDK_API.md +63 -10
- package/docs/UTILITY_INDEX.md +9 -6
- package/docs/WALLET_CONNECTION.md +32 -10
- package/package.json +22 -8
- package/dist/core/activity/schemaVersion.js +0 -2
- package/dist/core/session/keyedRecordStore.js +0 -18
- package/dist/core/session/settingsSession.js +0 -1
- package/dist/core/session/settingsSessions.js +0 -45
- package/dist/review-app/brand-dark.svg +0 -1
- package/dist/review-app/brand-light.svg +0 -1
- package/dist/review-app/favicon.svg +0 -1
- package/dist/review-app/settings.css +0 -1
- package/dist/review-app/settings.js +0 -1
- package/dist/review-app/ui.css +0 -809
- package/dist/review-server/assets.js +0 -48
- package/dist/review-server/html.js +0 -45
- package/dist/review-server/http.js +0 -47
- package/dist/review-server/middleware/reviewToken.js +0 -7
- package/dist/review-server/settingsApi.js +0 -158
- package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -815
- package/docs/FRONTEND_POLICY.md +0 -534
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { workflowConnectionEligibility } from "./workflowState.js";
|
|
1
2
|
import { SessionStoreError } from "./sessionErrors.js";
|
|
2
3
|
import { Transaction } from "@mysten/sui/transactions";
|
|
3
4
|
import { verifyTransactionSignature } from "@mysten/sui/verify";
|
|
@@ -18,7 +19,9 @@ export class WalletWorkflow {
|
|
|
18
19
|
observing = new Map();
|
|
19
20
|
preparing = new Set();
|
|
20
21
|
pendingWrites = new Map();
|
|
22
|
+
parentSignatureWork = new Set();
|
|
21
23
|
eventListener;
|
|
24
|
+
settingsRecovery;
|
|
22
25
|
constructor(options) {
|
|
23
26
|
this.options = options;
|
|
24
27
|
}
|
|
@@ -42,12 +45,13 @@ export class WalletWorkflow {
|
|
|
42
45
|
report(stage) { try {
|
|
43
46
|
this.options.logger.error("Wallet operation failed", { stage });
|
|
44
47
|
}
|
|
45
|
-
catch {
|
|
48
|
+
catch { } }
|
|
46
49
|
async start() {
|
|
47
50
|
this.assertCurrent();
|
|
48
51
|
if (this.started)
|
|
49
52
|
throw new WorkflowConflict("The wallet service has already started.");
|
|
50
53
|
this.started = true;
|
|
54
|
+
this.options.records.setWalletRunId(this.options.runtime.runId);
|
|
51
55
|
this.options.records.recover(this.now());
|
|
52
56
|
this.bindEvents();
|
|
53
57
|
this.options.runtime.start((event) => this.eventListener?.(event));
|
|
@@ -61,9 +65,11 @@ export class WalletWorkflow {
|
|
|
61
65
|
return;
|
|
62
66
|
const runtime = this.options.runtime;
|
|
63
67
|
if (event.type === "snapshot") {
|
|
64
|
-
this.options.records.applyWalletObservation(event.snapshot, event.previous, event.ready);
|
|
65
|
-
if (event.ready)
|
|
68
|
+
this.options.records.applyWalletObservation(event.snapshot, event.previous, event.ready, () => this.settingsRecovery?.runtimeEvent(event));
|
|
69
|
+
if (event.ready) {
|
|
66
70
|
runtime.publishReady(event.runId);
|
|
71
|
+
this.settingsRecovery?.readyPublished(event.runId);
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
else if (event.type === "stage") {
|
|
69
75
|
this.options.records.publishWalletState();
|
|
@@ -71,8 +77,38 @@ export class WalletWorkflow {
|
|
|
71
77
|
else if (event.type === "lost") {
|
|
72
78
|
this.pairings.clear();
|
|
73
79
|
this.retainWrite({ kind: "runtime_failure", runId: event.runId, observedAt: this.now().toISOString() });
|
|
80
|
+
this.settingsRecovery?.runtimeEvent(event);
|
|
81
|
+
}
|
|
82
|
+
else if (event.type === "exit") {
|
|
83
|
+
this.retainWrite({ kind: "runtime_exit", runId: event.runId, observedAt: this.now().toISOString(), parentProcessing: [...this.parentSignatureWork] });
|
|
74
84
|
}
|
|
75
85
|
}
|
|
86
|
+
attachSettingsRecovery(recovery) {
|
|
87
|
+
if (this.settingsRecovery)
|
|
88
|
+
throw new Error("Settings recovery is already attached.");
|
|
89
|
+
this.settingsRecovery = recovery;
|
|
90
|
+
}
|
|
91
|
+
recoveryContext() {
|
|
92
|
+
this.synchronizeWalletState();
|
|
93
|
+
return this.currentRecoveryFacts();
|
|
94
|
+
}
|
|
95
|
+
currentRecoveryFacts() {
|
|
96
|
+
return { availability: this.walletAvailability(), impact: this.options.records.recoveryImpact(this.options.runtime.runId) };
|
|
97
|
+
}
|
|
98
|
+
blockSettingsAdmissionFailure() {
|
|
99
|
+
const runId = this.options.runtime.runId;
|
|
100
|
+
this.options.runtime.block("wallet_state_unavailable");
|
|
101
|
+
this.retainWrite({ kind: "runtime_failure", runId, observedAt: this.now().toISOString() });
|
|
102
|
+
}
|
|
103
|
+
fenceForSettings(runId) { this.assertCurrent(); this.options.runtime.fence(runId); }
|
|
104
|
+
replaceForSettings(runId, reserve) {
|
|
105
|
+
this.assertCurrent();
|
|
106
|
+
return this.options.runtime.replace(runId, (nextRunId) => {
|
|
107
|
+
this.synchronizeWalletState();
|
|
108
|
+
reserve(nextRunId);
|
|
109
|
+
this.options.records.setWalletRunId(nextRunId);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
76
112
|
dataReplaced() {
|
|
77
113
|
this.pairings.clear();
|
|
78
114
|
this.pendingWrites.clear();
|
|
@@ -92,14 +128,13 @@ export class WalletWorkflow {
|
|
|
92
128
|
this.retainWrite(completion);
|
|
93
129
|
}
|
|
94
130
|
retainWrite(completion) {
|
|
95
|
-
// An old data generation cannot add repair work to the replacement data.
|
|
96
131
|
try {
|
|
97
132
|
this.assertCurrent();
|
|
98
133
|
}
|
|
99
134
|
catch {
|
|
100
135
|
return;
|
|
101
136
|
}
|
|
102
|
-
const key = completion.kind === "runtime_failure" ?
|
|
137
|
+
const key = completion.kind === "runtime_failure" || completion.kind === "runtime_exit" ? `${completion.kind}:${completion.runId}` :
|
|
103
138
|
completion.kind === "request" ? `request:${completion.id}:${completion.field}` : `connection:${completion.cardId}:${completion.runId}`;
|
|
104
139
|
if (!this.pendingWrites.has(key))
|
|
105
140
|
this.pendingWrites.set(key, completion);
|
|
@@ -115,7 +150,9 @@ export class WalletWorkflow {
|
|
|
115
150
|
this.options.records.settle(completion.id, completion.field, false);
|
|
116
151
|
else if (completion.kind === "connection")
|
|
117
152
|
this.options.records.settleConnection(completion.id, completion);
|
|
118
|
-
else if (completion.
|
|
153
|
+
else if (completion.kind === "runtime_exit")
|
|
154
|
+
this.options.records.recordRunExit(completion.runId, completion.parentProcessing);
|
|
155
|
+
else
|
|
119
156
|
this.options.records.recordWalletFailure(completion);
|
|
120
157
|
this.pendingWrites.delete(key);
|
|
121
158
|
}
|
|
@@ -133,10 +170,10 @@ export class WalletWorkflow {
|
|
|
133
170
|
failure = error;
|
|
134
171
|
}
|
|
135
172
|
}
|
|
136
|
-
// A completed callback's pending flag is housekeeping, not authority to
|
|
137
|
-
// read recorded facts. Retain failed repairs for the next applicable read.
|
|
138
173
|
if (failure && walletDependent)
|
|
139
174
|
throw failure;
|
|
175
|
+
if (walletDependent)
|
|
176
|
+
this.settingsRecovery?.synchronize();
|
|
140
177
|
if (walletDependent)
|
|
141
178
|
this.options.runtime.synchronize();
|
|
142
179
|
}
|
|
@@ -159,16 +196,23 @@ export class WalletWorkflow {
|
|
|
159
196
|
}
|
|
160
197
|
return { status: "ready" };
|
|
161
198
|
}
|
|
162
|
-
|
|
199
|
+
reconcileConnectionContext() {
|
|
163
200
|
this.assertCurrent();
|
|
164
201
|
this.synchronizeWalletState();
|
|
165
202
|
this.recoverDisconnects();
|
|
166
|
-
const state = this.options.records.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
203
|
+
const state = this.options.records.reconcileAndEvaluate({ purpose: "current_wallet", walletAvailability: this.walletAvailability(), walletObservation: this.observation() });
|
|
204
|
+
return { connections: state.connections, connectionConflict: state.connectionConflict, walletAvailability: state.walletAvailability,
|
|
205
|
+
walletObservation: state.walletObservation, manageableConnectionIds: state.manageableConnectionIds,
|
|
206
|
+
connectionReconciliationRequired: state.connectionReconciliationRequired, assetReadAccount: state.assetReadAccount ?? { status: "address_required" } };
|
|
207
|
+
}
|
|
208
|
+
currentConnectionContext() {
|
|
209
|
+
this.assertCurrent();
|
|
210
|
+
const facts = this.options.records.readConnectionFacts(this.now()), availability = this.walletAvailability();
|
|
211
|
+
const { selection: _selection, ...eligibility } = workflowConnectionEligibility({ ...facts, walletAvailability: availability });
|
|
212
|
+
return { connections: facts.connections, walletAvailability: availability, walletObservation: facts.walletObservation,
|
|
213
|
+
connectionConflict: eligibility.connectionConflict, manageableConnectionIds: eligibility.manageableConnectionIds,
|
|
214
|
+
connectionReconciliationRequired: eligibility.connectionReconciliationRequired,
|
|
215
|
+
assetReadAccount: eligibility.assetReadAccount ?? { status: "address_required" } };
|
|
172
216
|
}
|
|
173
217
|
recoverDisconnects() {
|
|
174
218
|
const available = this.walletAvailability().status === "available";
|
|
@@ -178,7 +222,6 @@ export class WalletWorkflow {
|
|
|
178
222
|
const pending = this.options.records.pendingDisconnect(record.connection.connectionId);
|
|
179
223
|
if (!pending)
|
|
180
224
|
continue;
|
|
181
|
-
// Disabled transports also return undefined; that is not session absence.
|
|
182
225
|
const absent = available && !!record.topic && this.observed(record.topic)?.status === "absent";
|
|
183
226
|
this.options.records.recoverDisconnect(pending.state.cardId, record.connection.connectionId, record.connection.revision, absent);
|
|
184
227
|
}
|
|
@@ -194,7 +237,9 @@ export class WalletWorkflow {
|
|
|
194
237
|
this.options.records.failPreparation(id, preparation, this.now());
|
|
195
238
|
const candidate = await this.options.sessions.inspectReview(id, this.now());
|
|
196
239
|
this.assertCurrent();
|
|
197
|
-
|
|
240
|
+
if (!candidate)
|
|
241
|
+
throw new SessionStoreError("session_not_found", "The saved review is unavailable.");
|
|
242
|
+
const evaluated = this.options.records.reconcileAndEvaluate({ purpose: "review", reviewSessionId: id, candidate, expectedCard, readTarget,
|
|
198
243
|
walletAvailability: this.walletAvailability(), walletObservation: this.observation(), uiObservation });
|
|
199
244
|
this.options.sessions.recordEvaluationEvents(evaluated.events);
|
|
200
245
|
return evaluated;
|
|
@@ -208,23 +253,22 @@ export class WalletWorkflow {
|
|
|
208
253
|
if (explicitResult) {
|
|
209
254
|
await this.observing.get(state.request.attemptId);
|
|
210
255
|
this.assertCurrent();
|
|
211
|
-
// An awaited receipt may have committed new facts. Return one freshly
|
|
212
|
-
// evaluated snapshot; ordinary status reads do not await external I/O.
|
|
213
256
|
state = await this.evaluateReview(id, undefined, false, state.request.attemptId);
|
|
214
257
|
}
|
|
215
258
|
}
|
|
216
259
|
if (!state.session)
|
|
217
260
|
return undefined;
|
|
218
261
|
return { session: state.session, request: state.request, hasReviewInput: state.hasReviewInput, connectionConflict: state.connectionConflict, walletAvailability: state.walletAvailability, walletObservation: this.observation(),
|
|
262
|
+
connectionReconciliationRequired: state.connectionReconciliationRequired,
|
|
219
263
|
progress: reviewProgress(!!state.session.preparationId, state.request, state.walletAvailability, { stopped: state.authority?.observation_stopped === 1, pending: state.authority?.lookup_pending === 1 }) };
|
|
220
264
|
}
|
|
221
265
|
async describe(record, uiObservation = false) {
|
|
222
266
|
this.assertCurrent();
|
|
223
267
|
if (record.state.kind === "connect")
|
|
224
|
-
this.
|
|
268
|
+
this.reconcileConnectionContext();
|
|
225
269
|
const evaluated = record.state.kind === "review"
|
|
226
270
|
? await this.evaluateReview(String(record.state.input.reviewSessionId), record, uiObservation)
|
|
227
|
-
: this.options.records.
|
|
271
|
+
: this.options.records.reconcileAndEvaluate({ purpose: "current_wallet", expectedCard: record, walletAvailability: this.walletAvailability(), walletObservation: this.observation() });
|
|
228
272
|
this.assertCurrent();
|
|
229
273
|
if (record.state.kind === "connect") {
|
|
230
274
|
const data = projectConnectionView(evaluated);
|
|
@@ -391,8 +435,6 @@ export class WalletWorkflow {
|
|
|
391
435
|
catch (error) {
|
|
392
436
|
displayFailure = error;
|
|
393
437
|
}
|
|
394
|
-
// The approval remains pending even if setup/QR recording fails or the
|
|
395
|
-
// wallet dependency is disabled. Settle only after that promise ends.
|
|
396
438
|
const session = await result.approval;
|
|
397
439
|
if (displayFailure)
|
|
398
440
|
throw displayFailure;
|
|
@@ -444,6 +486,7 @@ export class WalletWorkflow {
|
|
|
444
486
|
await this.performSignature(request, connectionId, bytesBase64, run);
|
|
445
487
|
}
|
|
446
488
|
finally {
|
|
489
|
+
this.parentSignatureWork.delete(request.attemptId);
|
|
447
490
|
this.completeCallback({ kind: "request", id: request.attemptId, field: "sdk_pending" });
|
|
448
491
|
}
|
|
449
492
|
}
|
|
@@ -458,6 +501,7 @@ export class WalletWorkflow {
|
|
|
458
501
|
this.assertWalletAvailable(run);
|
|
459
502
|
response = await run.sign({ topic: connection.topic, account: request.account, transactionBytesBase64: bytesBase64,
|
|
460
503
|
sessionVersion: proof.observed.version }, request.attemptId);
|
|
504
|
+
this.parentSignatureWork.add(request.attemptId);
|
|
461
505
|
this.assertWalletAvailable(run);
|
|
462
506
|
}
|
|
463
507
|
catch {
|
|
@@ -494,8 +538,6 @@ export class WalletWorkflow {
|
|
|
494
538
|
return;
|
|
495
539
|
}
|
|
496
540
|
catch (error) {
|
|
497
|
-
// SDK verification can itself depend on a remote source. An exception is
|
|
498
|
-
// not proof of a mismatched transaction or signer.
|
|
499
541
|
if (!this.stopped)
|
|
500
542
|
this.options.records.transitionRequest(request.attemptId, "request_failed", this.now(), {
|
|
501
543
|
reason: this.walletAvailability().status !== "available" ? "Wallet operations became unavailable before submission. The returned signature will not be submitted."
|
|
@@ -506,7 +548,7 @@ export class WalletWorkflow {
|
|
|
506
548
|
try {
|
|
507
549
|
await this.options.submitTransaction(bytes, response.signature);
|
|
508
550
|
}
|
|
509
|
-
catch {
|
|
551
|
+
catch { }
|
|
510
552
|
finally {
|
|
511
553
|
this.completeCallback({ kind: "request", id: request.attemptId, field: "submit_pending" });
|
|
512
554
|
}
|
|
@@ -527,8 +569,6 @@ export class WalletWorkflow {
|
|
|
527
569
|
const current = this.options.records.request(id);
|
|
528
570
|
if (!current || !(isInitialChainObservation(current.requestStatus) || explicit && current.requestStatus === "outcome_unknown"))
|
|
529
571
|
return;
|
|
530
|
-
// Starting another lookup needs its own previous callback to be settled.
|
|
531
|
-
// Stored terminal facts above never depend on this write succeeding.
|
|
532
572
|
for (const [key, write] of this.pendingWrites)
|
|
533
573
|
if (write.kind === "request" && write.id === id && write.field === "lookup_pending") {
|
|
534
574
|
this.settlePendingWrite(key, write);
|
|
@@ -549,7 +589,7 @@ export class WalletWorkflow {
|
|
|
549
589
|
if (receipt.status === "found")
|
|
550
590
|
details = projectReadCardResult("receipt", { digest: request.transactionDigest }, receipt);
|
|
551
591
|
}
|
|
552
|
-
catch {
|
|
592
|
+
catch { }
|
|
553
593
|
}
|
|
554
594
|
this.assertCurrent();
|
|
555
595
|
this.options.records.transitionRequest(id, "completed", this.now(), { execution: {
|
|
@@ -581,7 +621,7 @@ export class WalletWorkflow {
|
|
|
581
621
|
this.recoverDisconnects();
|
|
582
622
|
const rows = [];
|
|
583
623
|
for (const card of this.options.records.pendingConnectionCards(this.now())) {
|
|
584
|
-
const evaluated = this.options.records.
|
|
624
|
+
const evaluated = this.options.records.reconcileAndEvaluate({ purpose: "current_wallet", expectedCard: card, walletAvailability: this.walletAvailability(), walletObservation: this.observation() });
|
|
585
625
|
if (evaluated.record?.state.state === "closed")
|
|
586
626
|
continue;
|
|
587
627
|
const data = projectConnectionView(evaluated);
|
|
@@ -2,8 +2,6 @@ import { walletConnectionSelection } from "./walletConnection.js";
|
|
|
2
2
|
import { isInitialChainObservation } from "./transactionRequest.js";
|
|
3
3
|
export const workflowActions = ["connect", "disconnect", "use_account", "stop_connection", "prepare_review",
|
|
4
4
|
"request_signature", "cancel", "stop_waiting", "read_result"];
|
|
5
|
-
// Read context may change; an existing review's account binding may not.
|
|
6
|
-
// Both evaluated choices and admission consume this relation with current facts.
|
|
7
5
|
export function reviewPreparationAccount(boundAccount, activeAccount, selectedAccount = activeAccount) {
|
|
8
6
|
if (!activeAccount)
|
|
9
7
|
return { allowed: false, message: "No account from a connected wallet is selected for this review." };
|
|
@@ -15,24 +13,26 @@ export function reviewPreparationAccount(boundAccount, activeAccount, selectedAc
|
|
|
15
13
|
message: "The selected account has changed since this request was made." };
|
|
16
14
|
return { allowed: true, account: activeAccount };
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
export function workflowConnectionEligibility(facts) {
|
|
17
|
+
const selection = walletConnectionSelection(facts.connections, Date.parse(facts.evaluatedAt), facts.connectionPresence, facts.activeAccount ? { address: facts.activeAccount, walletId: facts.activeAccountConnectionId } : undefined);
|
|
18
|
+
const available = facts.walletAvailability.status === "available", dependent = facts.walletDependent !== false;
|
|
19
|
+
const usableConnectionId = available && dependent ? selection.connection?.connectionId : undefined;
|
|
20
|
+
const assetReadAccount = !dependent ? undefined
|
|
21
|
+
: usableConnectionId ? selection.assetReadAccount : { status: "address_required" };
|
|
22
|
+
return { selection, usableConnectionId, assetReadAccount, connectionConflict: selection.conflict,
|
|
23
|
+
manageableConnectionIds: available ? selection.manageableConnectionIds : [],
|
|
24
|
+
connectionReconciliationRequired: available && dependent ? selection.connectionReconciliationRequired : undefined };
|
|
25
|
+
}
|
|
20
26
|
export function workflowEligibility(facts) {
|
|
21
27
|
const { record, session, request, authority, walletAvailability } = facts;
|
|
22
28
|
const at = Date.parse(facts.evaluatedAt);
|
|
23
29
|
const cardRemaining = record?.ownerId === facts.ownerId ? Math.max(0, Date.parse(record.state.expiresAt) - at) : 0;
|
|
24
|
-
// A later-created input card cannot extend its review session. Admitted
|
|
25
|
-
// requests and management cards retain their separate observation authority.
|
|
26
30
|
const remaining = record?.scope === "review" && session && !request
|
|
27
31
|
? Math.min(cardRemaining, Math.max(0, Date.parse(session.expiresAt) - at)) : cardRemaining;
|
|
28
32
|
const inputAvailable = remaining > 0 && record?.state.state === "ready" && record.acceptedInput === undefined;
|
|
29
33
|
const allowedActions = [];
|
|
30
|
-
const selection =
|
|
31
|
-
|
|
32
|
-
const usableConnectionId = walletAvailability.status === "available" && facts.walletDependent !== false
|
|
33
|
-
? selection.connection?.connectionId : undefined;
|
|
34
|
-
const assetReadAccount = facts.walletDependent === false ? undefined
|
|
35
|
-
: usableConnectionId ? selection.assetReadAccount : { status: "address_required" };
|
|
34
|
+
const { selection, ...connectionEligibility } = workflowConnectionEligibility(facts);
|
|
35
|
+
const { assetReadAccount } = connectionEligibility;
|
|
36
36
|
let preparationIssue;
|
|
37
37
|
if (record?.scope === "connect") {
|
|
38
38
|
if (inputAvailable) {
|
|
@@ -40,7 +40,7 @@ export function workflowEligibility(facts) {
|
|
|
40
40
|
if (walletAvailability.status === "available") {
|
|
41
41
|
if (["connect", "manage"].includes(String(record.state.input.intent)) && selection.pairingAllowed)
|
|
42
42
|
allowedActions.push("connect");
|
|
43
|
-
if (
|
|
43
|
+
if (selection.manageableConnectionIds.length)
|
|
44
44
|
allowedActions.push("disconnect");
|
|
45
45
|
if (selection.connection)
|
|
46
46
|
allowedActions.push("use_account");
|
|
@@ -74,7 +74,7 @@ export function workflowEligibility(facts) {
|
|
|
74
74
|
session.status === "ready_for_wallet_review" && (!request || request.reviewRevision !== session.reviewRevision)
|
|
75
75
|
? session.reviewState?.humanReadableReview?.freshness.expiresAt : undefined;
|
|
76
76
|
const nextRead = expiry && session ? Math.min(remaining, Date.parse(session.expiresAt) - at, Date.parse(expiry) - at) : undefined;
|
|
77
|
-
return { allowedActions, preparationIssue,
|
|
77
|
+
return { allowedActions, preparationIssue, ...connectionEligibility,
|
|
78
78
|
actionRemainingMs: remaining, inputRemainingMs: inputAvailable ? remaining : 0,
|
|
79
79
|
...(nextRead !== undefined && nextRead > 0 ? { nextStateReadAfterMs: nextRead } : {}) };
|
|
80
80
|
}
|
|
@@ -9,7 +9,6 @@ import { reviewProgress, EXECUTION_POLLING_INTERVAL_SECONDS } from "./status.js"
|
|
|
9
9
|
import { walletRunIdSchema, walletRecoverySchema, walletObservationSchema } from "./walletRuntime.js";
|
|
10
10
|
export const CONNECT_BOUNDARY = "A wallet connection provides account context; it is not a transaction approval or proof of address ownership.";
|
|
11
11
|
export const REVIEW_BOUNDARY = "Review evidence is not a safety guarantee. Only an explicit card action and wallet approval may authorize this exact transaction.";
|
|
12
|
-
// A projection of an admitted card operation, not a new wallet connection state.
|
|
13
12
|
export const connectionViewSchema = walletConnectionSchema.extend({ pendingAction: z.literal("disconnect").optional() });
|
|
14
13
|
export { assetReadAccountSchema } from "./walletConnection.js";
|
|
15
14
|
export const pendingConnectionStatusSchema = z.enum(["input_required", "awaiting_approval", "disconnect_pending"]);
|
|
@@ -37,6 +36,8 @@ export const workflowViewSchema = z.object({
|
|
|
37
36
|
walletObservation: walletObservationSchema.optional(),
|
|
38
37
|
runtimeRecovery: walletRecoverySchema.optional(),
|
|
39
38
|
connectionConflict: connectionConflictSchema.optional(),
|
|
39
|
+
manageableConnectionIds: z.array(z.string().min(1)),
|
|
40
|
+
connectionReconciliationRequired: z.literal(true).optional(),
|
|
40
41
|
usableConnectionId: z.string().min(1).optional(),
|
|
41
42
|
assetReadAccount: assetReadAccountSchema.optional(),
|
|
42
43
|
acceptedWalletRunId: walletRunIdSchema.optional(),
|
|
@@ -60,17 +61,24 @@ export const workflowViewSchema = z.object({
|
|
|
60
61
|
}).strict().superRefine((view, context) => {
|
|
61
62
|
const connection = view.connections.find((item) => item.connectionId === view.usableConnectionId);
|
|
62
63
|
if (view.usableConnectionId && (!connection || connection.status !== "connected" ||
|
|
63
|
-
view.walletAvailability.status !== "available" || view.connectionConflict ||
|
|
64
|
+
view.walletAvailability.status !== "available" || view.connectionConflict || view.connectionReconciliationRequired ||
|
|
64
65
|
view.connections.some((item) => item.pendingAction || item.status === "awaiting_approval"))) {
|
|
65
66
|
context.addIssue({ code: "custom", path: ["usableConnectionId"], message: "The usable connection must match current connection facts." });
|
|
66
67
|
}
|
|
68
|
+
if (new Set(view.manageableConnectionIds).size !== view.manageableConnectionIds.length ||
|
|
69
|
+
view.manageableConnectionIds.some((id) => !view.connections.some((item) => item.connectionId === id &&
|
|
70
|
+
!item.pendingAction && item.status !== "awaiting_approval"))) {
|
|
71
|
+
context.addIssue({ code: "custom", path: ["manageableConnectionIds"], message: "Management must name distinct recorded targets without pending operations." });
|
|
72
|
+
}
|
|
73
|
+
if (view.connectionReconciliationRequired && (view.usableConnectionId || view.assetReadAccount?.status === "available" ||
|
|
74
|
+
view.allowedActions.some((action) => ["connect", "use_account", "prepare_review", "request_signature"].includes(action)))) {
|
|
75
|
+
context.addIssue({ code: "custom", path: ["connectionReconciliationRequired"], message: "Unrecorded SDK connections cannot authorize connection or account use." });
|
|
76
|
+
}
|
|
67
77
|
if (view.assetReadAccount?.status === "available" && (!connection ||
|
|
68
78
|
!connection.accounts.includes(view.assetReadAccount.account) || view.activeAccount !== view.assetReadAccount.account)) {
|
|
69
79
|
context.addIssue({ code: "custom", path: ["assetReadAccount"], message: "The default address must belong to the usable connection and stored selection." });
|
|
70
80
|
}
|
|
71
81
|
});
|
|
72
|
-
// Automatic preparation uses the same current selection and typed admission as
|
|
73
|
-
// a deliberate retry. A failed computation is not a reason to compute in a loop.
|
|
74
82
|
function automaticReviewAction(input) {
|
|
75
83
|
const { record, session, request } = input;
|
|
76
84
|
if (!session || record?.scope !== "review" || request || session.preparationError ||
|
|
@@ -87,7 +95,6 @@ function automaticReviewAction(input) {
|
|
|
87
95
|
return { action: "prepare_review", walletRunId: input.walletAvailability.walletRunId, connectionId: candidates[0].connectionId,
|
|
88
96
|
account, reviewRevision: session.reviewRevision };
|
|
89
97
|
}
|
|
90
|
-
// Method support filters wallet choices; it does not authorize a signature.
|
|
91
98
|
export function reviewWalletChoices(selection, account, requireSigningMethod = false) {
|
|
92
99
|
return selection.connections.filter((item) => item.connectionId === selection.usableConnectionId && item.accounts.includes(account) &&
|
|
93
100
|
(!requireSigningMethod || item.methods.includes("sui_signTransaction")));
|
|
@@ -101,10 +108,11 @@ function nextStateRead(input) {
|
|
|
101
108
|
.filter((value) => value !== undefined && value > 0);
|
|
102
109
|
return delays.length ? Math.min(...delays) : undefined;
|
|
103
110
|
}
|
|
104
|
-
// These projections do not read clocks, storage, or admission rules. The
|
|
105
|
-
// evaluated facts and choices were collected together by the database owner.
|
|
106
111
|
export function projectConnectionView(input) {
|
|
107
|
-
const {
|
|
112
|
+
const { walletAvailability } = input;
|
|
113
|
+
const stored = input.record?.state.data;
|
|
114
|
+
const connection = input.record?.acceptedInput?.action === "disconnect" && stored?.disconnectOutcome
|
|
115
|
+
? connectionViewSchema.parse(stored.disconnectOutcome) : input.connection;
|
|
108
116
|
const readAfter = nextStateRead(input);
|
|
109
117
|
const progress = input.runtimeRecovery ? { status: "phase" in input.runtimeRecovery ? "waiting" : "idle" } :
|
|
110
118
|
workflowProgress(connection?.status === "awaiting_approval" || connection?.pendingAction === "disconnect", true, walletAvailability);
|
|
@@ -114,6 +122,7 @@ export function projectConnectionView(input) {
|
|
|
114
122
|
? { automaticAction: { action: "connect", walletRunId: walletAvailability.walletRunId } } : {}),
|
|
115
123
|
runtimeRecovery: input.runtimeRecovery, walletObservation: input.walletObservation,
|
|
116
124
|
connectionConflict: input.connectionConflict, usableConnectionId: input.usableConnectionId, assetReadAccount: input.assetReadAccount,
|
|
125
|
+
manageableConnectionIds: input.manageableConnectionIds, connectionReconciliationRequired: input.connectionReconciliationRequired,
|
|
117
126
|
connectionAction: ["connect", "disconnect"].includes(String(input.record?.acceptedInput?.action)) ? input.record?.acceptedInput?.action : undefined,
|
|
118
127
|
acceptedWalletRunId: input.record?.acceptedInput?.walletRunId,
|
|
119
128
|
...(readAfter === undefined ? {} : { nextStateReadAfterMs: readAfter }),
|
|
@@ -133,6 +142,7 @@ export function projectReviewView(input) {
|
|
|
133
142
|
return workflowViewSchema.parse({ kind: "review", mode: record.scope === "review_manage" ? "review_manage" : "review",
|
|
134
143
|
allowedActions: input.allowedActions, actionRemainingMs: input.actionRemainingMs, observe: progress.status === "waiting", progress, walletAvailability,
|
|
135
144
|
connections: input.connections, connectionConflict: input.connectionConflict, usableConnectionId: input.usableConnectionId,
|
|
145
|
+
manageableConnectionIds: input.manageableConnectionIds, connectionReconciliationRequired: input.connectionReconciliationRequired,
|
|
136
146
|
assetReadAccount: input.assetReadAccount, activeAccount: input.activeAccount, boundary: REVIEW_BOUNDARY, receipt: input.receipt,
|
|
137
147
|
walletObservation: input.walletObservation,
|
|
138
148
|
acceptedWalletRunId: record.acceptedInput?.walletRunId,
|
package/dist/core/suiAddress.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { isValidSuiAddress, isValidTransactionDigest, normalizeSuiAddress } from "@mysten/sui/utils";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
// Use the string-only schema at MCP/HTTP output boundaries because zod transforms
|
|
4
|
-
// cannot be represented in MCP JSON Schema. Use the normalized schema for values
|
|
5
|
-
// before storing them as local session state.
|
|
6
3
|
export const suiAddressStringSchema = z
|
|
7
4
|
.string()
|
|
8
5
|
.refine(isValidSuiAddress, "Expected a 32-byte Sui address");
|
|
9
|
-
// Shared Sui transaction digest schema (pinned SDK source of truth). Used at
|
|
10
|
-
// MCP/HTTP boundaries and by the signable-adapter commitment fields.
|
|
11
6
|
export const suiTransactionDigestSchema = z
|
|
12
7
|
.string()
|
|
13
8
|
.refine(isValidTransactionDigest, "Expected a Sui transaction digest");
|
package/dist/core/suiEndpoint.js
CHANGED
|
@@ -35,9 +35,6 @@ export function parseGrpcUrl(value) {
|
|
|
35
35
|
}
|
|
36
36
|
return `${parsed.protocol}//${authority}`;
|
|
37
37
|
}
|
|
38
|
-
// GraphQL endpoints commonly include a path such as /graphql and may omit an
|
|
39
|
-
// explicit port. gRPC endpoints stay stricter because the pinned gRPC client
|
|
40
|
-
// runtime expects only scheme, host, and explicit port.
|
|
41
38
|
export function parseGraphqlUrl(value) {
|
|
42
39
|
let parsed;
|
|
43
40
|
if (value.length > MAX_SUI_GRAPHQL_URL_LENGTH) {
|
package/dist/mcp/prompts.js
CHANGED
|
@@ -24,9 +24,6 @@ export const MCP_PROMPTS = [
|
|
|
24
24
|
].join("\n")
|
|
25
25
|
}
|
|
26
26
|
];
|
|
27
|
-
// Platform-owned boundary language appended to every adapter prompt surface.
|
|
28
|
-
// Adapters contribute copy about their own action only; they cannot weaken
|
|
29
|
-
// or omit these lines.
|
|
30
27
|
const PLATFORM_PROMPT_BOUNDARY_LINES = [
|
|
31
28
|
"Present the internal Review card and summarize the returned review checks.",
|
|
32
29
|
"This MCP response never contains signing data, transaction bytes, or signing readiness; only an explicit UI action and wallet approval authorize the backend request; request status is separate from server-verified chain execution facts."
|
|
@@ -76,18 +73,9 @@ export function registerMcpPrompts(server, surfaces = []) {
|
|
|
76
73
|
]
|
|
77
74
|
}));
|
|
78
75
|
}
|
|
79
|
-
// Adapter prompt surfaces: action-first names (`swap-deep`) with one
|
|
80
|
-
// free-text intent argument so clients can pass the whole request in one
|
|
81
|
-
// line (Claude Code: /mcp__sui-mcp__swap-deep 10 sui to usdc;
|
|
82
|
-
// Claude Desktop shows a single input field). The model parses the
|
|
83
|
-
// intent; this server never does.
|
|
84
76
|
for (const surface of surfaces) {
|
|
85
77
|
registerSurfacePrompt(server, promptNameFor(surface), surface, surface.description);
|
|
86
78
|
}
|
|
87
|
-
// Bare action prompts are always registered. One protocol: straight to it.
|
|
88
|
-
// Several protocols: an optional `protocol` argument (completion suggests
|
|
89
|
-
// the slugs) and explicit instructions to ask the user - the venue is the
|
|
90
|
-
// user's choice, never the server's.
|
|
91
79
|
for (const [action, group] of actionGroups(surfaces)) {
|
|
92
80
|
registerBareActionPrompt(server, action, group);
|
|
93
81
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EXTERNAL_PROPOSAL_SETTLEMENT_TOKEN_SELECTION_UNSUPPORTED_CLAIM_ID } from "../core/proposal/types.js";
|
|
2
2
|
import { TOOL_NAMES } from "./toolNames.js";
|
|
3
|
-
export function interactionStatusUserAnswerUse(hasWalletObservation = false, hasConnectionConflict = false) {
|
|
3
|
+
export function interactionStatusUserAnswerUse(hasWalletObservation = false, hasConnectionConflict = false, reconciliationRequired = false) {
|
|
4
4
|
return {
|
|
5
5
|
canAnswer: [
|
|
6
6
|
"stored_active_account_read_context",
|
|
@@ -8,6 +8,7 @@ export function interactionStatusUserAnswerUse(hasWalletObservation = false, has
|
|
|
8
8
|
"connection_qualified_default_asset_account",
|
|
9
9
|
"pending_local_wallet_connection_interactions",
|
|
10
10
|
...(hasConnectionConflict ? ["multiple_saved_connections_require_user_targeted_disconnection"] : []),
|
|
11
|
+
...(reconciliationRequired ? ["unrecorded_service_connection_requires_wallet_removal_and_confirmed_absence"] : []),
|
|
11
12
|
"pending_local_review_interactions",
|
|
12
13
|
"wallet_operation_availability_separate_from_stored_transaction_facts"
|
|
13
14
|
],
|
|
@@ -24,9 +25,11 @@ export function interactionStatusUserAnswerUse(hasWalletObservation = false, has
|
|
|
24
25
|
"complete_wallet_history",
|
|
25
26
|
"profit_or_pnl"
|
|
26
27
|
],
|
|
27
|
-
preconditionFields: ["walletAvailability", "assetReadAccount", ...(hasConnectionConflict ? ["connectionConflict"] : [])
|
|
28
|
+
preconditionFields: ["walletAvailability", "assetReadAccount", ...(hasConnectionConflict ? ["connectionConflict"] : []),
|
|
29
|
+
...(reconciliationRequired ? ["connectionReconciliationRequired"] : [])],
|
|
28
30
|
answerFields: ["activeAccount", "connections", "assetReadAccount", "pendingWalletConnections", "pendingReviewSessions", "walletAvailability",
|
|
29
31
|
...(hasConnectionConflict ? ["connectionConflict"] : []),
|
|
32
|
+
"manageableConnectionIds", ...(reconciliationRequired ? ["connectionReconciliationRequired"] : []),
|
|
30
33
|
...(hasWalletObservation ? ["walletObservation"] : [])],
|
|
31
34
|
diagnosticOnlyFields: [
|
|
32
35
|
"pendingWalletConnections.truncated",
|
|
@@ -66,7 +69,8 @@ export function executionResultUserAnswerUse(fields = {}) {
|
|
|
66
69
|
"profit_or_pnl"
|
|
67
70
|
],
|
|
68
71
|
answerFields: [
|
|
69
|
-
"walletAvailability", "progress", ...(fields.hasConnectionConflict ? ["connectionConflict"] : []),
|
|
72
|
+
"walletAvailability", "progress", ...(fields.hasConnectionConflict ? ["connectionConflict"] : []),
|
|
73
|
+
...(fields.reconciliationRequired ? ["connectionReconciliationRequired"] : []), ...(hasWaitOutcome ? ["waitOutcome"] : []),
|
|
70
74
|
"reviewSessionId",
|
|
71
75
|
"status",
|
|
72
76
|
"pollingStatus",
|
|
@@ -91,7 +95,7 @@ export function executionResultUserAnswerUse(fields = {}) {
|
|
|
91
95
|
}
|
|
92
96
|
};
|
|
93
97
|
}
|
|
94
|
-
export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle = false, hasHumanReadableReview = false, hasSimulation = false, hasConnectionConflict = false) {
|
|
98
|
+
export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle = false, hasHumanReadableReview = false, hasSimulation = false, hasConnectionConflict = false, reconciliationRequired = false) {
|
|
95
99
|
return {
|
|
96
100
|
canAnswer: [
|
|
97
101
|
"current_local_review_session_status",
|
|
@@ -115,7 +119,8 @@ export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle =
|
|
|
115
119
|
"profit_or_pnl"
|
|
116
120
|
],
|
|
117
121
|
answerFields: [
|
|
118
|
-
"walletAvailability", "progress", ...(hasConnectionConflict ? ["connectionConflict"] : []),
|
|
122
|
+
"walletAvailability", "progress", ...(hasConnectionConflict ? ["connectionConflict"] : []),
|
|
123
|
+
...(reconciliationRequired ? ["connectionReconciliationRequired"] : []), "reviewSessionId",
|
|
119
124
|
"status",
|
|
120
125
|
"pollingStatus",
|
|
121
126
|
"statusCategory",
|
package/dist/mcp/schemas.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
// No-parameter tools. The `.optional()` is load-bearing: it lets the tool be called with
|
|
3
|
-
// no arguments at all (undefined args validate). A bare `{}`/`z.object({})` would instead
|
|
4
|
-
// REQUIRE an (empty) object and reject an argument-less call, so this is not a no-op.
|
|
5
2
|
export const noParamsInputSchema = z.object({}).optional();
|
|
6
3
|
export function successOutputSchema(dataShape) {
|
|
7
4
|
return {
|
package/dist/mcp/serverInfo.js
CHANGED
|
@@ -50,11 +50,11 @@ export const SERVER_INSTRUCTIONS = [
|
|
|
50
50
|
"For USD-denominated payment coverage, balance total, or shortfall questions, call read.get_server_status, then read.list_settlement_asset_groups, then read.preview_intent_evidence. Use toolAvailability.requiredToolsAvailable and responseSummary.doNotCallQuoteToolsForThisQuestion; do not call wallet inventory or quote tools unless user asks a separate inventory or conversion question. For parity, use read.summarize_settlement_asset_group_parity. If toolAvailability cannot be used, say the current MCP server build cannot support the answer.",
|
|
51
51
|
"The active account context is read-only, not login, custody or signing permission. Implicit asset reads require session.get_interaction_status.assetReadAccount: available. Otherwise ask for an address only if the tool accepts account; connected-only tools require connection/account selection or wallet recovery.",
|
|
52
52
|
"Unsupported: other chains, autonomous trading, alerts, investment advice, arbitrary Move/package calls, P&L/tax.",
|
|
53
|
-
"One wallet connection at a time;
|
|
53
|
+
"One wallet connection at a time; docs: suimcp://docs/agent-behavior, suimcp://docs/mcp-tools."
|
|
54
54
|
].join("\n");
|
|
55
55
|
export const IMPLEMENTED_TOOLS = [
|
|
56
56
|
TOOL_NAMES.uiOpenAccount, TOOL_NAMES.uiOpenReceipt, TOOL_NAMES.uiOpenChart,
|
|
57
|
-
TOOL_NAMES.uiReadCard, TOOL_NAMES.uiSubmitCard, TOOL_NAMES.uiActCard,
|
|
57
|
+
TOOL_NAMES.uiReadCard, TOOL_NAMES.uiSubmitCard, TOOL_NAMES.uiActCard, TOOL_NAMES.uiReadSettingsFile,
|
|
58
58
|
TOOL_NAMES.readGetServerStatus,
|
|
59
59
|
TOOL_NAMES.readListSupportedProtocols,
|
|
60
60
|
TOOL_NAMES.readListDeepbookPools,
|
|
@@ -86,7 +86,6 @@ export const IMPLEMENTED_TOOLS = [
|
|
|
86
86
|
TOOL_NAMES.actionPrepareSuiActionReview,
|
|
87
87
|
TOOL_NAMES.actionPrepareExternalProposalReview,
|
|
88
88
|
TOOL_NAMES.accountGetActiveAccount,
|
|
89
|
-
TOOL_NAMES.accountClearActiveAccount,
|
|
90
89
|
TOOL_NAMES.sessionCreateWalletConnection,
|
|
91
90
|
TOOL_NAMES.sessionGetWalletConnection,
|
|
92
91
|
TOOL_NAMES.sessionWaitWalletConnection,
|
|
@@ -127,5 +126,5 @@ export const SERVER_LIMITATIONS = [
|
|
|
127
126
|
`DeepBook orderbook, mid-price, and quote outputs are ${DEEPBOOK_SOURCE_OWNER_RUNTIME_WORDING.pinnedSdkSnapshots} at fetchedAt; treat them as stale if the user delays.`,
|
|
128
127
|
`DeepBook USDC price history and price-at-time reads use ${DEEPBOOK_SOURCE_OWNER_RUNTIME_WORDING.officialIndexerCandleData} for the requested official interval. They are external candle evidence only, not live quotes, historical mid prices, global market prices, fiat USD values, USDC/USD peg guarantees, route recommendations, transaction-building inputs, signing readiness, P&L, cost basis, user-account transaction history, or user-account balance history. Sui MCP does not independently recompute those candle values from chain history for the response.`,
|
|
129
128
|
`ui.open_chart displays ${DEEPBOOK_SOURCE_OWNER_RUNTIME_WORDING.officialIndexerUsdcDenominatedCandles} in an internal read-only card. ui.open_account displays a public-address asset snapshot; ui.open_receipt displays the executed transaction graph when bound UI metadata is available, followed by on-chain facts and a Details slide. Card inputs end after backend admission, expiry or invalidation; chat navigation and frame recreation do not end valid unsubmitted input. Read the same DB state after a lost reply rather than resubmitting. Saved results do not authorize a new query or transaction. These cards are not live feeds, trading interfaces, route recommendations, fiat USD value, P&L, tax, cost basis, signing data, or signing readiness.`,
|
|
130
|
-
"Settings
|
|
129
|
+
"Settings uses an internal card. MCP settings tools create the card or read current settings; only the user's scoped card confirmation applies local changes or restarts the connection service. Backup bytes stay in app-only file transfer. No external Settings URL exists."
|
|
131
130
|
];
|
package/dist/mcp/toolNames.js
CHANGED
|
@@ -5,6 +5,7 @@ export const TOOL_NAMES = {
|
|
|
5
5
|
uiReadCard: "ui.read_card",
|
|
6
6
|
uiSubmitCard: "ui.submit_card",
|
|
7
7
|
uiActCard: "ui.act_card",
|
|
8
|
+
uiReadSettingsFile: "ui.read_settings_file",
|
|
8
9
|
sessionOpenReviewManagement: "session.open_review_management",
|
|
9
10
|
readGetServerStatus: "read.get_server_status",
|
|
10
11
|
readListSupportedProtocols: "read.list_supported_protocols",
|
|
@@ -43,8 +44,7 @@ export const TOOL_NAMES = {
|
|
|
43
44
|
sessionGetReviewStatus: "session.get_review_status",
|
|
44
45
|
sessionGetExecutionResult: "session.get_execution_result",
|
|
45
46
|
sessionWaitExecutionResult: "session.wait_execution_result",
|
|
46
|
-
accountGetActiveAccount: "account.get_active_account"
|
|
47
|
-
accountClearActiveAccount: "account.clear_active_account"
|
|
47
|
+
accountGetActiveAccount: "account.get_active_account"
|
|
48
48
|
};
|
|
49
49
|
export function assertValidToolName(name) {
|
|
50
50
|
if (!/^[A-Za-z0-9_.-]{1,128}$/.test(name)) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { activeAccountResponse, activeAccountResponseSchema } from "../../activeAccountResponse.js";
|
|
3
|
-
import { registerSuiMcpTool } from "../../registerTool.js";
|
|
4
3
|
import { okToolResult } from "../../result.js";
|
|
5
|
-
import { noParamsInputSchema
|
|
4
|
+
import { noParamsInputSchema } from "../../schemas.js";
|
|
6
5
|
import { activityStoreToolError } from "../../toolErrors.js";
|
|
7
6
|
import { TOOL_NAMES } from "../../toolNames.js";
|
|
8
7
|
export function registerAccountTools(server, deps) {
|
|
@@ -24,21 +23,4 @@ export function registerAccountTools(server, deps) {
|
|
|
24
23
|
return activityStoreToolError(error, deps.logger);
|
|
25
24
|
}
|
|
26
25
|
});
|
|
27
|
-
registerSuiMcpTool(server, TOOL_NAMES.accountClearActiveAccount, {
|
|
28
|
-
title: "Clear active account read context",
|
|
29
|
-
description: "Remove the active read context. Does not disconnect a wallet or revoke onchain permission.",
|
|
30
|
-
inputSchema: noParamsInputSchema,
|
|
31
|
-
outputSchema: successOutputSchema({
|
|
32
|
-
status: z.literal("cleared")
|
|
33
|
-
}),
|
|
34
|
-
annotations: { readOnlyHint: false, openWorldHint: false }
|
|
35
|
-
}, async () => {
|
|
36
|
-
try {
|
|
37
|
-
await deps.activityStore.clearActiveAccount();
|
|
38
|
-
return okToolResult({ status: "cleared" });
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return activityStoreToolError(error, deps.logger);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
26
|
}
|
|
@@ -32,12 +32,6 @@ export function registerActionTools(server, deps) {
|
|
|
32
32
|
if (!supportsCards(server))
|
|
33
33
|
return errorToolResult({ kind: "ui_unavailable", details: { reason: "An internal review card is required." } });
|
|
34
34
|
const now = new Date();
|
|
35
|
-
// A swap review is account-bound: its evidence (balances, transaction
|
|
36
|
-
// material, digest, simulation) is computed for a specific sender, and
|
|
37
|
-
// no transaction can be built without that account. Refuse here when no
|
|
38
|
-
// wallet account is connected instead of creating a hollow proposal that
|
|
39
|
-
// can never be computed or signed. Connect first via
|
|
40
|
-
// session.create_wallet_connection.
|
|
41
35
|
let activeAccount;
|
|
42
36
|
try {
|
|
43
37
|
activeAccount = await deps.activityStore.getActiveAccount();
|