@coinrithm/mcp-trading 0.7.14 → 0.7.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +811 -613
  2. package/README.md +415 -366
  3. package/dist/agent/bench/bench.d.ts +59 -0
  4. package/dist/agent/bench/bench.js +319 -0
  5. package/dist/agent/bench/cassette.d.ts +97 -0
  6. package/dist/agent/bench/cassette.js +181 -0
  7. package/dist/agent/bench/costs.d.ts +28 -0
  8. package/dist/agent/bench/costs.js +60 -0
  9. package/dist/agent/bench/labelBuilder.d.ts +123 -0
  10. package/dist/agent/bench/labelBuilder.js +282 -0
  11. package/dist/agent/bench/labels.d.ts +75 -0
  12. package/dist/agent/bench/labels.js +259 -0
  13. package/dist/agent/bench/recordingClient.d.ts +51 -0
  14. package/dist/agent/bench/recordingClient.js +193 -0
  15. package/dist/agent/bench/replayClient.d.ts +41 -0
  16. package/dist/agent/bench/replayClient.js +262 -0
  17. package/dist/agent/bench/report.d.ts +115 -0
  18. package/dist/agent/bench/report.js +435 -0
  19. package/dist/agent/cli.d.ts +25 -0
  20. package/dist/agent/cli.js +300 -2
  21. package/dist/agent/client.d.ts +14 -0
  22. package/dist/agent/client.js +113 -4
  23. package/dist/agent/configurationWarnings.js +11 -0
  24. package/dist/agent/decision.d.ts +3 -0
  25. package/dist/agent/decision.js +85 -3
  26. package/dist/agent/decisionReceipt.d.ts +5 -1
  27. package/dist/agent/decisionReceipt.js +52 -3
  28. package/dist/agent/decisionValidator.d.ts +30 -0
  29. package/dist/agent/decisionValidator.js +147 -2
  30. package/dist/agent/engine.d.ts +3 -3
  31. package/dist/agent/engine.js +3 -3
  32. package/dist/agent/futuresEligibility.d.ts +36 -0
  33. package/dist/agent/futuresEligibility.js +90 -0
  34. package/dist/agent/observe.d.ts +35 -2
  35. package/dist/agent/observe.js +969 -128
  36. package/dist/agent/pmContext.d.ts +10 -1
  37. package/dist/agent/pmContext.js +163 -1
  38. package/dist/agent/prompt.d.ts +18 -1
  39. package/dist/agent/prompt.js +229 -33
  40. package/dist/agent/promptTables.d.ts +1 -0
  41. package/dist/agent/promptTables.js +33 -0
  42. package/dist/agent/providerCapabilities.d.ts +9 -0
  43. package/dist/agent/providerCapabilities.js +57 -4
  44. package/dist/agent/providers.d.ts +28 -7
  45. package/dist/agent/providers.js +200 -50
  46. package/dist/agent/resolve.js +3 -0
  47. package/dist/agent/runner.d.ts +8 -0
  48. package/dist/agent/runner.js +159 -26
  49. package/dist/agent/setups.d.ts +2 -1
  50. package/dist/agent/setups.js +17 -16
  51. package/dist/agent/signals.d.ts +33 -0
  52. package/dist/agent/signals.js +89 -0
  53. package/dist/agent/skill.js +37 -0
  54. package/dist/agent/skillValidator.js +33 -0
  55. package/dist/agent/strictLint.js +29 -0
  56. package/dist/agent/templates.js +30 -30
  57. package/dist/agent/types.d.ts +266 -13
  58. package/dist/agent/types.js +4 -0
  59. package/dist/agent/universe.d.ts +78 -0
  60. package/dist/agent/universe.js +215 -0
  61. package/dist/client.d.ts +9 -2
  62. package/dist/client.js +22 -10
  63. package/dist/http.d.ts +1 -0
  64. package/dist/http.js +17 -2
  65. package/dist/httpCompletion.d.ts +1 -1
  66. package/dist/httpCompletion.js +1 -0
  67. package/dist/index.js +2 -1
  68. package/dist/instructions.d.ts +1 -0
  69. package/dist/instructions.js +16 -0
  70. package/dist/tools.d.ts +1 -0
  71. package/dist/tools.js +150 -66
  72. package/package.json +98 -97
package/dist/tools.js CHANGED
@@ -7,8 +7,9 @@
7
7
  import { z } from "zod";
8
8
  import { bearerFromHeader } from "./client.js";
9
9
  import { PAPER_EXECUTION_VERSION } from "./executionPolicy.js";
10
- // Served on tool descriptions. Names the versioned execution policy and never
11
- // claims costless execution (drift-tested in executionPolicy.test.ts).
10
+ // Served once in the MCP server instructions (instructions.ts). Names the
11
+ // versioned execution policy and never claims costless execution
12
+ // (drift-tested in executionPolicy.test.ts).
12
13
  export const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). Not financial advice. " +
13
14
  `Paper fills run under the versioned ${PAPER_EXECUTION_VERSION} policy and ` +
14
15
  "apply a disclosed execution cost folded into realized PnL: " +
@@ -17,27 +18,22 @@ export const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). N
17
18
  "shaped taker fee, with entryProbability kept at the mid for calibration. " +
18
19
  "See the executionModel in quote/trade results — a rehearsal cost, not an " +
19
20
  "exchange fill guarantee.";
21
+ // Per-tool form of PAPER_NOTE. The full note is sent once in the server
22
+ // instructions (instructions.ts); every tool still names the versioned
23
+ // execution policy and its disclosed cost (drift-tested with PAPER_NOTE).
24
+ export const PAPER_NOTE_SHORT = `Paper trading only (virtual mUSD). Fills follow ${PAPER_EXECUTION_VERSION} ` +
25
+ "with a disclosed execution cost; see executionModel in quote/trade results.";
26
+ // The same result envelope on every tool: { httpStatus, ok, ledgerEventId,
27
+ // ledgerStatus, body }. It is sent 40 times in tools/list, so its field
28
+ // descriptions live in the server instructions' wording, not here: httpStatus
29
+ // is CoinRithm's HTTP status (0 = network error), ok is a 2xx, the ledger
30
+ // fields come from /api/agent/* writes, and body is the parsed response.
20
31
  const API_RESULT_OUTPUT_SCHEMA = {
21
- httpStatus: z
22
- .number()
23
- .int()
24
- .describe("HTTP status returned by CoinRithm, or 0 for network errors."),
25
- ok: z
26
- .boolean()
27
- .describe("True when CoinRithm returned a successful 2xx response."),
28
- ledgerEventId: z
29
- .string()
30
- .nullable()
31
- .optional()
32
- .describe("Private AgentActionEvent id returned by /api/agent/*, when present."),
33
- ledgerStatus: z
34
- .string()
35
- .nullable()
36
- .optional()
37
- .describe("Ledger write status header returned by CoinRithm, when present."),
38
- body: z
39
- .unknown()
40
- .describe("Parsed CoinRithm response body, or raw text when the response is not JSON."),
32
+ httpStatus: z.number().int(),
33
+ ok: z.boolean(),
34
+ ledgerEventId: z.string().nullable().optional(),
35
+ ledgerStatus: z.string().nullable().optional(),
36
+ body: z.unknown(),
41
37
  };
42
38
  const AGENT_TRACE_SCHEMA = z
43
39
  .object({
@@ -165,9 +161,7 @@ function present(result) {
165
161
  body: result.data,
166
162
  };
167
163
  return {
168
- content: [
169
- { type: "text", text: JSON.stringify(payload, null, 2) },
170
- ],
164
+ content: [{ type: "text", text: JSON.stringify(payload) }],
171
165
  structuredContent: payload,
172
166
  isError: !result.ok,
173
167
  };
@@ -226,6 +220,9 @@ const OUTCOME_SUMMARY_FIELDS = [
226
220
  "lifecycle",
227
221
  "priorProbability",
228
222
  "venueTerms",
223
+ "hasObservedPrice",
224
+ "priceBasis",
225
+ "sourceObservation",
229
226
  ];
230
227
  const WHALE_TRADE_FIELDS = [
231
228
  "source",
@@ -233,6 +230,7 @@ const WHALE_TRADE_FIELDS = [
233
230
  "eventSlug",
234
231
  "eventTitle",
235
232
  "wallet",
233
+ "walletAddress",
236
234
  "traderName",
237
235
  "side",
238
236
  "outcome",
@@ -256,6 +254,32 @@ function boundedText(value, maxLength) {
256
254
  return value;
257
255
  return `${value.slice(0, maxLength - 1)}…`;
258
256
  }
257
+ function compactSourceObservation(value) {
258
+ if (value === null)
259
+ return null;
260
+ if (!isJsonRecord(value))
261
+ return undefined;
262
+ return pick(value, [
263
+ "version",
264
+ "basis",
265
+ "provider",
266
+ "observedAt",
267
+ "tradeDate",
268
+ ]);
269
+ }
270
+ function compactOutcomeSummary(value) {
271
+ if (!isJsonRecord(value))
272
+ return value;
273
+ const outcome = pick(value, OUTCOME_SUMMARY_FIELDS);
274
+ if ("sourceObservation" in outcome) {
275
+ const sourceObservation = compactSourceObservation(value.sourceObservation);
276
+ if (sourceObservation === undefined)
277
+ delete outcome.sourceObservation;
278
+ else
279
+ outcome.sourceObservation = sourceObservation;
280
+ }
281
+ return outcome;
282
+ }
259
283
  function compactWhaleTrade(value) {
260
284
  return isJsonRecord(value) ? pick(value, WHALE_TRADE_FIELDS) : value;
261
285
  }
@@ -308,9 +332,7 @@ function eventSummary(value) {
308
332
  : left.index - right.index;
309
333
  })
310
334
  .slice(0, 5)
311
- .map(({ outcome }) => isJsonRecord(outcome)
312
- ? pick(outcome, OUTCOME_SUMMARY_FIELDS)
313
- : outcome)
335
+ .map(({ outcome }) => isJsonRecord(outcome) ? compactOutcomeSummary(outcome) : outcome)
314
336
  : [];
315
337
  return {
316
338
  ...pick(value, EVENT_SUMMARY_FIELDS),
@@ -392,9 +414,7 @@ function compactSnapshot(value) {
392
414
  outcomeCount: value.outcomes.length,
393
415
  outcomes: value.outcomes
394
416
  .slice(0, 10)
395
- .map((outcome) => isJsonRecord(outcome)
396
- ? pick(outcome, OUTCOME_SUMMARY_FIELDS)
397
- : outcome),
417
+ .map((outcome) => isJsonRecord(outcome) ? compactOutcomeSummary(outcome) : outcome),
398
418
  }
399
419
  : {}),
400
420
  };
@@ -425,6 +445,8 @@ function compactComparison(value) {
425
445
  "label",
426
446
  "eventAProbability",
427
447
  "eventBProbability",
448
+ "eventAHasObservedPrice",
449
+ "eventBHasObservedPrice",
428
450
  "deltaPoints",
429
451
  "presentInA",
430
452
  "presentInB",
@@ -727,7 +749,7 @@ export function registerTools(server, client) {
727
749
  "(equity.totalUsd plus available/frozen/frozenPm/frozenFutures/" +
728
750
  "cashTotal cash partitions), period PnL (pnl.24hUsd … allTimePct), " +
729
751
  "open spot orders, and a progression block (league/XP). " +
730
- PAPER_NOTE,
752
+ PAPER_NOTE_SHORT,
731
753
  inputSchema: {
732
754
  fiat: z
733
755
  .string()
@@ -744,7 +766,7 @@ export function registerTools(server, client) {
744
766
  description: "Get raw cash balances: USDT available plus the three frozen partitions " +
745
767
  "(frozen = spot orders, frozenPm = PM, frozenFutures = futures margin). " +
746
768
  "Optionally include one coin asset. " +
747
- PAPER_NOTE,
769
+ PAPER_NOTE_SHORT,
748
770
  inputSchema: {
749
771
  coinId: z
750
772
  .string()
@@ -761,7 +783,7 @@ export function registerTools(server, client) {
761
783
  "across coins, or pass one to filter. Response includes asOf — pass it " +
762
784
  "back as updatedSince on the next call to poll only rows that changed " +
763
785
  "(delta polling). " +
764
- PAPER_NOTE,
786
+ PAPER_NOTE_SHORT,
765
787
  inputSchema: {
766
788
  coinId: z
767
789
  .string()
@@ -793,7 +815,7 @@ export function registerTools(server, client) {
793
815
  "unrealized mark on open ones). Response includes asOf — pass it back " +
794
816
  "as updatedSince on the next call to poll only positions that changed " +
795
817
  "(catches worker-fired SL/TP, liquidations, and settlements). " +
796
- PAPER_NOTE,
818
+ PAPER_NOTE_SHORT,
797
819
  inputSchema: {
798
820
  venue: z
799
821
  .enum(["futures", "pm"])
@@ -817,7 +839,7 @@ export function registerTools(server, client) {
817
839
  "CoinGecko category tags. Use this FIRST to get the coinId that the " +
818
840
  "wallet / quote / order tools need — don't guess UCIDs (symbols are not " +
819
841
  "unique). " +
820
- PAPER_NOTE,
842
+ PAPER_NOTE_SHORT,
821
843
  inputSchema: {
822
844
  q: z
823
845
  .string()
@@ -837,7 +859,7 @@ export function registerTools(server, client) {
837
859
  "PnL event (spot sells, futures closes/liquidations, PM settlements) " +
838
860
  "with a cumulative running total — use it for active intraday agents. " +
839
861
  "days = look-back window (1-365, default 30). " +
840
- PAPER_NOTE,
862
+ PAPER_NOTE_SHORT,
841
863
  inputSchema: {
842
864
  days: z
843
865
  .number()
@@ -865,7 +887,7 @@ export function registerTools(server, client) {
865
887
  "asOf — pass it back as updatedSince on the next call to fetch only " +
866
888
  "NEW closes since your last poll (how you discover worker-fired " +
867
889
  "stop-loss/take-profit, liquidations, and PM settlements). " +
868
- PAPER_NOTE,
890
+ PAPER_NOTE_SHORT,
869
891
  inputSchema: {
870
892
  venue: z
871
893
  .enum(["all", "spot", "futures", "pm"])
@@ -899,7 +921,7 @@ export function registerTools(server, client) {
899
921
  "market's depth/tradability — and up to 6 similar coins (shared category " +
900
922
  "/ market-cap peers). Facts only — no generated thesis. Call " +
901
923
  "resolve_symbol first to get the coinId. " +
902
- PAPER_NOTE,
924
+ PAPER_NOTE_SHORT,
903
925
  inputSchema: {
904
926
  coinId: z
905
927
  .string()
@@ -921,7 +943,7 @@ export function registerTools(server, client) {
921
943
  "These are sampled composite-price bars, not venue trade candles. " +
922
944
  "v is the mean rolling 24-hour quote-volume observation in the bar, " +
923
945
  "NOT volume traded during that candle; do not sum v across bars. " +
924
- PAPER_NOTE,
946
+ PAPER_NOTE_SHORT,
925
947
  inputSchema: {
926
948
  coinId: z
927
949
  .string()
@@ -940,6 +962,47 @@ export function registerTools(server, client) {
940
962
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
941
963
  annotations: readOnlyAnnotations("Get OHLCV candles"),
942
964
  }, async ({ coinId, range, fiat, agentTrace }, extra) => present(await client.getCandles(coinId, { range, fiat }, requestKey(extra), agentTrace)));
965
+ server.registerTool("get_news", {
966
+ title: "Get scored crypto news",
967
+ description: "Recent crypto news for up to 25 coins, linked to each coin by " +
968
+ "CoinRithm's curated coin-news graph. Only AI-scored stories are " +
969
+ "returned: sentiment (bullish, bearish or neutral) with its " +
970
+ "confidence and importance 0-10 (8+ = market-moving), ranked by " +
971
+ "importance then recency. ageMinutes is each story's age. Scoring can " +
972
+ "lag publication by hours, so an empty or older list does not prove " +
973
+ "there is no news. Needs an API key with read scope.",
974
+ inputSchema: {
975
+ coins: z
976
+ .string()
977
+ .min(1)
978
+ .max(400)
979
+ .describe('Comma-separated symbols or slugs, e.g. "BTC,ETH" (max 25).'),
980
+ limit: z
981
+ .number()
982
+ .int()
983
+ .min(1)
984
+ .max(25)
985
+ .optional()
986
+ .describe("Stories to return, 1-25 (default 8)."),
987
+ hours: z
988
+ .number()
989
+ .int()
990
+ .min(1)
991
+ .max(168)
992
+ .optional()
993
+ .describe("Look-back window in hours, 1-168 (default 48)."),
994
+ minImportance: z
995
+ .number()
996
+ .int()
997
+ .min(0)
998
+ .max(10)
999
+ .optional()
1000
+ .describe("Only stories at or above this importance (default 0)."),
1001
+ agentTrace: AGENT_TRACE_SCHEMA,
1002
+ },
1003
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
1004
+ annotations: readOnlyAnnotations("Get scored crypto news"),
1005
+ }, async ({ coins, limit, hours, minImportance, agentTrace }, extra) => present(await client.getNews({ coins, limit, hours, minImportance }, requestKey(extra), agentTrace)));
943
1006
  server.registerTool("discover_pm_markets", {
944
1007
  title: "Discover prediction markets",
945
1008
  description: "Find active-open, quote-ready-first prediction markets on the mock-PM " +
@@ -951,7 +1014,7 @@ export function registerTools(server, client) {
951
1014
  "while the market stays visible). This is discovery only — call pm_quote " +
952
1015
  "with one returned outcomeExternalMarketId before open_pm_position " +
953
1016
  "because pm_quote is the final eligibility source. " +
954
- PAPER_NOTE,
1017
+ PAPER_NOTE_SHORT,
955
1018
  inputSchema: {
956
1019
  q: z
957
1020
  .string()
@@ -996,7 +1059,7 @@ export function registerTools(server, client) {
996
1059
  "PnL (mUSD), trade count, win/loss/neutral counts, and win rate (null " +
997
1060
  "until there are decided trades). Closed trades only — the scorecard for " +
998
1061
  "this agent. " +
999
- PAPER_NOTE,
1062
+ PAPER_NOTE_SHORT,
1000
1063
  inputSchema: {
1001
1064
  agentTrace: AGENT_TRACE_SCHEMA,
1002
1065
  },
@@ -1009,7 +1072,7 @@ export function registerTools(server, client) {
1009
1072
  "rejects, idempotent replays, latency, sanitized summaries, and optional " +
1010
1073
  "run/decision trace metadata. Only rows for the calling key are returned. " +
1011
1074
  "Use this to audit a reproducible paper-trading run. " +
1012
- PAPER_NOTE,
1075
+ PAPER_NOTE_SHORT,
1013
1076
  inputSchema: {
1014
1077
  venue: z.string().optional().describe("Optional venue filter."),
1015
1078
  eventType: z
@@ -1057,7 +1120,7 @@ export function registerTools(server, client) {
1057
1120
  description: "Export up to 1,000 private ledger rows for the calling API key as JSON. " +
1058
1121
  "Use filters to export a specific runId or decisionId for reproducible " +
1059
1122
  "evaluation. No public Arena user can see this data. " +
1060
- PAPER_NOTE,
1123
+ PAPER_NOTE_SHORT,
1061
1124
  inputSchema: {
1062
1125
  venue: z.string().optional().describe("Optional venue filter."),
1063
1126
  eventType: z
@@ -1083,7 +1146,7 @@ export function registerTools(server, client) {
1083
1146
  "The bundle includes sanitized ledger rows, execution assumptions, " +
1084
1147
  "retention policy, outcome attribution, and the evidence checklist. " +
1085
1148
  "No public Arena user can see this data. " +
1086
- PAPER_NOTE,
1149
+ PAPER_NOTE_SHORT,
1087
1150
  inputSchema: {
1088
1151
  runId: z.string().min(1).describe("Required run id to export."),
1089
1152
  agentTrace: AGENT_TRACE_SCHEMA,
@@ -1105,7 +1168,7 @@ export function registerTools(server, client) {
1105
1168
  "and where you stand — pair " +
1106
1169
  "with get_performance (your own scorecard) and get_arena_agent (drill " +
1107
1170
  "into one handle). Public data: agent names + performance only. " +
1108
- PAPER_NOTE,
1171
+ PAPER_NOTE_SHORT,
1109
1172
  inputSchema: {
1110
1173
  page: z
1111
1174
  .number()
@@ -1130,14 +1193,14 @@ export function registerTools(server, client) {
1130
1193
  },
1131
1194
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
1132
1195
  annotations: readOnlyAnnotations("Get Agent Arena leaderboard"),
1133
- }, async ({ page, pageSize, window }, extra) => present(await client.getArenaLeaderboard({ page, pageSize, window }, requestKey(extra))));
1196
+ }, async ({ page, pageSize, window }) => present(await client.getArenaLeaderboard({ page, pageSize, window })));
1134
1197
  server.registerTool("get_arena_agent", {
1135
1198
  title: "Get Agent Arena profile",
1136
1199
  description: "One agent's public Arena profile by handle (the `handle` field from " +
1137
1200
  "get_arena_leaderboard, e.g. 'a42-momentum-scout'): rank, total + " +
1138
1201
  "per-venue realized PnL, decided/total trade counts, and win rate. " +
1139
1202
  "Public data only — no account or key identity. " +
1140
- PAPER_NOTE,
1203
+ PAPER_NOTE_SHORT,
1141
1204
  inputSchema: {
1142
1205
  handle: z
1143
1206
  .string()
@@ -1146,14 +1209,14 @@ export function registerTools(server, client) {
1146
1209
  },
1147
1210
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
1148
1211
  annotations: readOnlyAnnotations("Get Agent Arena profile"),
1149
- }, async ({ handle }, extra) => present(await client.getArenaAgent(handle, requestKey(extra))));
1212
+ }, async ({ handle }) => present(await client.getArenaAgent(handle)));
1150
1213
  // ---------------- quotes (read scope, read-only) ----------------
1151
1214
  server.registerTool("futures_quote", {
1152
1215
  title: "Futures quote",
1153
1216
  description: "Read-only futures quote: entry price, notional, size, liquidation price, " +
1154
1217
  "and eligibility. Never mutates state — always quote before opening. " +
1155
1218
  "leverage 1-20, marginMusd >= 10. " +
1156
- PAPER_NOTE,
1219
+ PAPER_NOTE_SHORT,
1157
1220
  inputSchema: {
1158
1221
  coinId: z.string().describe("Coin UCID."),
1159
1222
  side: z
@@ -1182,7 +1245,7 @@ export function registerTools(server, client) {
1182
1245
  "stakeMusd must be > 0 (min to open is 10). Pass side: 'no' to quote " +
1183
1246
  "backing the NO side (omitted = yes); a NO entry fills at 100 minus the " +
1184
1247
  "outcome probability and pays out if the outcome resolves false. " +
1185
- PAPER_NOTE,
1248
+ PAPER_NOTE_SHORT,
1186
1249
  inputSchema: {
1187
1250
  source: z.string().describe("Source slug (e.g. kalshi, polymarket)."),
1188
1251
  slug: z.string().describe("Event slug."),
@@ -1231,7 +1294,7 @@ export function registerTools(server, client) {
1231
1294
  "before place_spot_order instead of buying/selling blind. Price age is " +
1232
1295
  "informational only (a market order fills regardless). coinId is a UCID, " +
1233
1296
  "NOT a ticker — use resolve_symbol first. " +
1234
- PAPER_NOTE,
1297
+ PAPER_NOTE_SHORT,
1235
1298
  inputSchema: {
1236
1299
  coinId: z.string().describe("Coin UCID (e.g. '1' = BTC)."),
1237
1300
  side: z
@@ -1255,7 +1318,7 @@ export function registerTools(server, client) {
1255
1318
  "per intent (reuse replays the original result — retry a timed-out " +
1256
1319
  "call with the SAME key; it will never double-execute). Requires the " +
1257
1320
  "trade:spot scope. CONFIRM with the user before calling. " +
1258
- PAPER_NOTE,
1321
+ PAPER_NOTE_SHORT,
1259
1322
  inputSchema: {
1260
1323
  coinId: z.string().describe('Coin UCID (e.g. "1" = BTC).'),
1261
1324
  side: z
@@ -1326,7 +1389,7 @@ export function registerTools(server, client) {
1326
1389
  "stopLossPrice/takeProfitPrice atomically at open (side-aware corridor: " +
1327
1390
  "long needs liq < SL < mark < TP; short inverted) — protecting every " +
1328
1391
  "position is good practice. Quote first and CONFIRM with the user. " +
1329
- PAPER_NOTE,
1392
+ PAPER_NOTE_SHORT,
1330
1393
  inputSchema: {
1331
1394
  coinId: z
1332
1395
  .string()
@@ -1384,7 +1447,7 @@ export function registerTools(server, client) {
1384
1447
  "live mark (liquidation always takes precedence); a fire closes the " +
1385
1448
  "FULL position at mark with realized PnL. Discover fills between polls " +
1386
1449
  "via my_trades with updatedSince. Requires the trade:futures scope. " +
1387
- PAPER_NOTE,
1450
+ PAPER_NOTE_SHORT,
1388
1451
  inputSchema: {
1389
1452
  positionId: z
1390
1453
  .number()
@@ -1420,7 +1483,7 @@ export function registerTools(server, client) {
1420
1483
  description: "Close or partially reduce a mock futures position. fraction in (0,1] " +
1421
1484
  "reduces partially; omit (or 1) for a full close. idempotencyKey is " +
1422
1485
  "REQUIRED. Requires the trade:futures scope. " +
1423
- PAPER_NOTE,
1486
+ PAPER_NOTE_SHORT,
1424
1487
  inputSchema: {
1425
1488
  positionId: z
1426
1489
  .number()
@@ -1453,7 +1516,7 @@ export function registerTools(server, client) {
1453
1516
  "REQUIRED. stakeMusd >= 10. Pass side: 'no' to back the NO side (omitted " +
1454
1517
  "= yes); a NO entry fills at 100 minus the outcome probability and pays " +
1455
1518
  "out if the outcome resolves false. Quote first and CONFIRM with the user. " +
1456
- PAPER_NOTE,
1519
+ PAPER_NOTE_SHORT,
1457
1520
  inputSchema: {
1458
1521
  source: z
1459
1522
  .string()
@@ -1683,7 +1746,8 @@ export function registerTools(server, client) {
1683
1746
  status: z
1684
1747
  .string()
1685
1748
  .optional()
1686
- .describe("Optional status filter (e.g. open or closed)."),
1749
+ .describe("Status filter: open (default), closed, or all. Without it " +
1750
+ "the API's newest-first list starts with long-closed markets."),
1687
1751
  sort: z.string().optional().describe("Optional sort key."),
1688
1752
  limit: z
1689
1753
  .number()
@@ -1708,7 +1772,9 @@ export function registerTools(server, client) {
1708
1772
  }, async ({ q, source, status, sort, limit, offset, fiat }) => present(mapSuccessfulBody(await client.listPublicPmEvents({
1709
1773
  q,
1710
1774
  source,
1711
- status,
1775
+ // Open by default: with no status the API's first page was 20
1776
+ // closed markets, 70+ days stale (audit 2026-09-29).
1777
+ status: status ?? "open",
1712
1778
  sort,
1713
1779
  limit,
1714
1780
  offset,
@@ -1729,6 +1795,14 @@ export function registerTools(server, client) {
1729
1795
  "2026-07-02 — read the event's volume trend directly from it). " +
1730
1796
  "The default summary bounds outcomes, related events, matches and tape " +
1731
1797
  "for agent context windows while preserving counts and core evidence. " +
1798
+ "Outcome summaries may include hasObservedPrice and bounded " +
1799
+ "sourceObservation: hasObservedPrice:false means the provider supplied " +
1800
+ "no usable observed price input, while an omitted hasObservedPrice field " +
1801
+ "is unknown; sourceObservation:null means unavailable provenance, not " +
1802
+ "proof that the outcome has no price. Neither field is a live, " +
1803
+ "liquidity, or trading guarantee. " +
1804
+ "priceBasis:'unquoted' (Polymarket/Kalshi) means never traded with no " +
1805
+ "usable book: that probability is not a price. " +
1732
1806
  "Set detail=full only when the untouched provider-rich record is needed. " +
1733
1807
  "This is the cross-venue research view; for tradability use pm_quote. " +
1734
1808
  "No API key required.",
@@ -1837,7 +1911,7 @@ export function registerTools(server, client) {
1837
1911
  .string()
1838
1912
  .trim()
1839
1913
  .regex(/^0x[0-9a-fA-F]{40}$/, "expected a 20-byte EVM wallet address")
1840
- .describe("Wallet address returned by pm_data_whale_wallets."),
1914
+ .describe("Full walletAddress from pm_data_whales or pm_data_event, or address from pm_data_whale_wallets. The shortened wallet display is not an address."),
1841
1915
  },
1842
1916
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
1843
1917
  annotations: readOnlyAnnotations("Inspect one prediction-market whale wallet"),
@@ -1870,7 +1944,7 @@ export function registerTools(server, client) {
1870
1944
  .min(1)
1871
1945
  .max(25)
1872
1946
  .optional()
1873
- .describe("Max clusters (1-25, default 10)."),
1947
+ .describe("Max clusters (1-25, default 3)."),
1874
1948
  offset: z
1875
1949
  .number()
1876
1950
  .int()
@@ -1923,7 +1997,9 @@ export function registerTools(server, client) {
1923
1997
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
1924
1998
  annotations: readOnlyAnnotations("Cross-venue disagreement clusters"),
1925
1999
  }, async ({ limit, offset, sort, minDivergence, sourceKind, status, maxSnapshotAgeMinutes, requirePriced, fiat, }) => present(mapSuccessfulBody(await client.getPublicPmMatches({
1926
- limit,
2000
+ // 3 by default: the API's default of 10 returned about 239k
2001
+ // characters, more than an assistant's context should take at once.
2002
+ limit: limit ?? 3,
1927
2003
  offset,
1928
2004
  sort,
1929
2005
  minDivergence,
@@ -1958,9 +2034,16 @@ export function registerTools(server, client) {
1958
2034
  "venue's slug. Omit `key` to page the directory of active canonicals " +
1959
2035
  "(uuid, slug, title, memberCount). Pass `key` (a canonical's uuid OR " +
1960
2036
  "slug) for one canonical's full record: its venue members (each with " +
1961
- "orientation — same/inverted/unknown, NEVER price-inferred — plus " +
2037
+ "orientation — same/flipped/unknown, NEVER price-inferred — plus " +
1962
2038
  "confidence and provenance basis) and an append-only judgment lineage " +
1963
- "(created/member_added/member_removed/merged, newest first). A MERGED " +
2039
+ "(created/member_added/member_removed/merged, newest first). It also " +
2040
+ "carries `consensus`: the current cross-venue reference probability " +
2041
+ "(kind, outcomeName, probability, venueCount, spreadPoints, " +
2042
+ "computedAt, methodologyVersion, listings), or null when the open " +
2043
+ "members do not agree on one current reference; and " +
2044
+ "`consensusHistory`, a daily tape whose points each carry their own " +
2045
+ "outcome label. `listings` may be a subset of the contributing venues. " +
2046
+ "A MERGED " +
1964
2047
  "canonical still resolves (status='merged' + a mergedInto pointer) so " +
1965
2048
  "a stable key never 404s. Use this to track one question across " +
1966
2049
  "venues by a durable identity instead of re-matching venue slugs " +
@@ -2012,8 +2095,9 @@ export function registerTools(server, client) {
2012
2095
  "biggest 24h price moves — top gainers or top losers, ordered by " +
2013
2096
  "24h change percent. Use this to DISCOVER candidates beyond your " +
2014
2097
  "watchlist (abnormal rapid moves), then deep-analyze each candidate " +
2015
- "with get_candles (OHLC + indicators) and get_market_context " +
2016
- "(sentiment, news) before any trade decision. Rows carry coinId, " +
2098
+ "with get_candles (OHLC + indicators), get_market_context " +
2099
+ "(sentiment, related markets) and get_news before any trade " +
2100
+ "decision. Rows carry coinId, " +
2017
2101
  "symbol, name, slug, change24hPct and priceUsd; data refreshes on the " +
2018
2102
  "~60s core price tick. Pass the row's coinId straight to get_candles " +
2019
2103
  "/ get_market_context — do NOT re-resolve it from the symbol, since " +