brink
A single-player cinematic geopolitical roleplay MCP server where an LLM game-master handles decision-making with persistent world state and rules enforcement.
README
brink
A single-player, cinematic geopolitical roleplay: you are the supreme executive of a great power, handling only the geopolitical brief — taking calls from world leaders, hearing your advisors, making decisions — in a persistent world grounded in current real-world geopolitics.
Built as a fork of DMCP (LLM-as-DM, MCP server as
persistent state + tools), with a geopolitical rules core ported from the BalanceOfTerror engine.
To play it, read docs/MANUAL.md.
The design brief is REBOOT-DESIGN-AND-REQUIREMENTS.md and it is the authority on everything below. Read it first if you're working on the code.
The shape of it
- The LLM is the Game-Master: it classifies a fuzzy action into which mechanic fires, narrates, and calls tools. It never adjudicates.
- The MCP server is referee + ledger: authoritative resolution, persistence, and the handful of rules the GM cannot talk its way out of.
Three hard rules that constrain every change here
- Show, don't tell. If the player-facing surface ever states a disposition or a raw value —
DOCTRINE: HAWK,cohesion 80.0,(DOVE advisor)— that is a bug, not a style preference. The player deduces state from behavior, dialogue, and consequence. - Resolve, then narrate. The GM never narrates an outcome before the server returns it. It calls the resolve tool, receives an authoritative outcome payload carrying an explicit narration constraint, and narrates from that payload. The fiction can be florid about the how; it can never lie about the what.
- No credential in the working tree. The GM runs keyless, on a Claude subscription (OAuth). There is no standing pay-per-token API key anywhere in this repo, and development works against a mock GM backend. See §2.4 of the brief — this is a requirement, not guidance.
Fork discipline (§10 of the brief)
Changes sort into three buckets, and generic mechanism must never share a commit with geopolitical content. That separation is what keeps the offer-back-upstream workflow mechanical instead of archaeological.
| Bucket | What | Where it lives |
|---|---|---|
| Use as-is | DMCP's persistence spine, factions, relationships-with-history, resources, locations, secrets/visibility, dice/checks, pause/resume, web UI | untouched |
| Offer back upstream | Generic, game-agnostic mechanism: atomicity, the declarative constraint layer, conserved resources, on-expiry consequences | offer/* branches — see docs/IMPROVEMENTS-TO-DMCP.md |
| brink-private | Geopolitical rules content, GM persona, situation-room UI, current-events refresh, avatar/voice | main |
The line: "a resource can be declared conserved" is a DMCP feature; "prestige is conserved across three great powers" is our game.
docs/IMPROVEMENTS-TO-DMCP.md is the full account of what changed in DMCP and which parts are generic enough to lift — written for DMCP users deciding whether any of it is worth taking, as much as for upstream.
Branch convention
upstreamremote tracks DMCP.git fetch upstream && git merge upstream/mainpulls their changes.- Work intended for upstream branches from
upstream/mainasoffer/<topic>, touches only DMCP-generic files, and carries no geopolitical content — so it cherry-picks cleanly. (offer/, notupstream/: a local branch namedupstream/foocollides with theupstreamremote's own ref namespace, and git will happily let you create the ambiguity.) - Everything else branches from
main. - Upstream's own README is preserved in history:
git show upstream/main:README.md(setup instructions and the full ~170-tool inventory).
Development
npm ci # install
npm run build # tsc + client build
npm run dev # run the server
npm run test:run # vitest
npm run lint # eslint (CI gate)
npm run typecheck # tsc --noEmit (CI gate)
Local MCP testing: npx @modelcontextprotocol/inspector node dist/index.js
Write lots of tests. DMCP arrived with essentially no coverage of business logic, so there is no upstream safety net under the invariants this game depends on. TDD is mandatory — failing test first. See CLAUDE.md for the test harness and the codebase gotchas worth knowing before you start.
Status
Tranche 1 is complete. The text-first vertical slice runs:
npm run build:server # once
npm run play -- --real # Claude as the game-master
npm run play # placeholder game-master, spends nothing
Graphics amplify a loop that already works; they cannot rescue one that doesn't — so this had to exist before anything cinematic.
What's in place:
- The referee. All three enforced rules from §4.2 have real mechanisms: prestige is a
conservedconstraint set moved only through an atomic transfer; DEFCON isboundedandresolve_only, so it has exactly one writer; declared on-expiry consequences are applied by the server itself, so a countdown lands regardless of the fiction. - The rules core, ported from the
BalanceOfTerrorPython engine as pure functions with injected RNG, verified against that engine's own test suite. - The resolve path, which makes resolve-then-narrate enforceable: the payload carries an explicit narration constraint, and tests re-read the database after every call to prove prose and ledger cannot disagree.
- The GM layer: system prompt, persona seeds whose dispositions exist only as behavior, a
GMBackendseam with a deterministic offline mock, and the real game-master — Claude Code driven headlessly under a subscription, with no credential anywhere in the tree.
Verified live: the GM streams an outcome-neutral reaction, calls the referee itself, and narrates only
after the payload lands. DEFCON moved 5→4 with a matching audit row — and since DEFCON carries a
resolve_only constraint, that write had no other route available to it.
Tracked in the issues.
Contributing, and security
- CONTRIBUTING.md — how to get set up, and the two rules that catch people out.
- SECURITY.md — how to report something, what leaves your machine, and why there is no API key in this repository.
- docs/PUBLISHING.md — the repository topology, and the two routes work takes
out of here: a tree copy to the public repo, and
offer/*branches to DMCP.
License
MIT. Copyright is shared: DMCP's original notice is retained alongside brink's own — see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.