@battlegrid/mcp-server 31.0.0 → 31.0.2

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
@@ -24,18 +24,28 @@ Seeing package `31.0.0` alongside handshake `battlegrid@30.0.0` is the system wo
24
24
 
25
25
  **What this changes for you:** nothing about how you call anything. Upgrading the package no longer waits on a server deploy, and a server deploy no longer strands you on a package that names the wrong contract — reconnect and the announcement follows. **Contract breaking-change notes are no longer keyed to package versions**, since a contract move is no longer a release here; the v11-and-earlier notes below are kept as history, and the live vocabulary is always discovery.
26
26
 
27
- ## Contract history — v12 → v30
27
+ ## Contract history — v12 → v33
28
28
 
29
- These are the server contract breaks between contract 12 and contract 30 — the span that shipped while the package sat at `11.0.0`. They are **contract** history, not package releases: from v31 the announced contract is relayed live and a contract move is no longer a release here. Grouped by what a client observes, with the contract version that introduced each.
29
+ These are the server contract breaks between contract 12 and contract 33. Most of the span shipped while the package sat at `11.0.0`; contract 31 landed after this package reached `31.0.0`, and the two numbers matching is coincidence — since v31 the announced contract is relayed from the server, so a package version says nothing about a contract version. They are **contract** history, not package releases: from v31 the announced contract is relayed live and a contract move is no longer a release here. Grouped by what a client observes, with the contract version that introduced each.
30
30
 
31
31
  **The proxy itself is unchanged.** It embeds no schemas, pins no contract version, and forwards `{ request }` verbatim. Every break below lands on whatever *authors* the payload or *reads* the result, never on the proxy.
32
32
 
33
33
  ### Changed meaning, unchanged shape — the one to read first
34
34
 
35
+ - **`compile_strategy_plan` is no longer read-only or idempotent** (33.0.0, `rehydrate-approved-plan-on-apply`). Its input, its output and its behaviour toward your strategy are unchanged — it still changes no strategy, agent or revision — but it now parks the plan it approved for its own apply to read, and **each call mints a distinct record and a distinct token**. `readOnlyHint` and `idempotentHint` are published as `false` accordingly. A client that retried a compile that had already succeeded, or fanned two out in parallel for one edit, was doing so on the strength of the old annotation: **do neither.** Compile once per reviewed payload. Nothing in the payload tells you this moved.
36
+
35
37
  - **Position-size presets are now a RISK BUDGET** (30.0.0, `split-stop-geometry-from-risk`). `smallPct` / `mediumPct` / `largePct` stop denoting a share of the ORDER (`notional = pct / 100 × headroom × leverage`) and start denoting the share of headroom placed **at risk** (`notional = headroom × riskPct / stopDistancePct`, capped at the margin headroom can post). Same keys, same types, same accepted range: **nothing in the payload tells you the meaning moved.** A client still sending `22.0` for MEDIUM is asking to risk 22% of its budget on one trade rather than roughly 2%. Typical risk budgets are `0.5`–`3`; the platform defaults moved to `1 / 2 / 3`. Leverage stops multiplying order size and becomes a constraint only.
36
38
 
37
39
  ### Rejected input — something you author is no longer accepted
38
40
 
41
+ - **`apply_strategy_plan` no longer accepts the plan** (33.0.0, `rehydrate-approved-plan-on-apply`). Its input narrows to `{ planToken, confirm }`. A `plan` member is **rejected as an unknown key** — not accepted, not ignored, and with no transitional dual shape — so every client that built the payload breaks on the next connection, which before this is what every published surface told it to do. The server keeps the plan its own compile approved and reads it back, so **you copy nothing out of the compile response**: forward `planToken` byte-for-byte and confirm.
42
+
43
+ Three consequences worth knowing. The 256,000-byte cap on the apply payload is **gone with the payload**, so a large authored surface no longer becomes impossible to apply through a conversational client; the compiled plan is still capped and compile still enforces it. `PLAN_APPROVAL_NOT_FOUND` joins the error vocabulary for a token no approved plan answers to — already applied, lapsed, or never issued, all one code, because the recovery is the same in each case: compile again. And a validation refusal (a quota, a name collision, a bound agent that changed, a moved catalog) now **leaves the approved plan applicable** — clear the cause and confirm again with the same token while it lives, rather than recompiling.
44
+
45
+ - **The agent brain is no longer a preset union** (32.0.0, `remove-agent-presets`). `create_intelligence_agent` stops taking the `brain` discriminated union: `modelId` and `behavior` become required top-level fields and the `{ kind: 'PRESET' | 'CUSTOM' }` wrapper is gone, so the old shape fails on the unknown key **and** on two now-missing required fields. `update_intelligence_agent` drops `brainPreset`; `modelId` and `behavior` stay independently optional and are now **always honoured**, closing an accept-and-ignore where a named preset silently discarded a model sent beside it.
46
+
47
+ - **`apply_strategy_plan` now publishes the same bounds as `compile_strategy_plan`** (31.0.0, resolving #4495). Eleven position-management dials were declared three times server-side and two copies had drifted, so apply advertised `trailingGivebackPct` as a bare number where compile advertised 25–55, and dropped `trailingTriggerR`'s `multipleOf 0.01` — the constraint pinning storage precision so a sub-precision value is rejected rather than rounded onto the trail-from-entry sentinel `0`. Nothing bad could ever commit (the digest would not match), but the refusal you got was a binding mismatch, which arrived as a bare `INTERNAL_ERROR`. Apply's published bounds only **narrow** to compile's; a client that copies values from `approvedPlan.postState`, as it should, is unaffected. Separately the trio `minStopLossAtrMultiple` / `maxStopLossAtrMultiple` / `minRiskRewardRatio` is published as a bare declaration by both tools, its real bounds being runtime-tunable and inexpressible in JSON Schema.
48
+
39
49
  - **The stop-loss ceiling changed unit** (30.0.0). `maxStopLossPct` (a percent of entry) becomes `maxStopLossAtrMultiple` (a multiple of ATR), and the accepted range narrows from `(0, 100]` to `(0, 3]`. It is a rename **and** a re-denomination — mapping the old value onto the new key sends a number one to two orders of magnitude too large. The objects are `.strict()`, so a 29.x client sending `maxStopLossPct` is rejected with an unknown-key error. A new cross-field rule comes with it: `minStopLossAtrMultiple < maxStopLossAtrMultiple` is now a real comparison and is enforced.
40
50
  - **The grid-confidence and trade-conviction bars left the agent** (28.0.0, `remove-agent-rule-defaults`). `tradingConfig.gridMinConfidence` and `minTradeConviction` are removed from the shared `.strict()` config; a bar is declared on the arena slot or radar slot that fires.
41
51
  - **The agent no longer carries either entry guard** (26.0.0). `create_agent` and `update_agent` stop
@@ -77,6 +87,10 @@ These are the server contract breaks between contract 12 and contract 30 — the
77
87
 
78
88
  ### Moved or reshaped output — a field you read is somewhere else
79
89
 
90
+ - **`AdminApprovedModelDTO.isActive` became `lifecycle`** (32.0.0, `remove-agent-presets`) — `AVAILABLE` / `DEPRECATED` / `RETIRED`. The boolean conflated "offered in the picker" with "bound agents may run", so there was no way to stop offering a model without hard-blocking every agent already on it. A client switching on `isActive` must switch on `lifecycle`, and **must not treat `DEPRECATED` as blocked**: that is the state which keeps bound agents running. The agent read DTO drops `brainPreset` in the same move — the marker recorded which named bundle an owner clicked, never a value the runtime read, and the model and soul it stamped are unchanged on every agent.
91
+
92
+ - **`approvedPlan` is one object, not an operation union** (31.0.0, resolving #4495). It was published as a discriminated union whose discriminator does not survive JSON-Schema conversion, so what actually shipped was a bare `anyOf`: validating a failing compile response gave you every arm's errors with empty instance paths, and the top one typically complained that an UPDATE was missing `creationSeed` — a CREATE-only key — while the field that really failed went unnamed. It is now one object with a literal `operation` discriminator, and `creationSeed`, `expectedRevision` and `bindingImpact` are **required and nullable on every operation**: a CREATE plan carries a seed and `expectedRevision: null`, an UPDATE/RESTORE plan the reverse. **If you narrowed on the union arms, read `operation` instead and expect explicit `null`s rather than absent keys.** If you read those fields without narrowing, nothing changes except that they may now be null.
93
+
80
94
  - **`get_radar_activity` gained an `EDGE_REARM` variant** (29.0.0, `add-radar-anchor-rearm`), and every member gained five `rearm*` margin keys plus a `rearmReasons` discriminator. Breaking on both counts if you parse the union strictly.
81
95
  - **`get_trading_config_catalog` drops four trade-default seeds** (27.0.0) — `defaultMinAtrPct`,
82
96
  `defaultMinStopLossPct`, `defaultMaxStopLossPct` and `defaultMinRiskRewardRatio` leave `defaults`.
@@ -121,6 +135,8 @@ These are the server contract breaks between contract 12 and contract 30 — the
121
135
 
122
136
  ### Widened enum — new members your own copy rejects
123
137
 
138
+ - **Seven plan-token failures became their own error codes** (31.0.0, resolving #4495). `TOKEN_EXPIRED`, `TOKEN_BINDING_MISMATCH`, `INVALID_TOKEN_SIGNATURE`, `INVALID_TOKEN_FORMAT`, `INVALID_TOKEN_CLAIMS`, `INVALID_DIGEST_MATERIAL` and `INVALID_MATERIALIZATION_FENCE` all used to arrive as a bare `INTERNAL_ERROR` — the server wrote the true reason to its own audit log and discarded it at the boundary, so a refused apply told you nothing. They now arrive as themselves, over MCP and HTTP alike, with 409-class status for the two state-conflict codes and 400-class for the five malformed-material codes. A client switching exhaustively on error codes must add the branches; one rendering unknown codes generically is unaffected. Two are worth handling by name: `TOKEN_EXPIRED` means recompile (the token lives five minutes), and `INVALID_TOKEN_SIGNATURE` usually means the token was not forwarded verbatim — it is opaque, so copy it byte-for-byte and never retype or reconstruct it.
139
+
124
140
  - `TradeEvaluationAttemptReasonCode` gains `OPEN_POSITION_CHECK_UNAVAILABLE` (19.4.0), splitting a code that previously reported a platform fault as a fact about your account.
125
141
  - `QualificationGateCode` gains `REQUIRED_CONDITION_FALSE` (19.3.0), from the SCAN-stage gate that now evaluates required conditions before a fire edge is spent.
126
142
  - `TradingPipelineGateStage` gains `EVALUATION` and `TradeEvaluationAttemptReasonCode` gains `EVALUATION_FAULTED` (18.2.0).
@@ -406,7 +422,7 @@ For paid games and autonomous wagering, enable **Server-Signed Wagers** in the M
406
422
 
407
423
  ## Strategy authoring (compile → review → apply)
408
424
 
409
- Strategies are authored through one strict, whole-plan workflow. **Compilation writes nothing; `apply_strategy_plan` is the only write.** Always review the exact returned plan before confirming.
425
+ Strategies are authored through one strict, whole-plan workflow. **Compilation changes no strategy, agent or revision; `apply_strategy_plan` is the only write to the strategy itself** — but compile is not read-only either: it parks the plan its own apply reads, and each call mints a distinct record and token, so compile once per reviewed payload and never retry or parallelise it. Always review the exact returned plan before confirming.
410
426
 
411
427
  1. **Choose the operation and revision.** `list_strategies` (add `includeInactive:true` when preparing a RESTORE) and `get_strategy` return the current `revision`; thread it into the next revisioned call.
412
428
  2. **Discover the report vocabulary live.** Walk `list_strategy_categories` → `list_strategy_vocabulary` → `get_metric_construction_hints` → `get_strategy_column_contract`, and use `get_strategy_section_template` / `preview_strategy_report`. Do not guess metric, transform, parameter, template, or enabled-timeframe facts — they are server-discovered.
@@ -415,7 +431,9 @@ Strategies are authored through one strict, whole-plan workflow. **Compilation w
415
431
  - **UPDATE** supplies at least one changed axis and `expectedRevision`.
416
432
  - **RESTORE** targets an owned inactive revision (with any repair axes).
417
433
  4. **Review before confirming.** Inspect the returned `approvedPlan` (complete post-state, proposed revision, diff, bound-agent impact, expiry) and `reviewContext` (column contracts, point-in-time report preview, open positions, quota/name admission). The plan token expires after five minutes; recompile after expiry or drift.
418
- 5. **Apply only the exact reviewed plan.** After explicit user approval, call `apply_strategy_plan({ request: { plan, planToken, confirm: true } })`. Build `plan` from the compiled `approvedPlan` by copying, byte-identical: `operation`; `postState.id` as `strategyId`; `expiresAt`; `expectedRevision` for UPDATE/RESTORE; `explicitRuleOverrides` as `rules`; and from `postState` — `name`, `description`, `tagline`, `timeframe`, `regimeAutoDerive`, `regimeTimeframe`, `marketReadText`, `sections` (including every generated `custom:` key), `conditions` (each carrying its own required, nullable `verdict`), `minAggregateScore`, `minRequiredCount`, `minAtrPct`. Send nothing else — the server re-derives the scorecard, diff, viability, mismatches, seed, revision, and bound-agent impact, and rejects `diff`, `viability`, `mismatches`, `signalRules`, `creationSeed`, `proposedRevision`, `bindingImpact`, `authoringCatalogDigest`, and `reviewContext` as unknown keys. `conditionVerdicts` is rejected too, with a message naming its replacement — the verdict belongs on the condition. Changed configuration propagates to every bound agent immediately.
434
+ 5. **Apply the plan the server already holds.** After explicit user approval, call `apply_strategy_plan({ request: { planToken, confirm: true } })`. **There is no `plan` member** — one is rejected as an unknown key. The server keeps the plan its own compile approved and reads it back, so nothing is copied out of the compile response and nothing can be mistyped or truncated in transit. Forward `planToken` byte-for-byte exactly as received: it is an opaque signed value, never retyped, paraphrased or rebuilt from memory, and a mangled one addresses no approved plan and is refused. `PLAN_APPROVAL_NOT_FOUND` means no approved plan answers to this token — already applied, lapsed, or never issued — and the recovery is to compile again; so is `TOKEN_EXPIRED` once the five minutes run out. Any other refusal (quota, name collision, a bound agent that changed, a moved catalog) **leaves the plan applicable**: clear the cause and confirm again with the same token while it lives. Changed configuration propagates to every bound agent immediately.
435
+
436
+ The authored axes — including normalized `sections` and `conditions`, each condition carrying its own required, nullable `verdict` — belong on the **compile** request. Every derived field (`diff`, `viability`, `mismatches`, `signalRules`, `creationSeed`, `proposedRevision`, `bindingImpact`, `authoringCatalogDigest`, `reviewContext`) is re-derived server-side and rejected as an unknown key if resubmitted, and `conditionVerdicts` is rejected with a message naming its replacement — the verdict belongs on the condition.
419
437
 
420
438
  `update_strategy_signal_rule({ request })` is the thin, focused one-rule edit. In multi-account mode every one of these calls uses the `{ account, request }` sibling envelope.
421
439
 
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
49
49
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
50
50
  * correction. Never move it to track the server.
51
51
  */
52
- export declare const PACKAGE_VERSION = "31.0.0";
52
+ export declare const PACKAGE_VERSION = "31.0.2";
53
53
  export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
54
54
  export interface EnvConfig {
55
55
  apiKeys: string[];
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema
52
52
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
53
53
  * correction. Never move it to track the server.
54
54
  */
55
- export const PACKAGE_VERSION = '31.0.0';
55
+ export const PACKAGE_VERSION = '31.0.2';
56
56
  export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
57
57
  const MAX_RETRIES = 3;
58
58
  const RETRY_DELAYS_MS = [2000, 4000, 8000];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@battlegrid/mcp-server",
3
- "version": "31.0.0",
3
+ "version": "31.0.2",
4
4
  "description": "BattleGrid MCP server — play crypto prediction games from AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",