使用说明
国际象棋mcp
最小值, 能力优先 ChatGPT应用程序,允许用户在ChatGPT内用仅显示的棋盘下棋, 权威规则执行,以及一个可选的LLM对手。
核心合同: (状态,移动Uci)→ 新状态
- 输入:
fen(董事会状态)+moveUci(UCI)- 输出:
fen(更新的棋盘状态)+游戏状态
目标
- 做: 采用合法的棋步,推进回合,并保持游戏的一致性。
- 显示: 在ChatGPT中呈现仅显示的棋盘UI。
- (可选)知道: 提供解释、提示或分析,而不会使UI/逻辑变得脆弱。
非目标(v1):完整的lichess风格分析套件、帐户、配对、打开浏览器等。
架构概述
组件
-
小部件UI(React)
- 通过以下方式在ChatGPT中作为iframe运行
text/html+skybridge. - 呈现来自的板和状态
window.openai.toolOutput. - 仅提供聊天指令;该模型驱动所有工具调用。
- 避免移动输入控件,使小部件保持仅显示状态。
- 通过以下方式在ChatGPT中作为iframe运行
-
MCP服务器(Python)
- 注册UI模板资源。
- 展示用于启动游戏、验证/应用招式和生成对手招式的工具。
- 强制执行 幂等性 和 权威过渡.
- 退货 小
structuredContent对于型号+可选_meta对于小部件。
-
型号(ChatGPT)
- 协调工具调用和叙述。
- 如果使用LLM对手,则必须从工具提供的合法招式中进行选择(没有自由形式的招式发明)。
数据模型
典型州:FEN
我们使用 FEN(福赛斯-爱德华兹符号) 作为经典游戏快照。
为什么?
- 编码板+侧面移动+铸造权+通行证+移动计数器。
- 紧凑、标准、易于验证。
标识符
gameId--游戏会话的稳定标识符。openai/widgetSessionId--由ChatGPT提供;可用于关联呼叫/日志。
工具合同(MCP)
设计原则:
- 工具具有权威性:小部件在工具确认合法性之前从不进行移动。
- 幂等:模型/运行时可能会重试工具。
- 结构化内容小:只有模型应该看到的。
- 仅在_meta中批量显示UI:合法搬家名单、悠久的历史等。
- 单个小部件渲染:仅
render_game返回小部件模板。
工具: render_game
渲染小部件快照。这是 唯一的工具 这设置
openai/outputTemplate.
输入
snapshot:object(必须包括fen和gameId,加上可选状态字段)
输出(结构化内容)
{
"type": "chess_snapshot",
"gameId": "g_123",
"fen": "...",
"status": "in_progress",
"turn": "w"
}
工具: new_game
开始一场新的象棋游戏。
输入
side(可选):"white" | "black"
输出(结构化内容)
{
"type": "chess_snapshot",
"gameId": "g_123",
"fen": "...",
"status": "in_progress",
"turn": "w"
}
工具: apply_move
验证并应用单个移动。
输入
gameIdfenmoveUci(字符串):UCI(e2e4,e7e8q)
输出(结构化内容)
{
"type": "chess_snapshot",
"gameId": "g_123",
"legal": true,
"fen": "...",
"status": "in_progress",
"turn": "b",
"lastMove": { "uci": "e2e4", "san": "e4" },
"check": false
}
如果非法:
{
"type": "chess_snapshot",
"gameId": "g_123",
"legal": false,
"fen": "<unchanged>",
"error": "Illegal move: king would be in check"
}
工具: legal_moves (只读)
返回当前职位的合法移动。
输入
fen
输出(结构化内容)
{ "type": "legal_moves", "movesUci": ["e2e4", "g1f3"] }
工具: choose_opponent_move (LLM对手模式)
通过将模型约束到合法的移动列表来选择对手的移动。
输入
fen
输出(结构化内容)
{
"type": "opponent_choice",
"movesUci": ["g8f6", "d7d5", "e7e5"],
"policy": {
"mustChooseFromMovesUci": true,
"chooseExactlyOne": true
}
}
模型侧合同(在服务器提示符中执行)
- 模型必须从以下位置返回一个UCI移动
movesUci. - 如果模型想要辞职/提供抽签,它必须使用明确的非移动工具(未来)。
服务器端安全
- 即使在模型选择之后,服务器也必须通过调用
apply_move(fen, moveUci). - 如果模型输出非法移动,请重新运行
choose_opponent_move以更严格的指示和/或减少候选人名单。
工具: play_turn (可选方便)
原子“玩家移动,然后对手移动”,以尽量减少往返。
输入
gameIdfenplayerMove
输出
- 更新快照+对手移动信息
小部件用户界面
职责
- 从中渲染棋盘和状态
toolOutput.fen. - 提供聊天优先指令,以便用户在对话中键入动作。
- 避免本地移动输入,依靠工具确认的更新。
州安置
- 业务数据(真相): MCP服务器/工具输出(
fen,status,history) - UI状态(短暂): 可选,仅用于显示首选项
- 跨会话首选项(可选): 您的后端数据库
React运行时集成
使用 useOpenAiGlobal("toolOutput") 以保持小部件的反应性。
UI手动测试检查表
- 构建小部件包:
cd web && npm install && npm run build. - 通过聊天(模特电话)开始新游戏
new_game). - 键入合法入住聊天,并确认模型通过以下方式呈现更新
render_game. - 键入一个非法的进场聊天,并确认模型报告了错误,没有更换电路板。
- 合法动作后,确认对手动作出现后,模型运行对手循环。
- 达到游戏结束状态(对弈/相持/对弈)并确认状态渲染。
当地E2E(剧作家)
这将使用本地MCP客户端在真实的MCP服务器上运行小部件 桥(不需要ChatGPT),使用Playwright的Python包。
前提条件:
- Python deps:
./.venv/bin/pip install -e server - 剧作家(Python):
./.venv/bin/pip install -r e2e/requirements.txt - 剧作家浏览器:
./.venv/bin/python -m playwright install --with-deps
运行:
./.venv/bin/pytest e2e/test_widget_e2e.py
笔记:
- 测试运行器构建小部件并自动启动MCP服务器。
- 服务器在上运行
http://127.0.0.1:8000/mcp通过server/run_http.py.
转弯循环
-
用户键入移动聊天。
-
模型将输入解析为UCI(或使用
legal_moves为了消除歧义)。 -
模型调用
apply_move({ gameId, fen, moveUci }). -
如果非法→ 模型报告错误。
-
如果合法→ 模型调用
render_game以渲染新快照。 -
对手步骤(选择一个):
- LLM对手: 呼叫
choose_opponent_move(fen)然后apply_move. - 发动机对手: 呼叫
get_best_move(fen)然后apply_move.
- LLM对手: 呼叫
-
使用渲染更新的快照
render_game,如果达成,则陷入僵局。
聊天驱动的移动解析
- 该模型应将用户输入解析为UCI动作。
- 如果输入不明确或无效,请调用
legal_moves并要求澄清。 - 总是打电话
apply_move验证和更新FEN(无乐观更新)。
回购布局
chess-mcp/
server/
app.py # MCP server (FastMCP/FastAPI)
chess_rules.py # move parsing + legality + FEN transitions
storage.py # optional: gameId → state/history persistence
templates/
chess-board-v1.html # text/html+skybridge wrapper
web/
src/
App.jsx # React widget
hooks/
useOpenAiGlobal.js
dist/
widget.js # bundled JS (inlined into template)
widget.css # bundled CSS (inlined into template)
README.md
小部件构建和服务(任务6)
FastMCP服务器将构建的小部件包直接嵌入天桥中 HTML模板。
构建小部件
cd web
npm install
npm run build
构建输出生命周期 web/dist/:
web/dist/widget.jsweb/dist/widget.css
服务器如何加载小部件
server/app.py从以下位置读取捆绑包web/dist/.server/templates/chess-board-v1.html包含内联CSS和JS的占位符:/* INLINE_CSS *//* INLINE_JS */
- 服务器在运行时替换这些标记,并返回一个
text/html+skybridge文件与<style>和<script type="module">.
CSP配置
openai/widgetCSP 由服务器设置。唯一需要的津贴是
服务器源本身 connect_domains。由于捆绑包是内联的,因此
没有可供分配的外部资源域。对于本地开发,set
WIDGET_ALLOW_LOCALHOST=true 包括 http://localhost:8000 在CSP中
connect_domains 列表。
缓存破坏模板URI
当您对小部件进行破坏性更改时,请插入版本 server/app.py:
WIDGET_VERSION = "v1"→"v2"- 模板文件名:
server/templates/chess-board-v1.html→chess-board-v2.html - 模板URI会自动更新:
ui://widget/chess-board-v2.html
部署(渲染)
-
部署
server/作为具有HTTPS的web服务。 -
确保小部件资源集:
openai/widgetDomainopenai/widgetCSP.connect_domains(您的渲染域)resource_domains(如果从CDN加载资产)
-
更新捆绑包时缓存中断UI模板URI:
ui://widget/chess-board-v1.html→...-v2.html
安全性和可靠性
- 永远不要归还秘密
structuredContent,content,_meta,或widgetState。 - 将只读工具标记为
annotations.readOnlyHint: true. - 保持
structuredContent小;放入大量数据_meta. - 对待
(fen, moveUci)作为幂等性的确定性转换。
路线图
- v1:合法游戏+小部件板+LLM对手受限于合法动作
- v1.1:移动历史记录、撤消、辞职、新游戏
- v1.2:提示(前3个候选动作),基本评估
- v2:谜题模式、课程、开场白、全屏分析视图