roll_dice
Roll dice using standard notation like '2d6' or '1d20+5' for tabletop games, character stats, and random number generation.
Instructions
Roll dice using standard dice notation (e.g., '2d6', '1d20+5', '3d8-2').
Args: notation: Dice notation string. Examples: - '1d6' - Roll one 6-sided die - '2d6' - Roll two 6-sided dice - '1d20+5' - Roll one 20-sided die and add 5 - '3d8-2' - Roll three 8-sided dice and subtract 2 - '4d6kh3' - Roll 4d6, keep highest 3 (D&D stat rolling) - '2d20kl1' - Roll 2d20, keep lowest 1 (disadvantage)
Returns: Dictionary with rolls, modifier, and total
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| notation | No | 1d6 |
Implementation Reference
- src/mcp_dice_roller/server.py:15-84 (handler)The roll_dice function is defined here as an MCP tool, containing the logic to parse dice notation and simulate dice rolls.
@mcp.tool() def roll_dice(notation: str = "1d6") -> dict: """ Roll dice using standard dice notation (e.g., '2d6', '1d20+5', '3d8-2'). Args: notation: Dice notation string. Examples: - '1d6' - Roll one 6-sided die - '2d6' - Roll two 6-sided dice - '1d20+5' - Roll one 20-sided die and add 5 - '3d8-2' - Roll three 8-sided dice and subtract 2 - '4d6kh3' - Roll 4d6, keep highest 3 (D&D stat rolling) - '2d20kl1' - Roll 2d20, keep lowest 1 (disadvantage) Returns: Dictionary with rolls, modifier, and total """ notation = notation.lower().strip() # Parse the notation: XdY[kh/kl Z][+/-M] pattern = r"^(\d+)d(\d+)(?:(kh|kl)(\d+))?([+-]\d+)?$" match = re.match(pattern, notation) if not match: return { "error": f"Invalid dice notation: '{notation}'", "hint": "Use format like '2d6', '1d20+5', '4d6kh3'", } num_dice = int(match.group(1)) die_sides = int(match.group(2)) keep_type = match.group(3) # 'kh' or 'kl' or None keep_count = int(match.group(4)) if match.group(4) else None modifier = int(match.group(5)) if match.group(5) else 0 # Validate inputs if num_dice < 1 or num_dice > 100: return {"error": "Number of dice must be between 1 and 100"} if die_sides < 2 or die_sides > 1000: return {"error": "Die sides must be between 2 and 1000"} if keep_count and keep_count > num_dice: return {"error": f"Cannot keep {keep_count} dice when only rolling {num_dice}"} # Roll the dice rolls = [random.randint(1, die_sides) for _ in range(num_dice)] original_rolls = rolls.copy() # Apply keep highest/lowest kept_rolls = rolls dropped_rolls = [] if keep_type and keep_count: sorted_rolls = sorted(rolls, reverse=(keep_type == "kh")) kept_rolls = sorted_rolls[:keep_count] dropped_rolls = sorted_rolls[keep_count:] subtotal = sum(kept_rolls) total = subtotal + modifier result = { "notation": notation, "rolls": original_rolls, "kept": kept_rolls if keep_type else original_rolls, "dropped": dropped_rolls if dropped_rolls else None, "subtotal": subtotal, "modifier": modifier if modifier != 0 else None, "total": total, } # Clean up None values return {k: v for k, v in result.items() if v is not None}