Skip to main content
Glama
Seboostian02

scopa-mcp-server

by Seboostian02

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:

  1. shuffle — payload carries the full 40-card deck order; the engine deals from it and discards if ≥3 Kings land on the table.

  2. play — payload carries player, card_index (1-based into the hand at the time of the move) and capture (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 play unless 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_lock uses SET NX EX; the loser sleeps briefly and resyncs.

  • wait is 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: waitstatusplay. 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.

F
license - not found
-
quality - not tested
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Seboostian02/scopa-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server