@coinrithm/mcp-trading 0.7.2 → 0.7.3

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 (46) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/README.md +9 -7
  3. package/dist/agent/act.d.ts +2 -2
  4. package/dist/agent/act.js +24 -3
  5. package/dist/agent/cli.js +59 -18
  6. package/dist/agent/client.d.ts +33 -0
  7. package/dist/agent/client.js +34 -7
  8. package/dist/agent/decision.d.ts +3 -0
  9. package/dist/agent/decision.js +26 -3
  10. package/dist/agent/decisionValidator.js +2 -1
  11. package/dist/agent/deploymentOverlay.js +25 -5
  12. package/dist/agent/engine.d.ts +2 -1
  13. package/dist/agent/engine.js +4 -1
  14. package/dist/agent/extract.js +3 -1
  15. package/dist/agent/gate.js +25 -5
  16. package/dist/agent/index.js +0 -1
  17. package/dist/agent/indicators.js +4 -2
  18. package/dist/agent/manifest.js +1 -1
  19. package/dist/agent/mechanical.d.ts +36 -0
  20. package/dist/agent/mechanical.js +286 -0
  21. package/dist/agent/observe.js +120 -52
  22. package/dist/agent/prompt.d.ts +3 -1
  23. package/dist/agent/prompt.js +17 -6
  24. package/dist/agent/providers.js +39 -4
  25. package/dist/agent/resolve.js +23 -6
  26. package/dist/agent/resolvePm.js +14 -3
  27. package/dist/agent/runEvidence.js +6 -2
  28. package/dist/agent/runner.d.ts +8 -2
  29. package/dist/agent/runner.js +363 -59
  30. package/dist/agent/scorecard.js +12 -4
  31. package/dist/agent/setups.js +57 -9
  32. package/dist/agent/skill.js +1 -1
  33. package/dist/agent/state.js +9 -4
  34. package/dist/agent/types.d.ts +17 -2
  35. package/dist/agent/types.js +2 -1
  36. package/dist/agent/util.js +11 -4
  37. package/dist/agent/version.d.ts +1 -1
  38. package/dist/agent/version.js +1 -1
  39. package/dist/client.d.ts +33 -0
  40. package/dist/client.js +12 -3
  41. package/dist/executionPolicy.d.ts +2 -0
  42. package/dist/executionPolicy.js +21 -0
  43. package/dist/http.js +10 -2
  44. package/dist/tools.d.ts +1 -0
  45. package/dist/tools.js +214 -29
  46. package/package.json +9 -1
@@ -42,7 +42,9 @@ export function loadState(file, runId) {
42
42
  ...base,
43
43
  ...parsed,
44
44
  seen: Array.isArray(parsed.seen) ? parsed.seen : [],
45
- intentSeq: parsed.intentSeq && typeof parsed.intentSeq === "object" && !Array.isArray(parsed.intentSeq)
45
+ intentSeq: parsed.intentSeq &&
46
+ typeof parsed.intentSeq === "object" &&
47
+ !Array.isArray(parsed.intentSeq)
46
48
  ? parsed.intentSeq
47
49
  : {},
48
50
  });
@@ -91,13 +93,16 @@ export function checkKillSwitch(spec, state) {
91
93
  return `consecutive model failures ${state.consecutiveModelFailures} >= ${threshold}`;
92
94
  }
93
95
  }
94
- if (ks.maxConsecutiveRejects > 0 && state.consecutiveRejectCycles >= ks.maxConsecutiveRejects) {
96
+ if (ks.maxConsecutiveRejects > 0 &&
97
+ state.consecutiveRejectCycles >= ks.maxConsecutiveRejects) {
95
98
  return `consecutive reject cycles ${state.consecutiveRejectCycles} >= ${ks.maxConsecutiveRejects}`;
96
99
  }
97
- if (ks.maxDrawdownMusd > 0 && state.peakRealizedMusd - state.realizedPnlMusd >= ks.maxDrawdownMusd) {
100
+ if (ks.maxDrawdownMusd > 0 &&
101
+ state.peakRealizedMusd - state.realizedPnlMusd >= ks.maxDrawdownMusd) {
98
102
  return `drawdown ${(state.peakRealizedMusd - state.realizedPnlMusd).toFixed(2)} >= ${ks.maxDrawdownMusd}`;
99
103
  }
100
- if (ks.onRateLimitPressure && state.rateLimitHits >= RATE_LIMIT_PRESSURE_THRESHOLD) {
104
+ if (ks.onRateLimitPressure &&
105
+ state.rateLimitHits >= RATE_LIMIT_PRESSURE_THRESHOLD) {
101
106
  return `rate-limit pressure: ${state.rateLimitHits} 429s this session`;
102
107
  }
103
108
  return null;
@@ -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" | "gemini" | "openai-compatible";
7
+ export type ProviderName = "anthropic" | "openai" | "groq" | "nvidia" | "gemini" | "openai-compatible" | "mechanical";
8
8
  export declare const PROVIDERS: readonly ProviderName[];
9
9
  export interface ModelConfig {
10
10
  provider: ProviderName;
@@ -159,6 +159,7 @@ export interface PmMarket {
159
159
  probability?: number;
160
160
  title?: string;
161
161
  freshness?: Freshness;
162
+ volumeUsd?: number;
162
163
  }
163
164
  export interface SetupSignal {
164
165
  symbol: string;
@@ -240,10 +241,11 @@ export type ProposedAction = {
240
241
  stakeMusd: number;
241
242
  confidence?: number;
242
243
  rationaleSummary?: string;
244
+ forecastProbability?: number;
243
245
  };
244
246
  export type ActionVenue = Venue;
245
247
  export declare function actionVenue(a: ProposedAction): ActionVenue;
246
- export declare function isWriteAction(a: ProposedAction): boolean;
248
+ export declare function isWriteAction(_a: ProposedAction): boolean;
247
249
  export declare function isOpenAction(a: ProposedAction): a is Extract<ProposedAction, {
248
250
  type: "futures_open" | "spot_order" | "pm_open";
249
251
  }>;
@@ -265,6 +267,8 @@ export interface QuoteEvidence {
265
267
  executionPrice?: number;
266
268
  estimatedCostMusd?: number;
267
269
  freshness?: Freshness;
270
+ openBlocked?: boolean;
271
+ openBlockReasons?: unknown;
268
272
  }
269
273
  export interface RunState {
270
274
  runId: string;
@@ -334,6 +338,17 @@ export interface CycleResult {
334
338
  decisionType?: "act" | "skip" | "gate_skip" | "model_error";
335
339
  writeAttempted?: number;
336
340
  writeAccepted?: number;
341
+ opportunity?: PostedOpportunity;
342
+ }
343
+ export interface PostedOpportunity {
344
+ kind: "abstained" | "forecast_only" | "quote_expired";
345
+ source?: string;
346
+ slug?: string;
347
+ outcomeExternalMarketId?: string;
348
+ universeSize?: number;
349
+ forecastProbability?: number;
350
+ marketProbability?: number;
351
+ reasonCode?: string;
337
352
  }
338
353
  export interface ResolveIssue {
339
354
  code: string;
@@ -27,6 +27,7 @@ export const PROVIDERS = [
27
27
  "nvidia",
28
28
  "gemini",
29
29
  "openai-compatible",
30
+ "mechanical",
30
31
  ];
31
32
  export const DEFAULT_TRIGGER_POLICY = {
32
33
  mode: "event_driven",
@@ -67,7 +68,7 @@ export function actionVenue(a) {
67
68
  return "spot";
68
69
  return "pm";
69
70
  }
70
- export function isWriteAction(a) {
71
+ export function isWriteAction(_a) {
71
72
  // Every proposed action mutates state (set-sltp/cancel included). The model
72
73
  // never proposes a read; reads are the runner's job during observe.
73
74
  return true;
@@ -1,6 +1,6 @@
1
1
  // Small dependency-free helpers shared across the runner.
2
2
  import { createHash } from "node:crypto";
3
- import { resolve as resolvePath, relative as relativePath, isAbsolute } from "node:path";
3
+ import { resolve as resolvePath, relative as relativePath, isAbsolute, } from "node:path";
4
4
  export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
5
  // Parse a cadence string ("30s", "15m", "1h", "4h", "1d") to milliseconds.
6
6
  // Returns null for anything unparseable so the skill validator can reject it.
@@ -14,7 +14,13 @@ export function parseCadenceMs(cadence) {
14
14
  if (!Number.isFinite(n) || n <= 0)
15
15
  return null;
16
16
  const unit = m[2].toLowerCase();
17
- const mult = unit === "s" ? 1000 : unit === "m" ? 60_000 : unit === "h" ? 3_600_000 : 86_400_000;
17
+ const mult = unit === "s"
18
+ ? 1000
19
+ : unit === "m"
20
+ ? 60_000
21
+ : unit === "h"
22
+ ? 3_600_000
23
+ : 86_400_000;
18
24
  return n * mult;
19
25
  }
20
26
  // A UTC day key (YYYY-MM-DD) for resetting per-day counters.
@@ -65,14 +71,15 @@ export function shortId() {
65
71
  // so a Windows checkout and a Linux checkout of the same file hash identically.
66
72
  export function normalizeContent(text) {
67
73
  return text
68
- .replace(/^/, "") // strip UTF-8 BOM (Windows editors add it)
74
+ .replace(/^\uFEFF/, "") // strip UTF-8 BOM (Windows editors add it)
69
75
  .replace(/\r\n/g, "\n")
70
76
  .replace(/\r/g, "\n")
71
77
  .replace(/\s+$/, "");
72
78
  }
73
79
  // SHA-256 of normalized content — the manifest lock's per-file contentHash.
74
80
  export function sha256(text) {
75
- return ("sha256:" + createHash("sha256").update(normalizeContent(text), "utf8").digest("hex"));
81
+ return ("sha256:" +
82
+ createHash("sha256").update(normalizeContent(text), "utf8").digest("hex"));
76
83
  }
77
84
  // Canonical POSIX path (forward slashes) so the lock is byte-identical on
78
85
  // Windows and Linux.
@@ -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.5.0";
8
+ readonly openapiVersion: "1.6.0";
9
9
  readonly mcpPackage: "@coinrithm/mcp-trading";
10
10
  readonly mcpVersion: string;
11
11
  };
@@ -20,7 +20,7 @@ export const COINRITHM_API = {
20
20
  mcpUrl: "https://mcp.coinrithm.com/mcp",
21
21
  // The API CONTRACT version (openapi.yaml info.version). Versioned independently
22
22
  // from the npm package below — hand-bump this when the OpenAPI contract changes.
23
- openapiVersion: "1.5.0",
23
+ openapiVersion: "1.6.0",
24
24
  mcpPackage: "@coinrithm/mcp-trading",
25
25
  mcpVersion: PACKAGE_VERSION,
26
26
  };
package/dist/client.d.ts CHANGED
@@ -24,6 +24,21 @@ export interface ApiResult {
24
24
  type TraceableBody<T extends Record<string, unknown>> = T & {
25
25
  agentTrace?: AgentTrace;
26
26
  };
27
+ export type ProvenanceReport = {
28
+ runtimeKind?: "hosted_scheduler" | "self_host_runner" | "byo_api" | "mcp_tool";
29
+ packageVersion?: string;
30
+ bundleId?: string;
31
+ bundleVersion?: string;
32
+ skillVersions?: Record<string, string>;
33
+ promptHash?: string;
34
+ configHash?: string;
35
+ modelProvider?: string;
36
+ modelName?: string;
37
+ evidenceRef?: {
38
+ snapshotIds?: string[];
39
+ sourceCapturedAt?: string;
40
+ };
41
+ };
27
42
  export declare class CoinRithmClient {
28
43
  private readonly defaultApiKey?;
29
44
  private readonly baseUrl;
@@ -176,6 +191,24 @@ export declare class CoinRithmClient {
176
191
  side?: "yes" | "no";
177
192
  stakeMusd: number;
178
193
  idempotencyKey: string;
194
+ forecastProbability?: number;
195
+ provenance?: ProvenanceReport;
196
+ }>, apiKey?: string): Promise<ApiResult>;
197
+ reportPmOpportunity(body: TraceableBody<{
198
+ kind: "abstained" | "forecast_only" | "quote_expired";
199
+ source?: string;
200
+ slug?: string;
201
+ outcomeExternalMarketId?: string;
202
+ forecastProbability?: number;
203
+ marketProbability?: number;
204
+ reasonCode?: string;
205
+ cohort?: {
206
+ universeSize?: number;
207
+ horizon?: string;
208
+ };
209
+ decisionId?: string;
210
+ runId?: string;
211
+ provenance?: ProvenanceReport;
179
212
  }>, apiKey?: string): Promise<ApiResult>;
180
213
  }
181
214
  export {};
package/dist/client.js CHANGED
@@ -20,7 +20,6 @@
20
20
  export const DEFAULT_BASE_URL = "https://api.coinrithm.com";
21
21
  export function log(...args) {
22
22
  // stderr only — stdout is reserved for the MCP protocol.
23
- // eslint-disable-next-line no-console
24
23
  console.error("[coinrithm-mcp]", ...args);
25
24
  }
26
25
  function resolveBaseUrl() {
@@ -69,7 +68,7 @@ const applyAgentTraceHeaders = (headers, trace) => {
69
68
  }
70
69
  };
71
70
  const traceFromBody = (body) => body && typeof body === "object" && "agentTrace" in body
72
- ? (body.agentTrace)
71
+ ? body.agentTrace
73
72
  : undefined;
74
73
  export class CoinRithmClient {
75
74
  // Default key for the stdio (single-user) path. Undefined in the multi-user
@@ -293,7 +292,9 @@ export class CoinRithmClient {
293
292
  return this.request("GET", "/api/arena", { query, apiKey });
294
293
  }
295
294
  getArenaAgent(handle, apiKey) {
296
- return this.request("GET", `/api/arena/${encodeURIComponent(handle)}`, { apiKey });
295
+ return this.request("GET", `/api/arena/${encodeURIComponent(handle)}`, {
296
+ apiKey,
297
+ });
297
298
  }
298
299
  listOpenOrders(query, apiKey, agentTrace) {
299
300
  return this.request("GET", "/api/agent/orders/open", {
@@ -347,4 +348,12 @@ export class CoinRithmClient {
347
348
  openPmPosition(body, apiKey) {
348
349
  return this.request("POST", "/api/agent/pm/open", { body, apiKey });
349
350
  }
351
+ // Report a NON-opened opportunity (scope: read — it is EVIDENCE, not a trade, so
352
+ // it never moves a wallet or a position). Captures the model ABSTAINING while
353
+ // markets were listed, forecasting WITHOUT trading (forecast_only), or a validated
354
+ // open whose quote EXPIRED at act time (quote_expired), so the public evaluation
355
+ // is not selection-biased toward opened trades. Idempotent by (key, decisionId).
356
+ reportPmOpportunity(body, apiKey) {
357
+ return this.request("POST", "/api/agent/pm/opportunity", { body, apiKey });
358
+ }
350
359
  }
@@ -0,0 +1,2 @@
1
+ export declare const PAPER_EXECUTION_VERSION = "paper_execution_v1";
2
+ export declare const EXECUTION_POLICY_SUMMARY: string;
@@ -0,0 +1,21 @@
1
+ // Mirror of the backend paper-execution policy. This package is published
2
+ // standalone and cannot import the backend, so the versioned policy name is
3
+ // mirrored here as the SINGLE place any package text that mentions execution
4
+ // cost refers to. That is what stops a served/tool description from drifting
5
+ // back into a "costless" claim while the backend charges real modeled costs.
6
+ //
7
+ // It deliberately does NOT restate the fee/spread/slippage bps — those live in
8
+ // the backend SSOT and are disclosed per fill in the response `executionModel`
9
+ // and in the OpenAPI cost-model description. Text points THERE instead of
10
+ // duplicating numbers that could silently diverge.
11
+ //
12
+ // Drift-tested in executionPolicy.test.ts: the summary names the versioned
13
+ // policy and never matches the costless regex.
14
+ export const PAPER_EXECUTION_VERSION = "paper_execution_v1";
15
+ export const EXECUTION_POLICY_SUMMARY = "Paper execution is not costless: every fill runs under the versioned " +
16
+ `${PAPER_EXECUTION_VERSION} policy — a disclosed taker fee (plus, on spot ` +
17
+ "market orders and PM entries, an adverse spread and size-based slippage; " +
18
+ "futures charge the taker fee and do not model funding) folded into realized " +
19
+ "PnL, so reported PnL is net of modeled costs. The exact per-fill amounts are " +
20
+ "disclosed in each quote/trade `executionModel`; see the OpenAPI cost-model " +
21
+ "description for the bps. A rehearsal cost model, not an exchange-fill guarantee.";
package/dist/http.js CHANGED
@@ -47,7 +47,11 @@ async function main() {
47
47
  app.use(express.json());
48
48
  // Lightweight, unauthenticated liveness probe (handy for Coolify/uptime checks).
49
49
  app.get("/healthz", (_req, res) => {
50
- res.json({ ok: true, service: "coinrithm-mcp", transport: "streamable-http" });
50
+ res.json({
51
+ ok: true,
52
+ service: "coinrithm-mcp",
53
+ transport: "streamable-http",
54
+ });
51
55
  });
52
56
  // Self-describing landing for humans and validate-before-recommend agents.
53
57
  // GET / and GET /mcp used to 404, which reads as a dead service to anyone
@@ -58,7 +62,11 @@ async function main() {
58
62
  service: "CoinRithm MCP",
59
63
  version: SERVER_VERSION,
60
64
  description: "Model Context Protocol server for CoinRithm: keyless public prediction-market data tools (pm_data_*) plus paper-trading tools (spot, futures, prediction markets) with a crk_live_ API key. Paper only — never real money.",
61
- endpoint: { url: "https://mcp.coinrithm.com/mcp", method: "POST", transport: "streamable-http" },
65
+ endpoint: {
66
+ url: "https://mcp.coinrithm.com/mcp",
67
+ method: "POST",
68
+ transport: "streamable-http",
69
+ },
62
70
  connect: "Point any MCP client at the endpoint above. Data tools need no key; trading tools take Authorization: Bearer crk_live_… (mint one at coinrithm.com → Settings → API Keys).",
63
71
  localAlternative: "npx -y @coinrithm/mcp-trading",
64
72
  docs: {
package/dist/tools.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { CoinRithmClient } from "./client.js";
3
+ export declare const PAPER_NOTE: string;
3
4
  export declare function registerTools(server: McpServer, client: CoinRithmClient): void;