@coinrithm/mcp-trading 0.4.0 → 0.7.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.
@@ -62,8 +62,10 @@ money. Edit this prose freely (any language) — it is your agent's borders.
62
62
  balances or what is already open.
63
63
  2. Scan the watchlist. A candidate is a coin whose short and medium momentum
64
64
  agree (both up, or both down) and is not already an open position.
65
- 3. Pick at most one strongest candidate. If nothing is clean, skip — a skipped
66
- cycle is cheaper than a forced trade.
65
+ 3. Pick the strongest candidate and commit when the read is clear — even a
66
+ moderate-confidence one — sized small with a stop. Skip only when the signals
67
+ contradict or the data is stale; a quiet tape where your edge is still real
68
+ is an act, not a skip.
67
69
  4. Quote before you open. Read the liquidation price and confirm it is sane. If
68
70
  the quote is not eligible, relay the reason and stop.
69
71
  5. Open small and protected: enter in the trend direction and set a stop-loss at
@@ -75,8 +77,10 @@ The hard caps (leverage, margin, watchlist) live in the config blocks above and
75
77
  are enforced by the runner — change them there, not in this prose.`;
76
78
  const PERSONA_STUB = `# Persona
77
79
 
78
- Patient and selective. Prefers to skip rather than force a marginal trade.
79
- States its reasoning plainly and never frames paper results as real-money advice.
80
+ Decisive and in character. Acts on a clear read — even a moderate-confidence one
81
+ — sized small and protected with a stop, and skips only when the read is
82
+ contradictory or the data is stale. States its reasoning plainly in its own
83
+ voice, and never frames paper results as real-money advice.
80
84
  `;
81
85
  export function buildAgentObject(name, preset) {
82
86
  const p = PRESETS[preset];
@@ -4,7 +4,7 @@ export type Venue = "spot" | "futures" | "pm";
4
4
  export declare const VENUES: readonly Venue[];
5
5
  export declare const ACTION_TYPES: readonly ["futures_open", "futures_close", "futures_set_sltp", "spot_order", "spot_cancel", "pm_open"];
6
6
  export type ActionType = (typeof ACTION_TYPES)[number];
7
- export type ProviderName = "anthropic" | "openai" | "groq" | "nvidia" | "openai-compatible";
7
+ export type ProviderName = "anthropic" | "openai" | "groq" | "nvidia" | "gemini" | "openai-compatible";
8
8
  export declare const PROVIDERS: readonly ProviderName[];
9
9
  export interface ModelConfig {
10
10
  provider: ProviderName;
@@ -15,6 +15,15 @@ export interface TriggerConfig {
15
15
  cadence: string;
16
16
  timezone?: string;
17
17
  }
18
+ export interface TriggerPolicy {
19
+ mode: "event_driven" | "always";
20
+ skipLlmWhenNoTrigger: boolean;
21
+ alwaysManageOpenPositions: boolean;
22
+ maxLlmCallsPerHour: number;
23
+ debounceMinutes: number;
24
+ pmEvalCooldownMinutes: number;
25
+ }
26
+ export declare const DEFAULT_TRIGGER_POLICY: TriggerPolicy;
18
27
  export interface RiskConfig {
19
28
  maxLeverage: number;
20
29
  perTradeMarginMusd: number;
@@ -52,7 +61,7 @@ export interface ObjectiveConfig {
52
61
  secondary: string[];
53
62
  horizon?: string;
54
63
  }
55
- export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators"];
64
+ export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news"];
56
65
  export type Capability = (typeof ALLOWED_CAPABILITIES)[number];
57
66
  export interface AgentSpec {
58
67
  name: string;
@@ -68,6 +77,7 @@ export interface AgentSpec {
68
77
  killSwitch: KillSwitchConfig;
69
78
  objective?: ObjectiveConfig;
70
79
  capabilities: Capability[];
80
+ triggerPolicy?: TriggerPolicy;
71
81
  }
72
82
  export interface ParsedSkill {
73
83
  spec: AgentSpec;
@@ -93,6 +103,7 @@ export interface WatchEntry {
93
103
  change1h?: number;
94
104
  change24h?: number;
95
105
  change7d?: number;
106
+ sentimentBullishPct?: number;
96
107
  freshness?: Freshness;
97
108
  indicators?: IndicatorSet;
98
109
  }
@@ -103,8 +114,14 @@ export interface OpenPosition {
103
114
  symbol?: string;
104
115
  side?: string;
105
116
  status?: string;
117
+ leverage?: number;
106
118
  marginMusd?: number;
107
119
  unrealizedPnlMusd?: number;
120
+ entryPrice?: number;
121
+ markPrice?: number;
122
+ liquidationPrice?: number;
123
+ stopLossPrice?: number;
124
+ takeProfitPrice?: number;
108
125
  }
109
126
  export interface SpotOrder {
110
127
  id: number;
@@ -121,15 +138,44 @@ export interface PmPosition {
121
138
  slug?: string;
122
139
  outcomeExternalMarketId?: string;
123
140
  stakeMusd?: number;
141
+ unrealizedPnlMusd?: number;
142
+ status?: string;
143
+ }
144
+ export interface PmResolution {
145
+ id: number;
146
+ eventTitle?: string;
147
+ slug?: string;
148
+ side?: string;
124
149
  status?: string;
150
+ pnlMusd?: number;
151
+ stakeMusd?: number;
125
152
  }
126
153
  export interface PmMarket {
154
+ ref?: string;
127
155
  source: string;
128
156
  slug: string;
129
157
  outcomeExternalMarketId: string;
158
+ outcomeName?: string;
159
+ probability?: number;
130
160
  title?: string;
131
161
  freshness?: Freshness;
132
162
  }
163
+ export interface SetupSignal {
164
+ symbol: string;
165
+ kind: "breakout" | "breakdown" | "uptrend" | "downtrend" | "stretched";
166
+ bias: "long" | "short" | "fade-long" | "fade-short";
167
+ strength: number;
168
+ note: string;
169
+ held?: "long" | "short";
170
+ }
171
+ export interface NewsItem {
172
+ title: string;
173
+ source?: string;
174
+ sentiment?: string;
175
+ importance?: number;
176
+ ageHours?: number;
177
+ coins?: string[];
178
+ }
133
179
  export interface Observation {
134
180
  asOf: string;
135
181
  scopes: string[];
@@ -138,11 +184,18 @@ export interface Observation {
138
184
  openPositions: OpenPosition[];
139
185
  openOrders: SpotOrder[];
140
186
  pmPositions: PmPosition[];
187
+ pmResolutions: PmResolution[];
141
188
  pmMarkets: PmMarket[];
142
189
  watch: WatchEntry[];
190
+ setups: SetupSignal[];
191
+ marketMood?: {
192
+ fearGreed: number;
193
+ label: string;
194
+ };
143
195
  syncCursor: string | null;
144
196
  newClosedTrades: Array<Record<string, unknown>>;
145
197
  polledBeforeWrite: boolean;
198
+ news?: NewsItem[];
146
199
  }
147
200
  export type ProposedAction = {
148
201
  type: "futures_open";
@@ -180,6 +233,7 @@ export type ProposedAction = {
180
233
  orderId: number;
181
234
  } | {
182
235
  type: "pm_open";
236
+ ref?: string;
183
237
  source: string;
184
238
  slug: string;
185
239
  outcomeExternalMarketId: string;
@@ -199,6 +253,7 @@ export declare function spotBuyCost(action: Extract<ProposedAction, {
199
253
  export interface Decision {
200
254
  decision: "skip" | "act";
201
255
  reason?: string;
256
+ rationale?: string;
202
257
  confidence?: number;
203
258
  actions: ProposedAction[];
204
259
  }
@@ -228,6 +283,13 @@ export interface RunState {
228
283
  realizedPnlTodayMusd: number;
229
284
  consecutiveExecFailures: number;
230
285
  intentSeq: Record<string, number>;
286
+ llmCallTimestamps?: number[];
287
+ lastLlmCallAt?: number;
288
+ lastTriggerFingerprint?: string;
289
+ journal?: Array<{
290
+ at: string;
291
+ did: string;
292
+ }>;
231
293
  }
232
294
  export interface AgentTrace {
233
295
  runId?: string;
@@ -256,11 +318,22 @@ export interface PlannedAction {
256
318
  export interface CycleResult {
257
319
  decision: "skip" | "act";
258
320
  skipReason?: string;
321
+ rationale?: string;
322
+ confidence?: number;
323
+ rawModelOutput?: string;
259
324
  planned: PlannedAction[];
260
325
  modelFailed?: boolean;
261
326
  disabled?: boolean;
262
327
  disabledReason?: string;
263
328
  live: boolean;
329
+ triggerCodes?: string[];
330
+ llmCallMade?: boolean;
331
+ tokensIn?: number;
332
+ tokensOut?: number;
333
+ estimatedCostUsd?: number;
334
+ decisionType?: "act" | "skip" | "gate_skip" | "model_error";
335
+ writeAttempted?: number;
336
+ writeAccepted?: number;
264
337
  }
265
338
  export interface ResolveIssue {
266
339
  code: string;
@@ -25,8 +25,17 @@ export const PROVIDERS = [
25
25
  "openai",
26
26
  "groq",
27
27
  "nvidia",
28
+ "gemini",
28
29
  "openai-compatible",
29
30
  ];
31
+ export const DEFAULT_TRIGGER_POLICY = {
32
+ mode: "event_driven",
33
+ skipLlmWhenNoTrigger: true,
34
+ alwaysManageOpenPositions: true,
35
+ maxLlmCallsPerHour: 0,
36
+ debounceMinutes: 0,
37
+ pmEvalCooldownMinutes: 10,
38
+ };
30
39
  // What the agent declares it is optimizing for — so two similar-looking agents
31
40
  // are distinguishable and the scorecard/Arena can read intent.
32
41
  export const OBJECTIVE_PRIMARIES = [
@@ -40,7 +49,11 @@ export const OBJECTIVE_PRIMARIES = [
40
49
  // slice. `websearch` = external lookups (an injection surface + a cost — it can
41
50
  // inform reasoning but NEVER widen a cap, since caps live in the runner);
42
51
  // `indicators` = runner-computed RSI/MACD/etc. fed into the observation.
43
- export const ALLOWED_CAPABILITIES = ["websearch", "indicators"];
52
+ export const ALLOWED_CAPABILITIES = [
53
+ "websearch",
54
+ "indicators",
55
+ "news",
56
+ ];
44
57
  export const ok = () => ({ valid: true });
45
58
  export const fail = (code, reason) => ({
46
59
  valid: false,
@@ -5,7 +5,7 @@ export declare const COINRITHM_API: {
5
5
  readonly kind: "coinrithm-agent-api";
6
6
  readonly baseUrl: "https://api.coinrithm.com";
7
7
  readonly mcpUrl: "https://mcp.coinrithm.com/mcp";
8
- readonly openapiVersion: "1.4.0";
8
+ readonly openapiVersion: "1.5.0";
9
9
  readonly mcpPackage: "@coinrithm/mcp-trading";
10
- readonly mcpVersion: "0.3.0";
10
+ readonly mcpVersion: string;
11
11
  };
@@ -1,8 +1,16 @@
1
1
  // Versions stamped into manifest.lock.json so a resolved agent is reproducible
2
2
  // only against the exact compile that produced it.
3
+ import { createRequire } from "node:module";
4
+ const require = createRequire(import.meta.url);
3
5
  export const RUNNER_VERSION = "0.1.0";
4
6
  export const RESOLVER_VERSION = "1";
5
7
  export const MANIFEST_SCHEMA = "coinrithm.manifest.v1";
8
+ // The published package version, read from package.json at runtime (same
9
+ // createRequire pattern as src/version.ts) so the API pin's mcpVersion can never
10
+ // drift from the real published version again. package.json lives outside
11
+ // rootDir=src; at runtime dist/agent/version.js resolves ../../package.json to
12
+ // the package root in both the repo and the published tarball.
13
+ const PACKAGE_VERSION = require("../../package.json").version;
6
14
  // The CoinRithm execution surface a generated agent talks to. Written into
7
15
  // functionality/coinrithm.yaml as a version PIN; the CLI warns when an agent's
8
16
  // pin lags this, but never blocks self-host use.
@@ -10,7 +18,9 @@ export const COINRITHM_API = {
10
18
  kind: "coinrithm-agent-api",
11
19
  baseUrl: "https://api.coinrithm.com",
12
20
  mcpUrl: "https://mcp.coinrithm.com/mcp",
13
- openapiVersion: "1.4.0",
21
+ // The API CONTRACT version (openapi.yaml info.version). Versioned independently
22
+ // from the npm package below — hand-bump this when the OpenAPI contract changes.
23
+ openapiVersion: "1.5.0",
14
24
  mcpPackage: "@coinrithm/mcp-trading",
15
- mcpVersion: "0.3.0",
25
+ mcpVersion: PACKAGE_VERSION,
16
26
  };
package/dist/client.d.ts CHANGED
@@ -29,6 +29,23 @@ export declare class CoinRithmClient {
29
29
  private readonly baseUrl;
30
30
  constructor(config: ClientConfig);
31
31
  private request;
32
+ private publicRequest;
33
+ getPublicPmOverview(query?: {
34
+ fiat?: string;
35
+ }): Promise<ApiResult>;
36
+ listPublicPmEvents(query?: {
37
+ q?: string;
38
+ source?: string;
39
+ status?: string;
40
+ sort?: string;
41
+ limit?: number;
42
+ offset?: number;
43
+ fiat?: string;
44
+ }): Promise<ApiResult>;
45
+ getPublicPmEvent(source: string, slug: string, query?: {
46
+ fiat?: string;
47
+ }): Promise<ApiResult>;
48
+ getPublicPmWhales(): Promise<ApiResult>;
32
49
  whoami(apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
33
50
  getPortfolio(query?: {
34
51
  fiat?: string;
@@ -111,6 +128,7 @@ export declare class CoinRithmClient {
111
128
  source: string;
112
129
  slug: string;
113
130
  outcomeExternalMarketId: string;
131
+ side?: "yes" | "no";
114
132
  stakeMusd: number;
115
133
  } & {
116
134
  agentTrace?: AgentTrace;
@@ -155,6 +173,7 @@ export declare class CoinRithmClient {
155
173
  source: string;
156
174
  slug: string;
157
175
  outcomeExternalMarketId: string;
176
+ side?: "yes" | "no";
158
177
  stakeMusd: number;
159
178
  idempotencyKey: string;
160
179
  }>, apiKey?: string): Promise<ApiResult>;
package/dist/client.js CHANGED
@@ -157,6 +157,62 @@ export class CoinRithmClient {
157
157
  data,
158
158
  };
159
159
  }
160
+ // Public, keyless GET against the free cross-venue data API
161
+ // (/api/prediction-markets/*). No Authorization header is ever attached:
162
+ // these endpoints require no key, and the caller's trading key must not
163
+ // leak into them. No ledger headers exist on this surface either.
164
+ async publicRequest(path, query) {
165
+ const url = new URL(this.baseUrl + path);
166
+ if (query) {
167
+ for (const [k, v] of Object.entries(query)) {
168
+ if (v !== undefined && v !== null && v !== "") {
169
+ url.searchParams.set(k, String(v));
170
+ }
171
+ }
172
+ }
173
+ let res;
174
+ try {
175
+ res = await fetch(url.toString(), {
176
+ method: "GET",
177
+ headers: { Accept: "application/json" },
178
+ });
179
+ }
180
+ catch (err) {
181
+ log(`network error calling GET ${path}:`, err);
182
+ return {
183
+ ok: false,
184
+ status: 0,
185
+ data: {
186
+ error: "network_error",
187
+ message: err instanceof Error ? err.message : String(err),
188
+ },
189
+ };
190
+ }
191
+ const text = await res.text();
192
+ let data = text;
193
+ if (text) {
194
+ try {
195
+ data = JSON.parse(text);
196
+ }
197
+ catch {
198
+ // leave as text
199
+ }
200
+ }
201
+ return { ok: res.ok, status: res.status, data };
202
+ }
203
+ // ---- public PM data (no key required) ----
204
+ getPublicPmOverview(query) {
205
+ return this.publicRequest("/api/prediction-markets/overview", query);
206
+ }
207
+ listPublicPmEvents(query) {
208
+ return this.publicRequest("/api/prediction-markets/events", query);
209
+ }
210
+ getPublicPmEvent(source, slug, query) {
211
+ return this.publicRequest(`/api/prediction-markets/events/${encodeURIComponent(source)}/${encodeURIComponent(slug)}`, query);
212
+ }
213
+ getPublicPmWhales() {
214
+ return this.publicRequest("/api/prediction-markets/whales");
215
+ }
160
216
  // Every method takes an optional trailing `apiKey` (the per-request key for
161
217
  // the multi-user HTTP path). When omitted, the constructor key (stdio) is used.
162
218
  // ---- reads (scope: read) ----
package/dist/tools.js CHANGED
@@ -6,7 +6,13 @@
6
6
  // isError results rather than thrown so the model can react.
7
7
  import { z } from "zod";
8
8
  import { bearerFromHeader } from "./client.js";
9
- const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). Not financial advice.";
9
+ const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). Not financial advice. " +
10
+ "Paper fills apply a disclosed execution cost folded into realized PnL: " +
11
+ "spot/futures pay a taker fee (spot market orders also pay half-spread + " +
12
+ "slippage); PM fills at the ask with size-based slippage and a Polymarket-" +
13
+ "shaped taker fee, with entryProbability kept at the mid for calibration. " +
14
+ "See the executionModel in quote/trade results — a rehearsal cost, not an " +
15
+ "exchange fill guarantee.";
10
16
  const API_RESULT_OUTPUT_SCHEMA = {
11
17
  httpStatus: z
12
18
  .number()
@@ -569,7 +575,9 @@ export function registerTools(server, client) {
569
575
  "estimate, max payout, eligibility, freshness, and decisionSupport " +
570
576
  "(market quality/liquidity/volume/spread tiers + flags) so you can " +
571
577
  "quote and gauge tradability in one call. Never mutates state. " +
572
- "stakeMusd must be > 0 (min to open is 10). " +
578
+ "stakeMusd must be > 0 (min to open is 10). Pass side: 'no' to quote " +
579
+ "backing the NO side (omitted = yes); a NO entry fills at 100 minus the " +
580
+ "outcome probability and pays out if the outcome resolves false. " +
573
581
  PAPER_NOTE,
574
582
  inputSchema: {
575
583
  source: z.string().describe("Source slug (e.g. kalshi, polymarket)."),
@@ -577,12 +585,18 @@ export function registerTools(server, client) {
577
585
  outcomeExternalMarketId: z
578
586
  .string()
579
587
  .describe("Case-sensitive outcome / market id."),
588
+ side: z
589
+ .enum(["yes", "no"])
590
+ .optional()
591
+ .describe("Which side of the binary outcome to back. NO pays out if it " +
592
+ "resolves false; fills at 100 minus the outcome probability. " +
593
+ "Omitted = yes."),
580
594
  stakeMusd: z.number().positive().describe("mUSD to stake (> 0)."),
581
595
  agentTrace: AGENT_TRACE_SCHEMA,
582
596
  },
583
597
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
584
598
  annotations: readOnlyAnnotations("Prediction-market quote"),
585
- }, async ({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, requestKey(extra))));
599
+ }, async ({ source, slug, outcomeExternalMarketId, side, stakeMusd, agentTrace }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, side, stakeMusd, agentTrace }, requestKey(extra))));
586
600
  server.registerTool("spot_quote", {
587
601
  title: "Spot quote",
588
602
  description: "Read-only spot MARKET quote: live execution price, estimated cost " +
@@ -800,7 +814,9 @@ export function registerTools(server, client) {
800
814
  description: "Open a mock prediction-market position (binary outcomes only). Requires " +
801
815
  "the trade:pm scope. Enabled now (server-flag gated — returns 403 'not " +
802
816
  "enabled' only if CoinRithm later disables it). idempotencyKey is " +
803
- "REQUIRED. stakeMusd >= 10. Quote first and CONFIRM with the user. " +
817
+ "REQUIRED. stakeMusd >= 10. Pass side: 'no' to back the NO side (omitted " +
818
+ "= yes); a NO entry fills at 100 minus the outcome probability and pays " +
819
+ "out if the outcome resolves false. Quote first and CONFIRM with the user. " +
804
820
  PAPER_NOTE,
805
821
  inputSchema: {
806
822
  source: z
@@ -810,6 +826,12 @@ export function registerTools(server, client) {
810
826
  outcomeExternalMarketId: z
811
827
  .string()
812
828
  .describe("Case-sensitive outcome or market id returned by discovery."),
829
+ side: z
830
+ .enum(["yes", "no"])
831
+ .optional()
832
+ .describe("Which side of the binary outcome to back. NO pays out if it " +
833
+ "resolves false; fills at 100 minus the outcome probability. " +
834
+ "Omitted = yes."),
813
835
  stakeMusd: z.number().min(10).describe("mUSD stake (>= 10)."),
814
836
  idempotencyKey: z
815
837
  .string()
@@ -821,12 +843,124 @@ export function registerTools(server, client) {
821
843
  annotations: mutatingAnnotations("Open prediction-market position", {
822
844
  idempotent: true,
823
845
  }),
824
- }, async ({ source, slug, outcomeExternalMarketId, stakeMusd, idempotencyKey, agentTrace, }, extra) => present(await client.openPmPosition({
846
+ }, async ({ source, slug, outcomeExternalMarketId, side, stakeMusd, idempotencyKey, agentTrace, }, extra) => present(await client.openPmPosition({
825
847
  source,
826
848
  slug,
827
849
  outcomeExternalMarketId,
850
+ side,
828
851
  stakeMusd,
829
852
  idempotencyKey,
830
853
  agentTrace,
831
854
  }, requestKey(extra))));
855
+ // ---- Public cross-venue PM data (no API key required) ----
856
+ // These wrap the free /api/prediction-markets/* endpoints — CoinRithm's
857
+ // citable cross-venue dataset. They never attach the caller's key.
858
+ server.registerTool("pm_data_overview", {
859
+ title: "Cross-venue prediction-market statistics",
860
+ description: "Free public cross-venue prediction-market statistics: total/open/" +
861
+ "closed market counts, total volume, 24h volume, and liquidity " +
862
+ "aggregated across Polymarket, Kalshi, Metaculus, PredictIt, " +
863
+ "Limitless, Manifold, and Smarkets, plus market highlights. Volume is " +
864
+ "reported on each venue's own basis (see the methodology at " +
865
+ "https://coinrithm.com/en/prediction-markets/stats) and monetary " +
866
+ "totals cover real-money venues only — these are self-computed " +
867
+ "aggregates, so cite CoinRithm when quoting them. No API key required.",
868
+ inputSchema: {
869
+ fiat: z
870
+ .string()
871
+ .optional()
872
+ .describe("Fiat currency code for monetary figures (default usd)."),
873
+ },
874
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
875
+ annotations: readOnlyAnnotations("Cross-venue prediction-market statistics"),
876
+ }, async ({ fiat }) => present(await client.getPublicPmOverview({ fiat })));
877
+ server.registerTool("pm_data_events", {
878
+ title: "Search prediction markets across all venues",
879
+ description: "Free public search over prediction-market events across ALL seven " +
880
+ "venues (Polymarket, Kalshi, Metaculus, PredictIt, Limitless, " +
881
+ "Manifold, Smarkets) — broader than discover_pm_markets, which is " +
882
+ "scoped to the paper-tradeable venues. Returns titles, probabilities, " +
883
+ "volume/liquidity, status, and source per event. Research/data only: " +
884
+ "to trade, use discover_pm_markets + pm_quote instead. No API key " +
885
+ "required.",
886
+ inputSchema: {
887
+ q: z.string().optional().describe("Optional search text."),
888
+ source: z
889
+ .string()
890
+ .optional()
891
+ .describe("Optional venue filter: polymarket, kalshi, metaculus, predictit, " +
892
+ "limitless, manifold, or smarkets."),
893
+ status: z
894
+ .string()
895
+ .optional()
896
+ .describe("Optional status filter (e.g. open or closed)."),
897
+ sort: z.string().optional().describe("Optional sort key."),
898
+ limit: z
899
+ .number()
900
+ .int()
901
+ .min(1)
902
+ .max(50)
903
+ .optional()
904
+ .describe("Max rows (1-50, default 20)."),
905
+ offset: z
906
+ .number()
907
+ .int()
908
+ .min(0)
909
+ .optional()
910
+ .describe("Pagination offset (default 0)."),
911
+ fiat: z
912
+ .string()
913
+ .optional()
914
+ .describe("Fiat currency code for monetary figures (default usd)."),
915
+ },
916
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
917
+ annotations: readOnlyAnnotations("Search prediction markets across all venues"),
918
+ }, async ({ q, source, status, sort, limit, offset, fiat }) => present(await client.listPublicPmEvents({
919
+ q,
920
+ source,
921
+ status,
922
+ sort,
923
+ limit,
924
+ offset,
925
+ fiat,
926
+ })));
927
+ server.registerTool("pm_data_event", {
928
+ title: "Get full prediction-market event detail",
929
+ description: "Free public detail for one prediction-market event by venue + slug: " +
930
+ "outcomes with probabilities, price snapshots, resolution evidence, " +
931
+ "crossSourceMatches (the SAME real-world question priced on other " +
932
+ "venues — read probability divergence directly from it), " +
933
+ "referenceProbability when present (CoinRithm's canonical cross-venue " +
934
+ "number: the liquidity-weighted median Yes probability across matched " +
935
+ "real-money venues, with venueCount and spreadPoints — quote all " +
936
+ "three together, venues disagree and the spread says by how much), " +
937
+ "recent whale trades on the event, related events, and related news. " +
938
+ "This is the cross-venue research view; for tradability use pm_quote. " +
939
+ "No API key required.",
940
+ inputSchema: {
941
+ source: z
942
+ .string()
943
+ .describe("Venue slug: polymarket, kalshi, metaculus, predictit, limitless, " +
944
+ "manifold, or smarkets."),
945
+ slug: z.string().describe("Event slug on that venue."),
946
+ fiat: z
947
+ .string()
948
+ .optional()
949
+ .describe("Fiat currency code for monetary figures (default usd)."),
950
+ },
951
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
952
+ annotations: readOnlyAnnotations("Get full prediction-market event detail"),
953
+ }, async ({ source, slug, fiat }) => present(await client.getPublicPmEvent(source, slug, { fiat })));
954
+ server.registerTool("pm_data_whales", {
955
+ title: "Get latest prediction-market whale trades",
956
+ description: "Free public tape of the latest large prediction-market trades " +
957
+ "(roughly $1k+ notional) across venues, newest first (top 50): side, " +
958
+ "outcome, USD value, price, market question, and the event it printed " +
959
+ "on. Polymarket rows are wallet-attributed; Kalshi rows are anonymized " +
960
+ "exchange prints. A large print is information, not a recommendation. " +
961
+ "No API key required.",
962
+ inputSchema: {},
963
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
964
+ annotations: readOnlyAnnotations("Get latest prediction-market whale trades"),
965
+ }, async () => present(await client.getPublicPmWhales()));
832
966
  }