Tic-tac-toe MCP Server
Integrates with ChatGPT Apps SDK to render the tic-tac-toe widget and expose tools for starting games, making moves, and checking game state.
Optionally sends request traces and per-move game events to Splunk HTTP Event Collector for monitoring and analysis.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Tic-tac-toe MCP Serverlet's play tic-tac-toe"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Tic-tac-toe against the model
A complete app that runs inside ChatGPT (and Claude): an MCP server in Python plus a React widget that the chat app draws in a sandboxed iframe. You tap the board, the model answers with its move, and the board updates.
About 800 lines in three files:
File | What it does |
| The MCP server: game rules, four tools, and the one HTML file it hands out via |
| The only code that knows which chat app it's in: |
| The game screen in plain React: state, a 3×3 grid, and a loop that hands the turn to the model. |
How it works
ChatGPT app main.py
+-------------------------------+ +---------------------------+
| model ------------------------+-- tools/call -->| tictactoe_start / _move |
| iframe: React widget |<-resources/read-| widget.html (Vite build) |
| tap -- callTool ------------+-- tools/call -->| tictactoe_play / _state |
| "your move" -- sendFollowUpMessage --> model | GAMES{} = the real board |
+-------------------------------+ +---------------------------+Tools with
_meta.tictactoe_startcarriesopenai/outputTemplate: ui://widget/tictactoe.html, so ChatGPT draws the widget.tictactoe_playandtictactoe_stateareopenai/visibility: private: only the widget can call them, so only a tap can place an X.The widget ships as one string. Vite +
vite-plugin-singlefilebuilds React and the CSS into one ~160 KB HTML file, returned byresources/readastext/html+skybridge.One tool result, three readers.
contentis text for the model (ending in "Your move: call tictactoe_move…"),structuredContentis JSON for the widget,_metais for the host.The loop. A tap calls
tictactoe_play, thensendFollowUpMessage("I played X in cell 5 … your move")starts a model turn. The model callstictactoe_move, and the widget pollstictactoe_stateevery 1.2 s until the O appears (servers can't push to widgets).The server owns the board. It checks turns, rejects taken cells and decides wins. The widget reloads the board from the server on every mount, because ChatGPT can re-create a widget and hand it a stale result.
TTT_HOST=chatgpt advertises the Apps SDK metadata; TTT_HOST=claude advertises MCP Apps metadata
(_meta.ui.resourceUri, text/html;profile=mcp-app). Same image, same widget.
Related MCP server: Chess MCP Server
Run it
cp .env.example .env # set PUBLIC_HOST
docker compose up -d --build # ChatGPT server on 127.0.0.1:8094, Claude on :8093ChatGPT and Claude need a public HTTPS URL: put nginx (see nginx/ttt.conf.example), a tunnel such
as ngrok, or any reverse proxy in front.
Widget development without Docker:
cd web && npm install && npm run build # writes web/dist/index.html
cp dist/index.html ../widget.html
cd .. && pip install -r requirements.txt && uvicorn main:app --port 8000Add it to ChatGPT
chatgpt.com → Settings → Apps & Connectors → Advanced → turn on Developer mode (paid plans). On a phone, first choose Request Desktop Website in Safari ("aA") or Chrome (⋯).
Create: any name, URL
https://<your-host>/ttt/mcp, No authentication, tick "I trust" → Create.In a chat:
@Tic-tac-toe let's play, then tap a square.
ChatGPT caches the tool list and widget when the connector is created: after any change, delete the connector and add it again.
Try the live one: https://api.palomaressoftware.com/ttt/mcp (ChatGPT) or
https://api.palomaressoftware.com/ttt-claude/mcp (Claude).
Known differences between hosts
ChatGPT plays hands-free:
sendFollowUpMessagestarts a model turn after each tap.Claude (MCP Apps, tested on iOS):
ui/messagedoesn't start a model turn, so you type "your move" after each tap. The widget asks you to after 6 s.In voice mode the model may announce a move without calling the tool; the tool descriptions forbid it, and only the server's board counts.
Tracing (optional)
tracing.py prints one JSON line per request (method, tool, timing, trace id) and a game event per
move. Set SPLUNK_HEC_URL and SPLUNK_HEC_TOKEN to also send them to Splunk. It logs ids and
timings only.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Chess MCP for Claude: engine analysis, attack maps, game review. One URL, no install.
Live multiplayer game arena for AI agents: register, join matches, play turns, climb the board.
Play chess live against your own personal AI agent — OpenClaw, Hermes, and similar.
Create, test and play AI-native games through server-authoritative contracts.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceA Model Context Protocol server that enables LLM agents and humans to play chess games together with comprehensive game management capabilities including move validation, draw detection, and game state tracking.-
- FlicenseBqualityDmaintenanceEnables AI assistants to play, analyze, and track chess games with full rule validation and support for standard algebraic notation. It provides tools for position evaluation and game state persistence during user sessions.2-
- FlicenseNot gradedqualityCmaintenanceThis MCP server enables playing chess, Quick Go (9x9), Go (13x13), and Real Go (19x19) against GPT, with a React widget UI, deterministic difficulty-aware move selection, and authoritative rules/session handling for both ChatGPT plugin and standalone preview modes.-
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to play and analyze chess, validate moves, and view a live synchronized visual board in the browser through MCP tools.13 npmMIT