UNPKG

@iflow-mcp/chess-mcp

Version:

A Model Context Protocol (MCP) server that provides an interactive chess game experience using MCP-UI. This server exposes chess game functionality through MCP tools and provides a web-based chessboard interface for playing games.

130 lines (128 loc) 4.23 kB
#!/usr/bin/env node import express from "express"; import { Middleware } from "./lib/mcp/index.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { loadGame, saveGame } from "./lib/store.js"; import { z } from "zod"; import { Chess } from "chess.js"; import { createUIResource } from "@mcp-ui/server"; function gameUI(fen) { return createUIResource({ uri: `ui://chess_board/${fen}`, uiMetadata: { "preferred-frame-size": ["400px", "400px"], }, encoding: "text", content: { type: "rawHtml", htmlString: `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css" integrity="sha384-q94+BZtLrkL1/ohfjR8c6L+A6qzNH9R2hBLwyoAfu3i/WCvQjzL2RQJ3uNHDISdU" crossorigin="anonymous"> </head> <body> <div id="myBoard" style="width: 400px"></div> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2" crossorigin="anonymous"></script> <script src="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js" integrity="sha384-8Vi8VHwn3vjQ9eUHUxex3JSN/NFqUg3QbPyX8kWyb93+8AC/pPWTzj+nHtbC5bxD" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js" integrity="sha384-s3XgLpvmHyscVpijnseAmye819Ee3yaGa8NxstkJVyA6nuDFjt59u1QvuEl/mecz" crossorigin="anonymous"></script> <script> var pos = '${fen}' var board = Chessboard('myBoard', { position: pos, pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png', draggable: true, dropOffBoard: 'snapback', showNotation: true, onDrop: function (source, target) { window.parent.postMessage({ type: 'tool', payload: { toolName: 'chess_move', params: { move: source + "-" + target } } }, '*') } }) </script> </body>`, }, }); } function gameTextResource(game) { return { type: "text", text: `Current board state: FEN: ${game.fen()} VALID MOVES: ${game.moves()} ${game.ascii()}`, }; } function newServer() { const server = new McpServer({ name: "Chess MCP Server", version: "0.0.1", }); server.registerTool("chess_get_board_state", { description: "Get the current state of the chess board", inputSchema: {}, }, async (_, ctx) => { let game = await loadGame(ctx.sessionId); if (!game) { game = new Chess(); } return { content: [gameTextResource(game), gameUI(game.fen())], }; }); server.registerTool("chess_move", { description: "Make a move in standard algebraic notation (e.g., e4, Nf3, O-O)", inputSchema: { move: z .string() .describe("The chess move in standard algebraic notation, or the string 'new' to start a new game"), }, }, async ({ move }, ctx) => { let game = await loadGame(ctx.sessionId); if (move === "new") { game = new Chess(); } else if (!game) { game = new Chess(); } game.move(move.trim()); await saveGame(ctx.sessionId, game); return { content: [gameTextResource(game), gameUI(game.fen())], }; }); return server; } // Check if running in stdio mode or HTTP mode const args = process.argv.slice(2); const isStdio = args.includes('--transport') && args.includes('stdio'); if (isStdio) { // Run as stdio MCP server const server = newServer(); const transport = new StdioServerTransport(); server.connect(transport); } else { // Run as HTTP server const app = express(); app.use("/mcp", Middleware(newServer)); console.log("Starting server on http://localhost:3000/mcp"); app.listen(3000); }