@zktx.io/sui-mcp 0.7.0 → 0.8.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.
- package/README.md +83 -42
- package/dist/core/activity/localDataService.js +8 -21
- package/dist/core/activity/localDataValidation.js +22 -46
- package/dist/core/activity/sqliteAccountStore.js +46 -0
- package/dist/core/activity/sqliteActivityStore.js +65 -147
- package/dist/core/activity/sqliteActivityStoreSchema.js +42 -26
- package/dist/core/activity/transactionActivityService.js +2 -2
- package/dist/core/preferences/preferencesStore.js +2 -2
- package/dist/core/preferences/sqlitePreferencesRepository.js +10 -0
- package/dist/core/session/cardResult.js +41 -0
- package/dist/core/session/cardSession.js +1 -1
- package/dist/core/session/cardSessionStore.js +51 -19
- package/dist/core/session/chainAccounts.js +120 -0
- package/dist/core/session/sessionStore.js +4 -8
- package/dist/core/session/settingsFileStore.js +3 -30
- package/dist/core/session/settingsOperation.js +27 -19
- package/dist/core/session/settingsWorkflow.js +161 -87
- package/dist/core/session/sqliteCardStore.js +34 -14
- package/dist/core/session/sqliteSessionStore.js +5 -5
- package/dist/core/session/sqliteSettingsWorkflowStore.js +135 -41
- package/dist/core/session/sqliteTransactionRequest.js +18 -0
- package/dist/core/session/sqliteWalletConnectionOperation.js +13 -0
- package/dist/core/session/sqliteWalletWorkflowStore.js +569 -333
- package/dist/core/session/status.js +2 -2
- package/dist/core/session/wait.js +6 -11
- package/dist/core/session/walletConnection.js +71 -46
- package/dist/core/session/walletConnectionOperations.js +24 -0
- package/dist/core/session/walletRuntime.js +7 -13
- package/dist/core/session/walletWorkflow.js +248 -99
- package/dist/core/session/workflowState.js +104 -38
- package/dist/core/session/workflowView.js +92 -101
- package/dist/mcp/activeAccountResponse.js +3 -0
- package/dist/mcp/responseGuidance.js +7 -9
- package/dist/mcp/serverInfo.js +11 -10
- package/dist/mcp/toolErrors.js +5 -3
- package/dist/mcp/toolNames.js +1 -0
- package/dist/mcp/tools/action/prepareSuiActionReview.js +9 -16
- package/dist/mcp/tools/read/readToolHelpers.js +9 -7
- package/dist/mcp/tools/session/executionResultTools.js +2 -2
- package/dist/mcp/tools/session/shared.js +2 -2
- package/dist/mcp/tools/session/statusTools.js +12 -10
- package/dist/mcp/tools/session/walletConnectionTools.js +26 -14
- package/dist/mcp/tools/settings/index.js +7 -4
- package/dist/mcp-app/account.html +7 -7
- package/dist/mcp-app/chart.html +3 -3
- package/dist/mcp-app/connect.html +13 -13
- package/dist/mcp-app/disconnect.html +3637 -0
- package/dist/mcp-app/disconnect.notices.txt +1480 -0
- package/dist/mcp-app/receipt.html +393 -393
- package/dist/mcp-app/review.html +459 -459
- package/dist/mcp-app/settings.html +7 -7
- package/dist/mcp-ui/contracts.js +3 -1
- package/dist/mcp-ui/tools.js +4 -4
- package/dist/runtime/application.js +1 -1
- package/dist/runtime/localSettingsService.js +7 -4
- package/dist/runtime/smokeMainnetRead.js +5 -4
- package/dist/runtime/walletConnectTransport.js +16 -16
- package/dist/runtime/walletSdkChildRuntime.js +29 -12
- package/dist/runtime/walletSdkIpc.js +5 -4
- package/dist/runtime/walletSdkProcess.js +24 -8
- package/docs/AGENT_BEHAVIOR.md +82 -54
- package/docs/LOCAL_DB_ARCHITECTURE.md +100 -70
- package/docs/MCP_SETUP.md +319 -339
- package/docs/MCP_TOOLS.md +292 -177
- package/docs/SDK_API.md +15 -6
- package/docs/TRANSACTION_ACTIVITY_LOG.md +4 -0
- package/docs/UTILITY_INDEX.md +9 -7
- package/docs/WALLET_CONNECTION.md +162 -167
- package/package.json +1 -1
|
@@ -1,80 +1,146 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CONNECTION_RECONCILIATION_MESSAGE, walletConnectionEligibility, usableWalletConnection, connectionHasSuiSigningScope, projectWalletConnection } from "./walletConnection.js";
|
|
2
2
|
import { isInitialChainObservation } from "./transactionRequest.js";
|
|
3
|
-
|
|
3
|
+
import { SUI_MAINNET_WALLET_CHAIN } from "./chainAccounts.js";
|
|
4
|
+
import { parseSuiAddress } from "../suiAddress.js";
|
|
5
|
+
export const workflowActions = ["connect", "disconnect", "stop_connection", "prepare_review",
|
|
4
6
|
"request_signature", "cancel", "stop_waiting", "read_result"];
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
export const workflowNextSteps = ["none", "wait", "check_status", "open_settings", "request_disconnect", "request_review"];
|
|
8
|
+
function walletServiceNextStep(status) {
|
|
9
|
+
if (status === "available")
|
|
10
|
+
return undefined;
|
|
11
|
+
return { kind: status === "unavailable" ? "open_settings" : "wait", reason: "wallet_unavailable",
|
|
12
|
+
message: status === "unavailable" ? "Open Settings, then Troubleshooting, to restart the connection service. No wallet request was sent."
|
|
13
|
+
: "Wait for the connection service to finish starting, then check status." };
|
|
14
|
+
}
|
|
15
|
+
export function walletActionDecision(facts, action, targetId) {
|
|
16
|
+
const deny = (kind, reason, message) => ({ allowed: false, nextStep: { kind, reason, message } });
|
|
17
|
+
const service = walletServiceNextStep(facts.walletAvailability.status);
|
|
18
|
+
if (service)
|
|
19
|
+
return { allowed: false, nextStep: service };
|
|
20
|
+
if (facts.pendingOperation)
|
|
21
|
+
return deny("check_status", "operation_busy", "Wait for the current wallet operation and its cleanup to finish, then check this request's status.");
|
|
22
|
+
const selection = walletConnectionEligibility(facts.connection, Date.parse(facts.evaluatedAt), facts.connectionPresence, facts.activeAccount, facts.readAddressCleared);
|
|
23
|
+
if (!facts.connectionPresence.confirmed || !facts.connectionPresence.managed || selection.connectionReconciliationRequired)
|
|
24
|
+
return deny("check_status", "connection_state_unconfirmed", selection.connectionReconciliationRequired ? CONNECTION_RECONCILIATION_MESSAGE : "The wallet session has not been confirmed. Check its status before continuing.");
|
|
25
|
+
if (action === "connect") {
|
|
26
|
+
if (!facts.requestedChains.length)
|
|
27
|
+
return deny("open_settings", "wallet_networks_required", "Choose networks in Settings before requesting a wallet connection.");
|
|
28
|
+
return selection.pairingAllowed ? { allowed: true, nextStep: { kind: "none" } }
|
|
29
|
+
: deny("request_disconnect", "connection_present", "Ask in chat for a Disconnect card before requesting a new wallet connection.");
|
|
30
|
+
}
|
|
31
|
+
if (targetId && facts.connection?.connectionId !== targetId)
|
|
32
|
+
return deny("check_status", "connection_target_changed", "The wallet session for this card is no longer current. Ask in chat for current connection status.");
|
|
33
|
+
return selection.manageable ? { allowed: true, nextStep: { kind: "none" } }
|
|
34
|
+
: deny("none", "no_session_to_disconnect", "There is no current wallet session to disconnect. No request was sent.");
|
|
35
|
+
}
|
|
36
|
+
export function fixedReviewTarget(session, input) {
|
|
37
|
+
const hasInputTarget = input?.connectionId !== undefined || input?.account !== undefined;
|
|
38
|
+
const inputId = typeof input?.connectionId === "string" && input.connectionId.length ? input.connectionId : undefined;
|
|
39
|
+
const inputAccount = typeof input?.account === "string" ? parseSuiAddress(input.account) : undefined;
|
|
40
|
+
if (hasInputTarget && (!inputId || !inputAccount))
|
|
41
|
+
return undefined;
|
|
42
|
+
if (inputId && session.walletConnectionId && inputId !== session.walletConnectionId ||
|
|
43
|
+
inputAccount && session.account && inputAccount !== session.account)
|
|
44
|
+
return undefined;
|
|
45
|
+
const connectionId = inputId ?? session.walletConnectionId, account = inputAccount ?? session.account;
|
|
46
|
+
return connectionId && account ? { connectionId, account } : undefined;
|
|
15
47
|
}
|
|
16
48
|
export function workflowConnectionEligibility(facts) {
|
|
17
|
-
const selection =
|
|
49
|
+
const selection = walletConnectionEligibility(facts.connection, Date.parse(facts.evaluatedAt), facts.connectionPresence, facts.activeAccount, facts.readAddressCleared);
|
|
18
50
|
const available = facts.walletAvailability.status === "available", dependent = facts.walletDependent !== false;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
:
|
|
22
|
-
|
|
23
|
-
|
|
51
|
+
return { selection, usableConnectionId: available && dependent ? selection.connection?.connectionId : undefined,
|
|
52
|
+
assetReadAccount: !dependent ? undefined : available ? selection.assetReadAccount : { status: "address_required" },
|
|
53
|
+
connection: projectWalletConnection(facts.connection, Date.parse(facts.evaluatedAt), facts.connectionPresence, available),
|
|
54
|
+
pendingOperation: facts.pendingOperation,
|
|
55
|
+
canDisconnect: available && selection.manageable,
|
|
24
56
|
connectionReconciliationRequired: available && dependent ? selection.connectionReconciliationRequired : undefined };
|
|
25
57
|
}
|
|
26
58
|
export function workflowEligibility(facts) {
|
|
27
59
|
const { record, session, request, authority, walletAvailability } = facts;
|
|
28
60
|
const at = Date.parse(facts.evaluatedAt);
|
|
29
|
-
const cardRemaining = record?.ownerId === facts.ownerId
|
|
61
|
+
const cardRemaining = record?.ownerId === facts.ownerId && record.state.reason !== "cancelled"
|
|
62
|
+
? Math.max(0, Date.parse(record.state.expiresAt) - at) : 0;
|
|
30
63
|
const remaining = record?.scope === "review" && session && !request
|
|
31
64
|
? Math.min(cardRemaining, Math.max(0, Date.parse(session.expiresAt) - at)) : cardRemaining;
|
|
32
65
|
const inputAvailable = remaining > 0 && record?.state.state === "ready" && record.acceptedInput === undefined;
|
|
33
66
|
const allowedActions = [];
|
|
34
67
|
const { selection, ...connectionEligibility } = workflowConnectionEligibility(facts);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
const reviewTarget = session && !session.plans[0]?.reviewModel ? fixedReviewTarget(session, record?.state.input) : undefined;
|
|
69
|
+
const targetId = reviewTarget?.connectionId, targetAccount = reviewTarget?.account;
|
|
70
|
+
const reviewConnection = walletAvailability.status === "available" && !facts.connectionPresence.unrecorded && facts.walletDependent !== false && targetId && targetAccount
|
|
71
|
+
? usableWalletConnection(facts.connection, at, facts.connectionPresence, targetId, SUI_MAINNET_WALLET_CHAIN, targetAccount) : undefined;
|
|
72
|
+
const preparationIssue = session && record?.scope === "review" && !session.plans[0]?.reviewModel && !reviewTarget
|
|
73
|
+
? "This review has no matching fixed wallet target. Start a new review." : undefined;
|
|
74
|
+
const scopedDisconnect = record?.scope === "disconnect";
|
|
75
|
+
const walletDecision = walletActionDecision(facts, scopedDisconnect ? "disconnect" : "connect", scopedDisconnect ? String(record?.state.input.connectionId) : undefined);
|
|
76
|
+
let nextStep = facts.operation?.status === "running" && facts.operation.cardId === record?.state.cardId
|
|
77
|
+
? { kind: "wait" } : walletDecision.nextStep;
|
|
78
|
+
const disconnectTargetMatches = !scopedDisconnect || record.state.input.connectionId === facts.connection?.connectionId;
|
|
79
|
+
if (record?.scope === "connect" || scopedDisconnect) {
|
|
38
80
|
if (inputAvailable) {
|
|
39
81
|
allowedActions.push("cancel");
|
|
40
82
|
if (walletAvailability.status === "available") {
|
|
41
|
-
if (
|
|
83
|
+
if (record.scope === "connect" && walletDecision.allowed)
|
|
42
84
|
allowedActions.push("connect");
|
|
43
|
-
if (
|
|
85
|
+
if (scopedDisconnect && walletDecision.allowed)
|
|
44
86
|
allowedActions.push("disconnect");
|
|
45
|
-
if (selection.connection)
|
|
46
|
-
allowedActions.push("use_account");
|
|
47
87
|
}
|
|
48
88
|
}
|
|
49
|
-
if (remaining > 0 && facts.
|
|
89
|
+
if (record.state.state !== "closed" && remaining > 0 && facts.operation?.action === "connect" && facts.operation.status === "running")
|
|
50
90
|
allowedActions.push("stop_connection");
|
|
51
91
|
}
|
|
52
92
|
if (session && inputAvailable && record?.scope === "review" && session.status !== "expired" && Date.parse(session.expiresAt) > at && !session.preparationId) {
|
|
53
93
|
allowedActions.push("cancel");
|
|
54
|
-
if (walletAvailability.status === "available" &&
|
|
55
|
-
|
|
56
|
-
if (!account.allowed)
|
|
57
|
-
preparationIssue = account.message;
|
|
58
|
-
else if (!facts.busyForAccount)
|
|
94
|
+
if (walletAvailability.status === "available" && reviewConnection && !session.plans[0]?.reviewModel) {
|
|
95
|
+
if (!facts.busyForAccount)
|
|
59
96
|
allowedActions.push("prepare_review");
|
|
60
|
-
if (!facts.busyForAccount && session.status === "ready_for_wallet_review" && session.reviewState?.transactionReviewData &&
|
|
97
|
+
if (!facts.busyForAccount && connectionHasSuiSigningScope(reviewConnection, targetAccount) && session.status === "ready_for_wallet_review" && session.reviewState?.transactionReviewData &&
|
|
61
98
|
(!request || request.reviewRevision !== session.reviewRevision))
|
|
62
99
|
allowedActions.push("request_signature");
|
|
63
100
|
}
|
|
64
101
|
}
|
|
65
|
-
if (record && session && remaining > 0 && request && request.reviewSessionId === session.id &&
|
|
102
|
+
if (record && record.state.state !== "closed" && session && remaining > 0 && request && request.reviewSessionId === session.id &&
|
|
66
103
|
authority?.owner_id === facts.ownerId &&
|
|
67
104
|
(record.operationId === request.attemptId || record.scope === "review_manage" && record.state.input.attemptId === request.attemptId)) {
|
|
68
|
-
if (
|
|
105
|
+
if (request.requestStatus === "awaiting_signature" || !authority.observation_stopped && isInitialChainObservation(request.requestStatus))
|
|
69
106
|
allowedActions.push("stop_waiting");
|
|
70
|
-
if (isInitialChainObservation(request.requestStatus) || request.requestStatus === "outcome_unknown")
|
|
107
|
+
if (record.scope === "review_manage" && record.state.state === "ready" && record.acceptedInput === undefined && (isInitialChainObservation(request.requestStatus) || request.requestStatus === "outcome_unknown"))
|
|
71
108
|
allowedActions.push("read_result");
|
|
72
109
|
}
|
|
73
110
|
const expiry = session && inputAvailable && record?.scope === "review" && !session.preparationId &&
|
|
74
111
|
session.status === "ready_for_wallet_review" && (!request || request.reviewRevision !== session.reviewRevision)
|
|
75
112
|
? session.reviewState?.humanReadableReview?.freshness.expiresAt : undefined;
|
|
76
|
-
const
|
|
77
|
-
|
|
113
|
+
const reviewRead = expiry && session ? Math.min(remaining, Date.parse(session.expiresAt) - at, Date.parse(expiry) - at) : undefined;
|
|
114
|
+
const connectionDeadlines = inputAvailable && facts.walletDependent !== false && facts.connection?.status === "connected" &&
|
|
115
|
+
(["connect", "disconnect"].includes(record?.scope ?? "") || facts.connection.connectionId === reviewTarget?.connectionId)
|
|
116
|
+
? [Date.parse(facts.connection.expiresAt) - at].filter((delay) => delay > 0) : [];
|
|
117
|
+
const readDelays = [reviewRead, ...connectionDeadlines].filter((delay) => delay !== undefined && delay > 0);
|
|
118
|
+
const nextRead = readDelays.length ? Math.min(...readDelays) : undefined;
|
|
119
|
+
if (session) {
|
|
120
|
+
nextStep = { kind: "none" };
|
|
121
|
+
if (!session.plans[0]?.reviewModel && !request) {
|
|
122
|
+
const service = walletServiceNextStep(walletAvailability.status);
|
|
123
|
+
if (service)
|
|
124
|
+
nextStep = service;
|
|
125
|
+
else if (facts.connectionPresence.unrecorded)
|
|
126
|
+
nextStep = { kind: "check_status", reason: "connection_state_unconfirmed", message: CONNECTION_RECONCILIATION_MESSAGE };
|
|
127
|
+
else if (facts.pendingOperation)
|
|
128
|
+
nextStep = { kind: "check_status", reason: "operation_busy", message: "Wait for the current wallet operation and its cleanup to finish, then check this review's status." };
|
|
129
|
+
else if (reviewTarget && connectionEligibility.connection?.connectionId === targetId && connectionEligibility.connection?.sessionPresence === "absent")
|
|
130
|
+
nextStep = { kind: "request_review", message: "The original wallet session is no longer present. Reconnect a wallet and ask in chat for a new transaction review." };
|
|
131
|
+
else if (reviewTarget && facts.connection && facts.connection.connectionId !== targetId)
|
|
132
|
+
nextStep = { kind: "request_review", message: "This review belongs to the earlier wallet connection. Ask in chat for a new transaction review." };
|
|
133
|
+
else if (!reviewConnection && connectionEligibility.canDisconnect && facts.connection?.connectionId === targetId)
|
|
134
|
+
nextStep = { kind: "request_disconnect", message: "The original wallet connection is unavailable. Ask in chat for a Disconnect card, then start a new review after reconnecting." };
|
|
135
|
+
else if (!reviewConnection)
|
|
136
|
+
nextStep = { kind: "check_status", message: "This review keeps its original account and connection. Ask in chat to check that wallet connection's status before continuing." };
|
|
137
|
+
else if (facts.busyForAccount)
|
|
138
|
+
nextStep = { kind: "wait", message: "A previous transaction request for this Sui account is still pending. Wait for it to finish, then check this review's status." };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { allowedActions, preparationIssue, nextStep, ...connectionEligibility, reviewTarget,
|
|
142
|
+
canDisconnect: connectionEligibility.canDisconnect && disconnectTargetMatches,
|
|
143
|
+
...(session ? { usableConnectionId: reviewConnection?.connectionId } : {}),
|
|
78
144
|
actionRemainingMs: remaining, inputRemainingMs: inputAvailable ? remaining : 0,
|
|
79
145
|
...(nextRead !== undefined && nextRead > 0 ? { nextStateReadAfterMs: nextRead } : {}) };
|
|
80
146
|
}
|
|
@@ -1,84 +1,86 @@
|
|
|
1
|
-
import { workflowActions } from "./workflowState.js";
|
|
1
|
+
import { workflowActions, workflowNextSteps } from "./workflowState.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { suiAddressStringSchema
|
|
3
|
+
import { suiAddressStringSchema } from "../suiAddress.js";
|
|
4
4
|
import { actionPlanSchema, internalSessionStatusSchema, reviewStateOutputSchema } from "../action/schemas.js";
|
|
5
|
-
import { walletConnectionSchema, walletAvailabilitySchema, workflowProgressSchema, WALLET_CONNECTION_POLL_SECONDS,
|
|
5
|
+
import { walletConnectionSchema, walletAvailabilitySchema, workflowProgressSchema, WALLET_CONNECTION_POLL_SECONDS, connectionChainStateSchema } from "./walletConnection.js";
|
|
6
6
|
import { transactionRequestSchema } from "./transactionRequest.js";
|
|
7
7
|
import { workflowProgress } from "./walletConnection.js";
|
|
8
8
|
import { reviewProgress, EXECUTION_POLLING_INTERVAL_SECONDS } from "./status.js";
|
|
9
|
-
import { walletRunIdSchema,
|
|
9
|
+
import { walletRunIdSchema, walletObservationSchema } from "./walletRuntime.js";
|
|
10
|
+
import { walletConnectionActionSchema, walletConnectionOperationSchema } from "./walletConnectionOperations.js";
|
|
11
|
+
import { walletChainSchema, walletChainsSchema, SUI_MAINNET_WALLET_CHAIN, normalizeChainAddress } from "./chainAccounts.js";
|
|
10
12
|
export const CONNECT_BOUNDARY = "A wallet connection provides account context; it is not a transaction approval or proof of address ownership.";
|
|
11
13
|
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
|
-
export const connectionViewSchema = walletConnectionSchema.extend({
|
|
14
|
+
export const connectionViewSchema = walletConnectionSchema.extend({
|
|
15
|
+
sessionPresence: z.enum(["present", "absent", "unusable", "unconfirmed"]),
|
|
16
|
+
chainStates: z.array(connectionChainStateSchema).optional(),
|
|
17
|
+
disconnectImpact: z.object({ impactId: z.string(), requestCount: z.number().int().nonnegative(), chains: walletChainsSchema }).strict().optional()
|
|
18
|
+
});
|
|
13
19
|
export { assetReadAccountSchema } from "./walletConnection.js";
|
|
14
|
-
export const pendingConnectionStatusSchema = z.enum(["
|
|
15
|
-
const connectAction = z.object({ action: z.literal("connect"), walletRunId: walletRunIdSchema }).strict();
|
|
16
|
-
const disconnectAction = z.object({ action: z.literal("disconnect"), walletRunId: walletRunIdSchema, connectionId: z.string().min(1) }).strict();
|
|
17
|
-
const accountAction = z.object({ action: z.literal("use_account"), walletRunId: walletRunIdSchema, connectionId: z.string().min(1), account: suiAddressStringSchema }).strict();
|
|
20
|
+
export const pendingConnectionStatusSchema = z.enum(["awaiting_approval", "disconnect_pending", "cleanup_pending"]);
|
|
18
21
|
const reviewAction = z.object({ action: z.enum(["prepare_review", "request_signature"]), connectionId: z.string().min(1),
|
|
19
22
|
walletRunId: walletRunIdSchema, account: suiAddressStringSchema, reviewRevision: z.number().int().nonnegative() }).strict();
|
|
20
|
-
const simpleAction = z.object({ action: z.enum(["cancel", "
|
|
21
|
-
|
|
22
|
-
export const
|
|
23
|
+
const simpleAction = z.object({ action: z.enum(["cancel", "stop_waiting", "read_result"]) }).strict();
|
|
24
|
+
const stopConnectionAction = z.object({ action: z.literal("stop_connection"), operationId: z.string().uuid() }).strict();
|
|
25
|
+
export const workflowActionSchema = z.union([walletConnectionActionSchema, stopConnectionAction, reviewAction, simpleAction]);
|
|
26
|
+
export const automaticWorkflowActionSchema = z.union([walletConnectionActionSchema.options[0], reviewAction.extend({ action: z.literal("prepare_review") })]);
|
|
23
27
|
export function parseWorkflowAction(input) {
|
|
24
28
|
const action = workflowActionSchema.parse(input);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
allowedActions: z.array(z.enum(workflowActions)),
|
|
31
|
-
actionRemainingMs: z.number().int().nonnegative(),
|
|
32
|
-
nextStateReadAfterMs: z.number().int().nonnegative().optional(),
|
|
33
|
-
automaticAction: automaticWorkflowActionSchema.optional(),
|
|
34
|
-
observe: z.boolean(),
|
|
35
|
-
walletAvailability: walletAvailabilitySchema,
|
|
36
|
-
walletObservation: walletObservationSchema.optional(),
|
|
37
|
-
runtimeRecovery: walletRecoverySchema.optional(),
|
|
38
|
-
connectionConflict: connectionConflictSchema.optional(),
|
|
39
|
-
manageableConnectionIds: z.array(z.string().min(1)),
|
|
40
|
-
connectionReconciliationRequired: z.literal(true).optional(),
|
|
41
|
-
usableConnectionId: z.string().min(1).optional(),
|
|
42
|
-
assetReadAccount: assetReadAccountSchema.optional(),
|
|
43
|
-
acceptedWalletRunId: walletRunIdSchema.optional(),
|
|
44
|
-
progress: workflowProgressSchema,
|
|
45
|
-
boundary: z.enum([CONNECT_BOUNDARY, REVIEW_BOUNDARY]),
|
|
46
|
-
connections: z.array(connectionViewSchema),
|
|
47
|
-
activeAccount: z.string().optional(),
|
|
48
|
-
connection: connectionViewSchema.optional(),
|
|
49
|
-
connectionAction: z.enum(["connect", "disconnect"]).optional(),
|
|
50
|
-
review: z.object({
|
|
51
|
-
reviewSessionId: z.string(), plan: actionPlanSchema, reviewRevision: z.number().int().nonnegative(),
|
|
52
|
-
status: internalSessionStatusSchema, account: z.string().optional(), preparing: z.boolean(),
|
|
53
|
-
accountRequestPending: z.literal(true).optional(),
|
|
54
|
-
error: z.string().optional(),
|
|
55
|
-
state: reviewStateOutputSchema.optional()
|
|
56
|
-
}).strict().optional(),
|
|
57
|
-
request: transactionRequestSchema.optional(),
|
|
58
|
-
receipt: z.unknown().optional(),
|
|
59
|
-
netGasMist: z.string().regex(/^-?[0-9]+$/).optional(),
|
|
60
|
-
observationStopped: z.boolean().optional()
|
|
61
|
-
}).strict().superRefine((view, context) => {
|
|
62
|
-
const connection = view.connections.find((item) => item.connectionId === view.usableConnectionId);
|
|
63
|
-
if (view.usableConnectionId && (!connection || connection.status !== "connected" ||
|
|
64
|
-
view.walletAvailability.status !== "available" || view.connectionConflict || view.connectionReconciliationRequired ||
|
|
65
|
-
view.connections.some((item) => item.pendingAction || item.status === "awaiting_approval"))) {
|
|
66
|
-
context.addIssue({ code: "custom", path: ["usableConnectionId"], message: "The usable connection must match current connection facts." });
|
|
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." });
|
|
29
|
+
if ("account" in action) {
|
|
30
|
+
const account = normalizeChainAddress(SUI_MAINNET_WALLET_CHAIN, action.account);
|
|
31
|
+
if (!account)
|
|
32
|
+
throw new Error("A valid chain account is required.");
|
|
33
|
+
return { ...action, account };
|
|
72
34
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
context.addIssue({ code: "custom", path: ["assetReadAccount"], message: "The default address must belong to the usable connection and stored selection." });
|
|
35
|
+
return action;
|
|
36
|
+
}
|
|
37
|
+
function checkReviewAccount(view, context) {
|
|
38
|
+
if (view.reviewTarget && (view.review?.account && view.review.account !== view.reviewTarget.account ||
|
|
39
|
+
view.request && view.request.account !== view.reviewTarget.account)) {
|
|
40
|
+
context.addIssue({ code: "custom", path: ["reviewTarget"], message: "The fixed review target must match its recorded account and request." });
|
|
80
41
|
}
|
|
81
|
-
}
|
|
42
|
+
}
|
|
43
|
+
const nextStepSchema = z.object({ kind: z.enum(workflowNextSteps), message: z.string().optional(), reason: z.string().optional() }).strict();
|
|
44
|
+
const liveFields = {
|
|
45
|
+
allowedActions: z.array(z.enum(workflowActions)), actionRemainingMs: z.number().int().nonnegative(),
|
|
46
|
+
nextStateReadAfterMs: z.number().int().nonnegative().optional(), observe: z.boolean(),
|
|
47
|
+
walletAvailability: walletAvailabilitySchema, walletObservation: walletObservationSchema.optional(),
|
|
48
|
+
progress: workflowProgressSchema, nextStep: nextStepSchema
|
|
49
|
+
};
|
|
50
|
+
export const connectViewSchema = z.object({ ...liveFields, kind: z.literal("connect"), mode: z.literal("connect"),
|
|
51
|
+
allowedActions: z.array(z.enum(["connect", "cancel", "stop_connection"])),
|
|
52
|
+
boundary: z.literal(CONNECT_BOUNDARY), requestedChains: walletChainsSchema,
|
|
53
|
+
operation: walletConnectionOperationSchema.optional(), automaticAction: walletConnectionActionSchema.options[0].optional() }).strict();
|
|
54
|
+
export const disconnectViewSchema = z.object({ ...liveFields, kind: z.literal("disconnect"), mode: z.literal("disconnect"),
|
|
55
|
+
allowedActions: z.array(z.enum(["disconnect", "cancel"])),
|
|
56
|
+
boundary: z.literal(CONNECT_BOUNDARY), connection: connectionViewSchema.optional(),
|
|
57
|
+
operation: walletConnectionOperationSchema.optional() }).strict();
|
|
58
|
+
const reviewPayloadSchema = z.object({
|
|
59
|
+
reviewSessionId: z.string(), plan: actionPlanSchema, reviewRevision: z.number().int().nonnegative(),
|
|
60
|
+
status: internalSessionStatusSchema, account: z.string().optional(), preparing: z.boolean(),
|
|
61
|
+
accountRequestPending: z.literal(true).optional(), error: z.string().optional(), state: reviewStateOutputSchema.optional()
|
|
62
|
+
}).strict();
|
|
63
|
+
const reviewTargetSchema = z.object({ connectionId: z.string(), account: suiAddressStringSchema }).strict();
|
|
64
|
+
const netGasSchema = z.string().regex(/^-?[0-9]+$/).optional();
|
|
65
|
+
export const reviewViewSchema = z.object({ ...liveFields, kind: z.literal("review"), mode: z.enum(["review", "review_manage"]),
|
|
66
|
+
allowedActions: z.array(z.enum(["prepare_review", "request_signature", "read_result", "stop_waiting", "cancel"])),
|
|
67
|
+
boundary: z.literal(REVIEW_BOUNDARY), reviewTarget: reviewTargetSchema.optional(), review: reviewPayloadSchema,
|
|
68
|
+
walletName: z.string().optional(), acceptedWalletRunId: walletRunIdSchema.optional(),
|
|
69
|
+
automaticAction: reviewAction.extend({ action: z.literal("prepare_review") }).optional(),
|
|
70
|
+
request: transactionRequestSchema.optional(), receipt: z.unknown().optional(), netGasMist: netGasSchema,
|
|
71
|
+
observationStopped: z.boolean().optional()
|
|
72
|
+
}).strict().superRefine(checkReviewAccount);
|
|
73
|
+
export const workflowViewSchema = z.discriminatedUnion("kind", [connectViewSchema, disconnectViewSchema, reviewViewSchema]);
|
|
74
|
+
const closedResult = z.object({ message: z.string(), recordedAt: z.string().datetime() }).strict();
|
|
75
|
+
const closedFields = { allowedActions: z.array(z.never()), observe: z.literal(false), actionRemainingMs: z.literal(0), result: closedResult };
|
|
76
|
+
export const closedWalletCardSchema = z.object({ ...closedFields, kind: z.enum(["connect", "disconnect"]),
|
|
77
|
+
result: closedResult.extend({ pendingWork: z.enum(["approval_response", "session_removal", "session_status"]).optional() }), walletName: z.string().optional(),
|
|
78
|
+
networks: z.array(z.object({ chainId: walletChainSchema, status: z.enum(["bound", "not_approved", "multiple_accounts", "invalid_response"]) }).strict()) }).strict();
|
|
79
|
+
export const closedReviewCardSchema = z.object({ ...closedFields, kind: z.literal("review"),
|
|
80
|
+
mode: z.enum(["review", "review_manage"]), review: reviewPayloadSchema.optional(),
|
|
81
|
+
reviewTarget: reviewTargetSchema.optional(), request: transactionRequestSchema.optional(),
|
|
82
|
+
receipt: z.unknown().optional(), netGasMist: netGasSchema,
|
|
83
|
+
observationStopped: z.boolean().optional(), boundary: z.literal(REVIEW_BOUNDARY) }).strict().superRefine(checkReviewAccount);
|
|
82
84
|
function automaticReviewAction(input) {
|
|
83
85
|
const { record, session, request } = input;
|
|
84
86
|
if (!session || record?.scope !== "review" || request || session.preparationError ||
|
|
@@ -86,18 +88,11 @@ function automaticReviewAction(input) {
|
|
|
86
88
|
return undefined;
|
|
87
89
|
const needsPreparation = !session.reviewState && session.reviewRevision === 0 ||
|
|
88
90
|
session.reviewState?.status === "refresh_required" && session.reviewState.refreshReason === "review_evidence_stale";
|
|
89
|
-
if (!needsPreparation || input.
|
|
90
|
-
return undefined;
|
|
91
|
-
const account = input.assetReadAccount.account;
|
|
92
|
-
const candidates = reviewWalletChoices(input, account);
|
|
93
|
-
if (candidates.length !== 1)
|
|
91
|
+
if (!needsPreparation || !input.reviewTarget)
|
|
94
92
|
return undefined;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
export function reviewWalletChoices(selection, account, requireSigningMethod = false) {
|
|
99
|
-
return selection.connections.filter((item) => item.connectionId === selection.usableConnectionId && item.accounts.includes(account) &&
|
|
100
|
-
(!requireSigningMethod || item.methods.includes("sui_signTransaction")));
|
|
93
|
+
const account = input.reviewTarget.account;
|
|
94
|
+
return { action: "prepare_review", walletRunId: input.walletAvailability.walletRunId,
|
|
95
|
+
connectionId: input.reviewTarget.connectionId, account, reviewRevision: session.reviewRevision };
|
|
101
96
|
}
|
|
102
97
|
function nextStateRead(input) {
|
|
103
98
|
const walletInput = input.record?.state.state === "ready" && input.record.scope !== "review_manage" && !input.request &&
|
|
@@ -109,24 +104,20 @@ function nextStateRead(input) {
|
|
|
109
104
|
return delays.length ? Math.min(...delays) : undefined;
|
|
110
105
|
}
|
|
111
106
|
export function projectConnectionView(input) {
|
|
112
|
-
const { walletAvailability } = input;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
connectionAction: ["connect", "disconnect"].includes(String(input.record?.acceptedInput?.action)) ? input.record?.acceptedInput?.action : undefined,
|
|
127
|
-
acceptedWalletRunId: input.record?.acceptedInput?.walletRunId,
|
|
128
|
-
...(readAfter === undefined ? {} : { nextStateReadAfterMs: readAfter }),
|
|
129
|
-
connections: input.connections, activeAccount: input.activeAccount, connection, boundary: CONNECT_BOUNDARY });
|
|
107
|
+
const { walletAvailability, operation } = input, readAfter = nextStateRead(input);
|
|
108
|
+
const progress = workflowProgress(!!operation && operation.status === "running", true, walletAvailability);
|
|
109
|
+
const common = { allowedActions: input.allowedActions, actionRemainingMs: input.actionRemainingMs,
|
|
110
|
+
observe: progress.status === "waiting", progress, walletAvailability, walletObservation: input.walletObservation,
|
|
111
|
+
operation, nextStep: input.nextStep, boundary: CONNECT_BOUNDARY,
|
|
112
|
+
...(readAfter === undefined ? {} : { nextStateReadAfterMs: readAfter }) };
|
|
113
|
+
if (input.record?.scope === "disconnect")
|
|
114
|
+
return disconnectViewSchema.parse({ ...common, kind: "disconnect", mode: "disconnect",
|
|
115
|
+
connection: input.connection?.connectionId === input.record.state.input.connectionId ? input.connection : undefined });
|
|
116
|
+
return connectViewSchema.parse({ ...common, kind: "connect", mode: "connect",
|
|
117
|
+
requestedChains: walletChainsSchema.parse(input.record?.state.input.requestedChains ?? input.requestedChains),
|
|
118
|
+
...(!operation && input.record?.operationId && input.allowedActions.includes("connect")
|
|
119
|
+
? { automaticAction: { action: "connect", operationId: input.record.operationId, walletRunId: walletAvailability.walletRunId,
|
|
120
|
+
chains: input.record.state.input.requestedChains } } : {}) });
|
|
130
121
|
}
|
|
131
122
|
export function projectReviewView(input) {
|
|
132
123
|
const { record, session, request, authority, walletAvailability, boundReview } = input;
|
|
@@ -139,11 +130,11 @@ export function projectReviewView(input) {
|
|
|
139
130
|
const gas = displayedReview?.simulation?.gasCostSummary;
|
|
140
131
|
const automaticAction = automaticReviewAction(input);
|
|
141
132
|
const readAfter = nextStateRead(input);
|
|
142
|
-
return
|
|
133
|
+
return reviewViewSchema.parse({ kind: "review", mode: record.scope === "review_manage" ? "review_manage" : "review",
|
|
143
134
|
allowedActions: input.allowedActions, actionRemainingMs: input.actionRemainingMs, observe: progress.status === "waiting", progress, walletAvailability,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
reviewTarget: input.reviewTarget, nextStep: input.nextStep,
|
|
136
|
+
walletName: input.connection?.connectionId === input.reviewTarget?.connectionId ? input.connection?.walletName : undefined,
|
|
137
|
+
boundary: REVIEW_BOUNDARY, receipt: input.receipt,
|
|
147
138
|
walletObservation: input.walletObservation,
|
|
148
139
|
acceptedWalletRunId: record.acceptedInput?.walletRunId,
|
|
149
140
|
...(readAfter === undefined ? {} : { nextStateReadAfterMs: readAfter }),
|
|
@@ -153,7 +144,7 @@ export function projectReviewView(input) {
|
|
|
153
144
|
status: boundReview?.status ?? session.status, account: boundReview?.account ?? session.account,
|
|
154
145
|
...(!request && !session.plans[0]?.reviewModel && input.busyForAccount ? { accountRequestPending: true } : {}),
|
|
155
146
|
preparing: !boundReview && !!session.preparationId,
|
|
156
|
-
error: !boundReview ? [input.preparationIssue && `
|
|
147
|
+
error: !boundReview ? [input.preparationIssue && `Review target: ${input.preparationIssue}`,
|
|
157
148
|
session.preparationError && `Earlier review message: “${session.preparationError}”`].filter(Boolean).join("\n\n") || undefined : undefined,
|
|
158
149
|
state: displayedReview },
|
|
159
150
|
request, observationStopped: authority ? authority.observation_stopped === 1 : undefined });
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { walletChainSchema } from "../core/session/chainAccounts.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
export const activeAccountResponseSchema = z.discriminatedUnion("status", [
|
|
3
4
|
z.object({
|
|
4
5
|
status: z.literal("set"),
|
|
5
6
|
account: z.string(),
|
|
7
|
+
chainId: walletChainSchema,
|
|
6
8
|
source: z.literal("wallet_connection"),
|
|
7
9
|
setAt: z.string(),
|
|
8
10
|
boundary: z.literal("read_context_only_not_signing_authorization")
|
|
@@ -16,6 +18,7 @@ export function activeAccountResponse(active) {
|
|
|
16
18
|
? {
|
|
17
19
|
status: "set",
|
|
18
20
|
account: active.address,
|
|
21
|
+
chainId: active.chainId,
|
|
19
22
|
source: active.source,
|
|
20
23
|
setAt: active.setAt,
|
|
21
24
|
boundary: "read_context_only_not_signing_authorization"
|
|
@@ -1,13 +1,12 @@
|
|
|
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,
|
|
3
|
+
export function interactionStatusUserAnswerUse(hasWalletObservation = false, reconciliationRequired = false) {
|
|
4
4
|
return {
|
|
5
5
|
canAnswer: [
|
|
6
6
|
"stored_active_account_read_context",
|
|
7
7
|
"wallet_connections_when_wallet_availability_is_available",
|
|
8
8
|
"connection_qualified_default_asset_account",
|
|
9
9
|
"pending_local_wallet_connection_interactions",
|
|
10
|
-
...(hasConnectionConflict ? ["multiple_saved_connections_require_user_targeted_disconnection"] : []),
|
|
11
10
|
...(reconciliationRequired ? ["unrecorded_service_connection_requires_wallet_removal_and_confirmed_absence"] : []),
|
|
12
11
|
"pending_local_review_interactions",
|
|
13
12
|
"wallet_operation_availability_separate_from_stored_transaction_facts"
|
|
@@ -25,11 +24,10 @@ export function interactionStatusUserAnswerUse(hasWalletObservation = false, has
|
|
|
25
24
|
"complete_wallet_history",
|
|
26
25
|
"profit_or_pnl"
|
|
27
26
|
],
|
|
28
|
-
preconditionFields: ["walletAvailability", "assetReadAccount",
|
|
27
|
+
preconditionFields: ["walletAvailability", "assetReadAccount",
|
|
29
28
|
...(reconciliationRequired ? ["connectionReconciliationRequired"] : [])],
|
|
30
|
-
answerFields: ["activeAccount", "
|
|
31
|
-
...(
|
|
32
|
-
"manageableConnectionIds", ...(reconciliationRequired ? ["connectionReconciliationRequired"] : []),
|
|
29
|
+
answerFields: ["activeAccount", "requestedChains", "connection", "assetReadAccount", "pendingWalletConnections", "pendingReviewSessions", "walletAvailability",
|
|
30
|
+
"canDisconnect", ...(reconciliationRequired ? ["connectionReconciliationRequired"] : []),
|
|
33
31
|
...(hasWalletObservation ? ["walletObservation"] : [])],
|
|
34
32
|
diagnosticOnlyFields: [
|
|
35
33
|
"pendingWalletConnections.truncated",
|
|
@@ -69,7 +67,7 @@ export function executionResultUserAnswerUse(fields = {}) {
|
|
|
69
67
|
"profit_or_pnl"
|
|
70
68
|
],
|
|
71
69
|
answerFields: [
|
|
72
|
-
"walletAvailability", "progress",
|
|
70
|
+
"walletAvailability", "progress",
|
|
73
71
|
...(fields.reconciliationRequired ? ["connectionReconciliationRequired"] : []), ...(hasWaitOutcome ? ["waitOutcome"] : []),
|
|
74
72
|
"reviewSessionId",
|
|
75
73
|
"status",
|
|
@@ -95,7 +93,7 @@ export function executionResultUserAnswerUse(fields = {}) {
|
|
|
95
93
|
}
|
|
96
94
|
};
|
|
97
95
|
}
|
|
98
|
-
export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle = false, hasHumanReadableReview = false, hasSimulation = false,
|
|
96
|
+
export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle = false, hasHumanReadableReview = false, hasSimulation = false, reconciliationRequired = false) {
|
|
99
97
|
return {
|
|
100
98
|
canAnswer: [
|
|
101
99
|
"current_local_review_session_status",
|
|
@@ -119,7 +117,7 @@ export function reviewStatusUserAnswerUse(hasReviewState, hasAdapterLifecycle =
|
|
|
119
117
|
"profit_or_pnl"
|
|
120
118
|
],
|
|
121
119
|
answerFields: [
|
|
122
|
-
"walletAvailability", "progress",
|
|
120
|
+
"walletAvailability", "progress",
|
|
123
121
|
...(reconciliationRequired ? ["connectionReconciliationRequired"] : []), "reviewSessionId",
|
|
124
122
|
"status",
|
|
125
123
|
"pollingStatus",
|
package/dist/mcp/serverInfo.js
CHANGED
|
@@ -44,14 +44,14 @@ export const EVIDENCE_POLICY = {
|
|
|
44
44
|
]
|
|
45
45
|
};
|
|
46
46
|
export const SERVER_INSTRUCTIONS = [
|
|
47
|
-
"Sui MCP is a mainnet-only Sui DeFi intent evidence toolkit. Ordinary MCP tools return evidence or internal cards, never signing authority, bytes or signatures. Only user card
|
|
48
|
-
"External proposals are untrusted
|
|
49
|
-
"PTB graphs are diagnostics
|
|
47
|
+
"Sui MCP is a mainnet-only Sui DeFi intent evidence toolkit. Ordinary MCP tools return evidence or internal cards, never signing authority, bytes or signatures. Only user card input and wallet approval authorize WalletConnect: verify reviewed digest and signer, submit once, then read chain effects. Request status is separate from chain success/failure; uncertain results are read by digest, never resubmitted.",
|
|
48
|
+
"External proposals are untrusted: plans[].reviewModel is non-signable context, not transaction material, route/settlement choice or execution readiness.",
|
|
49
|
+
"PTB graphs are diagnostics, not transaction input, authorization, readiness, route quality or safety.",
|
|
50
50
|
"Use response-local fields: userAnswerUse.answerFields/preconditionFields/conclusionRuleFields/cannotAnswer/diagnosticOnlyFields/followUp, pollingHint, and quantitySemantics.",
|
|
51
|
-
"
|
|
52
|
-
"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
|
|
53
|
-
"Unsupported: other
|
|
54
|
-
"One
|
|
51
|
+
"USD coverage/total/shortfall: read_get_server_status, then read_list_settlement_asset_groups, then read_preview_intent_evidence. Follow toolAvailability.requiredToolsAvailable and responseSummary.doNotCallQuoteToolsForThisQuestion. Inventory/quotes require a separate user request. Parity: read_summarize_settlement_asset_group_parity. Missing toolAvailability means this build cannot answer.",
|
|
52
|
+
"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 account only if supported; connected-only reads need a saved address and usable connection. Settings Clear preserves connection and transaction authority.",
|
|
53
|
+
"Unsupported: other-chain financial actions, autonomous trading, alerts, investment advice, arbitrary Move/package calls, P&L/tax.",
|
|
54
|
+
"One connection fixes one address per approved chain: Sui/Ethereum/Solana. Wallet/address/scope changes require disconnect/reconnect. Finance is Sui-only. Connect/Disconnect cards end with address-free results and no actions. Docs: suimcp://docs/agent-behavior, suimcp://docs/mcp-tools."
|
|
55
55
|
].join("\n");
|
|
56
56
|
export const IMPLEMENTED_TOOLS = [
|
|
57
57
|
TOOL_NAMES.uiOpenAccount, TOOL_NAMES.uiOpenReceipt, TOOL_NAMES.uiOpenChart,
|
|
@@ -88,6 +88,7 @@ export const IMPLEMENTED_TOOLS = [
|
|
|
88
88
|
TOOL_NAMES.actionCreateProposalReview,
|
|
89
89
|
TOOL_NAMES.accountGetActiveAccount,
|
|
90
90
|
TOOL_NAMES.sessionOpenWalletConnectionCard,
|
|
91
|
+
TOOL_NAMES.sessionOpenWalletDisconnectionCard,
|
|
91
92
|
TOOL_NAMES.sessionGetWalletConnectionCard,
|
|
92
93
|
TOOL_NAMES.sessionWaitWalletConnectionCard,
|
|
93
94
|
TOOL_NAMES.sessionOpenReviewManagement,
|
|
@@ -114,7 +115,7 @@ export function toolAvailability(requiredTools) {
|
|
|
114
115
|
};
|
|
115
116
|
}
|
|
116
117
|
export const SERVER_LIMITATIONS = [
|
|
117
|
-
"Current asset reads without a user-provided address require
|
|
118
|
+
"Current asset reads without a user-provided address require the fixed Sui address with a usable current wallet connection. session_get_interaction_status.assetReadAccount reports whether that default is available. Only tools with an account input accept explicit addresses; DeepBook account inventory requires the current connected Sui address. Explicit-address coin balance reads are public-address snapshots and do not create active account context. Active account is read context only, not signing authorization, login, custody, or persistent permission.",
|
|
118
119
|
"Review activity tools summarize local Sui MCP review evidence only. Sui activity tools use user-requested bounded GraphQL reads and store normalized transaction facts only for known wallet context; function activity scans are sent-transaction scans only. They are not background indexers, complete history, balance history, complete gas history, affected-object history, or P&L.",
|
|
119
120
|
`read_get_account_asset_timeline reads stored local account activity facts and returns observed raw net-flow bars with scan coverage. It does not start scans, prove complete wallet history, provide held balances without balanceBars, compute USD value, P&L, tax, cost basis, route advice, transaction-building input, signing data, or signing readiness. Optional USDC references are token-denominated ${DEEPBOOK_SOURCE_OWNER_RUNTIME_WORDING.officialIndexerCandleReferences} only, ${DEEPBOOK_SOURCE_OWNER_RUNTIME_WORDING.usdcNotFiatUsdAndNotPeg}.`,
|
|
120
121
|
"Sui activity transactionContext facts are transaction-level facts without transaction-wide balance-change aggregates, and they are answer fields only when the response returns them. requestedAccountTransactionFacts, requestedAccount, and requestedAccountEffect are the account-specific balance-change surfaces. Activity quantitySemantics marks balance amountRaw fields as raw integers that require verified decimals for display conversion. accountBalanceChangeInferencePolicy marks whether returned account balance rows can be used or transaction-level context must not be used for account amount inference. Gas raw fields use MIST, and returned gasCost display facts use @mysten/sui MIST_PER_SUI.",
|
|
@@ -122,10 +123,10 @@ export const SERVER_LIMITATIONS = [
|
|
|
122
123
|
"High-risk read responses include userAnswerUse. Prefer userAnswerUse.preconditionFields before answering, userAnswerUse.answerFields for the answer path, userAnswerUse.conclusionRuleFields for conclusion limits, userAnswerUse.diagnosticOnlyFields for source or troubleshooting context, and userAnswerUse.followUp when a different tool and field are required. USD intent and parity responses also include toolAvailability as a precondition field for current server-build support.",
|
|
123
124
|
"Intent evidence maps natural-language USD-denominated targets to pinned DeepBook SDK settlement asset groups and current wallet balance evidence. responseSummary.answerCompleteness names the answer class and required fields. Settlement-asset-only answers use responseSummary; selected-target evidence requires user selection provenance and also uses selectedTarget, candidateConversions, and requiredUserChoices when userAnswerUse.answerFields lists them. Direct pool quote evidence is returned and supported only when a quoted candidate exists. responseSummary.doNotUseForConclusion names quote results, outside-settlement-group assets, and route-dependent support as excluded from the conclusion. Intent evidence does not silently choose settlement assets, rank venues or routes, evaluate gas reserve, build transactions, or produce signing material.",
|
|
124
125
|
"DeepBook quotes convert explicit raw or display source inputs through pinned token units and return scoped SDK simulation facts plus raw quote evidence. Their quantitySemantics says canUseForPaymentAnswer and canUseForShortfallAnswer are false, doNotCombineWithPaymentAnswer is true, requiredPaymentAnswerTool is read_preview_intent_evidence, and requiredPaymentAnswerField is responseSummary.",
|
|
125
|
-
"Action preparation returns internal Review cards. A current card with valid UI permission automatically prepares and renews verified conditions for
|
|
126
|
+
"Action preparation returns internal Review cards. A current card with valid UI permission automatically prepares and renews verified conditions for its original fixed wallet/account; only the explicit Request wallet approval action can request a signature. Supported DeepBook account-bound swap review verifies stored transaction material, digest, ownership, quote/policy, human-readable facts, simulation and PTB evidence. Only a scoped app-only user action and wallet approval authorize the backend WalletConnect request; ordinary MCP calls never sign or submit. Transaction bytes and signatures never enter card/model results. The backend verifies returned transaction digest and signer, submits once, and independently reads chain effects. requestStatus is separate from chain execution success/failure: stopped, request_failed and outcome_unknown do not prove a chain failure. Lost replies are resolved by reading the same request/digest, never automatic resubmission. External proposals remain non-signable. Receipts do not establish execution guarantees, route quality, fiat value, P&L, tax, peg proof or best-price advice.",
|
|
126
127
|
"read_list_deepbook_pools and read_list_deepbook_tokens return static SDK registry metadata, not live liquidity, live token discovery, or active pool state.",
|
|
127
128
|
`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
129
|
`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
130
|
`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 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
|
+
"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. Closed cards are immutable; another delivery requires a new backupDelivery card for the same retained snapshot, and operationStatus records current cleanup without repeating an effect. No external Settings URL exists."
|
|
131
132
|
];
|