@liquiditytech/rapidx-cli 1.0.26 → 1.0.28

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/dist/cli/help.js CHANGED
@@ -13,7 +13,7 @@ export function formatCliHelp() {
13
13
  " rapidx market get-ticker --symbol BINANCE_PERP_BTC_USDT --json",
14
14
  " rapidx order preview --input '{\"symbol\":\"BINANCE_PERP_BTC_USDT\",\"side\":\"BUY\",\"orderType\":\"LIMIT\",\"price\":\"1\",\"quantity\":\"0.001\",\"maxNotional\":\"1\",\"clientOrderId\":\"example\"}' --json",
15
15
  " rapidx order cancel-preview --input '{\"orderId\":\"<order-id>\"}' --json",
16
- " rapidx trade verify-live --input '{\"symbol\":\"BINANCE_PERP_BTC_USDT\",\"side\":\"BUY\",\"maxNotional\":\"10\",\"clientOrderId\":\"verify-001\",\"explicitUserConsent\":true}' --json",
16
+ " rapidx trade verify-live --input '{\"symbol\":\"BINANCE_PERP_BTC_USDT\",\"side\":\"BUY\",\"maxNotional\":\"10\",\"clientOrderId\":\"verify-001\",\"explicitUserConsent\":true,\"acceptedRiskText\":\"I authorize a real verification order for BINANCE_PERP_BTC_USDT BUY maxNotional 10 with cancel cleanup.\"}' --json",
17
17
  " rapidx mcp serve",
18
18
  "",
19
19
  "Domains:",
@@ -38,8 +38,8 @@ export const CAPABILITIES = [
38
38
  { capabilityId: "algo.amend", cliCommand: "rapidx algo amend", mcpTool: "rapidx/algo/amend", operationType: "TRADE_WRITE", riskLevel: "trade-write", inputSchema: "AlgoAmendInput", outputSchema: "AlgoOrderStatus", previewRequired: true },
39
39
  { capabilityId: "algo.cancel", cliCommand: "rapidx algo cancel", mcpTool: "rapidx/algo/cancel", operationType: "TRADE_WRITE", riskLevel: "trade-write", inputSchema: "AlgoCancelInput", outputSchema: "AlgoOrderStatus", previewRequired: true },
40
40
  { capabilityId: "algo.list", cliCommand: "rapidx algo list", mcpTool: "rapidx/algo/list", operationType: "TRADE_READ", riskLevel: "trade-read", inputSchema: "AlgoListInput", outputSchema: "AlgoList", previewRequired: false },
41
- { capabilityId: "trading.verify", cliCommand: "rapidx self-check trade-verify", mcpTool: "rapidx/trading-verification", operationType: "TRADE_WRITE", riskLevel: "critical-trade-write", inputSchema: "TradingVerificationInput", outputSchema: "TradingVerificationReport", previewRequired: false },
42
- { capabilityId: "trading.verify-live", cliCommand: "rapidx trade verify-live", mcpTool: "rapidx/trade/verify-live", operationType: "TRADE_WRITE", riskLevel: "critical-trade-write", inputSchema: "TradingVerificationInput", outputSchema: "TradingVerificationReport", previewRequired: false },
41
+ { capabilityId: "trading.verify", cliCommand: "rapidx self-check trade-verify", mcpTool: "rapidx/trading-verification", operationType: "TRADE_WRITE", riskLevel: "critical-trade-write", inputSchema: "TradingVerificationInput", outputSchema: "TradingVerificationReport", previewRequired: false, containsRealOrder: true, requiresExplicitHumanConfirmation: true, confirmationMode: "internal-preview-and-parameter-bound-consent" },
42
+ { capabilityId: "trading.verify-live", cliCommand: "rapidx trade verify-live", mcpTool: "rapidx/trade/verify-live", operationType: "TRADE_WRITE", riskLevel: "critical-trade-write", inputSchema: "TradingVerificationInput", outputSchema: "TradingVerificationReport", previewRequired: false, containsRealOrder: true, requiresExplicitHumanConfirmation: true, confirmationMode: "internal-preview-and-parameter-bound-consent" },
43
43
  { capabilityId: "invocation.check", cliCommand: "rapidx invocation check", operationType: "DIAGNOSTIC", riskLevel: "read", inputSchema: "InvocationCheckInput", outputSchema: "InvocationCompliance", previewRequired: false }
44
44
  ];
45
45
  export function getSchemaResult() {
@@ -1,7 +1,7 @@
1
1
  import { SCHEMA_VERSION } from "./types.js";
2
2
  import { RAPIDX_VERSION } from "../version.js";
3
3
  export const RAPIDX_SKILLS_DISTRIBUTION = "github";
4
- export const RAPIDX_SKILLS_VERSION = "1.0.1";
4
+ export const RAPIDX_SKILLS_VERSION = "1.0.2";
5
5
  export const RAPIDX_SKILLS_SCHEMA_VERSION = "1.0.0";
6
6
  export function buildCompatibilityReport(input = {}) {
7
7
  const checks = [
@@ -4,8 +4,8 @@ const booleanSchema = { type: "boolean" };
4
4
  const numberSchema = { type: "number" };
5
5
  const symbolSchema = {
6
6
  type: "string",
7
- description: "RapidX symbol, for example BINANCE_PERP_BTC_USDT or OKX_SWAP_BTC_USDT.",
8
- examples: ["BINANCE_PERP_BTC_USDT", "OKX_SWAP_BTC_USDT"]
7
+ description: "RapidX symbol. Recommended format: BINANCE_PERP_<BASE>_<QUOTE>.",
8
+ examples: ["BINANCE_PERP_BTC_USDT", "BINANCE_PERP_ETH_USDT"]
9
9
  };
10
10
  const sideSchema = {
11
11
  type: "string",
@@ -46,6 +46,15 @@ const clientOrderIdSchema = {
46
46
  description: "Caller-generated idempotency key for the order request.",
47
47
  examples: ["agent-test-20260529-001"]
48
48
  };
49
+ const explicitUserConsentSchema = {
50
+ type: "boolean",
51
+ description: "Must be true only after the human user explicitly authorizes this exact live verification request."
52
+ };
53
+ const acceptedRiskTextSchema = {
54
+ type: "string",
55
+ description: "Human confirmation text bound to the exact symbol, side, maxNotional, real-order risk, and cancel/cleanup behavior.",
56
+ examples: ["I authorize a real verification order for BINANCE_PERP_BTC_USDT BUY maxNotional 10 with cancel cleanup."]
57
+ };
49
58
  const previewIdSchema = {
50
59
  type: "string",
51
60
  description: "Preview id returned by the matching preview tool or command.",
@@ -230,7 +239,14 @@ export function inputSchemaForName(name) {
230
239
  case "AlgoCancelInput":
231
240
  return objectSchema({ algoOrderId: stringSchema, clientOrderId: clientOrderIdSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]);
232
241
  case "TradingVerificationInput":
233
- return objectSchema({ symbol: symbolSchema, side: sideSchema, maxNotional: maxNotionalSchema, clientOrderId: clientOrderIdSchema, explicitUserConsent: booleanSchema, acceptedRiskText: stringSchema }, ["symbol", "side", "maxNotional", "clientOrderId"]);
242
+ return objectSchema({
243
+ symbol: symbolSchema,
244
+ side: sideSchema,
245
+ maxNotional: maxNotionalSchema,
246
+ clientOrderId: clientOrderIdSchema,
247
+ explicitUserConsent: explicitUserConsentSchema,
248
+ acceptedRiskText: acceptedRiskTextSchema
249
+ }, ["symbol", "side", "maxNotional", "clientOrderId", "explicitUserConsent", "acceptedRiskText"]);
234
250
  case "ConfigGetInput":
235
251
  case "ConfigUnsetInput":
236
252
  return objectSchema({ key: stringSchema }, ["key"]);
@@ -97,7 +97,7 @@ async function cleanupCheck(input) {
97
97
  openOrders: orders.filter((order) => {
98
98
  const state = normalizeOrderStatus(stringField(order, "orderState") ?? "OPEN");
99
99
  const orderSymbol = stringField(order, "sym") ?? stringField(order, "symbol") ?? symbol;
100
- return orderSymbol === symbol && state !== "CANCELED" && state !== "REJECTED" && state !== "FILLED";
100
+ return orderSymbol === symbol && state !== "CANCELED" && state !== "REJECTED" && state !== "EXPIRED" && state !== "FILLED";
101
101
  }).length,
102
102
  unexpectedPositions: positions.filter((position) => {
103
103
  const positionSymbol = stringField(position, "sym") ?? stringField(position, "symbol");
@@ -244,6 +244,9 @@ function normalizeOrderStatus(state) {
244
244
  case "CANCELED":
245
245
  case "CANCEL_COMPLETE":
246
246
  return "CANCELED";
247
+ case "EXPIRED":
248
+ case "EXPIRE":
249
+ return "EXPIRED";
247
250
  case "REJECTED":
248
251
  return "REJECTED";
249
252
  default:
@@ -1,4 +1,4 @@
1
- import { getSchemaResult } from "../contracts/capabilities.js";
1
+ import { getSchemaResult, listMcpCapabilities } from "../contracts/capabilities.js";
2
2
  import { ProductError } from "../errors/product-error.js";
3
3
  import { checkForUpdate } from "../update/check-update.js";
4
4
  export async function runSelfCheck(options = {}) {
@@ -14,7 +14,9 @@ export async function runSelfCheck(options = {}) {
14
14
  timestamp: new Date().toISOString()
15
15
  });
16
16
  }
17
- add({ name: "discovery", status: "PASS", source: "local_check", message: `${getSchemaResult().capabilities.length} capabilities loaded.` });
17
+ const canonicalCapabilityCount = getSchemaResult().capabilities.length;
18
+ const mcpToolCount = new Set(listMcpCapabilities().map((capability) => capability.mcpTool)).size;
19
+ add({ name: "discovery", status: "PASS", source: "local_check", message: `${canonicalCapabilityCount} canonical capabilities loaded; ${mcpToolCount} MCP tools available.` });
18
20
  add({ name: "schema-compatibility", status: "PASS", source: "local_check", message: "Canonical schema is available." });
19
21
  let update;
20
22
  if (options.input?.checkUpdates) {
@@ -13,7 +13,7 @@ export async function runTradingVerification(rawInput, probes = {}) {
13
13
  }
14
14
  catch (error) {
15
15
  if (error instanceof ProductError) {
16
- return { submittedRealOrder: false, status: "FAIL", cleanupStatus: "NOT_VERIFIED", steps, errorCode: error.code, errorMessage: error.message };
16
+ return { submittedRealOrder: false, status: "FAIL", cleanupStatus: "NOT_VERIFIED", steps, errorCode: error.code, errorMessage: error.message, errorStage: "input-validation" };
17
17
  }
18
18
  throw error;
19
19
  }
@@ -26,20 +26,30 @@ export async function runTradingVerification(rawInput, probes = {}) {
26
26
  : selfCheckOptions);
27
27
  steps.push({ name: "self-check", status: selfCheck.status, toolOrCommandEvidence: "rapidx self-check --read-only --json" });
28
28
  if (selfCheck.status !== "PASS") {
29
- return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE10002" };
30
- }
31
- if (!input.explicitUserConsent) {
32
- steps.push({ name: "preview", status: "BLOCKED", toolOrCommandEvidence: "user consent missing for small real trade verification" });
33
- return { submittedRealOrder: false, status: "BLOCKED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE20001" };
29
+ return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE10002", errorStage: "self-check" };
30
+ }
31
+ const consentProblem = validateParameterBoundConsent(input);
32
+ if (consentProblem) {
33
+ steps.push({ name: "preview", status: "BLOCKED", toolOrCommandEvidence: consentProblem });
34
+ return {
35
+ submittedRealOrder: false,
36
+ status: "BLOCKED",
37
+ cleanupStatus: "NOT_VERIFIED",
38
+ steps,
39
+ errorCode: "RCORE20001",
40
+ errorMessage: consentProblem,
41
+ errorStage: "user-consent",
42
+ recommendedAction: "Ask the human user to confirm the exact symbol, side, maxNotional, real-order risk, and cancel cleanup behavior before running verify-live."
43
+ };
34
44
  }
35
45
  if (!probes.marketRules) {
36
46
  steps.push({ name: "market", status: "NOT_VERIFIED", toolOrCommandEvidence: "market rules probe missing" });
37
- return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE10002" };
47
+ return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE10002", errorStage: "market" };
38
48
  }
39
49
  const marketRules = await probes.marketRules(input);
40
50
  if (Number(marketRules.minNotional) > Number(input.maxNotional)) {
41
51
  steps.push({ name: "market", status: "BLOCKED", toolOrCommandEvidence: `minNotional=${marketRules.minNotional};maxNotional=${input.maxNotional}` });
42
- return { submittedRealOrder: false, status: "BLOCKED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE24001" };
52
+ return { submittedRealOrder: false, status: "BLOCKED", cleanupStatus: "NOT_VERIFIED", steps, errorCode: "RCORE24001", errorStage: "market" };
43
53
  }
44
54
  steps.push({ name: "market", status: "PASS", toolOrCommandEvidence: `minNotional=${marketRules.minNotional};tickSize=${marketRules.tickSize}` });
45
55
  const capability = findCapabilityById("order.preview");
@@ -60,18 +70,18 @@ export async function runTradingVerification(rawInput, probes = {}) {
60
70
  steps.push({ name: "preview", status: "PASS", toolOrCommandEvidence: preview.previewId });
61
71
  if (!probes.placeOrder) {
62
72
  steps.push({ name: "place", status: "NOT_VERIFIED", toolOrCommandEvidence: "placeOrder probe missing" });
63
- return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001" };
73
+ return { submittedRealOrder: false, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "place" };
64
74
  }
65
75
  const placed = await probes.placeOrder({ ...input, previewId: preview.previewId, orderType: "LIMIT", postOnly: true, price, quantity });
66
76
  steps.push({ name: "place", status: "PASS", toolOrCommandEvidence: placed.requestId ?? placed.orderId });
67
77
  if (!probes.queryOrder) {
68
78
  steps.push({ name: "query", status: "NOT_VERIFIED", toolOrCommandEvidence: "queryOrder probe missing" });
69
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001" };
79
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "query" };
70
80
  }
71
81
  const queried = await probes.queryOrder(placed);
72
82
  if (queried.status === "UNKNOWN") {
73
83
  steps.push({ name: "query", status: "NOT_VERIFIED", toolOrCommandEvidence: queried.orderId });
74
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001" };
84
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "query", lastObservedOrderState: queried.status };
75
85
  }
76
86
  steps.push({ name: "query", status: "PASS", toolOrCommandEvidence: `${queried.orderId}:${queried.status}` });
77
87
  let currentOrder = queried;
@@ -79,7 +89,7 @@ export async function runTradingVerification(rawInput, probes = {}) {
79
89
  currentOrder = await probes.amendOrder(currentOrder);
80
90
  steps.push({ name: "amend", status: currentOrder.status === "UNKNOWN" ? "NOT_VERIFIED" : "PASS", toolOrCommandEvidence: `${currentOrder.orderId}:${currentOrder.status}` });
81
91
  if (currentOrder.status === "UNKNOWN") {
82
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001" };
92
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "amend", lastObservedOrderState: currentOrder.status };
83
93
  }
84
94
  }
85
95
  else {
@@ -87,22 +97,23 @@ export async function runTradingVerification(rawInput, probes = {}) {
87
97
  }
88
98
  if (!probes.cancelOrder) {
89
99
  steps.push({ name: "cancel", status: "FAIL", toolOrCommandEvidence: "cancelOrder probe missing" });
90
- return { submittedRealOrder: true, status: "FAIL", cleanupStatus: "FAIL", steps, previewId: preview.previewId, errorCode: "RCORE24002" };
100
+ return cleanupFailureReport(steps, preview.previewId, "cancel", undefined, "cancelOrder probe missing");
91
101
  }
92
- currentOrder = await probes.cancelOrder(currentOrder);
93
- if (currentOrder.status !== "CANCELED") {
94
- steps.push({ name: "cancel", status: "FAIL", toolOrCommandEvidence: `${currentOrder.orderId}:${currentOrder.status}` });
95
- return { submittedRealOrder: true, status: "FAIL", cleanupStatus: "FAIL", steps, previewId: preview.previewId, errorCode: "RCORE24002" };
102
+ const cancelConfirmation = await confirmCancel(currentOrder, probes);
103
+ currentOrder = cancelConfirmation.order;
104
+ if (!cancelConfirmation.confirmed) {
105
+ steps.push({ name: "cancel", status: "FAIL", toolOrCommandEvidence: cancelConfirmation.evidence });
106
+ return cleanupFailureReport(steps, preview.previewId, "cancel-confirmation", currentOrder.status);
96
107
  }
97
- steps.push({ name: "cancel", status: "PASS", toolOrCommandEvidence: `${currentOrder.orderId}:${currentOrder.status}` });
108
+ steps.push({ name: "cancel", status: "PASS", toolOrCommandEvidence: cancelConfirmation.evidence });
98
109
  if (!probes.cleanupCheck) {
99
110
  steps.push({ name: "cleanup-check", status: "NOT_VERIFIED", toolOrCommandEvidence: "cleanupCheck probe missing" });
100
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE24002" };
111
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE24002", errorStage: "cleanup-check", recommendedAction: "Query order/list and position/list before retrying verify-live." };
101
112
  }
102
113
  const cleanup = await probes.cleanupCheck(currentOrder);
103
114
  if (cleanup.openOrders !== 0 || cleanup.unexpectedPositions !== 0) {
104
115
  steps.push({ name: "cleanup-check", status: "FAIL", toolOrCommandEvidence: `openOrders=${cleanup.openOrders};unexpectedPositions=${cleanup.unexpectedPositions}` });
105
- return { submittedRealOrder: true, status: "FAIL", cleanupStatus: "FAIL", steps, previewId: preview.previewId, errorCode: "RCORE24002" };
116
+ return cleanupFailureReport(steps, preview.previewId, "cleanup-check", currentOrder.status, `cleanup still shows openOrders=${cleanup.openOrders}; unexpectedPositions=${cleanup.unexpectedPositions}`);
106
117
  }
107
118
  steps.push({ name: "cleanup-check", status: "PASS", toolOrCommandEvidence: "openOrders=0;unexpectedPositions=0" });
108
119
  return {
@@ -127,6 +138,80 @@ function normalizeTradingVerificationInput(input) {
127
138
  }
128
139
  return normalized;
129
140
  }
141
+ function validateParameterBoundConsent(input) {
142
+ if (!input.explicitUserConsent) {
143
+ return "user consent missing for small real trade verification";
144
+ }
145
+ const text = input.acceptedRiskText?.trim() ?? "";
146
+ const upperText = text.toUpperCase();
147
+ const lowerText = text.toLowerCase();
148
+ const missing = [];
149
+ if (text.length < 20) {
150
+ missing.push("acceptedRiskText");
151
+ }
152
+ if (!upperText.includes(input.symbol.toUpperCase())) {
153
+ missing.push("symbol");
154
+ }
155
+ if (!upperText.includes(input.side)) {
156
+ missing.push("side");
157
+ }
158
+ if (!text.includes(input.maxNotional)) {
159
+ missing.push("maxNotional");
160
+ }
161
+ if (!lowerText.includes("real") && !text.includes("真实")) {
162
+ missing.push("real-order risk");
163
+ }
164
+ if (!lowerText.includes("cancel") && !lowerText.includes("cleanup") && !text.includes("取消") && !text.includes("清理")) {
165
+ missing.push("cancel cleanup");
166
+ }
167
+ if (missing.length > 0) {
168
+ return `acceptedRiskText must include ${missing.join(", ")} for this exact verify-live request`;
169
+ }
170
+ return undefined;
171
+ }
172
+ async function confirmCancel(order, probes) {
173
+ const observations = [];
174
+ let current = await probes.cancelOrder(order);
175
+ observations.push(current.status);
176
+ if (isSafeCancelTerminalState(current.status)) {
177
+ return { confirmed: true, order: current, evidence: `${current.orderId}:${observations.join("->")}` };
178
+ }
179
+ const delays = probes.cancelConfirmationDelaysMs ?? [100, 250, 500, 1_000, 2_000];
180
+ const wait = probes.wait ?? defaultWait;
181
+ for (const delay of delays) {
182
+ await wait(delay);
183
+ if (!probes.queryOrder) {
184
+ break;
185
+ }
186
+ current = await probes.queryOrder(current);
187
+ observations.push(current.status);
188
+ if (isSafeCancelTerminalState(current.status)) {
189
+ return { confirmed: true, order: current, evidence: `${current.orderId}:${observations.join("->")}` };
190
+ }
191
+ }
192
+ return { confirmed: false, order: current, evidence: `${current.orderId}:${observations.join("->")}` };
193
+ }
194
+ function isSafeCancelTerminalState(status) {
195
+ return status === "CANCELED" || status === "EXPIRED" || status === "REJECTED";
196
+ }
197
+ function defaultWait(milliseconds) {
198
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
199
+ }
200
+ function cleanupFailureReport(steps, previewId, errorStage, lastObservedOrderState, detail) {
201
+ return {
202
+ submittedRealOrder: true,
203
+ status: "FAIL",
204
+ cleanupStatus: "FAIL",
205
+ steps,
206
+ previewId,
207
+ errorCode: "RCORE24002",
208
+ errorMessage: detail ?? "Trading verification cleanup could not be confirmed.",
209
+ errorStage,
210
+ ...(lastObservedOrderState ? { lastObservedOrderState } : {}),
211
+ expectedOrderStates: ["CANCELED", "EXPIRED", "REJECTED"],
212
+ recommendedAction: "Query order/get, order/list, and position/list before retrying; do not submit another verification until cleanup is confirmed."
213
+ };
214
+ }
130
215
  function quantityForNotional(minNotional, price) {
131
216
  const priceNumber = Number(price);
132
217
  const minNotionalNumber = Number(minNotional);
@@ -1 +1 @@
1
- export const RAPIDX_VERSION = "1.0.26";
1
+ export const RAPIDX_VERSION = "1.0.28";
@@ -10,13 +10,20 @@ export function getMcpToolDefinitions() {
10
10
  const canonical = capabilityForTool(capability.mcpTool);
11
11
  tools.push({
12
12
  name: capability.mcpTool,
13
- description: `RapidX ${canonical.capabilityId} (${canonical.riskLevel}, schema ${SCHEMA_VERSION})`,
13
+ description: toolDescription(canonical),
14
14
  inputSchema: inputSchemaForName(canonical.inputSchema),
15
15
  capability: canonical
16
16
  });
17
17
  }
18
18
  return tools.sort((a, b) => a.name.localeCompare(b.name));
19
19
  }
20
+ function toolDescription(capability) {
21
+ const base = `RapidX ${capability.capabilityId} (${capability.riskLevel}, schema ${SCHEMA_VERSION})`;
22
+ if (!capability.containsRealOrder) {
23
+ return base;
24
+ }
25
+ return `${base}. This submits a real verification order and requires explicit human confirmation bound to symbol, side, maxNotional, and cancel cleanup behavior.`;
26
+ }
20
27
  export function capabilityForTool(toolName) {
21
28
  const overrideId = toolName === "rapidx/tools"
22
29
  ? "schema.discover"
@@ -13,7 +13,10 @@ export async function runMcpTool(toolName, input = {}) {
13
13
  riskLevel: tool.capability.riskLevel,
14
14
  inputSchema: tool.capability.inputSchema,
15
15
  outputSchema: tool.capability.outputSchema,
16
- previewRequired: tool.capability.previewRequired
16
+ previewRequired: tool.capability.previewRequired,
17
+ containsRealOrder: tool.capability.containsRealOrder === true,
18
+ requiresExplicitHumanConfirmation: tool.capability.requiresExplicitHumanConfirmation === true,
19
+ confirmationMode: tool.capability.confirmationMode
17
20
  }))
18
21
  };
19
22
  const auditId = writeMcpAudit("tools/list", "PASS", { toolName });
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@liquiditytech/rapidx-cli",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "RapidX CLI, MCP server mode, registry, and release assets.",
5
5
  "type": "module",
6
6
  "private": false,
7
- "repository": {
8
- "type": "git",
9
- "url": "git+ssh://git@github.com/LiquidityTech/ltp-rapidx-cli-mcp.git"
10
- },
7
+ "license": "UNLICENSED",
11
8
  "publishConfig": {
12
9
  "registry": "https://registry.npmjs.org/",
13
10
  "access": "public"
@@ -31,6 +31,6 @@ Key trading tools:
31
31
  - `rapidx/trade/preview` creates preview tokens for non-place trade writes by `targetCapabilityId`, including position, account, and algo writes.
32
32
  - `rapidx/position/history` reads historical positions.
33
33
  - `rapidx/account/set-position-mode` changes account position mode and requires preview plus explicit continuation consent.
34
- - `rapidx/trade/verify-live` runs the optional small real-trade verification flow with `explicitUserConsent`; it creates its own internal preview and does not accept an external `previewId`. `rapidx/trading-verification` remains supported as a compatibility alias.
34
+ - `rapidx/trade/verify-live` runs the optional small real-trade verification flow with `explicitUserConsent` and parameter-bound `acceptedRiskText`; it creates its own internal preview and does not accept an external `previewId`. `rapidx/trading-verification` remains supported as a compatibility alias.
35
35
 
36
36
  Agents must discover the full tool list through `rapidx/tools` and must not invent tool names.
@@ -126,4 +126,4 @@ When submitting the actual write, pass the returned `previewId` and use `confirm
126
126
 
127
127
  `maxNotional` is a safety upper bound, not the target order amount. If a requested amount is below the symbol `minNotional`, ask the user to confirm the larger amount before submitting. For `position.close`, do not pass `side` or `quantity`; RapidX determines the close side from the current position and closes the target symbol/positionSide. Use a reduce-only order flow for partial closes, then check final exposure with `position list`.
128
128
 
129
- Use `rapidx trade verify-live --json` only when the user explicitly authorizes a small real-trade verification. The older `rapidx self-check trade-verify --json` command remains supported for compatibility.
129
+ Use `rapidx trade verify-live --json` only when the user explicitly authorizes a small real-trade verification. Include `acceptedRiskText` that names the exact symbol, side, maxNotional, real-order risk, and cancel cleanup behavior. The older `rapidx self-check trade-verify --json` command remains supported for compatibility.
@@ -30,6 +30,6 @@ The canonical source is `rapidx schema --json` or `rapidx/tools`.
30
30
  - Position writes: close and set leverage.
31
31
  - Account writes: set position mode.
32
32
  - Algo writes: place, amend, and cancel.
33
- - Live verification: `rapidx/trade/verify-live` is the clearer alias for the small real-trade verification flow. `rapidx/trading-verification` remains supported for compatibility.
33
+ - Live verification: `rapidx/trade/verify-live` is the clearer alias for the small real-trade verification flow. It submits a real verification order and requires `acceptedRiskText` bound to the exact symbol, side, maxNotional, real-order risk, and cancel cleanup behavior. `rapidx/trading-verification` remains supported for compatibility.
34
34
 
35
35
  All write tools require preview where the schema marks `previewRequired: true`.
@@ -1,25 +1,25 @@
1
1
  {
2
- "version": "1.0.26",
2
+ "version": "1.0.28",
3
3
  "artifacts": [
4
4
  {
5
5
  "name": "rapidx-mcp-registry",
6
6
  "path": "packages/distribution/registry/rapidx.mcp.json",
7
7
  "channel": "offline",
8
- "checksum": "sha256:a27d7e64537c0b39574b06887e1a0f4a689851901614216a509fd19c04fdbf98",
8
+ "checksum": "sha256:0e4bbf784d784442bd296e4ca6d490733ccd40e50f7b299b211b5fdb4dcb465d",
9
9
  "status": "ready"
10
10
  },
11
11
  {
12
12
  "name": "rapidx-quickstart-doc",
13
13
  "path": "packages/distribution/docs/quickstart.md",
14
14
  "channel": "offline",
15
- "checksum": "sha256:7d8f511a5dbe803e7a19ede0be79f6bf1b77942dbafa45d0ce87fb3b098f7829",
15
+ "checksum": "sha256:0f78bb814c60ec3f5ac1053cac49c1c1e768ff529b70b32e44a31f96ad8a1e95",
16
16
  "status": "ready"
17
17
  },
18
18
  {
19
19
  "name": "rapidx-tools-doc",
20
20
  "path": "packages/distribution/docs/tools.md",
21
21
  "channel": "offline",
22
- "checksum": "sha256:ee6adae99a6facfd5f1c3dced86c7a28eafd11ecbe7e2a7eda159a6aa788a245",
22
+ "checksum": "sha256:ab953a5e40024e9171ad7510bd37fef20e6aa9c6d89bfc219160cb33bb465712",
23
23
  "status": "ready"
24
24
  }
25
25
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rapidx",
3
3
  "packageName": "@liquiditytech/rapidx-cli",
4
- "version": "1.0.26",
4
+ "version": "1.0.28",
5
5
  "command": "rapidx",
6
6
  "args": ["mcp", "serve"],
7
7
  "launchCommand": "rapidx",