@okx_ai/okx-trade-mcp 1.3.6 → 1.3.8-beta.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/dist/index.js CHANGED
@@ -2475,6 +2475,7 @@ var MODULES = [
2475
2475
  "event",
2476
2476
  "news",
2477
2477
  "smartmoney",
2478
+ "outcomes",
2478
2479
  ...EARN_SUB_MODULE_IDS,
2479
2480
  ...BOT_SUB_MODULE_IDS,
2480
2481
  "skills"
@@ -8049,6 +8050,706 @@ function registerSmartmoneyTools() {
8049
8050
  ];
8050
8051
  return tools;
8051
8052
  }
8053
+ function toNum(v) {
8054
+ if (v === void 0 || v === null || v === "") return void 0;
8055
+ const n = Number(v);
8056
+ return Number.isFinite(n) ? n : void 0;
8057
+ }
8058
+ function decimalPlaces(v) {
8059
+ if (v === void 0 || v === null || v === "") return 0;
8060
+ const s = String(v);
8061
+ const dot = s.indexOf(".");
8062
+ return dot < 0 ? 0 : s.length - dot - 1;
8063
+ }
8064
+ function fmtFixed(n, places) {
8065
+ return n.toFixed(places);
8066
+ }
8067
+ function deriveTickerView(ticker, outcome) {
8068
+ const bidNum = toNum(ticker.bidPx);
8069
+ const askNum = toNum(ticker.askPx);
8070
+ const lastStr = ticker.last !== void 0 ? String(ticker.last) : void 0;
8071
+ const bidPlaces = decimalPlaces(ticker.bidPx);
8072
+ const askPlaces = decimalPlaces(ticker.askPx);
8073
+ const maxPlaces = Math.max(bidPlaces, askPlaces);
8074
+ const bidStr = bidNum !== void 0 ? fmtFixed(bidNum, bidPlaces) : void 0;
8075
+ const askStr = askNum !== void 0 ? fmtFixed(askNum, askPlaces) : void 0;
8076
+ const view = { last: lastStr, bidPx: bidStr, askPx: askStr };
8077
+ if (bidNum !== void 0 && askNum !== void 0) {
8078
+ view.midpoint = fmtFixed((bidNum + askNum) / 2, maxPlaces);
8079
+ view.spread = fmtFixed(askNum - bidNum, maxPlaces);
8080
+ }
8081
+ if (outcome !== void 0 && askNum !== void 0) {
8082
+ const outcomeLower = outcome.toLowerCase();
8083
+ if (outcomeLower === "yes") {
8084
+ view.derivedPrice = fmtFixed(askNum, askPlaces);
8085
+ } else if (outcomeLower === "no") {
8086
+ view.derivedPrice = fmtFixed(1 - askNum, askPlaces);
8087
+ }
8088
+ }
8089
+ return view;
8090
+ }
8091
+ var PATH_EVENTS = "/api/v5/predictions/events";
8092
+ var PATH_EVENTS_SEARCH = "/api/v5/predictions/events/search";
8093
+ var PATH_MARKETS = "/api/v5/predictions/markets";
8094
+ var PATH_BALANCE = "/api/v5/predictions/balance";
8095
+ var PATH_ORDERS = "/api/v5/predictions/orders";
8096
+ var PATH_POSITIONS = "/api/v5/predictions/positions";
8097
+ var PATH_TRADES = "/api/v5/predictions/trades";
8098
+ var PATH_TICKER = "/api/v5/market/ticker";
8099
+ var PATH_CANDLES = "/api/v5/market/candles";
8100
+ var PATH_PM_BOOKS = "/api/v5/market/pm-books";
8101
+ var OUTCOMES_RPS = 10;
8102
+ function requireString2(args, key, label) {
8103
+ const v = readString(args, key);
8104
+ if (!v) throw new Error(`${label} is required`);
8105
+ return v;
8106
+ }
8107
+ function pathSegment(id) {
8108
+ return encodeURIComponent(id);
8109
+ }
8110
+ async function runBatchQuery(ids, fn) {
8111
+ if (ids.length === 0) {
8112
+ throw new Error("At least one ID is required");
8113
+ }
8114
+ const results = await Promise.allSettled(ids.map((id) => fn(id)));
8115
+ return results.map((result, i) => {
8116
+ const id = ids[i];
8117
+ if (result.status === "fulfilled") {
8118
+ return { id, data: result.value };
8119
+ }
8120
+ const errMsg = result.reason instanceof Error ? result.reason.message : String(result.reason);
8121
+ return { id, error: errMsg };
8122
+ });
8123
+ }
8124
+ function registerOutcomesTools() {
8125
+ const tools = [
8126
+ /* ================================================================ */
8127
+ /* DATA MODULE (7) */
8128
+ /* ================================================================ */
8129
+ /* D1. List events */
8130
+ {
8131
+ name: "outcomes_get_events",
8132
+ title: "List Prediction Events",
8133
+ module: "outcomes",
8134
+ description: "List available prediction (binary outcomes) events on OKX, optionally filtered by status, category, tag, league, sort, or cursor. Use when: discovering events to trade on. Default limit 20. See also: `outcomes_get_trending_events` (no args, server-chosen trending events), `outcomes_search_events` (keyword search).",
8135
+ isWrite: false,
8136
+ inputSchema: {
8137
+ type: "object",
8138
+ properties: {
8139
+ status: { type: "string", description: "Filter by event status, e.g. `active` | `closed` | `settled`." },
8140
+ category: { type: "string", description: "Event category, e.g. `crypto` | `sports` | `politics`." },
8141
+ tag: { type: "string", description: "Event tag for further classification." },
8142
+ league: { type: "string", description: "League / competition name for sports events." },
8143
+ sort: { type: "string", description: "Sort key, e.g. `trending` | `newest`." },
8144
+ cursor: { type: "string", description: "Pagination cursor from previous response." },
8145
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8146
+ }
8147
+ },
8148
+ handler: async (rawArgs, context) => {
8149
+ const args = asRecord(rawArgs);
8150
+ const response = await context.client.privateGet(
8151
+ PATH_EVENTS,
8152
+ compactObject({
8153
+ status: readString(args, "status"),
8154
+ category: readString(args, "category"),
8155
+ tag: readString(args, "tag"),
8156
+ league: readString(args, "league"),
8157
+ sort: readString(args, "sort"),
8158
+ cursor: readString(args, "cursor"),
8159
+ limit: readNumber(args, "limit") ?? 20
8160
+ }),
8161
+ privateRateLimit("outcomes_get_events", OUTCOMES_RPS)
8162
+ );
8163
+ return normalizeResponse(response);
8164
+ }
8165
+ },
8166
+ /* D2. Get event detail */
8167
+ {
8168
+ name: "outcomes_get_event_detail",
8169
+ title: "Get Prediction Event Detail",
8170
+ module: "outcomes",
8171
+ description: "Get full detail of a single prediction event by its ID, including description, resolution criteria, and linked markets. Use when: inspecting a specific event before trading.",
8172
+ isWrite: false,
8173
+ inputSchema: {
8174
+ type: "object",
8175
+ properties: {
8176
+ id: { type: "string", description: "Event ID, e.g. from `outcomes_get_events` results." }
8177
+ },
8178
+ required: ["id"]
8179
+ },
8180
+ handler: async (rawArgs, context) => {
8181
+ const args = asRecord(rawArgs);
8182
+ const id = requireString2(args, "id", "Event ID");
8183
+ const response = await context.client.privateGet(
8184
+ `${PATH_EVENTS}/${pathSegment(id)}`,
8185
+ {},
8186
+ privateRateLimit("outcomes_get_event_detail", OUTCOMES_RPS)
8187
+ );
8188
+ return normalizeResponse(response);
8189
+ }
8190
+ },
8191
+ /* D3. Get event markets */
8192
+ {
8193
+ name: "outcomes_get_event_markets",
8194
+ title: "Get Markets for a Prediction Event",
8195
+ module: "outcomes",
8196
+ description: "Get all tradable markets for a prediction event. Each market corresponds to a binary outcome (YES/NO). Use when: listing which outcomes are available to trade for an event. See also: `outcomes_get_market_detail` (single market by market ID).",
8197
+ isWrite: false,
8198
+ inputSchema: {
8199
+ type: "object",
8200
+ properties: {
8201
+ id: { type: "string", description: "Event ID." }
8202
+ },
8203
+ required: ["id"]
8204
+ },
8205
+ handler: async (rawArgs, context) => {
8206
+ const args = asRecord(rawArgs);
8207
+ const id = requireString2(args, "id", "Event ID");
8208
+ const response = await context.client.privateGet(
8209
+ `${PATH_EVENTS}/${pathSegment(id)}/markets`,
8210
+ {},
8211
+ privateRateLimit("outcomes_get_event_markets", OUTCOMES_RPS)
8212
+ );
8213
+ return normalizeResponse(response);
8214
+ }
8215
+ },
8216
+ /* D4. Get market detail */
8217
+ {
8218
+ name: "outcomes_get_market_detail",
8219
+ title: "Get Prediction Market Detail",
8220
+ module: "outcomes",
8221
+ description: "Get full detail of a single prediction market by its market ID (e.g. current probability, settlement info, linked event). Use when: inspecting a specific binary market before placing an order.",
8222
+ isWrite: false,
8223
+ inputSchema: {
8224
+ type: "object",
8225
+ properties: {
8226
+ id: { type: "string", description: "Market ID." }
8227
+ },
8228
+ required: ["id"]
8229
+ },
8230
+ handler: async (rawArgs, context) => {
8231
+ const args = asRecord(rawArgs);
8232
+ const id = requireString2(args, "id", "Market ID");
8233
+ const response = await context.client.privateGet(
8234
+ `${PATH_MARKETS}/${pathSegment(id)}`,
8235
+ {},
8236
+ privateRateLimit("outcomes_get_market_detail", OUTCOMES_RPS)
8237
+ );
8238
+ return normalizeResponse(response);
8239
+ }
8240
+ },
8241
+ /* D5. Trending events (alias of events, server defaults sort) */
8242
+ {
8243
+ name: "outcomes_get_trending_events",
8244
+ title: "Get Trending Prediction Events",
8245
+ module: "outcomes",
8246
+ description: "Get trending prediction events \u2014 server-selected, most-active events. Equivalent to `outcomes_get_events` with no sort parameter; the server applies its trending algorithm. Use when: user asks for 'what's hot / trending' in predictions. See also: `outcomes_get_events` (full filter support).",
8247
+ isWrite: false,
8248
+ inputSchema: {
8249
+ type: "object",
8250
+ properties: {
8251
+ cursor: { type: "string", description: "Pagination cursor." },
8252
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8253
+ }
8254
+ },
8255
+ handler: async (rawArgs, context) => {
8256
+ const args = asRecord(rawArgs);
8257
+ const response = await context.client.privateGet(
8258
+ PATH_EVENTS,
8259
+ compactObject({
8260
+ cursor: readString(args, "cursor"),
8261
+ limit: readNumber(args, "limit") ?? 20
8262
+ }),
8263
+ privateRateLimit("outcomes_get_trending_events", OUTCOMES_RPS)
8264
+ );
8265
+ return normalizeResponse(response);
8266
+ }
8267
+ },
8268
+ /* D6. Get ticker */
8269
+ {
8270
+ name: "outcomes_get_ticker",
8271
+ title: "Get Prediction Asset Ticker",
8272
+ module: "outcomes",
8273
+ description: "Get the current ticker (last traded price, best bid/ask, volume) for a prediction market asset ID. Use when: checking the latest price for a prediction market instrument. Returns the full raw ticker \u2014 use `outcomes_get_market_price` for outcome-derived pricing.",
8274
+ isWrite: false,
8275
+ inputSchema: {
8276
+ type: "object",
8277
+ properties: {
8278
+ assetId: { type: "string", description: "Prediction asset instrument ID, e.g. `BTC-USD-PRED`." }
8279
+ },
8280
+ required: ["assetId"]
8281
+ },
8282
+ handler: async (rawArgs, context) => {
8283
+ const args = asRecord(rawArgs);
8284
+ const assetId = readString(args, "assetId") ?? "";
8285
+ const response = await context.client.privateGet(
8286
+ PATH_TICKER,
8287
+ { instId: assetId },
8288
+ privateRateLimit("outcomes_get_ticker", OUTCOMES_RPS)
8289
+ );
8290
+ return normalizeResponse(response);
8291
+ }
8292
+ },
8293
+ /* D7. Get candles */
8294
+ {
8295
+ name: "outcomes_get_candles",
8296
+ title: "Get Prediction Asset Candles",
8297
+ module: "outcomes",
8298
+ description: "Get OHLCV candlestick data for a prediction asset. OKX pagination convention: `after` returns candles with timestamp BEFORE the cursor; `before` returns candles AFTER. Use when: charting or analysing price history of a prediction market instrument.",
8299
+ isWrite: false,
8300
+ inputSchema: {
8301
+ type: "object",
8302
+ properties: {
8303
+ assetId: { type: "string", description: "Prediction asset instrument ID." },
8304
+ bar: { type: "string", enum: ["1m", "5m", "15m", "30m", "1H", "4H", "1D"], description: "Candlestick bar size (default `1m`)." },
8305
+ after: { type: "string", description: "Pagination cursor \u2014 returns candles with ts BEFORE this value." },
8306
+ before: { type: "string", description: "Pagination cursor \u2014 returns candles with ts AFTER this value." },
8307
+ limit: { type: "integer", minimum: 1, maximum: 300, default: 100, description: "Max candles (default 100)." }
8308
+ },
8309
+ required: ["assetId"]
8310
+ },
8311
+ handler: async (rawArgs, context) => {
8312
+ const args = asRecord(rawArgs);
8313
+ const assetId = readString(args, "assetId") ?? "";
8314
+ const response = await context.client.privateGet(
8315
+ PATH_CANDLES,
8316
+ compactObject({
8317
+ instId: assetId,
8318
+ bar: readString(args, "bar"),
8319
+ after: readString(args, "after"),
8320
+ before: readString(args, "before"),
8321
+ limit: readNumber(args, "limit") ?? 100
8322
+ }),
8323
+ privateRateLimit("outcomes_get_candles", OUTCOMES_RPS)
8324
+ );
8325
+ return normalizeResponse(response);
8326
+ }
8327
+ },
8328
+ /* ================================================================ */
8329
+ /* SEARCH MODULE (1) */
8330
+ /* ================================================================ */
8331
+ /* S1. Search events */
8332
+ {
8333
+ name: "outcomes_search_events",
8334
+ title: "Search Prediction Events",
8335
+ module: "outcomes",
8336
+ description: "Search prediction events by keyword. Returns events matching the keyword in title, description, or tags. Use when: user asks about a specific topic (e.g. 'Will BTC hit 100k?', 'US election'). See also: `outcomes_get_events` (full browse), `outcomes_get_trending_events` (trending).",
8337
+ isWrite: false,
8338
+ inputSchema: {
8339
+ type: "object",
8340
+ properties: {
8341
+ keyword: { type: "string", description: "Search keyword." },
8342
+ cursor: { type: "string", description: "Pagination cursor." },
8343
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8344
+ },
8345
+ required: ["keyword"]
8346
+ },
8347
+ handler: async (rawArgs, context) => {
8348
+ const args = asRecord(rawArgs);
8349
+ const response = await context.client.privateGet(
8350
+ PATH_EVENTS_SEARCH,
8351
+ compactObject({
8352
+ keyword: readString(args, "keyword"),
8353
+ cursor: readString(args, "cursor"),
8354
+ limit: readNumber(args, "limit") ?? 20
8355
+ }),
8356
+ privateRateLimit("outcomes_search_events", OUTCOMES_RPS)
8357
+ );
8358
+ return normalizeResponse(response);
8359
+ }
8360
+ },
8361
+ /* ================================================================ */
8362
+ /* CLOB MODULE (8) */
8363
+ /* 6 of these call /api/v5/market/ticker with client-side derive. */
8364
+ /* 2 (book/books) call /api/v5/market/pm-books. */
8365
+ /* ================================================================ */
8366
+ /* C1. Market price (single) */
8367
+ {
8368
+ name: "outcomes_get_market_price",
8369
+ title: "Get Prediction Market Price",
8370
+ module: "outcomes",
8371
+ description: "Get the derived trading price for a binary prediction market outcome (YES or NO side). Internally fetches the ticker and derives the best-ask for the specified outcome. Use `outcome=yes` for YES side, `outcome=no` for NO side. See also: `outcomes_get_market_prices` (batch), `outcomes_get_market_midpoint` (mid price).",
8372
+ isWrite: false,
8373
+ inputSchema: {
8374
+ type: "object",
8375
+ properties: {
8376
+ mktId: { type: "string", description: "Market instrument ID." },
8377
+ outcome: { type: "string", enum: ["yes", "no"], description: "Which outcome side: `yes` or `no`." }
8378
+ },
8379
+ required: ["mktId"]
8380
+ },
8381
+ handler: async (rawArgs, context) => {
8382
+ const args = asRecord(rawArgs);
8383
+ const mktId = readString(args, "mktId") ?? "";
8384
+ const outcome = readString(args, "outcome");
8385
+ const response = await context.client.privateGet(
8386
+ PATH_TICKER,
8387
+ { instId: mktId },
8388
+ privateRateLimit("outcomes_get_market_price", OUTCOMES_RPS)
8389
+ );
8390
+ const normalized = normalizeResponse(response);
8391
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8392
+ const view = deriveTickerView(ticker, outcome);
8393
+ return { ...normalized, data: { ...ticker, ...view } };
8394
+ }
8395
+ },
8396
+ /* C2. Market prices (batch) */
8397
+ {
8398
+ name: "outcomes_get_market_prices",
8399
+ title: "Get Prediction Market Prices (Batch)",
8400
+ module: "outcomes",
8401
+ description: "Get derived trading prices for multiple prediction market IDs in parallel. Per-row error isolation: if any single market fails, only that row has an `error` field; the rest succeed normally. See also: `outcomes_get_market_price` (single market), `outcomes_get_market_midpoints` (batch midpoints).",
8402
+ isWrite: false,
8403
+ inputSchema: {
8404
+ type: "object",
8405
+ properties: {
8406
+ ids: { type: "array", items: { type: "string" }, description: "List of market instrument IDs." },
8407
+ outcome: { type: "string", enum: ["yes", "no"], description: "Outcome side for price derivation." }
8408
+ },
8409
+ required: ["ids"]
8410
+ },
8411
+ handler: async (rawArgs, context) => {
8412
+ const args = asRecord(rawArgs);
8413
+ const ids = Array.isArray(args["ids"]) ? args["ids"] : [];
8414
+ const outcome = readString(args, "outcome");
8415
+ const rows = await runBatchQuery(ids, async (id) => {
8416
+ const response = await context.client.privateGet(
8417
+ PATH_TICKER,
8418
+ { instId: id },
8419
+ privateRateLimit("outcomes_get_market_prices", OUTCOMES_RPS)
8420
+ );
8421
+ const normalized = normalizeResponse(response);
8422
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8423
+ return { ...ticker, ...deriveTickerView(ticker, outcome) };
8424
+ });
8425
+ return {
8426
+ endpoint: PATH_TICKER,
8427
+ requestTime: (/* @__PURE__ */ new Date()).toISOString(),
8428
+ data: rows
8429
+ };
8430
+ }
8431
+ },
8432
+ /* C3. Market midpoint (single) */
8433
+ {
8434
+ name: "outcomes_get_market_midpoint",
8435
+ title: "Get Prediction Market Midpoint",
8436
+ module: "outcomes",
8437
+ description: "Get the midpoint price for a prediction market: (bidPx + askPx) / 2. Use when: estimating the fair mid-market price. See also: `outcomes_get_market_midpoints` (batch), `outcomes_get_market_spread` (bid-ask spread).",
8438
+ isWrite: false,
8439
+ inputSchema: {
8440
+ type: "object",
8441
+ properties: {
8442
+ mktId: { type: "string", description: "Market instrument ID." }
8443
+ },
8444
+ required: ["mktId"]
8445
+ },
8446
+ handler: async (rawArgs, context) => {
8447
+ const args = asRecord(rawArgs);
8448
+ const mktId = readString(args, "mktId") ?? "";
8449
+ const response = await context.client.privateGet(
8450
+ PATH_TICKER,
8451
+ { instId: mktId },
8452
+ privateRateLimit("outcomes_get_market_midpoint", OUTCOMES_RPS)
8453
+ );
8454
+ const normalized = normalizeResponse(response);
8455
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8456
+ const view = deriveTickerView(ticker, void 0);
8457
+ return { ...normalized, data: { ...ticker, ...view } };
8458
+ }
8459
+ },
8460
+ /* C4. Market midpoints (batch) */
8461
+ {
8462
+ name: "outcomes_get_market_midpoints",
8463
+ title: "Get Prediction Market Midpoints (Batch)",
8464
+ module: "outcomes",
8465
+ description: "Get midpoint prices for multiple prediction markets in parallel. Per-row error isolation. See also: `outcomes_get_market_midpoint` (single), `outcomes_get_market_spreads` (batch spreads).",
8466
+ isWrite: false,
8467
+ inputSchema: {
8468
+ type: "object",
8469
+ properties: {
8470
+ ids: { type: "array", items: { type: "string" }, description: "List of market instrument IDs." }
8471
+ },
8472
+ required: ["ids"]
8473
+ },
8474
+ handler: async (rawArgs, context) => {
8475
+ const args = asRecord(rawArgs);
8476
+ const ids = Array.isArray(args["ids"]) ? args["ids"] : [];
8477
+ const rows = await runBatchQuery(ids, async (id) => {
8478
+ const response = await context.client.privateGet(
8479
+ PATH_TICKER,
8480
+ { instId: id },
8481
+ privateRateLimit("outcomes_get_market_midpoints", OUTCOMES_RPS)
8482
+ );
8483
+ const normalized = normalizeResponse(response);
8484
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8485
+ return { ...ticker, ...deriveTickerView(ticker, void 0) };
8486
+ });
8487
+ return {
8488
+ endpoint: PATH_TICKER,
8489
+ requestTime: (/* @__PURE__ */ new Date()).toISOString(),
8490
+ data: rows
8491
+ };
8492
+ }
8493
+ },
8494
+ /* C5. Market spread (single) */
8495
+ {
8496
+ name: "outcomes_get_market_spread",
8497
+ title: "Get Prediction Market Spread",
8498
+ module: "outcomes",
8499
+ description: "Get the bid-ask spread for a prediction market: askPx - bidPx. Use when: assessing liquidity or trading cost. See also: `outcomes_get_market_spreads` (batch), `outcomes_get_market_midpoint` (midpoint price).",
8500
+ isWrite: false,
8501
+ inputSchema: {
8502
+ type: "object",
8503
+ properties: {
8504
+ mktId: { type: "string", description: "Market instrument ID." }
8505
+ },
8506
+ required: ["mktId"]
8507
+ },
8508
+ handler: async (rawArgs, context) => {
8509
+ const args = asRecord(rawArgs);
8510
+ const mktId = readString(args, "mktId") ?? "";
8511
+ const response = await context.client.privateGet(
8512
+ PATH_TICKER,
8513
+ { instId: mktId },
8514
+ privateRateLimit("outcomes_get_market_spread", OUTCOMES_RPS)
8515
+ );
8516
+ const normalized = normalizeResponse(response);
8517
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8518
+ const view = deriveTickerView(ticker, void 0);
8519
+ return { ...normalized, data: { ...ticker, ...view } };
8520
+ }
8521
+ },
8522
+ /* C6. Market spreads (batch) */
8523
+ {
8524
+ name: "outcomes_get_market_spreads",
8525
+ title: "Get Prediction Market Spreads (Batch)",
8526
+ module: "outcomes",
8527
+ description: "Get bid-ask spreads for multiple prediction markets in parallel. Per-row error isolation. See also: `outcomes_get_market_spread` (single), `outcomes_get_market_midpoints` (batch midpoints).",
8528
+ isWrite: false,
8529
+ inputSchema: {
8530
+ type: "object",
8531
+ properties: {
8532
+ ids: { type: "array", items: { type: "string" }, description: "List of market instrument IDs." }
8533
+ },
8534
+ required: ["ids"]
8535
+ },
8536
+ handler: async (rawArgs, context) => {
8537
+ const args = asRecord(rawArgs);
8538
+ const ids = Array.isArray(args["ids"]) ? args["ids"] : [];
8539
+ const rows = await runBatchQuery(ids, async (id) => {
8540
+ const response = await context.client.privateGet(
8541
+ PATH_TICKER,
8542
+ { instId: id },
8543
+ privateRateLimit("outcomes_get_market_spreads", OUTCOMES_RPS)
8544
+ );
8545
+ const normalized = normalizeResponse(response);
8546
+ const ticker = Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : {};
8547
+ return { ...ticker, ...deriveTickerView(ticker, void 0) };
8548
+ });
8549
+ return {
8550
+ endpoint: PATH_TICKER,
8551
+ requestTime: (/* @__PURE__ */ new Date()).toISOString(),
8552
+ data: rows
8553
+ };
8554
+ }
8555
+ },
8556
+ /* C7. Order book (single) */
8557
+ {
8558
+ name: "outcomes_get_order_book",
8559
+ title: "Get Prediction Market Order Book",
8560
+ module: "outcomes",
8561
+ description: "Get the CLOB order book (bids and asks) for a prediction market. Use when: examining market depth before a limit order. See also: `outcomes_get_order_books` (batch).",
8562
+ isWrite: false,
8563
+ inputSchema: {
8564
+ type: "object",
8565
+ properties: {
8566
+ mktId: { type: "string", description: "Market instrument ID." },
8567
+ sz: { type: "integer", minimum: 1, maximum: 400, description: "Order book depth (levels to return)." }
8568
+ },
8569
+ required: ["mktId"]
8570
+ },
8571
+ handler: async (rawArgs, context) => {
8572
+ const args = asRecord(rawArgs);
8573
+ const mktId = readString(args, "mktId") ?? "";
8574
+ const sz = readNumber(args, "sz");
8575
+ const response = await context.client.privateGet(
8576
+ PATH_PM_BOOKS,
8577
+ compactObject({ instId: mktId, sz }),
8578
+ privateRateLimit("outcomes_get_order_book", OUTCOMES_RPS)
8579
+ );
8580
+ return normalizeResponse(response);
8581
+ }
8582
+ },
8583
+ /* C8. Order books (batch) */
8584
+ {
8585
+ name: "outcomes_get_order_books",
8586
+ title: "Get Prediction Market Order Books (Batch)",
8587
+ module: "outcomes",
8588
+ description: "Get CLOB order books for multiple prediction markets in parallel. Per-row error isolation. See also: `outcomes_get_order_book` (single).",
8589
+ isWrite: false,
8590
+ inputSchema: {
8591
+ type: "object",
8592
+ properties: {
8593
+ ids: { type: "array", items: { type: "string" }, description: "List of market instrument IDs." },
8594
+ sz: { type: "integer", minimum: 1, maximum: 400, description: "Order book depth per market." }
8595
+ },
8596
+ required: ["ids"]
8597
+ },
8598
+ handler: async (rawArgs, context) => {
8599
+ const args = asRecord(rawArgs);
8600
+ const ids = Array.isArray(args["ids"]) ? args["ids"] : [];
8601
+ const sz = readNumber(args, "sz");
8602
+ const rows = await runBatchQuery(ids, async (id) => {
8603
+ const response = await context.client.privateGet(
8604
+ PATH_PM_BOOKS,
8605
+ compactObject({ instId: id, sz }),
8606
+ privateRateLimit("outcomes_get_order_books", OUTCOMES_RPS)
8607
+ );
8608
+ const normalized = normalizeResponse(response);
8609
+ return Array.isArray(normalized.data) && normalized.data.length > 0 ? normalized.data[0] : normalized.data;
8610
+ });
8611
+ return {
8612
+ endpoint: PATH_PM_BOOKS,
8613
+ requestTime: (/* @__PURE__ */ new Date()).toISOString(),
8614
+ data: rows
8615
+ };
8616
+ }
8617
+ },
8618
+ /* ================================================================ */
8619
+ /* ACCOUNT MODULE (5) */
8620
+ /* ================================================================ */
8621
+ /* A1. Account balance */
8622
+ {
8623
+ name: "outcomes_get_account_balance",
8624
+ title: "Get Predictions Account Balance",
8625
+ module: "outcomes",
8626
+ description: "Get the balance of the user's predictions account (collateral available for trading binary outcomes). Use when: checking how much is available to bet on prediction markets.",
8627
+ isWrite: false,
8628
+ inputSchema: { type: "object", properties: {} },
8629
+ handler: async (_rawArgs, context) => {
8630
+ const response = await context.client.privateGet(
8631
+ PATH_BALANCE,
8632
+ {},
8633
+ privateRateLimit("outcomes_get_account_balance", OUTCOMES_RPS)
8634
+ );
8635
+ return normalizeResponse(response);
8636
+ }
8637
+ },
8638
+ /* A2. Order detail */
8639
+ {
8640
+ name: "outcomes_get_order_detail",
8641
+ title: "Get Prediction Order Detail",
8642
+ module: "outcomes",
8643
+ description: "Get full detail of a single prediction order by order ID (status, fill info, timestamps). Use when: checking whether a specific order was filled. Also reachable via `okx outcomes clob order {ID}` (CLI alias).",
8644
+ isWrite: false,
8645
+ inputSchema: {
8646
+ type: "object",
8647
+ properties: {
8648
+ id: { type: "string", description: "Order ID." }
8649
+ },
8650
+ required: ["id"]
8651
+ },
8652
+ handler: async (rawArgs, context) => {
8653
+ const args = asRecord(rawArgs);
8654
+ const id = requireString2(args, "id", "Order ID");
8655
+ const response = await context.client.privateGet(
8656
+ `${PATH_ORDERS}/${pathSegment(id)}`,
8657
+ {},
8658
+ privateRateLimit("outcomes_get_order_detail", OUTCOMES_RPS)
8659
+ );
8660
+ return normalizeResponse(response);
8661
+ }
8662
+ },
8663
+ /* A3. Account orders */
8664
+ {
8665
+ name: "outcomes_get_account_orders",
8666
+ title: "List Prediction Account Orders",
8667
+ module: "outcomes",
8668
+ description: "List the user's prediction orders, optionally filtered by status (`open` | `closed`, default `open`) and paginated. Use when: reviewing pending or historical orders. Also reachable via `okx outcomes clob orders` (CLI alias).",
8669
+ isWrite: false,
8670
+ inputSchema: {
8671
+ type: "object",
8672
+ properties: {
8673
+ status: { type: "string", enum: ["open", "closed"], default: "open", description: "Order status filter (default `open`)." },
8674
+ cursor: { type: "string", description: "Pagination cursor." },
8675
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8676
+ }
8677
+ },
8678
+ handler: async (rawArgs, context) => {
8679
+ const args = asRecord(rawArgs);
8680
+ const response = await context.client.privateGet(
8681
+ PATH_ORDERS,
8682
+ compactObject({
8683
+ status: readString(args, "status") ?? "open",
8684
+ cursor: readString(args, "cursor"),
8685
+ limit: readNumber(args, "limit") ?? 20
8686
+ }),
8687
+ privateRateLimit("outcomes_get_account_orders", OUTCOMES_RPS)
8688
+ );
8689
+ return normalizeResponse(response);
8690
+ }
8691
+ },
8692
+ /* A4. Account positions */
8693
+ {
8694
+ name: "outcomes_get_account_positions",
8695
+ title: "List Prediction Account Positions",
8696
+ module: "outcomes",
8697
+ description: "List the user's prediction positions, optionally filtered by status (`open` | `closed`, default `open`), market ID, or limit. The MCP schema enum enforces valid status values at the boundary; server-side mapping of unknown values to `open` is a defensive fallback only. Use when: checking which prediction markets the user currently holds positions in.",
8698
+ isWrite: false,
8699
+ inputSchema: {
8700
+ type: "object",
8701
+ properties: {
8702
+ status: { type: "string", enum: ["open", "closed"], default: "open", description: "Position status filter (default `open`)." },
8703
+ market: { type: "string", description: "Optional market instrument ID to filter by." },
8704
+ cursor: { type: "string", description: "Pagination cursor." },
8705
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8706
+ }
8707
+ },
8708
+ handler: async (rawArgs, context) => {
8709
+ const args = asRecord(rawArgs);
8710
+ const response = await context.client.privateGet(
8711
+ PATH_POSITIONS,
8712
+ compactObject({
8713
+ status: readString(args, "status") ?? "open",
8714
+ market: readString(args, "market"),
8715
+ cursor: readString(args, "cursor"),
8716
+ limit: readNumber(args, "limit") ?? 20
8717
+ }),
8718
+ privateRateLimit("outcomes_get_account_positions", OUTCOMES_RPS)
8719
+ );
8720
+ return normalizeResponse(response);
8721
+ }
8722
+ },
8723
+ /* A5. Account trades */
8724
+ {
8725
+ name: "outcomes_get_account_trades",
8726
+ title: "List Prediction Account Trades",
8727
+ module: "outcomes",
8728
+ description: "List the user's prediction trade fills (executed trades), paginated. Use when: reviewing trading history on prediction markets. Also reachable via `okx outcomes clob trades` (CLI alias).",
8729
+ isWrite: false,
8730
+ inputSchema: {
8731
+ type: "object",
8732
+ properties: {
8733
+ cursor: { type: "string", description: "Pagination cursor." },
8734
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 20, description: "Max results (default 20)." }
8735
+ }
8736
+ },
8737
+ handler: async (rawArgs, context) => {
8738
+ const args = asRecord(rawArgs);
8739
+ const response = await context.client.privateGet(
8740
+ PATH_TRADES,
8741
+ compactObject({
8742
+ cursor: readString(args, "cursor"),
8743
+ limit: readNumber(args, "limit") ?? 20
8744
+ }),
8745
+ privateRateLimit("outcomes_get_account_trades", OUTCOMES_RPS)
8746
+ );
8747
+ return normalizeResponse(response);
8748
+ }
8749
+ }
8750
+ ];
8751
+ return tools;
8752
+ }
8052
8753
  function buildContractTradeTools(cfg) {
8053
8754
  const { prefix, module, label, instTypes, instIdExample, titleLabel } = cfg;
8054
8755
  const [defaultType, otherType] = instTypes;
@@ -11839,6 +12540,7 @@ function allToolSpecs() {
11839
12540
  ...registerBotTools(),
11840
12541
  ...registerAllEarnTools(),
11841
12542
  ...registerSmartmoneyTools(),
12543
+ ...registerOutcomesTools(),
11842
12544
  ...registerAuditTools(),
11843
12545
  ...registerSkillsTools()
11844
12546
  ];
@@ -12397,7 +13099,7 @@ var _require = createRequire(import.meta.url);
12397
13099
  var pkg = _require("../package.json");
12398
13100
  var SERVER_NAME = "okx-trade-mcp";
12399
13101
  var SERVER_VERSION = pkg.version;
12400
- var GIT_HASH = true ? "92abfa77" : "dev";
13102
+ var GIT_HASH = true ? "740ac6f1" : "dev";
12401
13103
 
12402
13104
  // src/server.ts
12403
13105
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";