dice-mcp-server
Click on "Deploy 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., "@dice-mcp-serverroll 4d6kh3 for my ability scores"
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.
@guan-tends/dice-mcp-server
Stateless dice engine MCP tool server. Pure dice math — no character sheets, no sessions, no persistence.
Three tools via the Model Context Protocol:
Tool | System | Character |
| Generic d20-style notation | expression in, number out |
| Legend of the Five Rings 4e Roll & Keep (XkY+Z) | structured in, number + raises out |
| Legend of the Five Rings 5e (FFG) ring/skill symbol dice | structured in, symbols out |
Built on @guan-tends/mcp-ai SimpleServer (HTTP transport, raw zod-shape schemas). Same composition-root pattern as @guan-tends/matrix-mcp-server.
Quick Start
Zero-config (stdio — most MCP clients, same as passgen):
{
"mcpServers": {
"dice": {
"command": "npx",
"args": ["-y", "@guan-tends/dice-mcp-server"]
}
}
}HTTP (daemon style):
npm install
npm test # 168 tests
npm start # serves MCP HTTP on 127.0.0.1:3777Point any MCP client at http://localhost:3777/ (Streamable HTTP transport).
Example: a d20 attack roll with advantage
{
"name": "dice_roll",
"arguments": { "expression": "2d20kl1+5", "dc": 15 }
}{
"success": true,
"expression": "2d20kl1+5",
"terms": [
{
"notation": "2d20kl1",
"rolls": [
{ "face": 17, "chain": [], "final": 17, "rerolled": false },
{ "face": 8, "chain": [], "final": 8, "rerolled": false }
],
"kept": [{ "face": 8, "chain": [], "final": 8, "rerolled": false }],
"dropped": [{ "face": 17, "chain": [], "final": 17, "rerolled": false }],
"subtotal": 13
}
],
"total": 18,
"dc": 15,
"success": true
}Example: a 4e katana strike (Agility 3, Kenjutsu 4, emphasis, Void spent)
{
"name": "l5r4_roll",
"arguments": {
"trait": 3,
"skill": 4,
"tn": 25,
"emphasis": true,
"rollBonus": 1,
"keepBonus": 1,
"label": "Katana strike"
}
}Returns 7d10k4 — pool, kept/dropped dice with explosion chains, totals,
TN verdict, and a notes[] array explaining every rule that fired. A
failed roll with raises carries wouldSucceedWithoutRaises: true — the
GM-narration hook ("he'd have made it, but the flourish cost him").
Example: a 5e Fire check (Fire 3, Tactics 2, advantage)
{
"name": "l5r5_roll",
"arguments": { "ring": 3, "skill": 2, "tn": 3, "advantage": true }
}Returns the full pool with per-die symbols, kept indices, conversions, tallies, and the derived totals (⚑ + 🔥 = totalSuccesses):
{
"tallies": { "successes": 3, "opportunities": 1, "strife": 1, "explosive": 2 },
"totalSuccesses": 5,
"bonusSuccesses": 2,
"success": true,
"composureExceeded": false
}Related MCP server: MCP Dice Roller
Tool Reference
dice_roll — generic notation
2d6+3 sum of 2d6 plus 3
d20 one d20
4d6kh3 keep highest 3 of 4d6 (ability scores)
2d20kl1 keep lowest 1 (disadvantage)
1d10! exploding: reroll-and-sum while max faces appear
1d12!11,12 exploding on specific faces (5e skill die: 11 and 12)
3d6r1 reroll initial 1s once (emphasis shape)
2d6+1d4+2 multi-termParam | Type | Notes |
| string | required; whitespace-tolerant, case-insensitive |
| int, optional | adds |
Parse errors are position-aware (... at position 4) so callers can fix
their own syntax. Doubled signs (2d6--3) and signless term
concatenation (2d61d4) are rejected with named errors.
l5r4_roll — Roll & Keep (4e)
Pools (Trait + Skill) d10s, keeps the highest Trait. D10s explode on
10s (trained rolls only). Every pool passes through the Ten Dice Rule:
no more than 10 rolled / 10 kept dice — excess kept dice add +2 each,
excess rolled dice convert 2:1 into kept dice (while kept < 10), and any
leftover rolled dice add +2 each. preCapPool and overflowBonus are
reported for audit.
Build a pool two ways:
Trait + Skill (skill rolls):
(Trait + Skill) k TraitDirect
rolled/kept(everything else): initiative1k4(Insight k Reflexes — kept > rolled is legal), melee damage6k2, Honor rolls6k6, spell casting3k2, unarmed3k1
Param | Range | Default | Notes |
| 1–10 | — | also the keep count; omit for direct pools |
| 0–10 | — | 0 (with no |
| 1–50 | — | direct pool input; overrides |
| enum | skill |
|
| bool | — | explicit flag; |
| — | — | 5 trivial · 10 easy · 15 average · 20 difficult · 25 very hard · 30 extreme · 40 near-impossible |
| 0–10 | 0 | +5 effective TN each; capped by |
| 0–10 | 0 | effect only, no TN, never counts vs cap |
| 1–10 | — | caps declared raises |
| bool | false | spend a Void Point: +1k1 to the pool |
| bool | false | reroll initial 1s once, BEFORE explosions; trained rolls only |
| int | 0 | wound/stance penalty — raises the effective TN, never the total (Nicked +3 … Down +40) |
| −10–10 | 0 | dice bonuses/penalties; penalties clamp kept ≤ rolled |
| int | 0 | flat bonus to the kept sum (Honor Rank on Fear rolls) |
| highest/lowest | highest |
|
| string | "10" |
|
Unskilled rolls (no skill ranks): trait dice only, ALL kept, no
explosions, no raises, no emphasis (raises/emphasis on unskilled rolls
are rejected or ignored with an explanatory note). Trait rolls are a
separate thing — rollType: "trait" explodes and allows raises.
l5r5_roll — Ring & Skill dice (5e)
Implements the corebook check pipeline (pp. 20–26, spec of record): assemble the pool (Step 3) → modify rolled dice (Step 4) → choose kept dice (Step 5) → resolve symbols on KEPT dice (Step 6).
The law, as implemented:
Total successes = ⚑ + 🔥 (p. 24: "the sum total of success and explosive success symbols"). A kept skill-12 (pure explosive face) counts as one success; a kept ring-6 counts as two.
Keep 1..ring dice (p. 24: "at least one... up to the value of the ring"; +1 per assisting character, p. 26). Under-keeping is legal and reported in notes.
Explosions resolve post-keep, from kept dice only (Step 6.1): each 🔥 on a kept die rolls one bonus die of the same type; a kept bonus die's own 🔥 chains.
Advantage + disadvantage cancel (p. 24 consolidate rule) — both flags together have no effect, per the book.
Assistance (p. 26): +1 skill die per skilled helper, +1 ring die per unskilled helper.
Param | Range | Default | Notes |
| 1–5 | required | also the keep maximum (before assistance) |
| 0–5 | 0 | 0 = untrained (ring dice only) |
| 1–10 | — | successes needed: 1 easy · 2 average · 3 difficult · 4 very hard · 5 extremely hard · 6 extraordinary · 7+ heroic |
| object | — |
|
| 1..keepMax | keepMax | under-keep via policy (book-legal); values above max clamp with a note |
| string | — |
|
| enum | auto_keep |
|
| string | — | explicit |
| bool | false | house simplification of named categories; cancels against |
| bool | false | house simplification; cancels against |
| enum | success_first |
|
| int | — | advisory |
| string | — | echoed in the result |
Deprecated: includeExplosionBonuses (bool) maps to bonusDice
(true → auto_keep, false → auto_drop); cannot be combined with it.
Result contract (the result is a full transcript — every automated
decision is visible): base pool with per-die symbols · kept /
dropped · bonusDice[] audit (sourceDieIndex, type, face, symbols,
chainDepth, disposition kept/dropped/pending) · conversions[] ·
tallies (raw ⚑/⧫/⏳/🔥 counts; bonusPending appears in manual
mode only) · totalSuccesses (⚑+🔥) · vs tn: success,
bonusSuccesses, shortfall · keepMax, requestedKeepCount,
keptIndices · explosiveTriggers, untrained · notes[] narrating
every automated decision.
House safety valve (disclosed, not book law): MAX_BONUS_CHAIN = 10
caps per-chain bonus-die depth — the book is naturally finite because a
player may always drop, but auto_keep automation needs a guard; the
cap firing is reported in notes.
Verified symbol charts (cross-checked against two independent 5e references):
Ring d6 | 1 | 2 | 3 | 4 | 5 | 6 |
symbols | — | opp+strife | opp | succ+strife | succ | succ+strife+expl |
Skill d12 | 1–2 | 3–5 | 6–7 | 8–9 | 10 | 11 | 12 |
symbols | — | opp | succ+strife | succ | succ+opp | succ+strife+expl | expl (no strife) |
Explosive faces on KEPT dice add one bonus die of the same type after
keep selection (book Step 6.1) — chained, capped, fully audited in the
bonusDice[] array. Kept bonus dice are tallied (their symbols count
toward the TN, per the Sakura worked example, p. 23).
Architecture
Functional core, imperative shell. All randomness is constructor-injected
(rng(sides) => int in [1, sides]): production uses crypto.randomInt
(unbiased CSPRNG); tests inject sequence RNGs, making every engine test
deterministic. Symbol tables are data — adding Genesys/Star Wars dice
later is a table entry, not a rewrite.
src/
├── index.js bootstrap only (lifecycle, graceful shutdown)
├── config.js config layers (defaults ← JSON5 ← env) + validation
├── mcp-server.js tool definitions + SimpleServer wiring
└── engine/
├── rng.js createCryptoRng / createSequenceRng
├── core.js rollDice: explode-on-faces, reroll, caps
├── notation.js expression parser (position-aware errors)
├── d20.js expression evaluator
├── symbols.js 5e symbol tables (data)
├── l5r4.js Roll & Keep engine
└── l5r5.js ring/skill symbol engineError rejection is two-layer: zod-schema violations (out-of-range params)
are rejected at the MCP protocol layer before the tool body runs;
cross-field rule violations (e.g. raises > Void Ring) surface as
isError JSON results with the engine's human-readable message.
Design Decisions
Stateless. No sheets, no sessions, no persistence. The caller is the GM brain; the server is dice math. Restart-safe by construction.
Teaching descriptions. Every tool description embeds its system's TN scale and worked examples — an LLM caller learns the system from the tool itself.
Deterministic tests. Sequence RNG injection makes explosion chains and keep decisions exactly reproducible; distribution tests with derived constants cover the real CSPRNG.
Dice-as-data. 5e symbol tables are frozen data; new symbol systems are new tables, not new engines.
No array-typed tool params. Kept indices, conversions, and explode faces are comma-strings — an MCP-gateway compatibility choice.
Interpretation notes (parameterized, documented, adjustable if a table ruling differs): 5e untrained = ring dice only; advantage converts pre-keep and never sacrifices an explosive ring die; composure is an explicit input with an advisory flag (no hardcoded formula); 4e wound penalties apply once to the total.
dcis permissive (any integer) — permissive inputs, strict dice math.
Transports
Transport | How | Default for |
| JSON-RPC over stdin/stdout pipes | the |
| Streamable HTTP | the library entry ( |
| Server-Sent Events | opt-in via env |
Selection precedence (highest wins): explicit transport option →
DICE_MCP_TRANSPORT env (stdio|http|sse) → JSON5 config file
transport key → entry default (bin: stdio, library: http).
DICE_MCP_TRANSPORT=stdio node bin/dice-mcp-server.mjs # stdio
DICE_MCP_TRANSPORT=http node bin/dice-mcp-server.mjs # http on :3777stdio mode writes diagnostics to stderr only — stdout carries
exclusively the MCP protocol. An explicit DICE_MCP_TRANSPORT (or
config-file transport) always beats the entry default.
Deployment
Systemd unit (example)
Adjust paths/user for your host. If you use nvm, point PATH and
ExecStart at your node install (or source nvm in an ExecStartPre).
[Unit]
Description=Dice MCP Server
After=network.target
[Service]
Type=simple
User=YOURUSER
WorkingDirectory=/opt/dice-mcp-server
ExecStart=/usr/bin/node src/index.js
Restart=on-failure
RestartSec=5
# Loopback-only enforcement (see below)
IPAddressDeny=any
IPAddressAllow=localhost
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetLoopback enforcement
mcp-ai SimpleServer's express listen() binds all interfaces and
ignores the host config field. Loopback-only is therefore enforced at
the deployment layer — systemd IPAddressDeny=any +
IPAddressAllow=localhost (cgroup packet filter) with UFW default-deny.
Until mcp-ai supports a server-side bind host, deploy with both
controls. (The port config IS honored.)
Gateway wiring (mcp-ai aggregator)
Add to the aggregator's mcps array (back up config.json first):
{ "id": "dice", "connection": { "type": "http", "url": "http://localhost:3777" } }Then restart the gateway.
Testing
npm test # 154 tests: engines, tools, e2e, distribution
npm run test:coverage # with coverage
npm run lint # eslint
npm run format:check # prettierTest layers:
Engine units — deterministic via sequence RNG; every rule path (explosions, emphasis ordering, untrained, raises/void, policies, conversions, overrides) asserted exactly.
Tool units — schema shape contract (raw zod shapes), happy paths, engine-error surfacing.
E2E — real MCP client over Streamable HTTP: initialize → listTools → callTool for every tool, both error layers.
Distribution sanity — real CSPRNG with derived constants (geometric-series explosion means, binomial success expectations).
Support
If this server powers your table or your agents, tips are appreciated — they fund compute, inference, and the rest of the tool fleet:
GitHub Sponsors: github.com/sponsors/guan-tends
Solana:
Eu8wQcW68TKMs1a6eqzZu8znzU52QLqQugAMG8uCD6y6EVM:
0x2733ff7c865C56d565a99BE1DC11B81cc76850A5XRP:
r4X6e7McAQj7e8vBCeued1RYu4mCJrREDG
Roadmap
Genesys / Star Wars dice — new symbol tables (data), same engine.
mcp-ai upstream: server-side bind-host support for express— DONE upstream in @guan-tends/mcp-ai 1.6.7-guan.0 (consumed here via the dependency bump); systemd loopback enforcement retained as defense-in-depth.listen()Fate / other systems — candidate engines behind the same tool interface.
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
D&D 5e MCP — wraps the D&D 5th Edition API (free, no auth)
Official remote MCP server for Archivist AI TTRPG campaign memory: characters, sessions, and more.
Read-only developer, date, finance, and text utilities. Authless remote MCP server by Clean.tools.
Manage your tabletop RPG campaign from any MCP client: worlds, sessions, quests, lore, recaps.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables dice rolling mechanics for tabletop RPGs including standard dice notation, coin flips, advantage/disadvantage rolls, exploding dice, and maintains a history of recent rolls. Supports D\&D 5e mechanics and various gaming dice types (d4, d6, d8, d10, d12, d20, d100).-
- AlicenseAqualityCmaintenanceAn MCP server that provides tools for rolling dice using standard notation, flipping coins, and selecting random items from lists. It supports advanced tabletop gaming features such as character stat generation and keep-highest/lowest mechanics.6MIT
- AlicenseAqualityDmaintenanceProvides comprehensive TRPG dice rolling functionality including standard notation, advantage/disadvantage mechanics, and success-counting dice pools. It enables users to perform complex dice logic and track roll history through an MCP-compliant interface.2MIT
- AlicenseAqualityDmaintenanceEnables dice rolling for RPG games using standard dice notation, supporting complex expressions like keep/drop, reroll, exploding dice, and batch rolls.43MIT