feat: MCP agent for AI-powered Magic gameplay - #34
Open
reifocS wants to merge 26 commits into
Open
Conversation
MCP server that lets Claude Code play MTG on a live Maginet board. Two modes: opponent (plays against human) and copilot (read-only advice). Connects via WebSocket transport alongside existing PeerJS sync.
12 tasks covering: package scaffold, game state, visibility filter, Scryfall client, WebSocket server, MCP tools, CLI entry, browser transport, peerStore wiring, setup UI, and e2e integration test.
Plain TypeScript class (no React/Zustand) that manages the agent's deck, hand and board shapes with actionId tracking; mirrors useCardReducer and useShapeStore patterns for the Node.js MCP context.
Implements AgentWebSocketServer (server.ts) with start/stop lifecycle, single-client connection tracking, bidirectional JSON message passing, and connect/disconnect event hooks. Covered by 4 integration tests.
Implements createToolHandlers() with 16 game tools (draw, play, tap, counter management, etc.) wired to AgentGameState, plus TOOL_DEFINITIONS array for MCP registration. Includes full TDD test suite (10 tests).
Wires AgentGameState, AgentWebSocketServer, and McpServer together. Parses --port/--visibility CLI args, registers all MCP tools (including loadDeck via Scryfall and loadSnapshot), and connects over stdio. Adds zod dependency and fixes null-check in server.ts address handling.
Adds an AI Agent panel to the multiplayer step in SetupScreen that lets users connect/disconnect a local MCP agent by port number (default 3210).
- Remove unused waitFor/wait helpers from e2e integration test - Replace `server as any` with `server as unknown as AgentWebSocketServer` in test files - Remove unused ToolContext import from mcp.test.ts - Rename unused _args params to _ in mcp.ts handler functions - Fix no-this-alias in websocket.test.ts by using a shared wsRef container object - Remove unused _peerId parameter names from websocket.ts connect/disconnect - Add argsIgnorePattern/varsIgnorePattern to ESLint config for _ prefix convention
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Adds an "Agent" pill button in the top-left toolbar next to "Connect". Click to connect to the local MCP agent, click again to disconnect. Shows "Agent On" with active styling when connected.
- Add CardMeta (name, typeLine, oracleText, manaCost, power, toughness) to Scryfall cards and expose via getHand/getBoardState - Fix sync message handling: properly parse channel-snapshot and channel-patch payloads with nested structure - Broadcast agent shape changes to browser on mutation and on connect - Add placeText and updateText MCP tools for board annotations - Track agent peer IDs in peerStore for proper connection filtering - Add .mcp.json config for local development
- Track action-log and action-log-snapshot messages in the MCP server - Add getActionLog MCP tool to query recent opponent actions - Forward browser action-log entries to the agent via sendAgentMessage - Add sendAgentMessage helper to peerStore for agent-directed messages
- Null ws reference on close in browser transport (prevents stale ref) - Call syncClient.stop() on unexpected disconnect in peerStore - Close previous client before accepting new one in agent server (handles browser page refresh without orphaning old socket)
sendMessage now forwards to the agent sync client, so custom messages (action-log, heartbeat, card-state-sync) reach the agent without manual sendAgentMessage calls. Remove sendAgentMessage — dead code.
Remove peer?.id guards on sendMessage calls so the agent receives action-log, random-event, and card-state-sync messages even when no PeerJS peer is connected (solo-vs-agent mode).
- Add lastPlayedSrcs to CardState and cardSrcs to ActionLogEntry - Add moveShape and placeRect MCP tools - Add fetchCardMetaByImageUrl for looking up opponent card metadata - Add registerCardMeta and cardNames to action log entries
- Critical: fix Scryfall deduplication — deduplicate names before API call, expand results back to requested counts (4x Lightning Bolt now correctly produces 4 cards) - Fix biased shuffle — replace sort-based shuffle with Fisher-Yates - Fix untapAll N broadcasts — add batch updateAgentShapes method, untapAll now notifies listeners once instead of N times - Fix card_faces null safety — filter faces without image_uris - Fix loadSnapshot — clear existing shapes before loading new ones - Fix unbounded actionLog — cap at 200 entries - Fix WS server error handler — persist after startup instead of only during the start() promise - Validate --visibility CLI argument - Add clearAgentShapes method to AgentGameState
- Fix pushActionLog calling itself instead of actionLog.push (would stack overflow on first action log message) - Replace .substr(2, 9) with .slice(2, 11) in all three files
- Add debugMessages MCP tool and WS message logging for sync debugging - Send hand card data (id, src) in card-state-sync payload for copilot - Use explicit opponentHandCount field in visibility filter - Resolve unknown opponent card metadata via Scryfall image URL lookup
This was referenced Mar 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
maginet-agent/Node.js package — an MCP server that lets Claude Code play or advise on Magic: The Gathering games on a live Maginet boardsrc/sync/transport/websocket.ts) implementing the existingSyncTransportinterface from@vescofire/peersyncTwo modes
16 MCP tools
Observation:
getGameState,getHand,getBoardState,getDeckInfoActions:
loadDeck,loadSnapshot,drawCard,mulligan,playCard,tapCard,untapAll,sendToHand,sendToDeck,shuffleDeck,addCounter,removeCounter,flipCard,transformCardHow it works
node maginet-agent/dist/index.js --port 3210)ws://localhost:3210Test plan