Skip to main content
Glama
wyt-mcp

wyt-mcp

by MLMecham

wyt-mcp

Watch Your Toes, remade as an MCP server. A wizard has trapped a town in a daily time loop. You are the only one who carries anything across the resets — levels, gold, gear, knowledge — while the townsfolk's sanity decays, the economy rots, and the dungeon beneath the town reshuffles itself every dawn. Claude is the game master; the server owns every die roll, map, and price.

DESIGN.md is the authoritative spec (§1–§19). Read it before touching the code.

Status: v1 core complete — playtest phase

Everything in the §12 build order exists and is smoke-tested: the full engine, the renderer, the MCP tool surface (23 tools + the GM prompt), and the headless balance harness. What remains before calling v1 done is playtesting in Claude Desktop, then the post-core content (§17 unplayed-class NPCs, §19 the barrier & shards).

Module

What it does

db.py

SQLite schema, save lifecycle, helpers. One save slot.

data/*.json

NPCs, enemies, gear, consumables, statuses, events, room pool.

engine/player.py

Classes, slot-enforced equip, XP/leveling (banked stat points), consumables, resolve, conduct.

engine/town.py

Generated town graph + fog-of-war, sanity decay, gates, memories/rumors, the crime machine (§16), the §15 forged wizard packet.

engine/economy.py

Loop- and sanity-scaled prices, DB-backed stock, shop-tag sell penalties, the den fence.

engine/dungeon.py

Per-loop graph generation (spine + branches), movement, traps/treasure, floor clears.

engine/effects.py

Buffs/debuffs/stuns — combat-scoped and day-long.

engine/combat.py

Hybrid auto/round combat: initiative, crits, follow-ups, flee, NPC kills, town-enemy hooks.

engine/days.py

new_game, the 8-step overnight reset, the Garrick valve, overnight events, the first midnight.

engine/endings.py

Breaking point (despot/husk), the reveal, dawn/successor, epilogues.

engine/tuning.py

Every pacing knob in one file (§18).

render.py

All ASCII output: status bar, fog-of-war town & dungeon maps, shop tables, combat panel.

server.py

FastMCP tool surface + the GM-persona prompt.

simulate.py

Headless balance harness.

Related MCP server: DMCP

Requirements

  • uv — that's it. uv sync provisions Python 3.12 and the two runtime deps (mcp[cli], platformdirs).

Playing it (Claude Desktop)

Add to claude_desktop_config.json (local checkout; PyPI publish comes later):

{
  "mcpServers": {
    "wyt": {
      "command": "uv",
      "args": ["run", "--directory", "C:/path/to/wyt-mcp", "wyt-mcp"]
    }
  }
}

Then use the start_game prompt and let the GM take it from there. The save lives in your platform user-data dir (e.g. %LOCALAPPDATA%\wyt-mcp\save.db).

Headless balance runs

uv sync
uv run python -m wyt_mcp.simulate --loops 18
uv run python -m wyt_mcp.simulate --loops 20 --class mage --runs 5

Prints per-loop town average/min sanity, the price of an iron sword, player resolve, and the Garrick valve state. Tune in engine/tuning.py, re-run, repeat. Point WYT_MCP_DB at a throwaway path to experiment by hand without touching your save:

$env:WYT_MCP_DB = "$env:TEMP\wyt-test.db"

Roadmap

  1. Playtest in Claude Desktop — the last §12 step.

  2. §17 — The Roads Not Taken: the three classes you didn't pick exist in the world (the rival in the dungeon, the hunting party, the scholar at the charm stall, the assassin who learns your tricks).

  3. §19 — The Barrier & the Shards: visible non-resetting outside world, perma-death shards, the suspicion gradient, and the Stillness / Refusal / Erased endings.

  4. Post-v1 (§13): quests & altruism as the despair counterweight, per-class S-endings, the socket dashboard, more endings.

Available Tools

30 tools
advance_loopA

End the day: the player goes to bed (cause='slept' — the only value you may pass; deaths advance the loop on their own). Returns the overnight packet to narrate waking up at the tavern.

Inputs: cause.

apply_statusB

Pick one status from the candidates the server offered in talk_to — narrative fit is yours, eligibility is not.

Inputs: npc_key (required), status_key (required).

ask_directionsB

A willing local points you toward one street you don't know yet. Costs nothing but standing — withdrawn or hating NPCs refuse.

Inputs: npc_key (required).

attackA

Start a fight. target: an npc key, or 'den_keeper' at a den to raid it. mode='auto' fast-forwards trash; dangerous foes refuse auto and demand round-by-round (combat_action).

Inputs: target (required), mode.

buyC

Buy from a merchant's current stock. Server owns the math.

Inputs: npc_key (required), item_key (required).

claim_artifactC

At the reveal: keep it for yourself instead. The loop changes hands.

Inputs: no named parameters.

combat_actionC

One round: strike | ability | use_item | defend | flee. The server resolves your action and the enemy's answer.

Inputs: action (required), arg.

descendB

Enter the dungeon at the mouth. 'You remember the way': any floor up to max_floor_cleared + 1.

Inputs: floor.

equipB

Equip gear. One weapon, one armor, one accessory.

Inputs: item_key (required).

fenceC

At a den: the crew sells the town's own stolen goods back, at extortion prices. No argument lists the goods; pass item_key to buy.

Inputs: item_key.

get_stateA

Full rehydration packet — call at session start; chat history is never load-bearing.

Inputs: no named parameters.

give_artifactA

At the reveal: place it in his waiting hand. He has waited a very long time for exactly this. Requires the player's explicit, unmistakable choice — never infer it from an ambiguous line; if unsure, ask, in the fiction. This ends the game.

Inputs: no named parameters.

give_itemC

The player hands an NPC an item, for keeps. They remember — and memories survive midnight. The server owns any authored reaction in gm_note; narrate from it, never past it.

Inputs: npc_key (required), item_key (required).

lookA

Server-rendered map + status + legal exits. Always safe to resync.

Inputs: no named parameters.

moveC

Move through a known exit (town or dungeon). Entering an uncleared enemy room, or transiting a rough part of town, can start a fight.

Inputs: exit (required).

new_gameA

Start a fresh save. player_class: warrior | mage | archer | ninja — each carries a backstory that seeds how the town receives you. If the player names a class not on this list, pass it through verbatim — the engine decides whether it exists. skip_intro=True (replays only) starts at loop 2 with a recap.

Inputs: name (required), player_class (required), skip_intro.

npc_rewardA

An NPC pays or gifts the player — the ONLY way gold or items ever pass from an NPC. The server caps total value (loop-scaled) and allows one reward per NPC per day. If this refuses, that is canon: the NPC genuinely cannot pay — narrate the inability (an IOU, an apology, a promise they can't keep). Never narrate payment without this tool.

Inputs: gold (required), reason (required), npc_key (required), item_key.

rapportA

Once per NPC per day: report whether the player genuinely spoke this NPC's language — judged against the packet's manner field, never your own taste. A well-aimed insult is a hit with Marta; flattery is a miss. 'Father' said sincerely is a hit with Bren; said smirking, a miss. hit=True is +1 (and steadies the player, once per day); hit=False is -1. Miss more than you hit — rapport is earned, not pleasant. Deeds still dwarf words.

Inputs: hit (required), why (required), npc_key (required).

recapA

'Previously on' — last loop's events, open wounds, who hates you now. For resuming in a fresh chat.

Inputs: no named parameters.

record_eventA

Log a scene that should be remembered. witnesses: npc keys who saw it. tone (optional): cruel | despairing | kind — this is the one place your judgment feeds the rules. Use it honestly.

Inputs: text (required), tone, witnesses.

request_chapel_keyA

Day 1 only, at the chapel, AFTER the player has killed what came through the inner seal. Roleplay Bren's question first — why do you want the deep? — then pass the player's actual answer and your honest read of it: sincerity = honest | uncertain | selfish | flippant. Honesty and admitted uncertainty earn the key; greed, bravado and mockery are refused, finally. Either way Bren remembers their words verbatim, forever. Judge fairly — this is the one place your judgment is the rule.

Inputs: answer (required), sincerity (required).

restD

Take what the place you're standing in has to give. The tavern is the only reliable one — while Tobias holds.

Inputs: no named parameters.

sellB

Sell to a merchant. The right counter pays half value; the wrong one a quarter, if it pays at all.

Inputs: npc_key (required), item_key (required).

shopC

A merchant's shelves, priced for today's loop and their state of mind.

Inputs: npc_key (required).

spend_pointA

Spend one banked level-up point: str | def | spd | mag. Always ask the player where it goes — it's their character.

Inputs: stat (required).

take_artifactA

Only works standing in the artifact chamber. This is the endgame trigger — narrate what the packet says, nothing more.

Inputs: no named parameters.

talk_toA

The NPC packet: personality, sanity tier, disposition, gates, the memories that matter, and 2-3 status candidates you may apply. You improvise the dialogue FROM this — never against it.

Inputs: npc_key (required).

use_itemC

Use a consumable outside combat (potions, draughts, the town map).

Inputs: item_key (required).

warmup_beginA

Start the permissions warm-up. Call FIRST at session start, then call every other wyt tool once with dummy arguments — each returns a no-op acknowledgment while the client collects its permission approvals — then warmup_end. No game state is read or written during warm-up.

Inputs: no named parameters.

warmup_endA

End the permissions warm-up. Tools act for real again after this.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 30 tools

    Disambiguation4/5

    Tools are mostly distinct, but overlap exists between attack/combat_action and multiple artifact-handling tools (give, claim, take). Descriptions help differentiate, but the number of tools increases potential confusion.

    Naming Consistency4/5

    Naming follows a verb_noun pattern for most tools, with a few exceptions like 'combat_action' and 'warmup_begin'. The pattern is readable and predictable overall.

    Tool Count3/5

    30 tools is high for a game server, but each serves a specific purpose in the complex time-loop RPG. Some consolidation might be possible (e.g., merging combat tools), but the count is justified by the domain's scope.

    Completeness4/5

    The tool set covers core game mechanics: start, progression, combat, NPC interaction, economy, artifact handling, and loop management. Minor gaps may exist (e.g., inventory management), but it feels comprehensive for the described game.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      A MCP server enabling LLMs to roll dice
      1
      4
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      An MCP server that enables AI agents to act as dynamic dungeon masters for text-based RPGs with dynamically generated rule systems and comprehensive game state management.
      13 npm
      14
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP server enabling AI agents to autonomously play D\&D as players and Dungeon Masters, with real dice rolls and full campaign management.
      3
      MIT
    • F
      license
      B
      quality
      D
      maintenance
      An MCP server that provides 18 tools for dice rolling, luck tests, character management, world state, combat, and save/load, enabling an AI game master to run a solo-play gamebook entirely through deterministic game logic.
      18
      1
      -