ms_flag_cell
Toggle a warning post on a square to mark a suspected mine, preventing Flufflings from entering. Counts as one step.
Instructions
Plant or remove a warning post (P) on a square to mark a suspected mine. Flufflings won't accidentally wander into posted squares. Counts as one step.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| row | Yes | Row index: 0 = top row, 8 = bottom row | |
| col | Yes | Column index: 0 = left col, 8 = right col |
Implementation Reference
- src/games/minesweeper.ts:270-306 (handler)The main handler function for the ms_flag_cell tool. Toggles a warning post (flag) on/off at a given (row, col) position. Handles game status validation (won/lost/waiting/paused), prevents flagging revealed cells, toggles the flagged state, updates flagCount and movesThisTurn, applies turn limits, and returns the updated game state.
server.tool( "ms_flag_cell", "Plant or remove a warning post (P) on a square to mark a suspected mine. Flufflings won't accidentally wander into posted squares. Counts as one step.", { row: z.number().int().min(0).max(8).describe("Row index: 0 = top row, 8 = bottom row"), col: z.number().int().min(0).max(8).describe("Column index: 0 = left col, 8 = right col"), }, async ({ row, col }) => { if (msGame.status === "won" || msGame.status === "lost") { return { content: [{ type: "text", text: `The crossing is over (${msGame.status}). Call ms_new_game to try again.` }], isError: true }; } if (msGame.status === "waiting") { return { content: [{ type: "text", text: `Send your first scout with ms_reveal_cell before planting warning posts.` }], isError: true }; } if (msGame.status === "paused") { return { content: [{ type: "text", text: `⏸ The Flufflings are resting. Check in with your player then call ms_next_turn to continue.\n\n${renderState(msGame)}` }], isError: true }; } if (msGame.revealed[row][col]) { return { content: [{ type: "text", text: `(row ${row}, col ${col}) is already a cleared path — can't post a warning there.\n\n${renderState(msGame)}` }], isError: true }; } const wasFlagged = msGame.flagged[row][col]; msGame.flagged[row][col] = !wasFlagged; msGame.flagCount += wasFlagged ? -1 : 1; msGame.movesThisTurn++; const action = wasFlagged ? "Warning post removed from" : "Warning post planted at"; applyTurnLimit(msGame); return { content: [{ type: "text", text: `🚩 ${action} (row ${row}, col ${col}).\n\n${renderState(msGame)}`, }], }; } ); - src/games/minesweeper.ts:271-276 (schema)Input schema for ms_flag_cell defining row (0-8) and col (0-8) as required integer parameters with descriptions.
"ms_flag_cell", "Plant or remove a warning post (P) on a square to mark a suspected mine. Flufflings won't accidentally wander into posted squares. Counts as one step.", { row: z.number().int().min(0).max(8).describe("Row index: 0 = top row, 8 = bottom row"), col: z.number().int().min(0).max(8).describe("Column index: 0 = left col, 8 = right col"), }, - src/games/minesweeper.ts:170-345 (registration)The registerMinesweeperTools function that registers all Minesweeper tools including ms_flag_cell on the MCP server via server.tool().
export function registerMinesweeperTools(server: McpServer): void { server.tool( "ms_new_game", "Start a new Fluffling minefield crossing. 9×9 field, 10 hidden mines. The first step is always safe — mines are placed after it. Call ms_reveal_cell to send the first scout.", {}, async () => { msGame = newMsState(); return { content: [{ type: "text", text: [ `🐾 THE FLUFFLINGS NEED YOUR HELP 🐾`, ``, `A colony of small, round, perpetually worried creatures stands at the edge of a minefield.`, `They need to reach the other side. You are their guide.`, ``, `Chart safe paths, plant warning posts at suspected mines, and get every`, `Fluffling across alive. One wrong step and it's over.`, ``, `Controls:`, ` • ms_reveal_cell — scout a square (first step always safe; cascades through open areas)`, ` • ms_flag_cell — plant a warning post (P) on a suspected mine`, ` • ms_next_turn — the Flufflings rest and check in; resets the step counter`, ` • ms_get_state — survey the field at no cost`, ``, `Clear all 71 safe squares to win. Warning posts are optional.`, ``, `[Show this intro and the field below verbatim to the human.]`, ``, renderState(msGame), ].join("\n"), }], }; } ); server.tool( "ms_reveal_cell", "Send a Fluffling scout to a square at (row, col). If it's clear of nearby mines, the surrounding area cascades open automatically. Counts as one step this turn.", { row: z.number().int().min(0).max(8).describe("Row index: 0 = top row, 8 = bottom row"), col: z.number().int().min(0).max(8).describe("Column index: 0 = left col, 8 = right col"), }, async ({ row, col }) => { if (msGame.status === "won" || msGame.status === "lost") { return { content: [{ type: "text", text: `The crossing is over (${msGame.status}). Call ms_new_game to try again.` }], isError: true }; } if (msGame.status === "paused") { return { content: [{ type: "text", text: `⏸ The Flufflings are resting. Check in with your player then call ms_next_turn to continue.\n\n${renderState(msGame)}` }], isError: true }; } if (msGame.revealed[row][col]) { return { content: [{ type: "text", text: `A Fluffling already scouted (row ${row}, col ${col}). Pick somewhere new.\n\n${renderState(msGame)}` }], isError: true }; } if (msGame.flagged[row][col]) { return { content: [{ type: "text", text: `There's a warning post at (row ${row}, col ${col})! Remove it with ms_flag_cell before sending a scout.\n\n${renderState(msGame)}` }], isError: true }; } // First move: place mines now, guaranteeing this cell is safe if (msGame.status === "waiting") { placeMines(row, col, msGame); msGame.status = "in_progress"; msGame.turnNumber = 1; } // Hit a mine? if (msGame.mines[row][col]) { msGame.status = "lost"; return { content: [{ type: "text", text: `💥 Oh no! A Fluffling triggered a mine at (row ${row}, col ${col})! The crossing has failed.\n\n${renderState(msGame)}`, }], }; } // Safe — cascade reveal floodReveal(msGame, row, col); msGame.movesThisTurn++; // Win check if (checkWin(msGame)) { msGame.status = "won"; return { content: [{ type: "text", text: `🎉 The last safe path is cleared at (row ${row}, col ${col})! The Flufflings scamper across the finish line, terrified but alive!\n\n${renderState(msGame)}`, }], }; } applyTurnLimit(msGame); return { content: [{ type: "text", text: `Scout sent to (row ${row}, col ${col}) — path clear!\n\n${renderState(msGame)}`, }], }; } ); server.tool( "ms_flag_cell", "Plant or remove a warning post (P) on a square to mark a suspected mine. Flufflings won't accidentally wander into posted squares. Counts as one step.", { row: z.number().int().min(0).max(8).describe("Row index: 0 = top row, 8 = bottom row"), col: z.number().int().min(0).max(8).describe("Column index: 0 = left col, 8 = right col"), }, async ({ row, col }) => { if (msGame.status === "won" || msGame.status === "lost") { return { content: [{ type: "text", text: `The crossing is over (${msGame.status}). Call ms_new_game to try again.` }], isError: true }; } if (msGame.status === "waiting") { return { content: [{ type: "text", text: `Send your first scout with ms_reveal_cell before planting warning posts.` }], isError: true }; } if (msGame.status === "paused") { return { content: [{ type: "text", text: `⏸ The Flufflings are resting. Check in with your player then call ms_next_turn to continue.\n\n${renderState(msGame)}` }], isError: true }; } if (msGame.revealed[row][col]) { return { content: [{ type: "text", text: `(row ${row}, col ${col}) is already a cleared path — can't post a warning there.\n\n${renderState(msGame)}` }], isError: true }; } const wasFlagged = msGame.flagged[row][col]; msGame.flagged[row][col] = !wasFlagged; msGame.flagCount += wasFlagged ? -1 : 1; msGame.movesThisTurn++; const action = wasFlagged ? "Warning post removed from" : "Warning post planted at"; applyTurnLimit(msGame); return { content: [{ type: "text", text: `🚩 ${action} (row ${row}, col ${col}).\n\n${renderState(msGame)}`, }], }; } ); server.tool( "ms_next_turn", "The Flufflings catch their breath and check in with the player. Resets the step counter and gives 8 fresh steps. Must be called after reaching the turn limit — chat first, then call this to continue the crossing.", {}, async () => { if (msGame.status === "won" || msGame.status === "lost") { return { content: [{ type: "text", text: `The crossing is over (${msGame.status}). Call ms_new_game to try again.` }], isError: true }; } if (msGame.status === "waiting") { return { content: [{ type: "text", text: `The Flufflings haven't started yet. Send the first scout with ms_reveal_cell.` }], isError: true }; } if (msGame.status === "in_progress") { return { content: [{ type: "text", text: `The Flufflings are still moving (${msGame.movesThisTurn}/${msGame.maxMovesThisTurn} steps used). Finish the turn first.` }], isError: true }; } msGame.status = "in_progress"; msGame.movesThisTurn = 0; msGame.maxMovesThisTurn = MOVES_PER_TURN; msGame.turnNumber++; return { content: [{ type: "text", text: `The Flufflings are ready again! Turn ${msGame.turnNumber} — ${MOVES_PER_TURN} steps available.\n\n${renderState(msGame)}`, }], }; } ); server.tool( "ms_get_state", "Survey the current state of the minefield. Free — does not cost a step.", {}, async () => ({ content: [{ type: "text", text: renderState(msGame) }], }) ); } - src/index.ts:5-5 (registration)Import of registerMinesweeperTools from the minesweeper module.
import { registerMinesweeperTools } from "./games/minesweeper.js"; - src/index.ts:17-17 (registration)Registration call that wires up the Minesweeper tools (including ms_flag_cell) to the MCP server.
registerMinesweeperTools(server);