scopa-mcp-server
An MCP server for playing the Italian card game Scopa, supporting 2-4 players, Redis-backed event logging, real-time synchronization, and an optional LLM opponent.
README
Scopa MCP Server
A Model Context Protocol (MCP) stdio server that manages the state for
a two-player (or 2–4 player) game of Italian Scopa, with an
append-only Redis event log, low-latency wait synchronisation via
Redis Pub/Sub, a live spectator mode, an LLM opponent driven by the
Anthropic SDK, and a read-only web view.
Each player runs as its own python main.py --game=<id> --player=<A|B|C|D>
process. The processes do not talk to each other directly — they
coordinate through an event log in Redis. Game state is fully
reconstructable by replaying the log, so any process can be killed and
restarted without losing the game.
Rules implemented
Neapolitan deck (40 cards, values 1–10 with Knave=8, Knight=9, King=10):
- Player A leads every round.
- Initial deal of 3 cards per player + 4 on the table. If the would-be table contains three or more Kings, the shuffle is discarded and a new one is drawn.
- Forced capture: you cannot trail a card if any capture is possible.
- Single-match priority: if a single table card matches the played card's value, you cannot take a multi-card subset that sums to it.
- Scopa (clearing the table) gives +1 point, except on the very last play of the round.
- Round end: any cards left on the table go to the last player who captured. Round scoring awards 1 point each for most-cards, most-coins, settebello (7 of Coins), and primiera, plus the scopa points accrued during the round.
- First player to reach 11 wins the game. Ties at the top force another round.
Architecture
Player A process Player B process Spectator process
(python main.py (python main.py (python main.py
--game=g --player=A) --game=g --player=B) --game=g --spectate)
│ │ │
└─────────────┬───────────┴────────────┬─────────────┘
│ │
Redis LIST Redis Pub/Sub
scopa:{g}:events scopa:{g}:events
(append-only event log) (push seq numbers)
Single source of truth = the event log. The engine
(scopa/engine.py) is a pure state machine: given a fresh
ScopaGame() and the full list of events, it always reaches the same
state. There are only two event types:
- shuffle — payload carries the full 40-card deck order; the engine deals from it and discards if ≥3 Kings land on the table.
- play — payload carries
player,card_index(1-based into the hand at the time of the move) andcapture(1-based table indices). The engine applies the play, handles mid-round refills, and runs end-of-round scoring from these alone.
Concurrency:
- Turn ordering is derived from the event log; a process refuses
to append a
playunless its replayed state says it's this player's turn. - Shuffle lock: at the start of each round, only one process must
generate the shuffle.
RedisStore.acquire_shuffle_lockusesSET NX EX; the loser sleeps briefly and resyncs. waitis push-based: the tool subscribes to the pub/sub channel and returns the instant the other player appends an event. No polling.- Spectator runs the same replay+subscribe loop but is read-only.
Running it
1. Redis (local Docker is easiest on Windows)
docker run -d -p 6379:6379 --name scopa-redis redis:alpine
2. Python deps
python -m venv .venv
source .venv/Scripts/activate # or .venv/bin/activate on macOS/Linux
pip install -e . redis aiohttp python-dotenv
Requires Python 3.11+.
3. Two-player greedy test
bash run_test.sh
Spawns two MCP subprocesses, plays a full game with a deterministic greedy strategy, and prints every status read and every play.
4. Wait-driven test with a live spectator
bash run_wait_test.sh
Two players run as concurrent asyncio tasks, each blocked on the
wait tool until it's their turn. A third subprocess joins in
--spectate mode and writes a live snapshot log to
spectator_<game>.log.
5. LLM opponent (Claude Haiku)
# put your key in .env first: echo 'ANTHROPIC_API_KEY=sk-...' > .env
# terminal 1
python play_greedy.py --game=llm_demo --player=A
# terminal 2
python python/chat.py --game-id=llm_demo
# or: bash llm_play.sh llm_demo
The LLM is driven through the Anthropic SDK's native tool-use
protocol. python/chat.py spawns an MCP subprocess for player B,
converts the MCP tool list to the Anthropic tool schema, and then
loops: wait → status → play. It does not use any LangChain /
LangGraph layer.
6. 3–4 players
python test_multi.py -n 4
The engine is generalised over an N-player tuple (2–4). main.py
takes --players=A,B,C,D on the first process to kick off a
multi-player game; later processes pick the player list up from the
Redis meta hash.
7. Read-only web view
python main.py --game=web_demo --player=A --web
# → http://127.0.0.1:19000/ (port = 19000 + offset(player))
Meta-refreshing HTML that shows the current hand, the table, the
per-player scores, and the most recent move, as seen from the running
player's seat. Deliberately bare-bones: the MCP status tool is still
the authoritative read path.
MCP tools exposed by main.py
| Tool | Purpose |
|---|---|
status |
Returns the human-readable view of the hand, the table, the scores and whose turn it is. |
play |
Play a card. card (1-based hand index) and capture (list of 1-based table indices; empty to trail). |
wait |
Block until it is this player's turn. Returns the most recent opponent move. |
spectate |
Full move history so far (available to both players and --spectate processes). |
Event log inspection
You can read the raw log of any running game directly from Redis:
docker exec -it scopa-redis redis-cli LRANGE 'scopa:<game_id>:events' 0 -1
Files
| Path | Role |
|---|---|
scopa/cards.py |
Neapolitan deck and primiera scoring table |
scopa/engine.py |
Pure Scopa state machine driven by events |
scopa/store.py |
Async Redis event store with pub/sub and shuffle lock |
scopa/web.py |
Read-only aiohttp view |
main.py |
MCP stdio server + argument parsing |
test.py / run_test.sh |
Two-player greedy test runner |
test_wait.py / run_wait_test.sh |
Wait-driven two-player test + live spectator |
test_multi.py |
3–4 player greedy test runner |
play_greedy.py |
Single-player greedy driver (useful as the opponent when the other player is the LLM) |
python/chat.py / llm_play.sh |
Claude-driven LLM opponent over the Anthropic SDK |
Known limitations
- Redis persistence is disabled by default; stopping the container drops the game state. That is intentional for demos.
- The greedy test player is deliberately unsophisticated — it is a harness for the server, not a strong opponent.
- The web view uses 2s meta-refresh rather than WebSockets; this keeps the server simple and well-behaved on Windows.
- No pytest suite per se — the three end-to-end test scripts are the regression coverage for the engine and the MCP server. A proper unit-test suite for the capture/scopa/primiera edge cases would be a good next step.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.