Skip to main content
Glama
Fair Dice

Fair Dice

README.md
# Fair Dice

Dice an AI game master can't cheat with. Before each roll, the model has to say what's at stake. The dice then decide the result, and the model can't re-roll.

When a language model narrates the outcome of your character's action, it decides by itself whether you succeed, and it tends to let you. Fair Dice takes that decision away from it:

- **resolve_action**: 2d6 for an uncertain action. Before rolling, the model declares what a partial success costs and what a failure brings. 10+ is a clean success, 7–9 is success with the declared cost, 6 or less is failure with the declared trouble.
- **ask_oracle**: a d100 yes/no answer about the world ("Is the gate guarded?"), rolled against a likelihood the model sets from the facts first. The extreme ends become "yes, and" / "no, and".
- **roll_dice**: plain dice notation for damage, tables and counts: `3d6`, `2d8+3`, `4d6kh3`, `2d20kl1`.

The model never chooses the numbers or the target. The server rolls with a cryptographic random generator and returns the outcome band itself. If the model asks about the same action or question again within 20 minutes, it gets the original result back. Every roll appears as a tool call in your chat.

It works in any app that supports MCP servers: LibreChat, Claude Desktop, Claude Code, Cursor, VS Code and others. There is also a [SillyTavern version](https://github.com/LROsocha-design/SillyTavern-FairDice).

## Two ways to play

| Mode | What happens |
|---|---|
| `gm` (default) | The model rolls only when an outcome is uncertain: risky actions, unsettled facts, damage. |
| `every-prompt` | Every message you send gets one 2d6 roll, which decides how well the message's central attempt turns out: an RP post, a writing instruction, anything. The roll is shown as the first line of the reply. |
| `off` | Tools only, no instructions. Use this if you bring your own rules. |

In apps that read MCP server instructions (LibreChat with `serverInstructions: true`, Claude Desktop, Claude Code), the server passes the chosen mode's rules to the model by itself. In other apps, add the rules as a skill or paste them into your system prompt (see [Skills and prompts](#skills-and-prompts)).

## Install

All setups need [Node.js](https://nodejs.org) 18 or newer, except Claude Desktop, which brings its own.

### LibreChat

Add this to `librechat.yaml` and restart LibreChat:

```yaml
mcpServers:
  fair-dice:
    type: stdio
    command: npx
    args: ["-y", "fair-dice-mcp"]
    serverInstructions: true
    chatMenu: true
    initTimeout: 60000
```

For a roll on every prompt, add `env: { FAIR_DICE_MODE: every-prompt }`. Then pick Fair Dice in the chat's MCP menu, or add its tools to an agent in the Agent Builder. The Docker image already has Node, so this works there too. The first start takes a little longer while npx downloads the package.

### Claude Desktop

Download `fair-dice.mcpb` from the [latest release](../../releases/latest) and double-click it, or install it from Settings → Extensions. The mode, the memory window and an optional log file can be set on the extension's settings page.

### Claude Code

```bash
claude mcp add fair-dice -- npx -y fair-dice-mcp
```

For every-prompt mode, add `-e FAIR_DICE_MODE=every-prompt` before the `--`. On native Windows (not WSL), use `cmd /c npx` in place of `npx`.

### Cursor, VS Code, Cline, Windsurf and other MCP apps

Most apps take the same JSON block:

```json
{
  "mcpServers": {
    "fair-dice": {
      "command": "npx",
      "args": ["-y", "fair-dice-mcp"],
      "env": { "FAIR_DICE_MODE": "gm" }
    }
  }
}
```

### Without npm

`npx` can also run the server straight from GitHub. Use `"args": ["-y", "github:LROsocha-design/fair-dice-mcp"]` in any of the setups above, or clone it:

```bash
git clone https://github.com/LROsocha-design/fair-dice-mcp
cd fair-dice-mcp
npm install
```

Then use `node` with the full path to `server.cjs` as the command in any of the setups above.

## Settings

Each setting can be an environment variable or a command-line flag (`--mode every-prompt`).

| Variable | Flag | Default | Meaning |
|---|---|---|---|
| `FAIR_DICE_MODE` | `--mode` | `gm` | `gm`, `every-prompt` or `off` (see above) |
| `FAIR_DICE_REPEAT_MINUTES` | `--repeat-minutes` | `20` | How long a result is remembered against re-rolls. `0` turns it off. |
| `FAIR_DICE_LOG` | `--log` | none | Path of a `.jsonl` file that records every roll, if you want to check the dice later. |

The re-roll memory belongs to the server process. LibreChat runs one process for all its users, so two users who describe the exact same action within the window would get the same result.

## Skills and prompts

The rules for both modes also come as Agent Skills and as plain text:

| File | Use it in |
|---|---|
| [`skills/dice-every-prompt/SKILL.md`](skills/dice-every-prompt/SKILL.md), `release/dice-every-prompt.zip` | LibreChat (Skills → upload the zip), claude.ai (Settings → Capabilities → Skills), Claude Code (`~/.claude/skills/`) |
| [`skills/fair-dice-gm/SKILL.md`](skills/fair-dice-gm/SKILL.md), `release/fair-dice-gm.zip` | the same |
| [`prompts/dice-every-prompt.md`](prompts/dice-every-prompt.md), [`prompts/fair-dice-gm.md`](prompts/fair-dice-gm.md) | paste into an agent's instructions or any system prompt |

Claude Desktop and Claude Code also offer both rule sets as MCP prompts (`fair-dice-gm`, `dice-every-prompt`), which you can attach to a chat from the prompt menu.

The skills need the dice tools. Without them, the model is told not to invent a roll. It either decides from the facts, or uses a 2d6 result you paste in yourself.

## How fair is it?

- The model can only describe the attempt and the stakes. It never chooses dice or targets, and the band comes back from the server already decided.
- A modifier is limited to −2…+2 and is refused without a stated reason.
- Asking again about the same action or question (ignoring case and punctuation) returns the first result.
- Over 3,000 test rolls, the three bands came out within a point of the expected 16.7% / 41.7% / 41.7%. `npm test` repeats this check.

What it can't stop: a model can still write a different outcome than the one rolled. In practice this is rare. In one long roleplay campaign, 69 of 73 failed rolls that could be checked were written as rolled, 3 were softened, and none were turned into a success. The `FAIR_DICE_LOG` file lets you check your own games.

## Development

```bash
npm install
npm test
python tools/build.py
```

`npm test` runs the server through a real MCP client: every tool, the modes, the re-roll memory, logging, and the odds. `tools/build.py` writes the skill files and zips and the Claude Desktop bundle, and checks the repo for leftover personal paths. It needs Python, plus Pillow for the icon.

## License

MIT