BattleGrid MCP Server
OfficialClick on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@BattleGrid MCP ServerList current market grid sessions"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@battlegrid/mcp-server
MCP server for BattleGrid — play crypto prediction games, author trading strategies, and manage intelligence agents from AI agents.
It is a thin, authenticated stdio proxy to BattleGrid's remote MCP server (Stripe @stripe/mcp pattern — no business logic). It discovers tools, prompts, and resources live from the server and re-exposes them to local MCP clients (Claude Desktop, Claude Code, Cursor). Capabilities are always discovered live — this package never hardcodes the tool catalog.
v11 — breaking cutover (v6 → v11)
v11 pairs with the BattleGrid server's MCP contract v11.x — currently v11.0.0. The package version tracks the server's wire contract, because the proxy announces battlegrid@<package version> in its own stdio handshake — the number a client reads has to be the contract it will actually reach. Upgrade the package and the server together: the major carries the breaking cutover described below, and the minor tracks additive contract moves that leave every existing call working.
This release absorbs six breaking contract majors at once. The published package went from 5.0.0 straight to 11.0.0, so no 6.x through 10.x client exists to upgrade from — the breaks are therefore grouped by what you will observe, with the contract version that introduced each, so a client hitting a specific rejection can find it here.
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.
Rejected input — something you author is no longer accepted
Challenge participation is no longer a field you set (10.0.0).
create_agentandupdate_agentstop acceptingarenaChallengeEnabled, and a deployment policy's slot rules and per-coin rules stop acceptingchallengeEnabled. All four schemas are.strict(), so a client still sending any of them is rejected, not silently ignored. Challenge participation is now identical to effective trade permission, resolved per coin: to stop an agent taking challenges at a venue, turn that venue's trading off — at the slot, or per coin for finer grain — using fields you already have.VOLUME_RATIOis retired and replaced byRVOL(6.0.0).MetricKeySchemaauto-derives from the server's metric catalog, so the published enum simply stops accepting the old key: a column authored withmetric: 'VOLUME_RATIO'is rejected against the enum. No alias exists — deliberately. The catalog audit adjudicated the old name as a name-level lie (the value is current volume ÷ its 20-period average, a multiple), and the correction was made at the root rather than grandfathered. If you hit an enum rejection namingVOLUME_RATIO, this note is the match.BB_WIDTHcan no longer be ranked (8.0.0).{ metric: 'BB_WIDTH', transformId: 'rank' }is now rejected.BBwidthis a price-unit spread (upper − lower) that had falsely declaredpercent, and that declaration was the only thing admitting it to exchange-wide ranking — the ordinal it produced sorted by token denomination rather than by compression. It re-declaressignedPriceand leaves the ranked contract. RankBB_WIDTH_PCT(bbWidthPct) instead, which ships in the same release: the capability moved, it was not removed.
Silently non-matching — a generated header you match on moved
This is the failure mode with no error attached to it. Nothing is rejected; your matcher simply stops finding the column.
vol→RVOL(6.0.0), and with itvol_trend→RVOL_trendandvol_rank_hi→RVOL_rank_hi. The header code moves with the metric key, because every generated header over that metric derives from the code alone.BBwidth_rank_lono longer exists (8.0.0) — it was the header of the ranked pairing retired above.
Moved or reshaped output — a field you read is somewhere else
estimatedTokenCountis gone (7.0.0).preview_strategy_reportandcompile_strategy_planno longer carry it; the same number now sits one level deeper asbudgetUsage.estimatedTokens.used, paired with thecapthat governs it — so a client reading the count changes one path and gains the ceiling it was never told.tokenCountModelis unchanged. Discovery grows to match:list_strategy_vocabularyand the report catalog addbudgets.estimatedTokensand apreviewExecutionLimitsobject carrying the serialized-result byte cap and the preview deadline. Those two are published cap-only and deliberately have nousedcompanion.approvedPlan.mismatcheschanged on both axes (9.0.0). Both report-coverage codes are renamed off "module", because the module is no longer the unit of coverage:Was
Is now
ACTIVE_SIGNAL_MODULE_NOT_IN_REPORTACTIVE_SIGNAL_DATA_NOT_IN_REPORTREPORT_MODULE_SIGNAL_OFFREPORT_DATA_SIGNAL_OFFEach mismatch also carries a required
data: CoverageDatum[]— the(metric, rung)pairs the mismatch is about: every MISSING datum for the not-in-report code, the PRESENT data for the signal-off code, empty forREQUIRED_SIGNAL_UNAVAILABLE. A client switching exhaustively on the old code strings stops matching. Behaviourally, coverage is now decided by whether the report renders a signal's declared metrics at the rung that signal reads, not by whether its module appears anywhere — so expect warnings you never saw before, and the disappearance of warnings no composition could clear. Mismatches remain advisory and non-blocking; nothing about apply gates on them.IntelligenceAgentDTOdropsarenaChallengeEnabled(10.0.0) — the read side of the input removal above.ResolvedSlotRulesDTO.challengeEnabledstays and keeps its shape; it is now derived server-side, carrying the same value and provenance astradingEnabled, so a client reading the resolved bundle needs no change.rangeis no longer a tuple (11.0.0). The closed positional pair[min, max]becomes the half-open object{ min: number; max?: number }. It travels throughScalarSchema, so this lands onlist_strategy_vocabulary,query_report_catalog, andget_metric_construction_hintsalike.This one fails silently. A client reading
range[0]/range[1]getsundefinedwith no error raised — readrange.min/range.maxinstead, and treat a missingmaxas unbounded above. The tuple could not state the truth about the volume/trade-count family, which is non-negative and unbounded above, andInfinityserializes tonullon the wire. Those six metrics now declare{ min: 0 }, and as a consequence theirfar/nearrank orderings are no longer offered — on a non-negative value that pair is a synonym pair under the magnitude gate's documented semantics.
Widened enum — new members your own copy rejects
The published
unitenum gainsratioandfraction(8.0.0), andRVOL,BUY_PRESSURE,BB_PCT_Bnow emit them instead ofpercent— none of the three is a percentage, andpercentappended a false%. This is a break in the opposite direction from the others: nothing you send is rejected, but a client holding its own closed copy of the unit enum rejects the new members. Easy to mistake for a purely additive change. Values are not rescaled —buyPresandpctBstay 0–1, so persisted thresholds comparing against0.5keep their meaning.
What you do NOT need to do
No stored strategy needs client action. Every persisted reference of both kinds — metric keys in section-column rows and revision snapshots, and everything addressing a column by its generated header — was migrated server-side by
20260805120000_rename_volume_ratio_to_rvol.sql. This is a client-literal problem only; there is no stored record to repair.
Additive in the same span
Nothing here is a break, but a client that enumerates these vocabularies will want them:
Four metric keys join the catalog —
SPOT_CVD,PERP_SPOT_FLOW,PERP_SPOT_STRENGTH,PERP_SPOT_CONFIRMS— andincludePerpSpotFlowjoins the context-source key set as its 23rd member, opt-in (5.2.0).Two prompt-section
kinds join the union:perp-spot-flow(5.2.0) andsession-field(5.1.0). Only a client that switches exhaustively onkindneeds a default branch; one that renderscontentgenerically needs nothing.The transform vocabulary grows 15 → 17 (11.0.0) —
efficiencyandmaxSharejoin, and both join the chain-outer enum thatchainSuccessorsis served as. Only a client that switches exhaustively ontransformIdneeds new branches; one that renders the served labels generically needs nothing.The deployment-policy resolution DTO gains
agentTradingMode(10.0.0) — the preview previously reported trade rules for an agent whose account-level trading was off, and the account gate is not part of rule resolution. Read it to tell "this rule permits the trade" from "this account can trade at all".PlatformSectionDTOgainscolumns(6.1.0) — a platform section's composition in the same wire shape a custom section's columns already travel in, empty for a registry-declared special. Not a copy source, which is where it differs from the identically-shapedCustomSectionTemplateDTO.columns: six platform columns pair a metric withclassifyState, a deliberate composability exclusion that authoring rejects at construction. A client that round-trips these into a custom section will be refused, and that refusal is correct.
Related MCP server: Enterprise Crypto MCP Gateway
v5 — breaking major (conditions/verdicts fusion)
Retained for authors upgrading from 4.x. Everything below still describes the current contract.
conditionVerdictsno longer exists. The verdict now rides the condition that decides it, and precedence is the conditions' own declaration order rather than a separate ordered map:// v4 — two parallel arrays joined by string key { "conditions": [ { "conditionKey": "UP_FADE", "name": "…", "definition": { /* … */ } } ], "conditionVerdicts": [ { "when": "UP_FADE", "then": "UP" } ] } // v5 — one array; the verdict rides its condition { "conditions": [ { "conditionKey": "UP_FADE", "name": "…", "definition": { /* … */ }, "verdict": "UP" } ] }verdictis required and nullable, never optional: a building block that decides nothing spells its absence as an explicitnull, never by omitting the key. An omittedverdictis a rejected payload, not a defaulted one.A submitted
conditionVerdictsis REJECTED, not ignored. Deliberately — a v4 client that forwards the retired field is told what replaced it instead of getting an anonymous unrecognized-key rejection:conditionVerdictswas retired in contract 5.0.0 — a condition now carries its ownverdict(UP|DOWN|NEITHER, ornullfor a building block). Move each mapping onto the condition it named and resubmit.The authorable verdict domain narrowed to three.
conditionGrammar.verdictsadvertised['UP','DOWN','NEITHER','UNRESOLVED']and now advertises['UP','DOWN','NEITHER'].UNRESOLVEDis an evaluation outcome — "a deciding condition could not be evaluated" — never an authored intent, so advertising it offered a value the schema then rejected. Any client mirroring the advertised enum into its own validation must narrow with it.The evaluated per-coin verdict is nullable. Resolution is first-TRUE-decides over the verdict-carrying conditions in declaration order, and its four outcomes are all distinct claims — collapsing any pair loses information a reader needs:
Evaluated verdict
Means
UP/DOWN/NEITHERas a decisionThe first verdict-carrying condition that resolved TRUE declared it
NEITHERas a fallthroughEvery verdict-carrying condition resolved FALSE
UNRESOLVEDNo carrier fired and at least one could not be evaluated — "could not be read", not "read as no setup"
nullThe strategy declares no verdict-carrying condition at all — it expresses no direction
nullis new in v5; it previously surfaced asNEITHER. A client that renders the verdict must handle it without collapsing it intoNEITHER.The proxy itself is unchanged. It embeds no schemas, pins no contract version, and forwards
{ request }verbatim, so the whole break lands on whatever builds the apply projection. Apply takes an allowlisted projection ofapprovedPlan, never the object itself (diff,viability,mismatches,signalRules,creationSeed,proposedRevision,bindingImpact,authoringCatalogDigestare rejected as unknown keys, and so are thepostStatefields apply does not accept:id,scope,systemKey,visibility,cadence,isActive,forkedFromStrategyId). The exact field list is step 5 of Strategy authoring (compile → review → apply) below. A client that forwards post-state fields generically — stripping the derived keys rather than enumerating the kept ones — picks the fusion up without an edit; one that enumerates the fields it copies must dropconditionVerdictsfrom that list.
The v3 authoring contract below is unchanged and still current:
Strict authoring envelopes.
get_strategy_section_template,update_strategy_signal_rule,compile_strategy_plan, andapply_strategy_planpublish one strict server-owned object,{ request: canonicalPayload }. In multi-account mode the proxy addsaccountonly as a sibling ofrequest, producing exactly{ account, request }; on a call it strips onlyaccountand forwards the unchanged{ request }. It never descends into, flattens, or reconstructs the nested request.create_strategyis retired. Direct strategy creation no longer exists. Author strategies with the compile → review → apply workflow below, and bind them to agents at agent-creation time (create_intelligence_agent({ …, strategyId })). There is no alias, shim, or flat-payload fallback.Rediscover after deployment. Publishing the package does not refresh a running proxy's cached capability snapshot. After the server cutover, restart/reconnect the proxy process and re-run
tools/list,prompts/list, andresources/list.
Earlier majors: v1.x single/multi-account stdio proxy; v2.0.0 moved the default
BATTLEGRID_API_URLto the/mcpsuffix; v3.0.0 the strategy-authoring major; v4.0.0 madeconditionsandconditionVerdictsrequired on the apply post-state; v5.0.0 fused the conditions/verdicts split (section above). v6.0.0 through v11.0.0 were never published as separate package versions — they are absorbed by the v11 cutover at the top. See Rediscovery & versioning.
Quick Start
Single account (stdio transport)
BATTLEGRID_API_KEY=bg_live_xxx npx @battlegrid/mcp-serverMultiple accounts (stdio transport)
BATTLEGRID_API_KEYS=bg_live_alice_key,bg_live_bob_key npx @battlegrid/mcp-serverWhen multiple keys are provided, the server discovers each account's identity and injects a required account parameter into every tool so the AI agent can choose which account to act as.
Remote server (streamable-http transport)
https://mcp.battlegrid.trade/mcpNo npm install required — connect directly from any MCP client that supports streamable-http.
Configuration
Claude Desktop
Single account:
{
"mcpServers": {
"battlegrid": {
"command": "npx",
"args": ["@battlegrid/mcp-server"],
"env": {
"BATTLEGRID_API_KEY": "bg_live_xxx"
}
}
}
}Multiple accounts:
{
"mcpServers": {
"battlegrid": {
"command": "npx",
"args": ["@battlegrid/mcp-server"],
"env": {
"BATTLEGRID_API_KEYS": "bg_live_alice_key,bg_live_bob_key"
}
}
}
}Claude Code
claude mcp add battlegrid -- npx @battlegrid/mcp-serverSet your API key(s):
# Single account
export BATTLEGRID_API_KEY=bg_live_xxx
# Multiple accounts
export BATTLEGRID_API_KEYS=bg_live_alice_key,bg_live_bob_keyCursor
{
"mcpServers": {
"battlegrid": {
"command": "npx",
"args": ["@battlegrid/mcp-server"],
"env": {
"BATTLEGRID_API_KEY": "bg_live_xxx"
}
}
}
}Use BATTLEGRID_API_KEYS (comma-separated) for multiple accounts.
ChatGPT Desktop
ChatGPT Desktop connects via OAuth 2.1 — no npm package or API key needed. ChatGPT handles the OAuth flow automatically.
Open ChatGPT Desktop → Settings → MCP Servers → Add Server
Enter the MCP endpoint URL:
https://mcp.battlegrid.trade/mcpSelect OAuth as the authentication method
ChatGPT discovers OAuth endpoints, registers as a client (Dynamic Client Registration), and opens BattleGrid's consent page
Log in to BattleGrid and click Authorize
Claude Desktop / Cursor | ChatGPT Desktop | |
Transport | stdio proxy ( | Direct HTTPS |
Auth | API key ( | OAuth 2.1 (Bearer token) |
Setup | npm package + env vars | URL + OAuth consent |
Multi-account |
| One OAuth grant per account |
Account management
Single account
Set BATTLEGRID_API_KEY with one API key. All tool calls use that account, and the tools are exactly the server-native shapes — the authoring tools take the strict { request } envelope with no account field.
Multiple accounts
Set BATTLEGRID_API_KEYS with a comma-separated list of API keys (one per BattleGrid account). On startup the proxy:
Calls
GET /mcp/identityfor each key to discover the account usernameInjects a required
accountenum parameter into every tool — as a sibling of the existing input, never nested inside itRoutes each tool call to the correct account using the matching Bearer token, stripping only
accountbefore forwarding
For the strict authoring tools, the multi-account input is exactly { account, request }:
{
"name": "compile_strategy_plan",
"inputSchema": {
"type": "object",
"properties": {
"account": {
"type": "string",
"enum": ["alice", "bob"],
"description": "Which BattleGrid account to use for this action"
},
"request": {
"oneOf": [
{ "properties": { "operation": { "const": "CREATE" } } },
{ "properties": { "operation": { "const": "UPDATE" } } },
{ "properties": { "operation": { "const": "RESTORE" } } }
]
}
},
"required": ["account", "request"],
"additionalProperties": false
}
}The proxy consumes only the outer account and forwards the unchanged { request } upstream. Never put account inside request, and never flatten request fields beside it.
If a key fails identity discovery (revoked, invalid), it is skipped with a warning. If all keys fail, the process exits. BATTLEGRID_API_KEYS takes precedence over BATTLEGRID_API_KEY when both are set.
Getting an API key
Go to battlegrid.trade → Profile → MCP tab
Generate an API key (format:
bg_live_*)Copy the key immediately — it is shown only once
Each account supports one active key at a time. Generating a new key automatically revokes the previous one — restart any running proxy process afterward, since keys are read once at startup.
For paid games and autonomous wagering, enable Server-Signed Wagers in the MCP tab (mcp:wager scope). Strategy discovery and non-financial configuration writes only need mcp:read.
Strategy authoring (compile → review → apply)
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.
Choose the operation and revision.
list_strategies(addincludeInactive:truewhen preparing a RESTORE) andget_strategyreturn the currentrevision; thread it into the next revisioned call.Discover the report vocabulary live. Walk
list_strategy_categories→list_strategy_vocabulary→get_metric_construction_hints→get_strategy_column_contract, and useget_strategy_section_template/preview_strategy_report. Do not guess metric, transform, parameter, template, or enabled-timeframe facts — they are server-discovered.Compile one complete plan. Call
compile_strategy_plan({ request })where the nested request is exactly one strict branch plus a boundedcoinSelection,intentSummary, andassumptions:CREATE supplies the full new strategy.
UPDATE supplies at least one changed axis and
expectedRevision.RESTORE targets an owned inactive revision (with any repair axes).
Review before confirming. Inspect the returned
approvedPlan(complete post-state, proposed revision, diff, bound-agent impact, expiry) andreviewContext(column contracts, point-in-time report preview, open positions, quota/name admission). The plan token expires after five minutes; recompile after expiry or drift.Apply only the exact reviewed plan. After explicit user approval, call
apply_strategy_plan({ request: { plan, planToken, confirm: true } }). Buildplanfrom the compiledapprovedPlanby copying, byte-identical:operation;postState.idasstrategyId;expiresAt;expectedRevisionfor UPDATE/RESTORE;explicitRuleOverridesasrules; and frompostState—name,description,tagline,timeframe,regimeAutoDerive,regimeTimeframe,marketReadText,sections(including every generatedcustom:key),conditions(each carrying its own required, nullableverdict),minAggregateScore,minRequiredCount,minAtrPct. Send nothing else — the server re-derives the scorecard, diff, viability, mismatches, seed, revision, and bound-agent impact, and rejectsdiff,viability,mismatches,signalRules,creationSeed,proposedRevision,bindingImpact,authoringCatalogDigest, andreviewContextas unknown keys.conditionVerdictsis rejected too, with a message naming its replacement — the verdict belongs on the condition. Changed configuration propagates to every bound agent immediately.
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.
Strategy-bound agents. Bind a strategy to an intelligence agent at creation time — create_intelligence_agent({ …, strategyId }) (discover strategyId via list_strategies). Rebinding via update_intelligence_agent requires confirm:true. There is no direct create_strategy operation.
Capabilities
Tools, prompts, and resources are discovered live from the connected server via tools/list, prompts/list, and resources/list. This package intentionally does not copy the server's catalog, formulas, signal IDs, or defaults — inspect the live connection for the authoritative, current surface. Broadly, the server exposes game play (Market Grid), market context, account state, leaderboards, intelligence agents and automation, strategy discovery/authoring, and trading signals/decisions.
Environment variables
Variable | Required | Description |
| One of these | Comma-separated API keys for multiple accounts |
| One of these | Single API key (fallback if |
| No | Override server URL (default: |
Rediscovery & versioning
Package/server contract-line pairing. This package's
MAJOR.MINORpairs with the BattleGrid server's published MCP contract version (MCP_CONTRACT_VERSION). The pairing is not decorative: the proxy re-announces itself asbattlegrid@<package version>to the local client, under the same server name the remote handshake uses, so a package left behind tells clients a contract number that no longer exists — and a package ahead tells them one that does not exist yet. The line, not just the major: the contract ships additive changes as minors (5.1.0,5.2.0,6.1.0), so matching majors alone would let the package advertise a minor the server does not serve. Only the PATCH is the package's own space. The publish workflow enforces this against the deployed endpoint; it is not left to memory.Rediscover after a server cutover. Package publication does not refresh a running proxy's cached startup snapshot. Restart/reconnect the proxy and re-run
tools/list,prompts/list, andresources/listafter the server deploys.Restart after key rotation. API keys are read once at process startup; rotate a key, then restart the proxy.
Version | Changes |
1.x | Single/multi-account stdio proxy, identity discovery, connection retry, capability discovery |
2.0.0 | Default |
3.0.0 | Strategy-authoring major: strict |
3.0.1 | Docs only — |
4.0.0 | Realigns the package major with the server's MCP contract v4.0.0, which broke on the conditions axis: |
5.0.0 | Pairs with the server's MCP contract v5.0.0, the conditions/verdicts fusion: |
5.1.0 | Pairs with the server's MCP contract v5.1.0, which is additive: |
5.2.0 | Server contract v5.2.0, additive: four |
6.0.0 | Server contract v6.0.0, breaking: |
6.1.0 | Server contract v6.1.0, additive: |
7.0.0 | Server contract v7.0.0, breaking: the strategy-report preview limits became discoverable and the bare token count was removed. |
8.0.0 | Server contract v8.0.0, breaking, on two axes. |
9.0.0 | Server contract v9.0.0, breaking: |
10.0.0 | Server contract v10.0.0, breaking: challenge participation stops being a declared setting anywhere and becomes identical to effective trade permission, resolved per coin. |
11.0.0 | Server contract v11.0.0, breaking: a catalogued numeric output's |
Maintainer release procedure
.github/workflows/publish.ymlis the executable release authority. If this recipe and the workflow diverge, correct them together before merging a version change.
Publishing runs only in GitHub-hosted Actions. Never run npm publish from the BattleGrid application VM or a maintainer workstation.
A version change on main is the release. Merge a pull request that changes package.json's version and the workflow does the rest: it confirms that version is not already on the registry, verifies every value expressing it agrees, asserts the deployed server serves that contract major, tests, builds, packs, publishes with npm provenance, and tags what shipped. There is no manual tag step — the tag is an output of a successful publish, not its prerequisite.
That inversion is deliberate. Publication used to be triggered by a tag, which meant a version bump with no tag published nothing and reported nothing — how 5.1.0 came to be declared in this repository and absent from the registry, caught by no check at all.
Release environments and prerequisites
Responsibility | Environment |
Confirm npm publishing trust | npmjs.com package settings |
Prepare and merge the version change | Pull request against |
Check, build, publish, and tag | GitHub-hosted |
Verify registry publication | Any shell |
The deploy check needs no credential. scripts/assert-deployed-contract.mjs reads GET /mcp/version, which the server serves unauthenticated by design — the contract version is announced to every connected client and committed to the app repo's docs/architecture/mcp-manifest.json, so it is not a secret. Requiring auth would have meant this workflow holding a BattleGrid API key, and every such key carries mcp:wager (there is no read-only variant), i.e. authority to submit wagers and close live positions in order to read a version number. The check fails closed on a mismatch, an unreachable endpoint, a non-200, or an unreadable body — but there is no secret to provision, scope, rotate, or leak.
Also confirm npm's Trusted Publisher for @battlegrid/mcp-server is GitHub Actions with organization playbattlegrid, repository battlegrid-mcp, workflow filename publish.yml, no environment name, and npm publish allowed. The workflow uses short-lived OIDC credentials; do not add a long-lived NPM_TOKEN.
Preparing the version change
Read the target version from the generated manifest, never from prose. The number this package pairs to is
server.contractVersioninbattlegrid-app'sdocs/architecture/mcp-manifest.json— generated bybuildMcpManifestand CI-verified, on a freshly fetchedorigin/main(a stale ref reports a superseded contract silently). A contract version quoted in an issue, a pull request, or any other hand-written text is not the target. During active contract development that number changes on merge, so prose carries a value that was true when written and is unverifiable when read. Every pairing before v9 was sourced from prose, and the v9 pairing was filed against a contract three majors stale.Move all four values together —
package.json, bothpackage-lock.jsonversion fields (the rootversionand the self-referencingpackages[""].version), and the exportedVERSIONinsrc/index.ts. The workflow compares all four against each other and fails closed on any disagreement.Merge the version change only after the server is deployed. The deploy assertion is a safety net, not a routine step: with the ordering right it never fires, and a red workflow on
mainmeans something is genuinely wrong rather than that you are waiting. Merging early blocks the publish until the deploy lands, then re-run the job — nothing was published and no tag exists to move.
Which releases need a server deploy
The deploy assertion compares the contract line — MAJOR.MINOR — not the full version and not the major alone.
A contract pairing — the server's contract moved, so the package follows. This needs the deploy to land first. Note this includes minor moves: the contract ships additive changes as minors (
5.1.0,5.2.0and6.1.0were all additive), and the proxy announces the full version it publishes, so a package minor ahead of the server would advertise additive features the deployed endpoint does not serve.A proxy-only release — a fix in this package's own code, a dependency bump, a documentation correction. Move the PATCH, which is the package's own space: the contract has never carried a non-zero patch, so a patch bump makes no claim about the server and needs no deploy. This is the historical norm, not an edge case —
1.0.1,1.0.2,1.1.2,1.1.4and3.0.1were all proxy-only, and3.0.1is recorded in the version table above as "Docs only — … No proxy behavior change".
Comparing full versions would reject every one of those patch releases; comparing majors alone would let the package advertise a contract minor the server does not serve. The contract line is the boundary that is actually true.
Verify publication
The workflow publishes and tags on its own; these confirm what landed.
release_version="$(node -p "require('./package.json').version")"
npm view "@battlegrid/mcp-server@${release_version}" version dist.integrity \
--json --registry=https://registry.npmjs.org/
npm view @battlegrid/mcp-server dist-tags \
--json --registry=https://registry.npmjs.org/
npm view "@battlegrid/mcp-server@${release_version}" dist.attestations \
--json --registry=https://registry.npmjs.org/Require the exact version, latest pointing at that version, and a provenance attestation. Confirm the workflow created mcp-server@${release_version} and that gitHead on the published version is the merge commit. Restart/reconnect running proxies and rediscover tools/list, prompts/list, and resources/list; publication alone does not refresh their startup cache.
There is no separate registry-reconciliation step to remember. "Is this version already published?" is the workflow's own first question — it decides whether the run publishes at all — so a bump can no longer sit in the repository unpublished and unreported the way 5.1.0 did.
The server-side release canary is not evidence about this package. battlegrid-app's server/scripts/release-canary-mcp.ts connects to the deployed endpoint and compares client.getServerVersion() against the server's own imported MCP_CONTRACT_VERSION — both sides are server-side, and it never queries npm. It passes with this package at any version, including one that was never published. Package-side evidence is exactly three things: the workflow's version-integrity gate, the registry checks above (plus gitHead matching the release commit and the handshake constant in the published dist/index.js), and a reconnect showing battlegrid@<version> in the stdio handshake.
Note the deploy assertion in the workflow is a different check from that canary and does not share its limitation: it reads the deployed handshake and compares it against this package's version, so both sides are not server-side.
If a run fails, inspect it before taking action. An ENEEDAUTH failure means the npm Trusted Publisher fields do not match the workflow — fix the publisher configuration and re-run the job. A failed deploy assertion means the server has not deployed that contract major yet; deploy, then re-run. In both cases nothing was published and no tag was created, so there is nothing to move or reuse. Never mutate a published release with npm audit fix; dependency remediation goes through a new reviewed commit and version.
Skills
Install the BattleGrid skill for AI agent instructions:
npx skills add playbattlegrid/battlegrid-mcpLicense
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityFmaintenanceEnables AI agents to interact with the ProfitPlay prediction market to trade short-term price movements of cryptocurrencies and stocks. It provides tools for market analysis, automated betting, account registration, and leaderboard tracking.91
- FlicenseNot gradedqualityCmaintenanceEnables MCP-compatible AI clients to access live crypto market data and AI-driven quantitative analysis, with structured outputs and full observability.
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to trade on Limitless prediction markets on Base via MCP, with tools for wallet management, market discovery, order placement, and portfolio tracking.
- AlicenseAqualityCmaintenanceEnables AI assistants to interact with the PocketOption trading platform via MCP, including balance checks, candle data, asset screening, and trade placement, with support for multi-agent coordination.244MIT
Related MCP Connectors
No-KYC managed MCP for AI agents: sandboxed TypeScript trading SDK, isolated sub-accounts, futures.
HiveCapital MCP Server — autonomous investment layer for AI agents
MCP server exposing the Backtest360 engine API as tools for AI agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/playbattlegrid/battlegrid-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server