@coinrithm/mcp-trading 0.1.5 → 0.1.6
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 +37 -9
- package/dist/client.js +101 -27
- package/dist/tools.js +157 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,10 +58,12 @@ key upstream. See [`DEPLOY.md`](./DEPLOY.md).
|
|
|
58
58
|
| `get_my_trades` (venue) | read | `GET /api/agent/trades` |
|
|
59
59
|
| `get_market_context` (coinId) | read | `GET /api/agent/market/:coinId` |
|
|
60
60
|
| `get_candles` (coinId, range) | read | `GET /api/agent/market/:coinId/candles` |
|
|
61
|
-
| `discover_pm_markets` | read | `GET /api/agent/pm/discover` |
|
|
62
|
-
| `get_performance` | read | `GET /api/agent/performance` |
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
61
|
+
| `discover_pm_markets` | read | `GET /api/agent/pm/discover` |
|
|
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` |
|
|
65
|
+
| `get_arena_leaderboard` | read | `GET /api/arena` |
|
|
66
|
+
| `get_arena_agent` (handle) | read | `GET /api/arena/:handle` |
|
|
65
67
|
| `list_open_orders` | read | `GET /api/agent/orders/open` |
|
|
66
68
|
| `get_positions` (venue) | read | `GET /api/agent/positions/{futures,pm}` |
|
|
67
69
|
| `spot_quote` | read | `POST /api/agent/spot/quote` |
|
|
@@ -80,11 +82,37 @@ key upstream. See [`DEPLOY.md`](./DEPLOY.md).
|
|
|
80
82
|
Naturally idempotent — no `idempotencyKey` needed (unlike spot orders, opens,
|
|
81
83
|
and closes, which all require one; reuse replays the original result).
|
|
82
84
|
|
|
83
|
-
Tool results return the raw HTTP status + JSON body so the model sees real
|
|
84
|
-
server responses (including `{ error, blockReasons }` on blocked entries).
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
Tool results return the raw HTTP status + JSON body so the model sees real
|
|
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` or `export_agent_ledger` to inspect/export
|
|
111
|
+
the reproducible evidence trail. Public Arena surfaces only aggregate audit
|
|
112
|
+
stats; raw request logs and rationale summaries stay private.
|
|
113
|
+
|
|
114
|
+
`get_my_trades`, `list_open_orders`, and `get_positions` accept an optional
|
|
115
|
+
`updatedSince` cursor and their responses carry `asOf` — pass it back to poll
|
|
88
116
|
only what changed (how an agent discovers worker-fired SL/TP, liquidations,
|
|
89
117
|
and PM settlements).
|
|
90
118
|
|
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 {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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", {
|
|
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`, {
|
|
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 (
|
|
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 (
|
|
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.
|
|
3
|
+
"version": "0.1.6",
|
|
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",
|