@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
JavaScript
#!/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);
}