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
npm install
npm test # 154 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, and tallies:
{
"tallies": { "successes": 3, "opportunities": 1, "strife": 1, "explosive": 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).
Param | Range | Default | Notes |
| 1–10 | required | also the keep count |
| 0–10 | 0 | 0 = untrained |
| — | — | 5 trivial · 10 easy · 15 average · 20 difficult · 25 very hard · 30 extreme · 40 near-impossible |
| 0–10 | 0 | +5 TN each; capped by |
| 0–10 | 0 | effect only, no TN, never counts vs cap |
| 1–10 | — | caps declared raises |
| bool | false | reroll initial 1s once, BEFORE explosions |
| int | 0 | wound/stance penalty, applied once to the total |
| 0–10 | 0 | Void Point = +1k1 |
Untrained (skill 0): trait dice only, ALL kept, no explosions, no raises (raises on untrained rolls are rejected with an explanatory error).
l5r5_roll — Ring & Skill dice (5e)
Rolls Ring d6 ring dice + Skill d12 skill dice, keeps Ring-rating
dice chosen AFTER seeing the pool.
Param | Range | Default | Notes |
| 1–5 | required | also the keep count |
| 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 |
| bool | false | convert non-explosive ring dice → skill dice |
| bool | false | convert worst skill die → ring die |
| string | — | explicit |
| enum | success_first |
|
| string | — |
|
| int | — | advisory |
| bool | true | expand explosive faces into bonus dice |
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 add one bonus die of the same type — chained, capped,
marked bonusFor, never consuming keep slots.
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.
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
listen()(removes the systemd-level loopback workaround).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