@coinrithm/mcp-trading 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,6 +60,8 @@ key upstream. See [`DEPLOY.md`](./DEPLOY.md).
60
60
  | `get_candles` (coinId, range) | read | `GET /api/agent/market/:coinId/candles` |
61
61
  | `discover_pm_markets` | read | `GET /api/agent/pm/discover` |
62
62
  | `get_performance` | read | `GET /api/agent/performance` |
63
+ | `get_agent_ledger` | read | `GET /api/agent/ledger` |
64
+ | `export_agent_ledger` | read | `GET /api/agent/ledger/export` |
63
65
  | `get_arena_leaderboard` | read | `GET /api/arena` |
64
66
  | `get_arena_agent` (handle) | read | `GET /api/arena/:handle` |
65
67
  | `list_open_orders` | read | `GET /api/agent/orders/open` |
@@ -82,6 +84,58 @@ and closes, which all require one; reuse replays the original result).
82
84
 
83
85
  Tool results return the raw HTTP status + JSON body so the model sees real
84
86
  server responses (including `{ error, blockReasons }` on blocked entries).
87
+ They also include `ledgerEventId` and `ledgerStatus` when CoinRithm records the
88
+ private action ledger row for the call.
89
+
90
+ ## Private ledger and trace metadata
91
+
92
+ Every `/api/agent/*` call is recorded privately for the calling key: reads,
93
+ quotes, writes, rejects, idempotent replays, latency, sanitized summaries, and
94
+ optional run/decision metadata. CoinRithm logs execution and performance for
95
+ paper trading; it does **not** run your agent or verify hidden reasoning.
96
+
97
+ All MCP read/quote/write tools accept optional `agentTrace`:
98
+
99
+ ```json
100
+ {
101
+ "runId": "run-2026-06-12",
102
+ "decisionId": "decision-7",
103
+ "strategyLabel": "momentum",
104
+ "confidence": 0.72,
105
+ "rationaleSummary": "Short private summary only; no chain-of-thought."
106
+ }
107
+ ```
108
+
109
+ Use the same `runId` across a session and a new `decisionId` per quote/write
110
+ intent. Then call `get_agent_ledger` to inspect rows or `export_agent_ledger`
111
+ with `runId` to export a private run-evidence bundle:
112
+
113
+ ```json
114
+ {
115
+ "runId": "run-2026-06-12",
116
+ "limit": 1000
117
+ }
118
+ ```
119
+
120
+ The export includes a manifest and summary: first/last event time, venues,
121
+ ledger statuses, quote/write/reject/replay counts, related paper-trade ids, and
122
+ the sanitized ledger rows. It also includes `executionAssumptions`: paper
123
+ account only, latest stored market/probability snapshots, no explicit
124
+ commission/slippage in v1, no futures funding/fees, and worker-driven resting
125
+ order / SL / TP / settlement timing. It is a reproducibility artifact for your
126
+ run; it is not a full point-in-time market archive and does not expose hidden
127
+ reasoning. Aggregate audit stats include trace coverage for `runId` and
128
+ `decisionId`. Run exports also include `retentionPolicy`: private ledger rows
129
+ use a rolling retention window and exports are capped. They include
130
+ `evidenceChecklist`, a derived pass/warn/fail checklist for trace completeness,
131
+ decision ids, quote-before-trade coverage, rejected calls, export truncation,
132
+ execution assumptions, and outcome attribution; it does not create additional
133
+ retained data. `outcomeSummary` derives best-effort realized PnL from existing
134
+ related trade/position ids, and spot orders can also match through their
135
+ idempotency keys once a terminal `ClosedOrder` exists. It reports whether
136
+ coverage is `none`, `partial`, or `complete`; it does not store new data. Public
137
+ Arena surfaces only aggregate audit stats; raw request logs and rationale
138
+ summaries stay private.
85
139
 
86
140
  `get_my_trades`, `list_open_orders`, and `get_positions` accept an optional
87
141
  `updatedSince` cursor and their responses carry `asOf` — pass it back to poll
package/dist/client.js CHANGED
@@ -54,6 +54,23 @@ export function bearerFromHeader(value) {
54
54
  const token = (m ? m[1] : raw).trim();
55
55
  return token || undefined;
56
56
  }
57
+ const applyAgentTraceHeaders = (headers, trace) => {
58
+ if (!trace)
59
+ return;
60
+ if (trace.runId)
61
+ headers["X-CoinRithm-Run-Id"] = trace.runId;
62
+ if (trace.decisionId)
63
+ headers["X-CoinRithm-Decision-Id"] = trace.decisionId;
64
+ if (trace.strategyLabel) {
65
+ headers["X-CoinRithm-Strategy-Label"] = trace.strategyLabel;
66
+ }
67
+ if (typeof trace.confidence === "number") {
68
+ headers["X-CoinRithm-Confidence"] = String(trace.confidence);
69
+ }
70
+ };
71
+ const traceFromBody = (body) => body && typeof body === "object" && "agentTrace" in body
72
+ ? (body.agentTrace)
73
+ : undefined;
57
74
  export class CoinRithmClient {
58
75
  // Default key for the stdio (single-user) path. Undefined in the multi-user
59
76
  // HTTP path, where every call must pass a per-request `apiKey` override.
@@ -88,6 +105,7 @@ export class CoinRithmClient {
88
105
  Authorization: `Bearer ${apiKey}`,
89
106
  Accept: "application/json",
90
107
  };
108
+ applyAgentTraceHeaders(headers, opts.agentTrace ?? traceFromBody(opts.body));
91
109
  if (opts.body !== undefined)
92
110
  headers["Content-Type"] = "application/json";
93
111
  let res;
@@ -131,43 +149,87 @@ export class CoinRithmClient {
131
149
  hint: "Rate limited. Wait retryAfterSeconds (or the Retry-After header) before retrying; pace future calls using the RateLimit-Remaining response header.",
132
150
  };
133
151
  }
134
- return { ok: res.ok, status: res.status, data };
152
+ return {
153
+ ok: res.ok,
154
+ status: res.status,
155
+ ledgerEventId: res.headers.get("x-coinrithm-ledger-event-id"),
156
+ ledgerStatus: res.headers.get("x-coinrithm-ledger-status"),
157
+ data,
158
+ };
135
159
  }
136
160
  // Every method takes an optional trailing `apiKey` (the per-request key for
137
161
  // the multi-user HTTP path). When omitted, the constructor key (stdio) is used.
138
162
  // ---- reads (scope: read) ----
139
- whoami(apiKey) {
140
- return this.request("GET", "/api/agent/me", { apiKey });
163
+ whoami(apiKey, agentTrace) {
164
+ return this.request("GET", "/api/agent/me", { apiKey, agentTrace });
141
165
  }
142
- getPortfolio(query, apiKey) {
143
- return this.request("GET", "/api/agent/portfolio", { query, apiKey });
166
+ getPortfolio(query, apiKey, agentTrace) {
167
+ return this.request("GET", "/api/agent/portfolio", {
168
+ query,
169
+ apiKey,
170
+ agentTrace,
171
+ });
144
172
  }
145
- getWallet(query, apiKey) {
146
- return this.request("GET", "/api/agent/wallet", { query, apiKey });
173
+ getWallet(query, apiKey, agentTrace) {
174
+ return this.request("GET", "/api/agent/wallet", {
175
+ query,
176
+ apiKey,
177
+ agentTrace,
178
+ });
147
179
  }
148
- resolveSymbol(query, apiKey) {
149
- return this.request("GET", "/api/agent/resolve", { query, apiKey });
180
+ resolveSymbol(query, apiKey, agentTrace) {
181
+ return this.request("GET", "/api/agent/resolve", {
182
+ query,
183
+ apiKey,
184
+ agentTrace,
185
+ });
150
186
  }
151
- getEquityCurve(query, apiKey) {
152
- return this.request("GET", "/api/agent/equity-curve", { query, apiKey });
187
+ getEquityCurve(query, apiKey, agentTrace) {
188
+ return this.request("GET", "/api/agent/equity-curve", {
189
+ query,
190
+ apiKey,
191
+ agentTrace,
192
+ });
153
193
  }
154
- getMyTrades(query, apiKey) {
155
- return this.request("GET", "/api/agent/trades", { query, apiKey });
194
+ getMyTrades(query, apiKey, agentTrace) {
195
+ return this.request("GET", "/api/agent/trades", {
196
+ query,
197
+ apiKey,
198
+ agentTrace,
199
+ });
156
200
  }
157
- getMarketContext(coinId, apiKey) {
158
- return this.request("GET", `/api/agent/market/${encodeURIComponent(coinId)}`, { apiKey });
201
+ getMarketContext(coinId, apiKey, agentTrace) {
202
+ return this.request("GET", `/api/agent/market/${encodeURIComponent(coinId)}`, { apiKey, agentTrace });
159
203
  }
160
- getCandles(coinId, query, apiKey) {
161
- return this.request("GET", `/api/agent/market/${encodeURIComponent(coinId)}/candles`, { query, apiKey });
204
+ getCandles(coinId, query, apiKey, agentTrace) {
205
+ return this.request("GET", `/api/agent/market/${encodeURIComponent(coinId)}/candles`, { query, apiKey, agentTrace });
162
206
  }
163
- discoverPmMarkets(query, apiKey) {
207
+ discoverPmMarkets(query, apiKey, agentTrace) {
164
208
  return this.request("GET", "/api/agent/pm/discover", {
165
209
  query,
166
210
  apiKey,
211
+ agentTrace,
167
212
  });
168
213
  }
169
- getPerformance(apiKey) {
170
- return this.request("GET", "/api/agent/performance", { apiKey });
214
+ getPerformance(apiKey, agentTrace) {
215
+ return this.request("GET", "/api/agent/performance", {
216
+ apiKey,
217
+ agentTrace,
218
+ });
219
+ }
220
+ getLedger(query, apiKey, agentTrace) {
221
+ return this.request("GET", "/api/agent/ledger", {
222
+ query,
223
+ apiKey,
224
+ agentTrace,
225
+ });
226
+ }
227
+ exportLedger(query, apiKey, agentTrace) {
228
+ return this.request("GET", "/api/agent/ledger/export", {
229
+ query,
230
+ apiKey,
231
+ agentTrace,
232
+ });
171
233
  }
172
234
  // Agent Arena (public leaderboard). The key is sent but ignored by these
173
235
  // endpoints — they expose only public agent names + realized performance.
@@ -177,17 +239,26 @@ export class CoinRithmClient {
177
239
  getArenaAgent(handle, apiKey) {
178
240
  return this.request("GET", `/api/arena/${encodeURIComponent(handle)}`, { apiKey });
179
241
  }
180
- listOpenOrders(query, apiKey) {
181
- return this.request("GET", "/api/agent/orders/open", { query, apiKey });
242
+ listOpenOrders(query, apiKey, agentTrace) {
243
+ return this.request("GET", "/api/agent/orders/open", {
244
+ query,
245
+ apiKey,
246
+ agentTrace,
247
+ });
182
248
  }
183
- getFuturesPositions(query, apiKey) {
249
+ getFuturesPositions(query, apiKey, agentTrace) {
184
250
  return this.request("GET", "/api/agent/positions/futures", {
185
251
  query,
186
252
  apiKey,
253
+ agentTrace,
187
254
  });
188
255
  }
189
- getPmPositions(query, apiKey) {
190
- return this.request("GET", "/api/agent/positions/pm", { query, apiKey });
256
+ getPmPositions(query, apiKey, agentTrace) {
257
+ return this.request("GET", "/api/agent/positions/pm", {
258
+ query,
259
+ apiKey,
260
+ agentTrace,
261
+ });
191
262
  }
192
263
  futuresQuote(body, apiKey) {
193
264
  return this.request("POST", "/api/agent/futures/quote", { body, apiKey });
@@ -202,8 +273,11 @@ export class CoinRithmClient {
202
273
  placeSpotOrder(body, apiKey) {
203
274
  return this.request("POST", "/api/agent/spot/order", { body, apiKey });
204
275
  }
205
- cancelSpotOrder(orderId, apiKey) {
206
- return this.request("POST", `/api/agent/spot/order/${orderId}/cancel`, { apiKey });
276
+ cancelSpotOrder(orderId, apiKey, agentTrace) {
277
+ return this.request("POST", `/api/agent/spot/order/${orderId}/cancel`, {
278
+ apiKey,
279
+ agentTrace,
280
+ });
207
281
  }
208
282
  openFuturesPosition(body, apiKey) {
209
283
  return this.request("POST", "/api/agent/futures/open", { body, apiKey });
package/dist/tools.js CHANGED
@@ -15,10 +15,49 @@ const API_RESULT_OUTPUT_SCHEMA = {
15
15
  ok: z
16
16
  .boolean()
17
17
  .describe("True when CoinRithm returned a successful 2xx response."),
18
+ ledgerEventId: z
19
+ .string()
20
+ .nullable()
21
+ .optional()
22
+ .describe("Private AgentActionEvent id returned by /api/agent/*, when present."),
23
+ ledgerStatus: z
24
+ .string()
25
+ .nullable()
26
+ .optional()
27
+ .describe("Ledger write status header returned by CoinRithm, when present."),
18
28
  body: z
19
29
  .unknown()
20
30
  .describe("Parsed CoinRithm response body, or raw text when the response is not JSON."),
21
31
  };
32
+ const AGENT_TRACE_SCHEMA = z
33
+ .object({
34
+ runId: z.string().min(1).optional().describe("Agent run id for grouping."),
35
+ decisionId: z
36
+ .string()
37
+ .min(1)
38
+ .optional()
39
+ .describe("Agent decision id for quote/write attribution."),
40
+ strategyLabel: z
41
+ .string()
42
+ .min(1)
43
+ .max(120)
44
+ .optional()
45
+ .describe("Short strategy label, self-reported by the caller."),
46
+ confidence: z
47
+ .number()
48
+ .min(0)
49
+ .max(1)
50
+ .optional()
51
+ .describe("Optional confidence score from 0 to 1."),
52
+ rationaleSummary: z
53
+ .string()
54
+ .min(1)
55
+ .max(1200)
56
+ .optional()
57
+ .describe("Optional concise rationale summary. Do not include chain-of-thought, secrets, or account identity."),
58
+ })
59
+ .optional()
60
+ .describe("Optional private trace metadata stored in the caller's ledger.");
22
61
  function readOnlyAnnotations(title) {
23
62
  return {
24
63
  title,
@@ -63,6 +102,8 @@ function present(result) {
63
102
  const payload = {
64
103
  httpStatus: result.status,
65
104
  ok: result.ok,
105
+ ledgerEventId: result.ledgerEventId ?? null,
106
+ ledgerStatus: result.ledgerStatus ?? null,
66
107
  body: result.data,
67
108
  };
68
109
  return {
@@ -83,10 +124,12 @@ export function registerTools(server, client) {
83
124
  "model/runtime label shown on the public Agent Arena when opted in). " +
84
125
  "Use this first to confirm what the key is allowed to do. " +
85
126
  PAPER_NOTE,
86
- inputSchema: {},
127
+ inputSchema: {
128
+ agentTrace: AGENT_TRACE_SCHEMA,
129
+ },
87
130
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
88
131
  annotations: readOnlyAnnotations("Who am I (CoinRithm)"),
89
- }, async (_args, extra) => present(await client.whoami(requestKey(extra))));
132
+ }, async ({ agentTrace }, extra) => present(await client.whoami(requestKey(extra), agentTrace)));
90
133
  // ---------------- reads ----------------
91
134
  server.registerTool("get_portfolio", {
92
135
  title: "Get portfolio",
@@ -101,10 +144,11 @@ export function registerTools(server, client) {
101
144
  .optional()
102
145
  .describe("Display fiat code (default USD). Equity stays USD-denominated."),
103
146
  locale: z.string().optional().describe("Locale (default en)."),
147
+ agentTrace: AGENT_TRACE_SCHEMA,
104
148
  },
105
149
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
106
150
  annotations: readOnlyAnnotations("Get portfolio"),
107
- }, async ({ fiat, locale }, extra) => present(await client.getPortfolio({ fiat, locale }, requestKey(extra))));
151
+ }, async ({ fiat, locale, agentTrace }, extra) => present(await client.getPortfolio({ fiat, locale }, requestKey(extra), agentTrace)));
108
152
  server.registerTool("get_wallet", {
109
153
  title: "Get wallet",
110
154
  description: "Get raw cash balances: USDT available plus the three frozen partitions " +
@@ -116,10 +160,11 @@ export function registerTools(server, client) {
116
160
  .string()
117
161
  .optional()
118
162
  .describe('Coin UCID (e.g. "1" = BTC) to also return that asset.'),
163
+ agentTrace: AGENT_TRACE_SCHEMA,
119
164
  },
120
165
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
121
166
  annotations: readOnlyAnnotations("Get wallet"),
122
- }, async ({ coinId }, extra) => present(await client.getWallet({ coinId }, requestKey(extra))));
167
+ }, async ({ coinId, agentTrace }, extra) => present(await client.getWallet({ coinId }, requestKey(extra), agentTrace)));
123
168
  server.registerTool("list_open_orders", {
124
169
  title: "List open spot orders",
125
170
  description: "List open (resting) spot orders. Omit coinId for ALL open orders " +
@@ -145,10 +190,11 @@ export function registerTools(server, client) {
145
190
  .optional()
146
191
  .describe("ISO 8601 cursor: only orders whose row changed since this " +
147
192
  "instant. Pass the previous response's asOf back here."),
193
+ agentTrace: AGENT_TRACE_SCHEMA,
148
194
  },
149
195
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
150
196
  annotations: readOnlyAnnotations("List open spot orders"),
151
- }, async ({ coinId, limit, updatedSince }, extra) => present(await client.listOpenOrders({ coinId, limit, updatedSince }, requestKey(extra))));
197
+ }, async ({ coinId, limit, updatedSince, agentTrace }, extra) => present(await client.listOpenOrders({ coinId, limit, updatedSince }, requestKey(extra), agentTrace)));
152
198
  server.registerTool("get_positions", {
153
199
  title: "Get positions",
154
200
  description: "List open + historical positions for a venue. venue='futures' returns " +
@@ -167,12 +213,13 @@ export function registerTools(server, client) {
167
213
  .optional()
168
214
  .describe("ISO 8601 cursor: only positions whose row changed since this " +
169
215
  "instant. Pass the previous response's asOf back here."),
216
+ agentTrace: AGENT_TRACE_SCHEMA,
170
217
  },
171
218
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
172
219
  annotations: readOnlyAnnotations("Get positions"),
173
- }, async ({ venue, updatedSince }, extra) => present(venue === "futures"
174
- ? await client.getFuturesPositions({ updatedSince }, requestKey(extra))
175
- : await client.getPmPositions({ updatedSince }, requestKey(extra))));
220
+ }, async ({ venue, updatedSince, agentTrace }, extra) => present(venue === "futures"
221
+ ? await client.getFuturesPositions({ updatedSince }, requestKey(extra), agentTrace)
222
+ : await client.getPmPositions({ updatedSince }, requestKey(extra), agentTrace)));
176
223
  server.registerTool("resolve_symbol", {
177
224
  title: "Resolve symbol -> coinId",
178
225
  description: "Resolve a human symbol / slug / name (e.g. 'BTC', 'ethereum') to a " +
@@ -186,10 +233,11 @@ export function registerTools(server, client) {
186
233
  .string()
187
234
  .min(1)
188
235
  .describe("Symbol, slug, or name (e.g. BTC, bitcoin, Ethereum)."),
236
+ agentTrace: AGENT_TRACE_SCHEMA,
189
237
  },
190
238
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
191
239
  annotations: readOnlyAnnotations("Resolve symbol to coinId"),
192
- }, async ({ q }, extra) => present(await client.resolveSymbol({ q }, requestKey(extra))));
240
+ }, async ({ q, agentTrace }, extra) => present(await client.resolveSymbol({ q }, requestKey(extra), agentTrace)));
193
241
  server.registerTool("get_equity_curve", {
194
242
  title: "Get equity curve",
195
243
  description: "Wallet equity time series for the paper account — the basis for " +
@@ -213,10 +261,11 @@ export function registerTools(server, client) {
213
261
  .optional()
214
262
  .describe("daily (default) = one point per day; realized = intraday point " +
215
263
  "per realized-PnL event with cumulative total."),
264
+ agentTrace: AGENT_TRACE_SCHEMA,
216
265
  },
217
266
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
218
267
  annotations: readOnlyAnnotations("Get equity curve"),
219
- }, async ({ days, granularity }, extra) => present(await client.getEquityCurve({ days, granularity }, requestKey(extra))));
268
+ }, async ({ days, granularity, agentTrace }, extra) => present(await client.getEquityCurve({ days, granularity }, requestKey(extra), agentTrace)));
220
269
  server.registerTool("get_my_trades", {
221
270
  title: "Get my trades",
222
271
  description: "Unified realized-PnL log of CLOSED trades across venues (spot fills, " +
@@ -244,10 +293,11 @@ export function registerTools(server, client) {
244
293
  .optional()
245
294
  .describe("ISO 8601 cursor: only trades closed/settled since this instant. " +
246
295
  "Pass the previous response's asOf back here."),
296
+ agentTrace: AGENT_TRACE_SCHEMA,
247
297
  },
248
298
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
249
299
  annotations: readOnlyAnnotations("Get my trades"),
250
- }, async ({ venue, limit, updatedSince }, extra) => present(await client.getMyTrades({ venue, limit, updatedSince }, requestKey(extra))));
300
+ }, async ({ venue, limit, updatedSince, agentTrace }, extra) => present(await client.getMyTrades({ venue, limit, updatedSince }, requestKey(extra), agentTrace)));
251
301
  server.registerTool("get_market_context", {
252
302
  title: "Get market context",
253
303
  description: "Compact factual context for ONE coin to form a thesis: price + " +
@@ -265,10 +315,11 @@ export function registerTools(server, client) {
265
315
  .string()
266
316
  .min(1)
267
317
  .describe('Coin UCID (e.g. "1" = BTC). Use resolve_symbol to find it.'),
318
+ agentTrace: AGENT_TRACE_SCHEMA,
268
319
  },
269
320
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
270
321
  annotations: readOnlyAnnotations("Get market context"),
271
- }, async ({ coinId }, extra) => present(await client.getMarketContext(coinId, requestKey(extra))));
322
+ }, async ({ coinId, agentTrace }, extra) => present(await client.getMarketContext(coinId, requestKey(extra), agentTrace)));
272
323
  server.registerTool("get_candles", {
273
324
  title: "Get OHLCV candles",
274
325
  description: "OHLCV candles for indicator/momentum strategies (RSI, moving " +
@@ -291,10 +342,11 @@ export function registerTools(server, client) {
291
342
  .string()
292
343
  .optional()
293
344
  .describe("Quote currency for o/h/l/c (default USD)."),
345
+ agentTrace: AGENT_TRACE_SCHEMA,
294
346
  },
295
347
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
296
348
  annotations: readOnlyAnnotations("Get OHLCV candles"),
297
- }, async ({ coinId, range, fiat }, extra) => present(await client.getCandles(coinId, { range, fiat }, requestKey(extra))));
349
+ }, async ({ coinId, range, fiat, agentTrace }, extra) => present(await client.getCandles(coinId, { range, fiat }, requestKey(extra), agentTrace)));
298
350
  server.registerTool("discover_pm_markets", {
299
351
  title: "Discover prediction markets",
300
352
  description: "Find active-open, quote-ready-first prediction markets on the mock-PM " +
@@ -337,10 +389,11 @@ export function registerTools(server, client) {
337
389
  ])
338
390
  .optional()
339
391
  .describe("Prediction-market sort (default best)."),
392
+ agentTrace: AGENT_TRACE_SCHEMA,
340
393
  },
341
394
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
342
395
  annotations: readOnlyAnnotations("Discover prediction markets"),
343
- }, async ({ q, source, limit, offset, sort }, extra) => present(await client.discoverPmMarkets({ q, source, limit, offset, sort }, requestKey(extra))));
396
+ }, async ({ q, source, limit, offset, sort, agentTrace }, extra) => present(await client.discoverPmMarkets({ q, source, limit, offset, sort }, requestKey(extra), agentTrace)));
344
397
  server.registerTool("get_performance", {
345
398
  title: "Get my performance",
346
399
  description: "The calling key's own realized performance: total + per-venue realized " +
@@ -348,10 +401,76 @@ export function registerTools(server, client) {
348
401
  "until there are decided trades). Closed trades only — the scorecard for " +
349
402
  "this agent. " +
350
403
  PAPER_NOTE,
351
- inputSchema: {},
404
+ inputSchema: {
405
+ agentTrace: AGENT_TRACE_SCHEMA,
406
+ },
352
407
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
353
408
  annotations: readOnlyAnnotations("Get my performance"),
354
- }, async (_args, extra) => present(await client.getPerformance(requestKey(extra))));
409
+ }, async ({ agentTrace }, extra) => present(await client.getPerformance(requestKey(extra), agentTrace)));
410
+ server.registerTool("get_agent_ledger", {
411
+ title: "Get private agent ledger",
412
+ description: "List this API key's private execution ledger: reads, quotes, writes, " +
413
+ "rejects, idempotent replays, latency, sanitized summaries, and optional " +
414
+ "run/decision trace metadata. Only rows for the calling key are returned. " +
415
+ "Use this to audit a reproducible paper-trading run. " +
416
+ PAPER_NOTE,
417
+ inputSchema: {
418
+ venue: z.string().optional().describe("Optional venue filter."),
419
+ eventType: z.string().optional().describe("Optional event type filter."),
420
+ runId: z.string().optional().describe("Optional run id filter."),
421
+ decisionId: z
422
+ .string()
423
+ .optional()
424
+ .describe("Optional decision id filter."),
425
+ status: z
426
+ .string()
427
+ .optional()
428
+ .describe("Optional ledgerStatus filter."),
429
+ from: z.string().optional().describe("Optional ISO start timestamp."),
430
+ to: z.string().optional().describe("Optional ISO end timestamp."),
431
+ limit: z
432
+ .number()
433
+ .int()
434
+ .min(1)
435
+ .max(100)
436
+ .optional()
437
+ .describe("Rows to return (1-100, default 25)."),
438
+ offset: z
439
+ .number()
440
+ .int()
441
+ .min(0)
442
+ .optional()
443
+ .describe("Pagination offset (default 0)."),
444
+ agentTrace: AGENT_TRACE_SCHEMA,
445
+ },
446
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
447
+ annotations: readOnlyAnnotations("Get private agent ledger"),
448
+ }, async ({ venue, eventType, runId, decisionId, status, from, to, limit, offset, agentTrace, }, extra) => present(await client.getLedger({ venue, eventType, runId, decisionId, status, from, to, limit, offset }, requestKey(extra), agentTrace)));
449
+ server.registerTool("export_agent_ledger", {
450
+ title: "Export private agent ledger",
451
+ description: "Export up to 1,000 private ledger rows for the calling API key as JSON. " +
452
+ "Use filters to export a specific runId or decisionId for reproducible " +
453
+ "evaluation. No public Arena user can see this data. " +
454
+ PAPER_NOTE,
455
+ inputSchema: {
456
+ venue: z.string().optional().describe("Optional venue filter."),
457
+ eventType: z.string().optional().describe("Optional event type filter."),
458
+ runId: z.string().optional().describe("Optional run id filter."),
459
+ decisionId: z
460
+ .string()
461
+ .optional()
462
+ .describe("Optional decision id filter."),
463
+ status: z
464
+ .string()
465
+ .optional()
466
+ .describe("Optional ledgerStatus filter."),
467
+ from: z.string().optional().describe("Optional ISO start timestamp."),
468
+ to: z.string().optional().describe("Optional ISO end timestamp."),
469
+ agentTrace: AGENT_TRACE_SCHEMA,
470
+ },
471
+ outputSchema: API_RESULT_OUTPUT_SCHEMA,
472
+ annotations: readOnlyAnnotations("Export private agent ledger"),
473
+ }, async ({ venue, eventType, runId, decisionId, status, from, to, agentTrace }, extra) => present(await client.exportLedger({ venue, eventType, runId, decisionId, status, from, to }, requestKey(extra), agentTrace)));
355
474
  server.registerTool("get_arena_leaderboard", {
356
475
  title: "Get Agent Arena leaderboard",
357
476
  description: "The public Agent Arena: opted-in agents ranked by total realized PnL " +
@@ -425,10 +544,11 @@ export function registerTools(server, client) {
425
544
  .number()
426
545
  .min(10)
427
546
  .describe("Isolated margin in mUSD (>= 10)."),
547
+ agentTrace: AGENT_TRACE_SCHEMA,
428
548
  },
429
549
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
430
550
  annotations: readOnlyAnnotations("Futures quote"),
431
- }, async ({ coinId, side, leverage, marginMusd }, extra) => present(await client.futuresQuote({ coinId, side, leverage, marginMusd }, requestKey(extra))));
551
+ }, async ({ coinId, side, leverage, marginMusd, agentTrace }, extra) => present(await client.futuresQuote({ coinId, side, leverage, marginMusd, agentTrace }, requestKey(extra))));
432
552
  server.registerTool("pm_quote", {
433
553
  title: "Prediction-market quote",
434
554
  description: "Read-only PM quote for a binary outcome: entry probability, share " +
@@ -444,10 +564,11 @@ export function registerTools(server, client) {
444
564
  .string()
445
565
  .describe("Case-sensitive outcome / market id."),
446
566
  stakeMusd: z.number().positive().describe("mUSD to stake (> 0)."),
567
+ agentTrace: AGENT_TRACE_SCHEMA,
447
568
  },
448
569
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
449
570
  annotations: readOnlyAnnotations("Prediction-market quote"),
450
- }, async ({ source, slug, outcomeExternalMarketId, stakeMusd }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, stakeMusd }, requestKey(extra))));
571
+ }, async ({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, requestKey(extra))));
451
572
  server.registerTool("spot_quote", {
452
573
  title: "Spot quote",
453
574
  description: "Read-only spot MARKET quote: live execution price, estimated cost " +
@@ -466,10 +587,11 @@ export function registerTools(server, client) {
466
587
  .number()
467
588
  .positive()
468
589
  .describe("Amount of the base coin (> 0)."),
590
+ agentTrace: AGENT_TRACE_SCHEMA,
469
591
  },
470
592
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
471
593
  annotations: readOnlyAnnotations("Spot quote"),
472
- }, async ({ coinId, side, quantity }, extra) => present(await client.spotQuote({ coinId, side, quantity }, requestKey(extra))));
594
+ }, async ({ coinId, side, quantity, agentTrace }, extra) => present(await client.spotQuote({ coinId, side, quantity, agentTrace }, requestKey(extra))));
473
595
  // ---------------- writes ----------------
474
596
  server.registerTool("place_spot_order", {
475
597
  title: "Place spot order",
@@ -503,10 +625,11 @@ export function registerTools(server, client) {
503
625
  .string()
504
626
  .min(1)
505
627
  .describe("Unique per intent; reuse replays the original result."),
628
+ agentTrace: AGENT_TRACE_SCHEMA,
506
629
  },
507
630
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
508
631
  annotations: mutatingAnnotations("Place spot order"),
509
- }, async ({ coinId, side, orderType, quantity, limitPrice, stopPrice, idempotencyKey }, extra) => present(await client.placeSpotOrder({
632
+ }, async ({ coinId, side, orderType, quantity, limitPrice, stopPrice, idempotencyKey, agentTrace, }, extra) => present(await client.placeSpotOrder({
510
633
  coinId,
511
634
  side,
512
635
  orderType,
@@ -514,6 +637,7 @@ export function registerTools(server, client) {
514
637
  limitPrice,
515
638
  stopPrice,
516
639
  idempotencyKey,
640
+ agentTrace,
517
641
  }, requestKey(extra))));
518
642
  server.registerTool("cancel_spot_order", {
519
643
  title: "Cancel spot order",
@@ -522,12 +646,13 @@ export function registerTools(server, client) {
522
646
  PAPER_NOTE,
523
647
  inputSchema: {
524
648
  orderId: z.number().int().positive().describe("Open order id."),
649
+ agentTrace: AGENT_TRACE_SCHEMA,
525
650
  },
526
651
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
527
652
  annotations: mutatingAnnotations("Cancel spot order", {
528
653
  destructive: true,
529
654
  }),
530
- }, async ({ orderId }, extra) => present(await client.cancelSpotOrder(orderId, requestKey(extra))));
655
+ }, async ({ orderId, agentTrace }, extra) => present(await client.cancelSpotOrder(orderId, requestKey(extra), agentTrace)));
531
656
  server.registerTool("open_futures_position", {
532
657
  title: "Open futures position",
533
658
  description: "Open (or add to) a mock futures position. Requires the trade:futures " +
@@ -570,12 +695,13 @@ export function registerTools(server, client) {
570
695
  .optional()
571
696
  .describe("Optional resting take-profit set atomically at open (USD " +
572
697
  "trigger; fired by the per-minute worker)."),
698
+ agentTrace: AGENT_TRACE_SCHEMA,
573
699
  },
574
700
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
575
701
  annotations: mutatingAnnotations("Open futures position", {
576
702
  idempotent: true,
577
703
  }),
578
- }, async ({ coinId, side, leverage, marginMusd, idempotencyKey, stopLossPrice, takeProfitPrice, }, extra) => present(await client.openFuturesPosition({
704
+ }, async ({ coinId, side, leverage, marginMusd, idempotencyKey, stopLossPrice, takeProfitPrice, agentTrace, }, extra) => present(await client.openFuturesPosition({
579
705
  coinId,
580
706
  side,
581
707
  leverage,
@@ -583,6 +709,7 @@ export function registerTools(server, client) {
583
709
  idempotencyKey,
584
710
  ...(stopLossPrice !== undefined ? { stopLossPrice } : {}),
585
711
  ...(takeProfitPrice !== undefined ? { takeProfitPrice } : {}),
712
+ agentTrace,
586
713
  }, requestKey(extra))));
587
714
  server.registerTool("set_futures_sl_tp", {
588
715
  title: "Set futures stop-loss / take-profit",
@@ -612,15 +739,17 @@ export function registerTools(server, client) {
612
739
  .nullable()
613
740
  .optional()
614
741
  .describe("Positive number sets; null clears; omit = unchanged."),
742
+ agentTrace: AGENT_TRACE_SCHEMA,
615
743
  },
616
744
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
617
745
  annotations: mutatingAnnotations("Set futures SL/TP", {
618
746
  idempotent: true,
619
747
  }),
620
- }, async ({ positionId, stopLossPrice, takeProfitPrice }, extra) => present(await client.setFuturesSlTp({
748
+ }, async ({ positionId, stopLossPrice, takeProfitPrice, agentTrace }, extra) => present(await client.setFuturesSlTp({
621
749
  positionId,
622
750
  ...(stopLossPrice !== undefined ? { stopLossPrice } : {}),
623
751
  ...(takeProfitPrice !== undefined ? { takeProfitPrice } : {}),
752
+ agentTrace,
624
753
  }, requestKey(extra))));
625
754
  server.registerTool("close_futures_position", {
626
755
  title: "Close futures position",
@@ -644,13 +773,14 @@ export function registerTools(server, client) {
644
773
  .string()
645
774
  .min(1)
646
775
  .describe("Unique per close intent; reuse replays the original result."),
776
+ agentTrace: AGENT_TRACE_SCHEMA,
647
777
  },
648
778
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
649
779
  annotations: mutatingAnnotations("Close futures position", {
650
780
  destructive: true,
651
781
  idempotent: true,
652
782
  }),
653
- }, async ({ positionId, fraction, idempotencyKey }, extra) => present(await client.closeFuturesPosition({ positionId, fraction, idempotencyKey }, requestKey(extra))));
783
+ }, async ({ positionId, fraction, idempotencyKey, agentTrace }, extra) => present(await client.closeFuturesPosition({ positionId, fraction, idempotencyKey, agentTrace }, requestKey(extra))));
654
784
  server.registerTool("open_pm_position", {
655
785
  title: "Open prediction-market position",
656
786
  description: "Open a mock prediction-market position (binary outcomes only). Requires " +
@@ -671,16 +801,18 @@ export function registerTools(server, client) {
671
801
  .string()
672
802
  .min(1)
673
803
  .describe("Unique per PM-open intent; reuse replays the original result."),
804
+ agentTrace: AGENT_TRACE_SCHEMA,
674
805
  },
675
806
  outputSchema: API_RESULT_OUTPUT_SCHEMA,
676
807
  annotations: mutatingAnnotations("Open prediction-market position", {
677
808
  idempotent: true,
678
809
  }),
679
- }, async ({ source, slug, outcomeExternalMarketId, stakeMusd, idempotencyKey }, extra) => present(await client.openPmPosition({
810
+ }, async ({ source, slug, outcomeExternalMarketId, stakeMusd, idempotencyKey, agentTrace, }, extra) => present(await client.openPmPosition({
680
811
  source,
681
812
  slug,
682
813
  outcomeExternalMarketId,
683
814
  stakeMusd,
684
815
  idempotencyKey,
816
+ agentTrace,
685
817
  }, requestKey(extra))));
686
818
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinrithm/mcp-trading",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "mcpName": "io.github.CoinRithm/mcp-trading",
5
5
  "description": "MCP server for paper-trading on CoinRithm (spot, futures, prediction markets) with a user-minted API key.",
6
6
  "type": "module",