@battlegrid/mcp-server 32.0.35 → 32.0.37

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
@@ -80,6 +80,43 @@ Reconnect the proxy, or restart it, so discovery reads the new tool list and sch
80
80
 
81
81
  - **`vocabularyDigest` moves.** A client caching vocabulary against it re-reads `list_strategy_vocabulary`.
82
82
 
83
+ ## Contract history — v135.2 (room for 32 conditions, a column's relation to the anchor, and a condition column's family)
84
+
85
+ **Additive at v135.2: two output fields, and two served limits rise.** v135.2 is the contract on top of v135.1. Nothing
86
+ a client sends is refused that v135.1 accepted. Reconnect the proxy, or restart it, so discovery reads the new output
87
+ schemas.
88
+
89
+ ### Added output
90
+
91
+ - **`get_strategy_column_contract`'s `timeframe` carries `anchorRelation`:** where the column reads against the strategy
92
+ anchor (`rung`: `above` · `at` · `below`), whether its read is still in progress at the strategy's close
93
+ (`developingRead`), and the column editor's served Read-row line for it (`readSummary`, ending "Default here." for a
94
+ Developing default at or below the anchor) and cost line (`readCost`, "No verdict, can’t be an exit.", for a
95
+ developing read). It is `null` for a column with no bar set to read under an anchor, a call with no
96
+ `anchorTimeframe`, or a reference that resolves to no timeframe.
97
+ - **`preview_strategy_report`'s `authoring.conditionColumns[].outputs[]` carry `family`:** the catalog family of the
98
+ metric the column reads, or `null` for a report-level scalar. It is display grouping only, and a stored condition
99
+ does not carry it.
100
+
101
+ ### Changed values — the same schema serves a different number
102
+
103
+ - **A strategy may declare 32 conditions, and a condition may hold 32 leaf clauses**, up from 16 each:
104
+ `budgets.strategyConditions` and `budgets.conditionClauses` read 32, and the refusals name 32.
105
+ `budgets.conditionFrameReads` stays 256, and every clause costs at least one frame read, so a strategy cannot fill
106
+ both caps at once.
107
+
108
+ ### Skill
109
+
110
+ - **The strategy-examples skill's recipes gain a *Recency and structure* section:** bars since a clause read TRUE
111
+ (under n, at least n, between a and b), the first cross above a level in N bars, the first non-edge reading in N bars
112
+ and k lower highs in N bars, each a whole condition built from the shipped hold, groups and operators. It reaches
113
+ this package through the export lane.
114
+
115
+ ### Digest move
116
+
117
+ - **The authoring catalog digest moves with the served budgets.** A client caching vocabulary against it re-reads
118
+ `list_strategy_vocabulary`.
119
+
83
120
  ## Contract history — v135.1 (the bar clock, trading-session levels and a period level that stops at its roll)
84
121
 
85
122
  **Additive at v135.1: thirty-five metrics and five studies, and one value moves.** v135.1 is the contract on top of
package/dist/index.d.ts CHANGED
@@ -47,7 +47,7 @@ import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/cli
47
47
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
48
48
  * correction. Never move it to track the server.
49
49
  */
50
- export declare const PACKAGE_VERSION = "32.0.35";
50
+ export declare const PACKAGE_VERSION = "32.0.37";
51
51
  export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
52
52
  export interface EnvConfig {
53
53
  apiKeys: string[];
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import { Client, SdkHttpError, StreamableHTTPClientTransport } from '@modelconte
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 const PACKAGE_VERSION = '32.0.35';
52
+ export const PACKAGE_VERSION = '32.0.37';
53
53
  export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
54
54
  const MAX_RETRIES = 3;
55
55
  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": "32.0.35",
3
+ "version": "32.0.37",
4
4
  "description": "BattleGrid MCP server — play crypto prediction games from AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
3
- "contractVersion": "135.1.0",
3
+ "contractVersion": "136.1.0",
4
4
  "files": {
5
5
  "battlegrid-agent-management/SKILL.md": "91d1be4de1e33d54e1852c970af66ad176068fd16c30b4326e49053b7a35eb32",
6
6
  "battlegrid-agent-management/references/agent-stage-examples.md": "dffcc353d61dfc7b5bb6a2b3077fc6bf8c760a34c287cafb3b59b99306f24195",
7
7
  "battlegrid-arena-play/SKILL.md": "fdaabcd2e5ce3946ee96e553ce2e1931a822b84f91b0c94ce86a8bde0cc1d50f",
8
8
  "battlegrid-arena-play/references/entry-stage-examples.md": "0223f3743171ff213e943789d27590b2f65648398e85e10cd24938d5eb6af665",
9
- "battlegrid-market-analysis/SKILL.md": "fcbe98e30e1edce39bb170c224cdf20181934f62b5f3a26b293787c70eeb6229",
9
+ "battlegrid-market-analysis/SKILL.md": "e50a63492c635af9e8748561148efd7e1d9aafdfb843acc657e557670b2f55e4",
10
10
  "battlegrid-officers/SKILL.md": "0e5b4fd7a11fc1b6643eecd0008dfe1528b915ee00d0a99be9aca1e3934d57f7",
11
11
  "battlegrid-officers/references/officer-stage-examples.md": "ef46ba26f4b36c9964b5bde95fdf98e5952c3786b77be5f136056cac792f0aec",
12
12
  "battlegrid-radar-deployment/SKILL.md": "cd058d887e1342f42ce855708178d5f746400835a204e92238b63ec1fe57bf30",
@@ -17,7 +17,7 @@
17
17
  "battlegrid-strategy-doctor/SKILL.md": "24c2208cf6832f1fc34e1b1c52acd8dae10f56b9860aaf27b450a62a9fb74634",
18
18
  "battlegrid-strategy-examples/SKILL.md": "cbbc7a7271aaa9f530a11d91511f4bf35a9632aa3ab67ecd996975c65ae72625",
19
19
  "battlegrid-strategy-examples/references/playbooks.md": "59996c37eea08e7ce398e49304171939e55c968400a1603597d6737faf9e1771",
20
- "battlegrid-strategy-examples/references/recipes.md": "95966649a68ec488a232b150328d94e9f80c45eff30be4da92caf305fba20fb0",
20
+ "battlegrid-strategy-examples/references/recipes.md": "e1fbd1a3d1b2ef1408fef8642032c3e131954aedec6bdcefa3af6a655948e392",
21
21
  "battlegrid-strategy-examples/references/tradingview-ports.md": "00f2ba287ff8fadf356273f61ad19630cceb3a706018c2088be40570ff1a899d",
22
22
  "battlegrid-trade-analysis/SKILL.md": "140d847f24e98845736d4f0c2e72b318a4d235413f72f072d2fe6d0daa5f64db",
23
23
  "battlegrid-trade-proposal/SKILL.md": "bf8f3794478cdecc989020bd775c3c91744f72b440b1a90f88f6700c9a61ca24"
@@ -27,8 +27,14 @@ Run these in order. Skip a step only when the player's question makes it irrelev
27
27
  2. **Market context.** `get_market_context` — this carries the breadth, funding and open-interest
28
28
  picture. Funding and OI are the two figures most often skipped and most often decisive: crowded
29
29
  positioning is the difference between "trend" and "trend about to be squeezed". Report both.
30
- 3. **Leaders and laggards.** `get_top_ranked_coins`. Name the actual coins at both ends, not just
31
- "strength in majors". A laggard list is as informative as a leader list and is usually omitted.
30
+ 3. **Leaders and laggards.** One ranked `preview_strategy_report` per end, each returning the coins
31
+ with their rows, so no per-coin follow-up is needed: `coinSelection: { mode: "ranked", limit: 10,
32
+ category: "ALL", rankBy: { metric: "ROC12", ordering: "hi" } }` for the leaders and `ordering: "lo"`
33
+ for the laggards, with `detail: "concise"` and `source: { kind: "FIELDS", timeframe, sections:
34
+ [{ kind: "platform", sectionKey: "includeRelativeStrength" }] }`. `CLOSE_CHANGE` under `"far"` names
35
+ the biggest movers in either direction. Omit `category` and the cohort is crypto only. Name the
36
+ actual coins at both ends, not just "strength in majors". A laggard list is as informative as a
37
+ leader list and is usually omitted.
32
38
  3b. **Many coins on the same indicators — one call, not one per coin.** When the answer needs the
33
39
  indicator modules ACROSS a set of coins (comparing RSI, funding or volatility over the leaders,
34
40
  over a category, over the session pool), use `preview_strategy_report`: one section JSON per
@@ -15,6 +15,7 @@ rather than events all live there. This document assumes them.
15
15
  **Contents**
16
16
  - Column recipes
17
17
  - Cross detection
18
+ - Recency and structure
18
19
  - Board-relative rank recipes
19
20
  - Cross-venue and perp/spot basis
20
21
  - Crowd positioning
@@ -48,6 +49,85 @@ An event column is a trigger inside a carrier, never a standing state; pair it w
48
49
  state for regime. The skill body's `## Header grammar` states that rule and is the authority on it —
49
50
  a condition that reads a `crossDetect` column as a state reads FALSE on nearly every bar, because a null reads FALSE.
50
51
 
52
+ ## Recency and structure
53
+
54
+ "How long since", "the first time in N bars" and "how many lower highs" are holds over a clause — no
55
+ transform counts bars since an event, and no column is built over a condition. Each recipe below is a
56
+ whole condition; adapt its clause, its numbers and its `required`, and keep its shape.
57
+
58
+ **Counting.** Bars since A counts the bar being decided as 0, so "A within n" — the clause held
59
+ `{ "atLeast": 1, "of": n }` — is "bars since A is at most n − 1". This counts bars since a clause read
60
+ TRUE; it is not a swing pivot's age, which `fractalHiAgo` / `fractalLoAgo` count differently (see
61
+ `tradingview-ports.md`).
62
+
63
+ **Absence.** A read that ran and found no value reads FALSE (the skill body, `## Conditions`), so a `NOT`
64
+ over it reads TRUE on a coin with no value — a listing younger than its indicator's warm-up, for one.
65
+ Every recipe that holds a `NOT` therefore sits inside an `ALL` with a member that needs the value, so a
66
+ recipe copied as a required gate reads FALSE, never TRUE, when the value is missing.
67
+
68
+ **Bars since A is under n** — RSI was oversold within the last 5 closes:
69
+
70
+ ```json
71
+ { "conditionKey": "OVERSOLD_WITHIN_5", "name": "Oversold within 5 closes", "verdict": null, "required": true, "exitSide": null,
72
+ "definition": { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 1, "of": 5 } } }
73
+ ```
74
+
75
+ **Bars since A is at least n** — RSI has not been oversold for 5 closes. The `NOT` alone would read TRUE
76
+ on a coin with no RSI, so `RSI14_now gte 0` requires the value to be there:
77
+
78
+ ```json
79
+ { "conditionKey": "NOT_OVERSOLD_FOR_5", "name": "Not oversold for 5 closes", "verdict": null, "required": true, "exitSide": null,
80
+ "definition": { "kind": "group", "op": "ALL", "hold": { "atLeast": 1, "of": 1 }, "members": [
81
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "gte", "value": 0, "hold": { "atLeast": 1, "of": 1 } },
82
+ { "kind": "group", "op": "NOT", "hold": { "atLeast": 1, "of": 1 }, "members": [
83
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 1, "of": 5 } } ] } ] } }
84
+ ```
85
+
86
+ **Bars since A is between a and b** — RSI was last oversold 3 to 6 closes ago: within `b + 1` = 7, and
87
+ not within `a` = 3. The `NOT` reads TRUE with no value, and the first member then reads FALSE:
88
+
89
+ ```json
90
+ { "conditionKey": "OVERSOLD_3_TO_6_BARS_AGO", "name": "Oversold 3 to 6 closes ago", "verdict": null, "required": true, "exitSide": null,
91
+ "definition": { "kind": "group", "op": "ALL", "hold": { "atLeast": 1, "of": 1 }, "members": [
92
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 1, "of": 7 } },
93
+ { "kind": "group", "op": "NOT", "hold": { "atLeast": 1, "of": 1 }, "members": [
94
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 1, "of": 3 } } ] } ] } }
95
+ ```
96
+
97
+ **The first cross above L in N bars** — RSI crossed above 50 on the decided bar, and read at or below 50
98
+ on the 5 closes before it: the cross, and `lte 50` on at least `N − 1` of the newest `N` = 6 closes (the
99
+ decided bar itself reads above 50). A cross is an edge, so it holds Once; the level clause carries the
100
+ count:
101
+
102
+ ```json
103
+ { "conditionKey": "FIRST_CROSS_ABOVE_50_IN_6", "name": "First cross above 50 in 6 closes", "verdict": null, "required": true, "exitSide": null,
104
+ "definition": { "kind": "group", "op": "ALL", "hold": { "atLeast": 1, "of": 1 }, "members": [
105
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "crossesAbove", "value": 50, "hold": { "atLeast": 1, "of": 1 } },
106
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lte", "value": 50, "hold": { "atLeast": 5, "of": 6 } } ] } }
107
+ ```
108
+
109
+ **The first A in N bars, for an A that is not an edge** — RSI is oversold now and was not oversold on 2
110
+ or more of the newest 6 closes, so this is its first oversold close in 6. With no value the first member
111
+ reads FALSE:
112
+
113
+ ```json
114
+ { "conditionKey": "FIRST_OVERSOLD_IN_6", "name": "First oversold close in 6", "verdict": null, "required": true, "exitSide": null,
115
+ "definition": { "kind": "group", "op": "ALL", "hold": { "atLeast": 1, "of": 1 }, "members": [
116
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 1, "of": 1 } },
117
+ { "kind": "group", "op": "NOT", "hold": { "atLeast": 1, "of": 1 }, "members": [
118
+ { "kind": "clause", "column": { "sectionKey": null, "header": "RSI14_now" }, "op": "lt", "value": 30, "hold": { "atLeast": 2, "of": 6 } } ] } ] } }
119
+ ```
120
+
121
+ **k lower highs in N bars** — the published swing high stepped down on at least 2 of the newest 8 closes,
122
+ over a `{ "metric": "SWING_FRACTAL_HIGH", "transformId": "value", "timeframe": { "rel": "anchor" } }`
123
+ column (header `fractalHi`). `decreased` is not an edge, so it counts like any clause; it counts each
124
+ step down of the decided pivot, which is a lower high against the pivot before it:
125
+
126
+ ```json
127
+ { "conditionKey": "TWO_LOWER_HIGHS_IN_8", "name": "Two lower highs in 8 closes", "verdict": null, "required": true, "exitSide": null,
128
+ "definition": { "kind": "clause", "column": { "sectionKey": null, "header": "fractalHi" }, "op": "decreased", "hold": { "atLeast": 2, "of": 8 } } }
129
+ ```
130
+
51
131
  ## Board-relative rank recipes
52
132
 
53
133
  `rank` turns any column into an ordinal across the scanned board. Ordering semantics: `hi` most