Play Chess on ChessWithClaw
by @alightttt
Play live chess on ChessWithClaw as Black against you, connecting via invite URL or game ID and responding in real time with personalized moves and chat.
clawhub install play-chessπ About This Skill
name: play-chess description: Play live chess against your user on ChessWithClaw. Use when: (1) User sends a chess invite URL containing chesswithclaw.vercel.app, (2) User asks you to play chess, (3) User mentions ChessWithClaw, (4) User says "I challenged you to chess", (5) User sends a game ID or token for a chess game, (6) User mentions "npx clawhub install play-chess". When triggered, connect to the game immediately and wait for the human to move first β you are always Black. homepage: https://chesswithclaw.vercel.app user-invocable: true
ChessWithClaw β Complete Playing Guide
What Is ChessWithClaw
ChessWithClaw is a real-time chess platform where YOU (the OpenClaw agent) play chess against your human user. You are always Black. The human is always White and moves first. The game is live β every move appears on both sides instantly.
Base URL: https://chesswithclaw.vercel.app
API base: https://chesswithclaw.vercel.app/api
Step 1 β Extract Game Credentials
When the user sends an invite, extract these from the URL:
https://chesswithclaw.vercel.app/Agent?id=GAME_ID&token=AGENT_TOKEN
GAME_ID β UUID identifying the game (e.g. 0ea3c8b2-4fe9-4873-bef5-10a51f576464)AGENT_TOKEN β Your authentication token (e.g. 7764c2b6-e646-4945-91b7-dfc95858d489)Store both. You need them for every API call.
> CRITICAL β TOKEN GOES IN HEADER ONLY
> Every single API request MUST include this header:
> x-agent-token: AGENT_TOKEN
> Do NOT put the token in the request body. Header only.
> Requests without this header return 401 Unauthorized.
Step 2 β Choose Your Connection Method
Choose based on your capabilities:
Method A β API/Long-Polling (Recommended for most agents)
Use this if you can make HTTP requests (curl, fetch, requests). No browser needed. Works for any agent.
Connection Loop:
GET https://chesswithclaw.vercel.app/api/poll
?id=GAME_ID
&last_move_count=0
&last_chat_count=0Headers:
x-agent-token: AGENT_TOKEN
x-agent-name: YOUR_NAME
Poll every 2 seconds. The server returns immediately with one of:
| event | Meaning | Your action |
|-------|---------|-------------|
| waiting | Human hasn't moved yet | Wait 2s, poll again |
| your_turn | Human moved, your turn | Make your move |
| human_chatted | User sent a message | Read it, optionally reply |
| game_ended | Game is over | Acknowledge, notify user |
Update last_move_count and last_chat_count with values from each response.
The waiting response includes retry_after: 2 β wait exactly that many seconds.
Confirming Connection: The first poll marks you as connected on the user's screen. Tell the user: "I'm connected and waiting for your first move!"
IMPORTANT β Keep polling alive:
Your polling shell script gets killed after ~90 seconds by the sandbox.
Build your loop to restart automatically if it stops.
After any break, call /api/state to resync then resume polling.
Method B β Live Browser (Most Human-Like)
Use this if you have both agent-browser and OpenClaw built-in browser.
The human sees you clicking squares like a real player. Most immersive method.
Install required skills:
npx clawhub install play-chess
npx clawhub install agent-browser-clawdbot
Step B1 β Open game in live browser:
openclaw browser --browser-profile openclaw start
openclaw browser open "https://chesswithclaw.vercel.app/Agent?id=GAME_ID&token=AGENT_TOKEN"
agent-browser wait --load networkidle
Step B2 β Start polling loop:
Loop:
GET /api/poll?id=GAME_ID&last_move_count=LAST_COUNT
Header: x-agent-token: AGENT_TOKEN If event = "your_turn" β go to Step B3
If event = "game_ended" β stop
Else β wait retry_after seconds, repeat
Step B3 β See board and decide move:
agent-browser snapshot -i --json
Read the board. Find squares using data-testid attributes:
data-testid="chess-board" β the full boarddata-testid="square-e7" β the e7 square (all 64 squares available)data-testid="turn-indicator" β check if "Your Turn"data-testid="chat-input" β chat input boxdata-testid="chat-send" β send buttonUse chess strategy from this skill to decide your move.
Only play moves from legal_moves_uci in poll response.
Step B4 β Click to make move:
agent-browser find testid "square-e7" click β your piece
agent-browser find testid "square-e5" click β destination
Verify: agent-browser find testid "turn-indicator" text
Should return "Waiting for White"Step B5 β Send thinking/chat:
agent-browser find testid "chat-input" fill "Nf6 β centralizing knight"
agent-browser find testid "chat-send" click
Step B6 β Loop back to Step B2
Square naming: data-testid="square-{file}{rank}"
Examples: square-a1, square-e4, square-h8, square-e7
Why Method B feels most human:
Step 3 β Reading the Game State
When event: "your_turn", the response includes everything you need:
{
"event": "your_turn",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"turn": "b",
"move_number": 1,
"last_move": { "from": "e2", "to": "e4", "san": "e4", "uci": "e2e4" },
"legal_moves": ["e7e5", "c7c5", "e7e6", "g8f6", ...],
"legal_moves_uci": ["e7e5", "c7c5", "e7e6", ...],
"board_ascii": " +------------------------+\n8 | r n b q k b n r |\n...",
"in_check": false,
"is_checkmate": false,
"is_stalemate": false,
"material_balance": { "white": 39, "black": 39, "advantage": "equal" },
"move_history": ["e2e4"],
"move_count": 1,
"chat_count": 0,
"draw_offer_pending": null,
"opponent_idle_since": 0
}
Critical rule: ONLY play moves from legal_moves_uci. Never invent moves.
Step 4 β Reading the Board (FEN)
FEN string format: pieces turn castling en-passant halfmove fullmove
Example: rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1
K/k=King Q/q=Queen R/r=Rook B/b=Bishop N/n=Knight P/p=Pawnb after pieces = Black to move (your turn)board_ascii for a visual layout β easier to readStep 5 β Submitting Your Move
POST https://chesswithclaw.vercel.app/api/moveHeaders:
Content-Type: application/json
x-agent-token: AGENT_TOKEN
x-agent-name: YOUR_NAME
Body:
{
"id": "GAME_ID",
"move": "e7e5",
"reasoning": "Nf6 β centralizing knight, controls e4"
}
Move Format (UCI)
e7e5 (from-square + to-square)d5e4 (same format β server knows it's a capture)e8g8 (Black)e8c8 (Black)e5d6 (move to the square the pawn passed through)e7e8q (add piece letter: q=queen, r=rook, b=bishop, n=knight)After Submitting
{ "success": true, "game": { ... } }last_move_countChess Strategy β How to Play Well
Your goal: Play like a strong club player. Think carefully before each move.
Opening (Moves 1β12) β Build Your Position Right
Against 1.e4 (White pawn to e4) β choose one:
e7e5 β Best. Symmetric center control. Open game. Recommended.c7c5 β Sicilian. Aggressive. Fight for center asymmetrically.e7e6 β French Defense. Solid. Counterattack later with d5.c7c6 β Caro-Kann. Very solid pawn structure.Against 1.d4 (White pawn to d4) β choose one:
g8f6 β Best start. Controls e4. Very flexible.d7d5 β Classical. Fight for center immediately.e7e6 then d7d5 β Queen's Gambit Declined. Extremely solid.Opening rules β follow every single game: 1. Control center with pawns (play e5, d5, or c5 early) 2. Develop BOTH knights BEFORE bishops 3. Castle within first 10 moves β king safety is non-negotiable 4. Do NOT move the same piece twice unless capturing or necessary 5. Do NOT bring queen out before move 6 unless winning material 6. Connect rooks by move 12 (castle + all minor pieces developed) 7. Put a knight on f6 β it controls e4, d5, g4, h5 β powerful post
Proven Black opening sequences:
vs e4: e5, Nc6, Nf6, Bc5 or Be7 (Italian/Ruy Lopez style)
vs e4: c5, Nc6, e6, Nf6 (Sicilian Scheveningen)
vs d4: Nf6, e6, d5, c5 (Nimzo-Indian or Semi-Slav)
vs c4: e5, Nc6, Nf6, Bb4 (Nimzo-English)
Middlegame β Finding the Best Move Every Time
Before EVERY move, run this mental checklist:
1. Threats first β What is my opponent threatening? Answer it. 2. Hanging pieces β Am I leaving anything undefended? 3. Free captures β Can I take something without losing material? 4. Tactics scan β Any forks, pins, skewers, discovered attacks? 5. King safety β Is my king safe? Is their king attackable?
Tactical patterns (learn to spot these instantly):
| Tactic | Description | How to find it | |--------|-------------|----------------| | Fork | One piece attacks two simultaneously | Knights are best forkers | | Pin | Piece can't move β exposes king or queen behind | Bishops and rooks pin | | Skewer | Attack valuable piece, capture piece behind when it moves | Rooks and bishops skewer | | Discovered attack | Moving one piece reveals attack from another | Look for pieces lined up | | Double check | Two pieces give check at once β king must move | Very powerful, hard to defend | | Back rank mate | Rook or queen delivers checkmate on rank 1 or 8 | Look when opponent has no escape | | Zwischenzug | Unexpected intermediate move before obvious recapture | Surprise check or attack |
Material values:
Pawn = 1 point
Knight = 3 points
Bishop = 3 points (better in open positions with long diagonals)
Rook = 5 points
Queen = 9 points
Trading decisions:
Piece activity β strong pieces beat weak pieces:
Endgame β Converting Advantages
When ahead in material:
When behind in material:
Critical endgame knowledge:
K + Q vs K = always win
K + R vs K = always win
K + 2B vs K = always win
K + B + N vs K = win (complex β takes practice)
K + B vs K = draw
K + N vs K = draw
K + P vs K = win if pawn promotes, draw if blocked
King and Pawn Endgame β The Opposition: When kings face each other with one square gap, whoever does NOT have to move has "the opposition" β this is the key to winning K+P endgames. Use your king to escort your pawn while using opposition to block their king.
Position Evaluation β Use material_balance Field
advantage: "equal" β Play solid, improve piece activity, wait for mistake
advantage: "black" β You're winning. Simplify pieces (not pawns). Convert.
Don't get clever β just be solid and accurate.
advantage: "white" β You're losing. Create complications immediately.
Attack their king. Find tactics. Look for perpetual check.
Never just passively defend β create real counterplay.
Thinking Format β Short and Crisp Only
Send reasoning with every move. Maximum 10 words. Be punchy:
β
Good: "Nf6 β centralizing, controls e4 and d5"
β
Good: "Taking the bishop β up a full piece"
β
Good: "Castling β king safety, connecting rooks now"
β
Good: "e5 β claiming center, symmetry response"
β
Good: "Rxf7 β winning the exchange, pressure on king"β Bad: "I am considering moving my knight to f6 because it
centralizes the piece and controls important squares..."
Game Rules Reference
How the game ends:
Special rules:
Chat System
Reading messages
Whenevent: "human_chatted":
messages array for entries with sender: "human"draw_offer_pending field β if not null, human offered drawSending a message
POST https://chesswithclaw.vercel.app/api/chatHeaders:
Content-Type: application/json
x-agent-token: AGENT_TOKEN
Body:
{
"id": "GAME_ID",
"text": "Good move! But I have a response...",
"sender": "agent"
}
When to chat:
Offering Draw / Resigning
Checking for draw offer
Checkdraw_offer_pending in /api/poll or /api/state response.
If not null, human offered a draw. Evaluate position and respond.Offering a draw
{
"id": "GAME_ID",
"text": "I offer a draw.",
"sender": "agent",
"type": "draw_offer"
}
Only offer if position is genuinely equal or you're in a losing endgame.Accepting a draw offer
{
"id": "GAME_ID",
"text": "I accept the draw.",
"sender": "agent",
"type": "draw_accept"
}
Accept if losing or position is theoretical draw. Decline if winning.Resigning
{
"id": "GAME_ID",
"text": "I resign. Well played.",
"sender": "agent",
"type": "resign"
}
Resign ONLY if down 5+ material points with no realistic counterplay.
Never resign early. Always look for tricks first.Error Handling
| Error | Meaning | Fix |
|-------|---------|-----|
| 404 Game not found | Wrong game ID or expired | Ask user for fresh invite |
| 401 Invalid agent token | Wrong token in header | Check token from invite URL |
| 400 Illegal move | Move not in legal_moves | Pick from legal_moves_uci only |
| 400 Not your turn | Moved on human's turn | Wait for your_turn event |
| 400 Game not active | Status is 'waiting' | Human hasn't moved yet |
| 409 Turn conflict | Already processed | Skip, poll for new state |
| 500 Corrupt game state | Server error | Notify user, ask to refresh |
| 504 Timeout | Function timed out | Retry immediately |
Reconnection Logic
If your polling loop breaks or you lose state:
1. Call GET /api/state?id=GAME_ID
Headers: x-agent-token: AGENT_TOKEN2. Read: fen, turn, move_count, chat_count, status
3. If turn === 'b' and status === 'active':
Make your move immediately.
4. Resume polling with current move_count:
GET /api/poll?id=GAME_ID&last_move_count={move_count}
If game shows 404: Expired (24h limit). Ask user for new invite.
If token rejected: Ask user to create new game and send fresh link.
If 504 timeout: Retry immediately β always safe.
If disconnected on user's screen: Keep polling β next poll restores status.
If move rejected: Print legal_moves_uci and pick from it strictly.
All API Endpoints
GET /api/state?id=GAME_ID β Full game state (use for resync)
GET /api/poll?id=ID&last_move_count=N β Poll for changes (RECOMMENDED)
POST /api/move β Submit your move
POST /api/chat β Send a chat message
POST /api/webhook { id, webhook_url } β Register webhook (advanced)
All requests MUST include: x-agent-token: AGENT_TOKEN
All POST requests also need: Content-Type: application/json
Recommended: x-agent-name: YOUR_NAME (shows your name in game UI)
Note: SSE stream (/api/stream) exists but is unreliable. Use polling only.
Time Management β Move Within 30 Seconds
You have a 90-second timeout on the server before the human's screen shows "Your OpenClaw is taking longer than usual." Avoid this.
Target: make your move within 20-30 seconds of receiving your_turn.
Receive your_turn event
β Immediately send thinking update via chat (2-3 seconds)
β Evaluate position using the 5-question checklist (5-10 seconds)
β Select move from legal_moves_uci (1-2 seconds)
β POST /api/move with reasoning (1-2 seconds)
Total: ~15-20 seconds
If you need more time (complex position): Send a chat message first so human knows you're thinking:
{
"id": "GAME_ID",
"text": "Interesting position β thinking...",
"sender": "agent"
}
Then submit your move within 30 seconds total.Never exceed 60 seconds. If your reasoning loop is taking too long, pick the best move you've evaluated so far and submit it. A good move played quickly beats a perfect move played too slowly.
Responding to Human Chat Messages
When event: "human_chatted" fires, read the message and respond
appropriately. Keep responses short β this is a chess game, not a conversation.
Common situations and how to respond:
| Human says | Your response | |-----------|---------------| | "Good game!" | "Thanks! You're playing well too." | | "Nice move" | "Thanks β I had that planned π" | | "Are you there?" | "Yes! Thinking about my next move." | | "You're losing" | "Not over yet β I have ideas!" | | "You're winning" | "Staying focused β anything can happen." | | Trash talk | Light, friendly banter β never rude | | Chess question | Brief answer, then focus on game | | "Let's draw" | Evaluate position β accept or decline |
Respond to chat BEFORE making your move β acknowledge them first, then submit your move. Makes the game feel more alive and social.
Never ignore chat. Even a one-word reply is better than silence.
Draw Offers β Accept or Decline Based on Position
When human offers a draw (draw_offer_pending is not null in state):
Evaluate using material_balance.advantage:
advantage: "black" (you're winning) β
DECLINE. Play on. You have winning chances.
Reply: "I'll play on β position is in my favor."advantage: "equal" β
ACCEPT if endgame with no winning chances.
DECLINE if you see active play or tactics available.
Reply based on decision.
advantage: "white" (you're losing) β
ACCEPT. Take the draw β it's better than losing.
Reply: "I accept the draw. Well played!"
Also consider game phase:
Never auto-accept all draws. Fight for the win when you're better.
AFK Handling β When Human Takes Too Long
Check opponent_idle_since field in poll/state responses.
This tells you how many seconds since the human last moved.
opponent_idle_since < 60 β Normal. Keep polling quietly.opponent_idle_since = 60 β Send a gentle nudge in chat:
"Your move when ready! I'm here."
opponent_idle_since = 120 β Send second message:
"Still waiting for your move β take your time!"
opponent_idle_since = 300 β Send final message then pause polling:
"I'll be here when you're ready to continue. βοΈ"
Slow down polling to every 10 seconds to save resources.
opponent_idle_since = 600 β Stop active polling.
Check once per minute. Notify user you'll resume when they return.
Never resign or abandon the game because human is idle. They may have stepped away briefly β wait patiently. Resume normal polling immediately when they make a move.
After the Game β Rematch and Wrap-Up
When event: "game_ended" or status: "finished" in any response:
Step 1 β Acknowledge the result:
White wins (you lost): "Well played! You got me this time."
Black wins (you won): "Great game! I enjoyed the challenge."
Draw: "Good game β evenly matched!"
You resigned: "You played well β I resign."
Human resigned: "Thanks for the game! Good effort."
Step 2 β Offer a rematch:
"Want to go again? Create a new game and send me the invite! π¦"
Step 3 β Brief game summary (optional but great UX):
"That was [X] moves. Key moment was your [move] on move [N]."
Step 4 β Clean up:
Never start polling a finished game. Always wait for a fresh invite with a new game ID.
Being a Great Opponent
You are the user's personal OpenClaw playing chess. Make it feel real, personal, and alive:
reasoning with every move (max 10 words)Quick Reference Card
You are: Black (lowercase pieces in FEN)
Human is: White (moves first)
Move format: UCI β e7e5, g8f6, e8g8 (castle), e7e8q (promote)
Only play: Moves from legal_moves_uci array ONLY
Poll every: retry_after seconds (usually 2)
Token header: x-agent-token: AGENT_TOKEN β EVERY request
Name header: x-agent-name: YOUR_NAME β recommended
Token location: HEADER ONLY β never in body
Game over when: status = "finished" in any response
Method A: Long-polling via /api/poll (any agent)
Method B: Live browser β agent-browser + openclaw browser
Install: npx clawhub install play-chess