@coinrithm/mcp-trading 0.7.13 → 0.7.15

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 (49) hide show
  1. package/CHANGELOG.md +692 -573
  2. package/README.md +400 -364
  3. package/dist/agent/act.d.ts +7 -2
  4. package/dist/agent/act.js +10 -3
  5. package/dist/agent/cli.d.ts +1 -0
  6. package/dist/agent/cli.js +25 -4
  7. package/dist/agent/client.d.ts +13 -0
  8. package/dist/agent/client.js +107 -4
  9. package/dist/agent/decisionReceipt.d.ts +5 -1
  10. package/dist/agent/decisionReceipt.js +95 -3
  11. package/dist/agent/decisionValidator.js +74 -0
  12. package/dist/agent/definitionSnapshot.d.ts +22 -0
  13. package/dist/agent/definitionSnapshot.js +28 -0
  14. package/dist/agent/engine.d.ts +1 -0
  15. package/dist/agent/engine.js +1 -0
  16. package/dist/agent/futuresEligibility.d.ts +36 -0
  17. package/dist/agent/futuresEligibility.js +90 -0
  18. package/dist/agent/mergeRules.d.ts +1 -1
  19. package/dist/agent/mergeRules.js +17 -2
  20. package/dist/agent/observe.d.ts +9 -1
  21. package/dist/agent/observe.js +507 -45
  22. package/dist/agent/pmContext.d.ts +10 -1
  23. package/dist/agent/pmContext.js +163 -1
  24. package/dist/agent/prompt.d.ts +14 -0
  25. package/dist/agent/prompt.js +138 -27
  26. package/dist/agent/providerCapabilities.d.ts +3 -0
  27. package/dist/agent/providerCapabilities.js +34 -4
  28. package/dist/agent/providers.d.ts +3 -0
  29. package/dist/agent/providers.js +12 -2
  30. package/dist/agent/resolve.js +8 -1
  31. package/dist/agent/runner.js +140 -18
  32. package/dist/agent/skill.js +9 -0
  33. package/dist/agent/skillValidator.js +11 -2
  34. package/dist/agent/strictLint.js +1 -0
  35. package/dist/agent/templates.js +30 -30
  36. package/dist/agent/types.d.ts +138 -1
  37. package/dist/agent/types.js +4 -0
  38. package/dist/client.d.ts +11 -2
  39. package/dist/client.js +22 -10
  40. package/dist/http.d.ts +1 -0
  41. package/dist/http.js +17 -2
  42. package/dist/httpCompletion.d.ts +1 -1
  43. package/dist/httpCompletion.js +1 -0
  44. package/dist/index.js +2 -1
  45. package/dist/instructions.d.ts +1 -0
  46. package/dist/instructions.js +16 -0
  47. package/dist/tools.d.ts +1 -0
  48. package/dist/tools.js +150 -66
  49. package/package.json +97 -97
@@ -1,3 +1,4 @@
1
+ import type { FuturesEntryEligibility } from "./futuresEligibility.js";
1
2
  import { IndicatorSet } from "./indicators.js";
2
3
  import type { DecisionInputRecord } from "./decisionReceipt.js";
3
4
  export declare const SPEC_VERSION = "coinrithm.agent.v1";
@@ -34,6 +35,7 @@ export interface RiskConfig {
34
35
  watchlist: string[];
35
36
  blocklist?: string[];
36
37
  direction?: "long_only" | "short_only";
38
+ pmMinEntryProbabilityPct?: number;
37
39
  }
38
40
  export interface LimitsConfig {
39
41
  maxTradesPerDay: number;
@@ -96,7 +98,7 @@ export interface ObjectiveConfig {
96
98
  secondary: string[];
97
99
  horizon?: string;
98
100
  }
99
- export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan"];
101
+ export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan", "whale_context"];
100
102
  export type Capability = (typeof ALLOWED_CAPABILITIES)[number];
101
103
  export interface AgentSpec {
102
104
  name: string;
@@ -133,6 +135,21 @@ export interface Freshness {
133
135
  asOf?: string;
134
136
  basis?: string;
135
137
  }
138
+ export interface IndicatorContext {
139
+ range: "1D";
140
+ nominalIntervalSeconds: 300;
141
+ barCount: number;
142
+ timestampedBarCount: number;
143
+ checkedIntervalCount: number;
144
+ irregularIntervalCount: number;
145
+ intervalStatus: "regular" | "irregular" | "unknown";
146
+ maxGapSeconds?: number;
147
+ asOf?: string;
148
+ recent15: {
149
+ barCount: number;
150
+ intervalStatus: "regular" | "irregular" | "unknown";
151
+ };
152
+ }
136
153
  export interface WatchEntry {
137
154
  symbol: string;
138
155
  coinId: string | null;
@@ -142,11 +159,16 @@ export interface WatchEntry {
142
159
  change24h?: number;
143
160
  change7d?: number;
144
161
  sentimentBullishPct?: number;
162
+ sentimentTotalVotes?: number;
163
+ sentimentDayUtc?: string;
164
+ sentimentUpdatedAt?: string;
145
165
  freshness?: Freshness;
146
166
  indicators?: IndicatorSet;
167
+ indicatorContext?: IndicatorContext;
147
168
  discovered?: boolean;
148
169
  slug?: string;
149
170
  fundamentals?: CoinFundamentals;
171
+ futuresEntryEligibility?: FuturesEntryEligibility;
150
172
  }
151
173
  export interface CoinFundamentals {
152
174
  categories?: string[];
@@ -230,6 +252,42 @@ export interface PmMarket {
230
252
  liquidityUsd?: number;
231
253
  quality?: PmQuality;
232
254
  decisionSupport?: PmDecisionSupport;
255
+ consensus?: PmConsensus | null;
256
+ rules?: PmSettlementRule | null;
257
+ outcomeRule?: PmOutcomeRule;
258
+ }
259
+ export interface PmCalibration {
260
+ settled: number;
261
+ brierAgent: number | null;
262
+ brierMarket: number | null;
263
+ meanForecastPct: number | null;
264
+ winRatePct: number | null;
265
+ bands: Array<{
266
+ fromPct: number;
267
+ toPct: number;
268
+ n: number;
269
+ meanForecastPct: number;
270
+ winRatePct: number;
271
+ }>;
272
+ }
273
+ export interface PmSettlementRule {
274
+ published: boolean;
275
+ text?: string;
276
+ sources?: string[];
277
+ scope?: "per_outcome";
278
+ }
279
+ export type PmOutcomeRule = {
280
+ primary: string;
281
+ secondary?: string;
282
+ } | {
283
+ unknown: "source_rules_unavailable" | "market_not_found" | "rule_missing" | "conflicting_duplicates" | "unreadable";
284
+ };
285
+ export interface PmConsensus {
286
+ prob: number;
287
+ venues: number;
288
+ spreadPts: number;
289
+ kind: "binary" | "leader";
290
+ outcome: string | null;
233
291
  }
234
292
  export interface PmQuality {
235
293
  decisionEligible?: boolean;
@@ -265,6 +323,78 @@ export interface NewsItem {
265
323
  publishedAt?: string;
266
324
  coins?: string[];
267
325
  }
326
+ export type WhaleContextStatus = "available" | "partial" | "unavailable";
327
+ export type WhaleContextFailure = "timeout" | "network_error" | "http_error" | "invalid_payload";
328
+ export interface WhaleTradeContext {
329
+ source: string;
330
+ eventSlug: string;
331
+ eventTitle?: string;
332
+ marketQuestion?: string;
333
+ side: string;
334
+ outcome?: string;
335
+ usdValue?: number;
336
+ price?: number;
337
+ sourceMarketRef?: string;
338
+ valueBasis?: string;
339
+ evidenceType?: string;
340
+ evidenceRef?: string;
341
+ nativeValue?: number;
342
+ nativeCurrency?: string;
343
+ availability?: "live" | "delayed" | "unavailable";
344
+ tradedAt?: string;
345
+ observedAt?: string;
346
+ latencySeconds?: number;
347
+ walletAddress?: string;
348
+ }
349
+ export interface WhaleWalletFillContext {
350
+ eventSlug?: string;
351
+ eventTitle?: string;
352
+ marketQuestion?: string;
353
+ side?: string;
354
+ outcome?: string;
355
+ usdValue?: number;
356
+ price?: number;
357
+ evidenceType?: string;
358
+ tradedAt?: string;
359
+ sourceMarketRef?: string;
360
+ }
361
+ export interface WhaleWalletContext {
362
+ source: string;
363
+ address: string;
364
+ asOf?: string;
365
+ summary30d?: {
366
+ basis?: string;
367
+ windowStart?: string;
368
+ tradeCount?: number;
369
+ notionalUsd?: number;
370
+ buyNotionalUsd?: number;
371
+ sellNotionalUsd?: number;
372
+ };
373
+ rollup?: {
374
+ available?: boolean;
375
+ coveredDays?: number;
376
+ from?: string;
377
+ to?: string;
378
+ computedAt?: string;
379
+ };
380
+ daily: Array<{
381
+ day?: string;
382
+ tradeCount?: number;
383
+ buyCount?: number;
384
+ sellCount?: number;
385
+ notionalUsd?: number;
386
+ }>;
387
+ recentFills: WhaleWalletFillContext[];
388
+ }
389
+ export interface WhaleContext {
390
+ status: WhaleContextStatus;
391
+ fetchedAt: string;
392
+ coverage: "relevant_events" | "no_relevant_events";
393
+ trades: WhaleTradeContext[];
394
+ wallets: WhaleWalletContext[];
395
+ omitted: number;
396
+ reason?: WhaleContextFailure;
397
+ }
268
398
  export interface Observation {
269
399
  asOf: string;
270
400
  scopes: string[];
@@ -276,16 +406,19 @@ export interface Observation {
276
406
  pmPositions: PmPosition[];
277
407
  pmResolutions: PmResolution[];
278
408
  pmMarkets: PmMarket[];
409
+ pmCalibration?: PmCalibration;
279
410
  watch: WatchEntry[];
280
411
  setups: SetupSignal[];
281
412
  marketMood?: {
282
413
  fearGreed: number;
283
414
  label: string;
415
+ fetchedAt?: string;
284
416
  };
285
417
  syncCursor: string | null;
286
418
  newClosedTrades: Array<Record<string, unknown>>;
287
419
  polledBeforeWrite: boolean;
288
420
  news?: NewsItem[];
421
+ whaleContext?: WhaleContext;
289
422
  universeMovers?: Array<{
290
423
  symbol: string;
291
424
  name?: string;
@@ -437,6 +570,10 @@ export interface RunState {
437
570
  lastLlmCallAt?: number;
438
571
  lastTriggerFingerprint?: string;
439
572
  consecutivePermanentModelErrors?: number;
573
+ permanentModelErrorRoute?: {
574
+ provider: string;
575
+ model: string;
576
+ };
440
577
  consecutiveAuthFailures?: number;
441
578
  journal?: Array<{
442
579
  at: string;
@@ -50,6 +50,9 @@ export const OBJECTIVE_PRIMARIES = [
50
50
  // slice. `websearch` = external lookups (an injection surface + a cost — it can
51
51
  // inform reasoning but NEVER widen a cap, since caps live in the runner);
52
52
  // `indicators` = runner-computed RSI/MACD/etc. fed into the observation.
53
+ // `whale_context` reads bounded public prediction-market whale context only
54
+ // after the deterministic gate fires, and only when PM is in the venue scope.
55
+ // It never widens the candidate set or acts as a trading signal by itself.
53
56
  // `universe_scan` (2026-08-18, direct user request): each cycle the runner
54
57
  // pulls the top 24h movers across CoinRithm's tracked coin universe, resolves
55
58
  // the top few into FULL watch entries (price, sentiment, indicators when that
@@ -64,6 +67,7 @@ export const ALLOWED_CAPABILITIES = [
64
67
  "indicators",
65
68
  "news",
66
69
  "universe_scan",
70
+ "whale_context",
67
71
  ];
68
72
  export const ok = () => ({ valid: true });
69
73
  export const fail = (code, reason) => ({
package/dist/client.d.ts CHANGED
@@ -39,6 +39,7 @@ export type ProvenanceReport = {
39
39
  sourceCapturedAt?: string;
40
40
  };
41
41
  };
42
+ export declare const API_KEY_MINT_URL = "https://www.coinrithm.com/en/settings/api-keys";
42
43
  export declare class CoinRithmClient {
43
44
  private readonly defaultApiKey?;
44
45
  private readonly baseUrl;
@@ -113,6 +114,12 @@ export declare class CoinRithmClient {
113
114
  range?: "1H" | "1D" | "1W" | "1M" | "3M";
114
115
  fiat?: string;
115
116
  }, apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
117
+ getNews(query: {
118
+ coins: string;
119
+ limit?: number;
120
+ hours?: number;
121
+ minImportance?: number;
122
+ }, apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
116
123
  discoverPmMarkets(query?: {
117
124
  q?: string;
118
125
  source?: "all" | "kalshi" | "polymarket";
@@ -145,8 +152,8 @@ export declare class CoinRithmClient {
145
152
  page?: number;
146
153
  pageSize?: number;
147
154
  window?: "7d" | "30d" | "all";
148
- }, apiKey?: string): Promise<ApiResult>;
149
- getArenaAgent(handle: string, apiKey?: string): Promise<ApiResult>;
155
+ }): Promise<ApiResult>;
156
+ getArenaAgent(handle: string): Promise<ApiResult>;
150
157
  listOpenOrders(query?: {
151
158
  coinId?: string;
152
159
  limit?: number;
@@ -174,6 +181,7 @@ export declare class CoinRithmClient {
174
181
  stakeMusd: number;
175
182
  forecastProbability?: number;
176
183
  bankrollMusd?: number;
184
+ minEntryProbabilityPct?: number;
177
185
  } & {
178
186
  agentTrace?: AgentTrace;
179
187
  }, apiKey?: string): Promise<ApiResult>;
@@ -221,6 +229,7 @@ export declare class CoinRithmClient {
221
229
  stakeMusd: number;
222
230
  idempotencyKey: string;
223
231
  forecastProbability?: number;
232
+ minEntryProbabilityPct?: number;
224
233
  thesis?: string;
225
234
  provenance?: ProvenanceReport;
226
235
  }>, apiKey?: string): Promise<ApiResult>;
package/dist/client.js CHANGED
@@ -71,6 +71,9 @@ const applyAgentTraceHeaders = (headers, trace) => {
71
71
  const traceFromBody = (body) => body && typeof body === "object" && "agentTrace" in body
72
72
  ? body.agentTrace
73
73
  : undefined;
74
+ // Where a user mints an API key (Settings -> API Keys). Named in every
75
+ // missing-key error so an assistant can tell its user where to go.
76
+ export const API_KEY_MINT_URL = "https://www.coinrithm.com/en/settings/api-keys";
74
77
  export class CoinRithmClient {
75
78
  // Default key for the stdio (single-user) path. Undefined in the multi-user
76
79
  // HTTP path, where every call must pass a per-request `apiKey` override.
@@ -88,8 +91,10 @@ export class CoinRithmClient {
88
91
  status: 401,
89
92
  data: {
90
93
  error: "missing_api_key",
91
- message: "No API key for this request. On the hosted MCP, send " +
92
- "`Authorization: Bearer crk_live_…` with your own key.",
94
+ message: "No API key for this request. Mint a free key at " +
95
+ `${API_KEY_MINT_URL}, then on the hosted MCP send ` +
96
+ "`Authorization: Bearer crk_live_…`. Public data tools " +
97
+ "(pm_data_*, get_arena_*, get_crypto_movers) need no key.",
93
98
  },
94
99
  };
95
100
  }
@@ -337,6 +342,13 @@ export class CoinRithmClient {
337
342
  getCandles(coinId, query, apiKey, agentTrace) {
338
343
  return this.request("GET", `/api/agent/market/${encodeURIComponent(coinId)}/candles`, { query, apiKey, agentTrace });
339
344
  }
345
+ getNews(query, apiKey, agentTrace) {
346
+ return this.request("GET", "/api/agent/news", {
347
+ query,
348
+ apiKey,
349
+ agentTrace,
350
+ });
351
+ }
340
352
  discoverPmMarkets(query, apiKey, agentTrace) {
341
353
  return this.request("GET", "/api/agent/pm/discover", {
342
354
  query,
@@ -364,15 +376,15 @@ export class CoinRithmClient {
364
376
  agentTrace,
365
377
  });
366
378
  }
367
- // Agent Arena (public leaderboard). The key is sent but ignored by these
368
- // endpoints — they expose only public agent names + realized performance.
369
- getArenaLeaderboard(query, apiKey) {
370
- return this.request("GET", "/api/arena", { query, apiKey });
379
+ // Agent Arena (public leaderboard): public agent names + realized
380
+ // performance only, served keyless by the API. These used to go through
381
+ // the keyed path, so a keyless MCP client got a 401 for public data
382
+ // (21 of 44 keyless tool calls failed in 14.6h, audit 2026-09-29).
383
+ getArenaLeaderboard(query) {
384
+ return this.publicRequest("/api/arena", query);
371
385
  }
372
- getArenaAgent(handle, apiKey) {
373
- return this.request("GET", `/api/arena/${encodeURIComponent(handle)}`, {
374
- apiKey,
375
- });
386
+ getArenaAgent(handle) {
387
+ return this.publicRequest(`/api/arena/${encodeURIComponent(handle)}`);
376
388
  }
377
389
  listOpenOrders(query, apiKey, agentTrace) {
378
390
  return this.request("GET", "/api/agent/orders/open", {
package/dist/http.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { CoinRithmClient } from "./client.js";
4
4
  import { type CompletionLogger } from "./httpCompletion.js";
5
+ export declare const MALFORMED_MCP_LINK: RegExp;
5
6
  export declare function createHttpApp(client: CoinRithmClient, options?: {
6
7
  completionLogger?: CompletionLogger;
7
8
  createServer?: () => McpServer;
package/dist/http.js CHANGED
@@ -34,6 +34,7 @@
34
34
  // Transport is stateless: a fresh McpServer + transport per request, which is
35
35
  // the correct isolation model for a multi-user, per-request-keyed surface — no
36
36
  // session state is shared between users.
37
+ import { SERVER_INSTRUCTIONS } from "./instructions.js";
37
38
  import express from "express";
38
39
  import { pathToFileURL } from "node:url";
39
40
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -42,6 +43,9 @@ import { CoinRithmClient, bearerFromHeader, loadHttpConfig, log, } from "./clien
42
43
  import { registerTools } from "./tools.js";
43
44
  import { SERVER_VERSION } from "./version.js";
44
45
  import { observeHttpCompletion, } from "./httpCompletion.js";
46
+ // "/mcp" immediately followed by the markdown "](" of a pasted link, literal or
47
+ // percent-encoded. Anything else under /mcp is not this family.
48
+ export const MALFORMED_MCP_LINK = /^\/mcp(?:\]|%5d)(?:\(|%28)/i;
45
49
  // Factory permits isolated localhost SDK tests without opening a listener on import.
46
50
  export function createHttpApp(client, options = {}) {
47
51
  const app = express();
@@ -108,13 +112,24 @@ export function createHttpApp(client, options = {}) {
108
112
  app.get("/robots.txt", (_req, res) => {
109
113
  res.type("text/plain").send("User-agent: *\nDisallow: /\n");
110
114
  });
115
+ // Recover one observed malformed URL family. A markdown link to this
116
+ // endpoint circulates with its "](https://…)" suffix pasted into the path,
117
+ // e.g. GET /mcp](https:/mcp.coinrithm.com/mcp): 408 such requests on
118
+ // 2026-09-25, all Claude-User web fetches that received Express's default
119
+ // 404. GET/HEAD go to the service descriptor. POST is served as MCP below:
120
+ // on 2026-09-26, 127 Claude-User connector POSTs in 6 h hit the same path
121
+ // and got 404, so a user who pasted that URL into a connector could never
122
+ // initialize. Every other unknown path behaves as before.
123
+ app.get(MALFORMED_MCP_LINK, (_req, res) => {
124
+ res.redirect(302, "/");
125
+ });
111
126
  app.get("/mcp", (_req, res) => {
112
127
  res.status(405).json({
113
128
  error: "method_not_allowed",
114
129
  message: "This is a POST-only streamable-HTTP MCP endpoint. Connect with an MCP client, or GET / for service info.",
115
130
  });
116
131
  });
117
- app.post("/mcp", async (req, res) => {
132
+ app.post(["/mcp", MALFORMED_MCP_LINK], async (req, res) => {
118
133
  // Per-request auth: read THIS caller's key from the Authorization header,
119
134
  // or from Smithery's non-reserved forwarding header.
120
135
  // It is optional at the transport layer so registries can initialize the
@@ -132,7 +147,7 @@ export function createHttpApp(client, options = {}) {
132
147
  new McpServer({
133
148
  name: "coinrithm-trading",
134
149
  version: SERVER_VERSION,
135
- });
150
+ }, { instructions: SERVER_INSTRUCTIONS });
136
151
  if (!options.createServer)
137
152
  registerTools(server, client);
138
153
  const transport = new StreamableHTTPServerTransport({
@@ -1,6 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
- export declare const COMPLETION_TOOL_NAMES: readonly ["whoami", "get_portfolio", "get_wallet", "list_open_orders", "get_positions", "resolve_symbol", "get_equity_curve", "get_my_trades", "get_market_context", "get_candles", "discover_pm_markets", "get_performance", "get_agent_ledger", "export_agent_ledger", "export_run_evidence", "get_arena_leaderboard", "get_arena_agent", "futures_quote", "pm_quote", "spot_quote", "place_spot_order", "cancel_spot_order", "open_futures_position", "set_futures_sl_tp", "close_futures_position", "open_pm_position", "report_pm_opportunity", "pm_data_overview", "pm_data_sources", "pm_data_sources_health", "pm_data_events", "pm_data_event", "pm_data_whales", "pm_data_whale_wallets", "pm_data_whale_wallet", "pm_data_disagreements", "pm_data_calibration", "pm_data_canonical", "pm_data_volume_history", "get_crypto_movers"];
3
+ export declare const COMPLETION_TOOL_NAMES: readonly ["whoami", "get_portfolio", "get_wallet", "list_open_orders", "get_positions", "resolve_symbol", "get_equity_curve", "get_my_trades", "get_market_context", "get_candles", "get_news", "discover_pm_markets", "get_performance", "get_agent_ledger", "export_agent_ledger", "export_run_evidence", "get_arena_leaderboard", "get_arena_agent", "futures_quote", "pm_quote", "spot_quote", "place_spot_order", "cancel_spot_order", "open_futures_position", "set_futures_sl_tp", "close_futures_position", "open_pm_position", "report_pm_opportunity", "pm_data_overview", "pm_data_sources", "pm_data_sources_health", "pm_data_events", "pm_data_event", "pm_data_whales", "pm_data_whale_wallets", "pm_data_whale_wallet", "pm_data_disagreements", "pm_data_calibration", "pm_data_canonical", "pm_data_volume_history", "get_crypto_movers"];
4
4
  type Operation = "initialize" | "tools_list" | "tools_call" | "notification" | "other" | "invalid";
5
5
  type Outcome = "result" | "tool_error" | "protocol_error" | "no_response" | "not_applicable";
6
6
  type ToolName = (typeof COMPLETION_TOOL_NAMES)[number] | "unknown" | null;
@@ -13,6 +13,7 @@ export const COMPLETION_TOOL_NAMES = [
13
13
  "get_my_trades",
14
14
  "get_market_context",
15
15
  "get_candles",
16
+ "get_news",
16
17
  "discover_pm_markets",
17
18
  "get_performance",
18
19
  "get_agent_ledger",
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
  // COINRITHM_API_URL (optional) base URL; defaults to https://api.coinrithm.com
11
11
  //
12
12
  // stdout is the JSON-RPC channel — we log ONLY to stderr.
13
+ import { SERVER_INSTRUCTIONS } from "./instructions.js";
13
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
15
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
16
  import { CoinRithmClient, loadConfig, log } from "./client.js";
@@ -21,7 +22,7 @@ async function main() {
21
22
  const server = new McpServer({
22
23
  name: "coinrithm-trading",
23
24
  version: SERVER_VERSION,
24
- });
25
+ }, { instructions: SERVER_INSTRUCTIONS });
25
26
  registerTools(server, client);
26
27
  const transport = new StdioServerTransport();
27
28
  await server.connect(transport);
@@ -0,0 +1 @@
1
+ export declare const SERVER_INSTRUCTIONS: string;
@@ -0,0 +1,16 @@
1
+ import { API_KEY_MINT_URL } from "./client.js";
2
+ import { PAPER_NOTE } from "./tools.js";
3
+ // Sent once in the MCP `initialize` result. Guidance that used to be repeated
4
+ // on every tool description lives here, so the tool list stays small enough to
5
+ // fit an assistant's context (tools/list was 114,668 bytes with the ~512-char
6
+ // paper disclaimer on 25 tools, audit 2026-09-29).
7
+ export const SERVER_INSTRUCTIONS = [
8
+ "CoinRithm MCP: crypto and prediction-market data from 12 venues, plus paper trading.",
9
+ "Start keyless: the pm_data_* tools, get_arena_leaderboard, get_arena_agent and get_crypto_movers need no API key.",
10
+ "pm_data_events returns open markets by default; pass status=closed or status=all for history.",
11
+ `Tools that read or act on an account need a key. The user mints one at ${API_KEY_MINT_URL}; start with a read-only key.`,
12
+ "Every tool returns { httpStatus, ok, ledgerEventId, ledgerStatus, body }: httpStatus is CoinRithm's HTTP status (0 = network error), ok means 2xx, body is the parsed response.",
13
+ "Always quote before a paper write (spot_quote, futures_quote or pm_quote), then open with that quote.",
14
+ "Prediction-market probabilities are venue prices, not CoinRithm forecasts. referenceProbability is a cross-venue consensus with a disagreement band (spreadPoints).",
15
+ PAPER_NOTE,
16
+ ].join("\n");
package/dist/tools.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { CoinRithmClient } from "./client.js";
3
3
  export declare const PAPER_NOTE: string;
4
+ export declare const PAPER_NOTE_SHORT: string;
4
5
  /**
5
6
  * Keep keyless discovery calls small enough for an agent context window.
6
7
  * Full event evidence remains available from pm_data_event.