A ChatGPT Chess application built with OpenAI Apps SDK, allowing you to play chess through conversation with an interactive board widget.
Now following OpenAI Apps SDK best practices!
- 🎮 Play chess using algebraic notation (e4, Nf3, O-O, etc.)
- 📊 Interactive chess board widget in ChatGPT
- 🤖 AI opponent suggestions (ChatGPT can play moves)
- 🔍 Stockfish engine analysis integration
- 📝 Move history tracking
- ✅ Full move validation and game state management
- 📋 Game status and player information display
- 🧩 Mate in 1 tactical puzzles (Easy, Medium, Hard)
- Backend: Python MCP Server using FastMCP with python-chess
- Frontend: React component with chess.js + react-chessboard
- Build System: Vite for optimal development experience
- Integration: OpenAI Apps SDK patterns with proper hooks
ChessMCP/
├── server/
│ ├── main.py # Python MCP server (renamed from server.py)
│ └── requirements.txt # Python dependencies
├── src/
│ ├── chess-board/ # Chess board component
│ │ └── index.tsx
│ ├── types.ts # Shared TypeScript types
│ ├── use-openai-global.ts # Hook for window.openai access
│ ├── use-widget-state.ts # Hook for widget state
│ └── use-widget-props.ts # Hook for tool props
├── assets/ # Build output (generated by Vite)
│ └── chess-board.html
├── vite.config.mts # Vite configuration
├── package.json # Root dependencies
├── tsconfig.json # TypeScript config
└── README.md
- Python 3.8+
- Node.js 18+
- Stockfish chess engine (optional, for analysis)
- Install Python dependencies:
cd server
pip3 install -r requirements.txt- Install Stockfish (optional, for engine analysis):
# macOS
brew install stockfish
# Ubuntu/Debian
sudo apt-get install stockfish- Install Node.js dependencies:
npm install- Build the frontend component:
npm run buildThis generates assets/chess-board.html which the server loads.
- The MCP server is configured in your
~/.cursor/mcp.json:
{
"mcpServers": {
"chess": {
"command": "python3",
"args": ["/path/to/ChessMCP/server/main.py"],
"env": {
"PYTHONPATH": "/path/to/ChessMCP/server"
}
}
}
}npm run build# Terminal 1: Start Vite dev server
npm run dev
# Terminal 2: Start Python server
cd server
python3 main.pynpm run servePlay chess directly without server/OAuth:
python3 chess_local_test.pyCommands:
♟️ > move e4
♟️ > move e5
♟️ > status
♟️ > stockfish
♟️ > puzzle medium
📖 See: LOCAL_TESTING.md
Test via HTTP (requires server running):
# Terminal 1: Start server
cd server && python3 main.py
# Terminal 2: Start client
python3 chess_client.py📖 See: CLIENT_USAGE.md
The Chess MCP server now includes Google OAuth 2.1 authentication for secure ChatGPT integration!
📖 Quick Start: OAUTH_QUICK_START.md (5 minutes)
📖 Detailed Setup: GOOGLE_OAUTH_SETUP.md (Step-by-step)
📖 Next Steps: NEXT_STEPS.md (What to do now)
📖 Troubleshooting: CHATGPT_CONNECTOR_TROUBLESHOOTING.md
Quick start:
# 1. Set up Google OAuth credentials (see GOOGLE_OAUTH_SETUP.md)
# 2. Create server/.env with credentials
# Terminal 1: Start server
cd server
pip3 install -r requirements.txt
python3 main.py
# Terminal 2: Expose with ngrok
ngrok http 8000
# Update server/.env with ngrok URL, restart server
# Then add connector in ChatGPT Settings > Connectors
# URL format: https://YOUR-SUBDOMAIN.ngrok-free.app (no /mcp suffix)In ChatGPT, start a chess game by making your first move:
ChessMCP e4
or
Let's play chess! I'll start with e4
The interactive chess board will appear showing your move.
Simply type your move in algebraic notation:
Nf3
e5
Bc4
Nc6
Supported notation:
- Basic moves:
e4,Nf3,d5 - Captures:
exd5,Nxf7 - Castling:
O-O(kingside),O-O-O(queenside) - Pawn promotion:
e8=Q,a1=N - Check:
Qh5+ - Checkmate:
Qf7#
Check game status:
chess_status
What's the current status?
Load a puzzle:
Show me a chess puzzle
Give me a hard puzzle
Get engine analysis:
Ask Stockfish for the best move
Reset game:
chess_reset
Let's start a new game
The server exposes five tools:
- Input:
move(string) - Algebraic notation - Output: Updated board state with FEN, move history, game status
- Widget: Renders interactive chess board
- Input:
depth(int, default: 15) - Analysis depth - Output: Best move, evaluation, principal variation
- Widget Accessible: Can be called from the widget UI
- Input: None
- Output: Confirmation of reset
- Widget: Shows fresh starting position
- Input: None
- Output: Game status, current turn, player names, move count, recent moves
- Use: Check game progress and who's moving
- Input:
difficulty(string: "easy", "medium", "hard") - Output: Mate-in-1 puzzle position with hint
- Widget: Shows puzzle position on the board
- Use: Practice tactical patterns and checkmate recognition
The component uses OpenAI Apps SDK hooks for clean, reactive code:
Access window.openai properties reactively:
const theme = useOpenAiGlobal("theme");
const displayMode = useOpenAiGlobal("displayMode");Get the current tool output:
const toolOutput = useToolOutput<ChessToolOutput>();
// Returns: { fen, move, status, turn }Get tool response metadata:
const metadata = useToolResponseMetadata<ChessMetadata>();
// Returns: { move_history_list, legal_moves, etc. }Persist component state across sessions:
const [widgetState, setWidgetState] = useWidgetState<ChessWidgetState>({
lastDepth: 15,
analysisVisible: false
});- ⚡️ Fast hot module replacement during development
- 📦 Optimized production builds
- 🎯 TypeScript support out of the box
- 🔧 Better error messages
- 🎨 Source maps for debugging
The build creates a single HTML file containing:
- Bundled React component
- All JavaScript dependencies
- Inline CSS
- Proper structure for Skybridge runtime
See vite.config.mts for the build configuration. The setup:
- Bundles
src/chess-board/index.tsx - Includes all dependencies
- Wraps in HTML with proper structure
- Outputs to
assets/chess-board.html
The server follows Apps SDK patterns:
# Load HTML from assets
@lru_cache(maxsize=None)
def load_widget_html(component_name: str) -> str:
html_path = ASSETS_DIR / f"{component_name}.html"
return html_path.read_text(encoding="utf8")
# Proper MIME type
MIME_TYPE = "text/html+skybridge"
# Resource registration
@mcp._mcp_server.list_resources()
async def list_resources() -> List[types.Resource]:
return [types.Resource(...)]
# Resource handler
async def handle_read_resource(req) -> types.ServerResult:
html = load_widget_html("chess-board")
return types.ServerResult(types.ReadResourceResult(...))All tools include proper metadata:
{
"openai/outputTemplate": "ui://widget/chess-board.html",
"openai/widgetAccessible": True,
"openai/resultCanProduceWidget": True,
"openai/toolInvocation/invoking": "Making move...",
"openai/toolInvocation/invoked": "Move played"
}- Ensure component is built:
npm run build - Verify
assets/chess-board.htmlexists - Check server logs for errors
Update the path in server/main.py:
STOCKFISH_PATH = "/path/to/stockfish"If TypeScript errors occur:
npm run typecheckMake sure all Python dependencies are installed:
cd server
pip3 install -r requirements.txt- Moved from
web/tosrc/directory - Root-level
package.jsonandvite.config.mts - Proper Apps SDK project structure
useOpenAiGlobalfor reactive window.openai accessuseWidgetStatefor persistent state managementuseToolOutputanduseToolResponseMetadatafor props
- Replaced esbuild with Vite
- Hot module replacement in development
- Better TypeScript support
- Faster builds
- Proper resource loading from
assets/ - Correct MIME type (
text/html+skybridge) - Better error handling
- CORS middleware for development
- Comprehensive TypeScript types
- Apps SDK-compatible interfaces
- Chess-specific type definitions
Feel free to enhance the app with:
- Opening analysis and endgame tablebases
- Multiple game sessions
- PGN import/export
- Time controls
- Online multiplayer
- More puzzle types
MIT License - feel free to use and modify!