zekel-mcp
by xephoid
README.md
# zekel-mcp
An MCP server for turn-based board games, with no games built in.
It keeps track of sessions, seats and moves. It runs AI turns, lets a second player join from another device, saves sessions (MongoDB, SQLite or memory), and offers numbered move menus, rules teaching and a web debug page. It knows no game rules: you register games, and every rule lives in them.
## The two packages
| Package | What it is |
|---|---|
| `@zekel/game-kit` (`packages/game-kit`) | What a game is built against: the `Game` interface, `SessionState`, `GameError`, and shared helpers (a seeded random-number helper, an MCTS search, seat names, rules sections, setup choices, the game log). It depends on nothing. |
| `@zekel/mcp` (`packages/mcp`) | The server. It depends on `@zekel/game-kit` and the MCP SDK. |
A game imports only `@zekel/game-kit`. A program that serves games depends on `@zekel/mcp`, registers its games and starts the server:
```js
import { registerGame, runServer, SERVER_INSTRUCTIONS } from '@zekel/mcp';
await runServer({
serverInfo: { name: 'my-games', version: '1.0.0' },
instructions: SERVER_INSTRUCTIONS,
registerGames: () => registerGame(myGame),
});
```
`examples/race-to-ten.mjs` is a complete, tiny game (two players take turns adding 1 or 2; whoever reaches exactly 10 wins), written so it can be read in one sitting.
## Running it
Needs Node 22.13 or later and pnpm.
```
pnpm install
pnpm build # builds both packages
pnpm test # the unit tests
pnpm smoke # plays a whole game of the example through the real MCP tools
```
Settings come from environment variables: `SESSION_STORE` (`mongodb`, the default; `sqlite`; or `memory`), `MONGODB_URI`, `SESSION_DB_PATH`, `MCP_TRANSPORT` (`stdio`, the default, or `http`), `PORT` and `MCP_BEARER_TOKEN`.
## Tests
The server's tests play a made-up game, Lucky Tic-Tac-Toe (`packages/mcp/src/mcp/__tests__/luckyTicTacToe.ts`): tic-tac-toe where players choose X or O at setup and each holds a secret lucky-square card, which is enough to exercise setup choices, secret information, private answers, reports from a physical table and on-screen play. It exists only for tests.
`clientMode.test.ts` replays fixed conversations and compares them with a recording (`__fixtures__/llmDrivenBaseline.json`), so any change to the text a model reads is made on purpose. After such a change, re-record with `UPDATE_BASELINE=1 pnpm test` and review the diff before committing it.
## What a game can add
Beyond the required methods, a game can add its own MCP tools and resources (`getExtraTools`, `getExtraResources`), a board drawing for the web debug page (`webAssetsDir`), a rules-teaching outline, setup checklists, a move-menu layout and more. These are the optional fields of `Game` in `packages/game-kit/src/game.ts`.
## Not done yet
- The instructions text was reworded to take its examples from the author's own games (Sweetlands Imperium, Cybernoir 2127, Neither Guts nor Gears). It has not yet been re-checked against weak models, which read it in every game.
- Saved sessions carry one version number for the whole server. When a game changes the shape of its saved state, that number has to change for everyone. Each game should version its own state.
- Only one copy of `@zekel/game-kit` may be installed: the server recognises a game's errors by their type. Game packages should list it as a peer dependency.
## Licence
Licensed under Apache 2.0. See `LICENSE`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues