NightFall 是一个本地优先的多人狼人杀项目,同时提供网页端、ChatGPT Apps SDK Widget 和通用 MCP tools。游戏服务器负责身份分配、阶段推进、夜晚行动、投票结算和胜负判断;网页、ChatGPT Widget 与 MCP 客户端只是不同的玩家入口。
狼人杀适合验证 MCP App 的边界,因为它同时具备多人协作、隐藏信息、强规则状态和自然语言博弈。
传统网页狼人杀解决了“点按钮开局”的问题,但玩家如果临时忘记规则、想快速理解局势、想让系统帮忙执行自然语言指令,就会离开游戏界面去问另一个工具。普通聊天机器人又缺少可靠的游戏状态、权限边界和可确认的写操作。
NightFall 的设计目标是把这两种体验接在一起:
- 网页端保留完整游戏体验,适合房主、玩家和观战者直接操作。
- ChatGPT Widget 提供嵌入式玩家控制台,适合在对话中创建房间、加入游戏、查看身份和执行行动。
- 自然语言用于解释规则、辅助操作、总结公开局势和生成主持叙事。
- MCP tools 把自然语言意图转成可验证的后端命令。
- 服务器始终是唯一权威裁判,AI 入口不能决定身份、死亡、投票、胜负或技能结果。
这个项目的核心取舍是:让 AI 成为受权限约束的玩家界面,而不是掌握隐藏信息的裁判。
- 完整网页游玩:创建房间、加入房间、选择规则包、配置计时器、查看身份、夜晚行动、白天发言、投票、终局揭示。
- ChatGPT 内嵌 Widget:在 ChatGPT 里打开可视玩家控制台,不只依赖工具调用文本。
- 自然语言辅助操作:可以对 ChatGPT 说“我加入房间 ABC123”“我要守护 2 号”“帮我推进阶段”“总结公开局势”。
- 写操作确认机制:投票、夜晚行动、推进阶段等写操作采用 prepare/confirm nonce,减少误操作。
- 隐藏信息隔离:REST、Socket.IO、MCP 与 Widget 都不会返回原始
GameState、全量身份表、夜晚目标或私密日志。 - AI DM 叙事:默认本地模板,可选 OpenAI provider。公开叙事和私密叙事使用不同的安全输入。
- 主持流程自动化:身份揭示、夜晚行动、白天讨论、投票倒计时,超时自动跳过或弃票。
- 公开观战与安全回放:观战页只显示公开状态、公开日志和 sanitized replay。
- 多客户端兼容:
/mcp-app面向 ChatGPT Apps SDK;/mcp保留纯 JSON MCP tools,方便其他 MCP 客户端接入。 - 可部署到公网:支持 SQLite 持久化、Docker、生产 CORS、rate limit、OAuth protected resource metadata 和 healthcheck。
房主可以选择规则包、AI DM 叙事模式和主持节奏。快速局、标准局、长讨论局都可以直接配置,也可以手动设置身份揭示、夜晚行动、白天讨论和投票时间。
每个玩家只看到自己的身份、阵营、私密提示、当前阶段、倒计时和可执行行动。玩家视角由 getPlayerView(state, viewerPlayerId) 生成,不直接返回原始状态。
观战页适合直播、旁观和复盘。它只显示公开阶段、公开玩家座位、公开日志和安全回放。回放条目不包含 actor id、command type、夜晚目标或隐藏身份。
ChatGPT 里可以打开 NightFall Widget,完成创建房间、加入房间、选择玩家身份、查看状态、发言、选择行动和确认写操作。对话窗口则负责规则解释、自然语言操作和复盘辅助。
pnpm install
pnpm start:local默认地址:
- Web:
http://localhost:5173 - API:
http://localhost:8787 - Health:
http://localhost:8787/health - MCP:
http://localhost:8787/mcp - ChatGPT Apps SDK MCP:
http://localhost:8787/mcp-app - 观战页:
http://localhost:5173/spectate/<ROOM_CODE>
一局游戏的基本流程:
- 房主打开 Web,输入昵称并创建房间。
- 房主选择规则包:
classic_mvp、classic_plus或advanced_12。 - 房主选择主持节奏:快速局、标准局、长讨论局,或手动设置每个阶段的秒数。
- 其他玩家输入房间代码加入。
- 达到规则包支持人数后,房主开始游戏。
- 玩家在私密控制台查看自己的身份和可执行行动。
- 夜晚阶段按角色顺序行动,超时自动跳过。
- 白天讨论结束后进入投票,超时自动弃票。
- 服务器结算死亡、技能、胜负和终局身份揭示。
- 观战页和安全回放用于旁观与复盘。
NightFall 的 ChatGPT 入口是 /mcp-app。它提供 Apps SDK tool metadata、output schema 和 ui://widget/nightfall.html widget resource。相关背景可参考 OpenAI 的 Apps SDK 文档。
本地临时测试:
brew install cloudflared
pnpm tunnel:mcp-app脚本会构建 server 和 widget,启动 HTTPS tunnel,并打印:
Connector URL: https://.../mcp-app
Resource metadata: https://.../.well-known/oauth-protected-resource
Local game UI: http://localhost:5173
在 ChatGPT Developer Mode 中创建 connector 后,可以直接输入:
创建一个 NightFall 房间,我叫 Alice
加入房间 ABC123,我叫 Bob
查看我的身份和可做行动
我要守护 2 号
我要投票给 4 号
帮我推进阶段
总结一下目前公开局势
投票、夜晚行动和推进阶段仍会进入确认流程。ChatGPT 可以辅助操作,但不能绕过服务器规则。
非 ChatGPT 客户端使用纯 JSON endpoint:
http://localhost:8787/mcp
公网部署后:
https://your-domain.example/mcp
这条路径适合 MCP Inspector、自动化测试,或未来支持 MCP 工具体系的其他客户端。它不依赖 ChatGPT Widget,因此输出保持 JSON 结构化。
cp .env.production.example .env.production
# 编辑 .env.production,替换 secrets、origins、OAuth issuer/audience/JWKS
docker compose up --build -d生产容器会从同一个 HTTPS origin 托管:
//room/.../spectate/.../api/mcp/mcp-app/.well-known/oauth-protected-resource
生产模式会拒绝本地默认 secret、占位 secret、内置 dev OAuth,并要求配置 ALLOWED_ORIGINS。
支持身份:
- 狼人
- 狼王
- 梦魇
- 预言家
- 女巫
- 守卫
- 猎人
- 白痴
- 骑士
- 村民
规则包:
| 规则包 | 人数 | 特点 |
|---|---|---|
classic_mvp |
5-12 | 狼人、预言家、女巫、村民,最小可玩闭环 |
classic_plus |
5-12 | 增加守卫、猎人、白痴,适合默认自由开局 |
advanced_12 |
12 | 增加狼王、梦魇、骑士,更接近进阶局 |
apps/
server/ Fastify API, Socket.IO realtime, SQLite store, MCP endpoints
web/ Vite + React Web client and public visual assets
widget/ Vite + React ChatGPT Apps SDK widget bundle
packages/
engine/ deterministic game engine, visibility layer, win checks
shared/ shared DTO and Zod schema helpers
configs/
role_packs/ role-pack JSON configuration
roles/ role definition JSON configuration
scripts/
start-nightfall.sh local launcher
start-mcp-app-tunnel.sh HTTPS tunnel launcher for ChatGPT Apps SDK testing
smoke-mcp-widget.mjs Playwright widget smoke test
核心数据流:
Raw GameState
-> deterministic engine
-> visibility layer
-> REST / Socket.IO / MCP / ChatGPT widget
普通接口不返回原始 GameState。玩家视角走 getPlayerView,公开视角走 getPublicView。AI 叙事 provider 只接收经过裁剪的 public/private narration input。
/mcp 暴露通用 Streamable HTTP MCP tools。
只读工具:
werewolf_get_my_viewwerewolf_get_public_statewerewolf_get_available_actionswerewolf_get_game_logwerewolf_get_public_replay
写入工具:
werewolf_create_roomwerewolf_join_roomwerewolf_start_gamewerewolf_submit_actionwerewolf_submit_votewerewolf_send_public_messagewerewolf_advance_phase
/mcp-app 暴露 ChatGPT Apps SDK 专用 tools:
nightfall_app_create_roomnightfall_app_join_roomnightfall_app_list_playersnightfall_app_select_playernightfall_app_bind_existing_playernightfall_app_get_my_viewnightfall_app_get_available_actionsnightfall_app_prepare_writenightfall_app_confirm_writenightfall_app_unbind_player
NightFall 的安全目标是让每个入口只拿到自己应该看到的数据。
- 服务器是唯一权威游戏主持。
- LLM 不决定身份、死亡、投票、胜负或药水/技能结果。
- 每个写操作验证
roomId、playerId、playerToken、阶段、生死状态、身份和行动可用性。 - ChatGPT App 写操作使用 prepare/confirm nonce。
- AI app 绑定不暴露原始 player token。
- 公开观战和 replay 只返回 sanitized public data。
- 生产部署启用 CORS allowlist、rate limit、Helmet security headers、body limit 和强 secret 检查。
- Monorepo: pnpm workspaces
- Language: TypeScript
- Web: Vite, React
- Widget: Vite, React, single-file Apps SDK resource
- Server: Fastify, Socket.IO, SQLite
- Engine: pure deterministic TypeScript package
- MCP: Streamable HTTP
- Tests: Vitest, Playwright smoke
- Deployment: Docker, Docker Compose, HTTPS reverse proxy or Cloudflare Tunnel
pnpm start:local # 一键本地启动
pnpm tunnel:mcp-app # 启动 HTTPS tunnel 和临时 dev OAuth
pnpm dev # 启动 server + web dev
pnpm db:push # 准备 SQLite schema
pnpm test # 运行 Vitest
pnpm smoke:widget # 构建并测试 ChatGPT widget
pnpm lint # ESLint
pnpm typecheck # TypeScript project references
pnpm build # 构建所有 app/package| 变量 | 说明 | 默认值 |
|---|---|---|
PORT / NIGHTFALL_API_PORT |
API 与 MCP 服务端口 | 8787 |
NIGHTFALL_WEB_PORT |
start:local 使用的 Vite Web 端口 |
5173 |
DATABASE_PATH |
SQLite 数据库路径 | data/werewolf.sqlite |
TOKEN_SECRET |
token hash 密钥 | nightfall-local-dev-secret |
RNG_SALT |
确定性身份分配盐值 | nightfall-local-rng |
AI_NARRATION_PROVIDER |
设为 openai 启用远程叙事 |
未设置 |
OPENAI_API_KEY |
OpenAI 叙事 API key,仅服务端使用 | 未设置 |
APP_INTEGRATION_SECRET |
启用 trusted adapter 模式 | 未设置 |
APP_OAUTH_ISSUER |
ChatGPT App access token 的 OAuth issuer | 未设置 |
APP_OAUTH_AUDIENCE |
ChatGPT App access token 的 audience | 未设置 |
APP_OAUTH_JWKS_URL |
RS256 access token 校验用 JWKS endpoint | 未设置 |
APP_OAUTH_SCOPES |
必须具备的 scopes | 未设置 |
DEV_OAUTH_ENABLED |
启用内置临时 OAuth provider,仅开发测试 | 未设置 |
WEB_DIST_PATH |
生产环境中由 API server 托管的 Web 构建目录 | 未设置 |
ALLOWED_ORIGINS |
CORS 允许的 origins,逗号分隔 | 未设置 |
RATE_LIMIT_MAX |
单客户端请求上限 | 120 |
RATE_LIMIT_WINDOW |
rate limit 时间窗口 | 1 minute |
BODY_LIMIT_BYTES |
最大请求体大小 | 1048576 |
MCP App 的价值不只是“让聊天窗口能调用工具”。更重要的是,它允许应用把权限、状态和交互拆开:后端负责可验证的业务规则,Widget 负责复杂状态展示,自然语言负责解释、协调和低摩擦操作。
狼人杀把这个问题放大了。一个可用的 AI 入口必须同时满足三件事:
- 能理解玩家的自然语言意图。
- 能把意图变成服务器可以验证的命令。
- 不能获得或泄露玩家不该知道的信息。
因此 NightFall 没有把大模型放在裁判位置,而是把它放在玩家入口和主持叙事层。这样既保留了 AI 对规则解释、行动辅助和复盘总结的价值,也保留了多人游戏最重要的公平性和确定性。
我对 MCP App 形态的判断是:未来会有更多应用不再把“网页 UI”和“聊天入口”割裂开。用户可以在网页里精确操作,也可以在对话中表达意图;复杂状态由嵌入式 UI 呈现,关键写操作由用户确认,核心规则仍由后端执行。
对 NightFall 来说,后续可以继续扩展:
- 更完整的公网 OAuth 与账号体系。
- 更成熟的房主后台、玩家席位管理和断线重连。
- 更多规则包、身份技能和可配置主持节奏。
- 更强的安全回放、赛后复盘和局势摘要。
- 面向不同 MCP 客户端的兼容测试。
- 可部署的公开 staging 与正式生产环境。
这个方向不仅适用于游戏,也适用于 CRM、项目管理、数据分析、教育训练和内部运营工具:把核心能力做成安全工具,把复杂状态做成可视 UI,把自然语言变成受控的操作入口。
MIT. See LICENSE.
NightFall is a local-first multiplayer Werewolf project with three interaction layers: a web app, a ChatGPT Apps SDK widget, and general MCP tools. The server owns role assignment, phase progression, night actions, voting, and win checks. Every client is only an interface into the same authoritative game state.
Werewolf is a useful test case for MCP Apps because it combines multiplayer state, hidden information, strict rules, and natural-language negotiation.
A normal web game handles buttons and screens well, but it does not help much when players want rule explanations, quick summaries, or natural-language actions. A plain chatbot can explain things, but it usually lacks reliable state, player permissions, and user-confirmed writes.
NightFall connects both sides:
- The web app remains the full game surface for hosts, players, and spectators.
- The ChatGPT widget gives players an embedded console inside the conversation.
- Natural language handles rule explanations, action assistance, public summaries, and narration.
- MCP tools turn user intent into server-validated commands.
- The server remains the only judge; the AI interface cannot decide roles, deaths, votes, skill outcomes, or victory.
- Complete web-based Werewolf flow.
- ChatGPT Apps SDK endpoint with a real widget UI.
- Pure JSON MCP endpoint for general MCP clients.
- Private player views and public spectator views.
- Prepare/confirm flow for votes, night actions, and phase advances.
- AI-DM narration with deterministic local templates and optional OpenAI provider.
- Timer-based hosting, timeout handling, public replay, and endgame reveal.
- Docker-based production staging path with OAuth protected resource metadata.
pnpm install
pnpm start:localDefault local URLs:
- Web:
http://localhost:5173 - API:
http://localhost:8787 - Health:
http://localhost:8787/health - MCP:
http://localhost:8787/mcp - ChatGPT Apps SDK MCP:
http://localhost:8787/mcp-app - Spectator:
http://localhost:5173/spectate/<ROOM_CODE>
Basic game flow:
- The host opens the web app, enters a nickname, and creates a room.
- The host chooses a role pack:
classic_mvp,classic_plus, oradvanced_12. - The host chooses a timer preset or custom phase durations.
- Other players join with the room code.
- The host starts the game once enough players have joined.
- Each player sees only their own role and available actions.
- Night roles act in order; timeouts skip unresolved actions.
- Day discussion leads into voting; timeouts become abstentions.
- The server resolves deaths, skills, win conditions, and endgame reveal.
- Spectator mode and sanitized replay support viewing and recap.
NightFall exposes /mcp-app for ChatGPT Apps SDK. It provides tool metadata, output schemas, and the ui://widget/nightfall.html widget resource. See the OpenAI Apps SDK docs for platform details.
For temporary local testing:
brew install cloudflared
pnpm tunnel:mcp-appUse the printed connector URL in ChatGPT Developer Mode:
https://.../mcp-app
Example prompts:
Create a NightFall room. My name is Alice.
Join room ABC123 as Bob.
Show my role and available actions.
I want to guard player 2.
Vote for player 4.
Advance the phase.
Summarize the public game state.
Votes, night actions, and phase advances still require confirmation.
Use the pure JSON MCP endpoint for non-ChatGPT clients:
http://localhost:8787/mcp
After public deployment:
https://your-domain.example/mcp
This endpoint is intended for MCP Inspector, automated tests, and other clients that support MCP tools without ChatGPT widget resources.
cp .env.production.example .env.production
# edit secrets, origins, OAuth issuer/audience/JWKS
docker compose up --build -dThe production container serves the web app and API from the same HTTPS origin:
//room/.../spectate/.../api/mcp/mcp-app/.well-known/oauth-protected-resource
Production mode rejects local default secrets, placeholder secrets, built-in dev OAuth, and missing ALLOWED_ORIGINS.
Supported roles:
- Werewolf
- Alpha Wolf
- Nightmare
- Seer
- Witch
- Guard
- Hunter
- Idiot
- Knight
- Villager
Role packs:
| Pack | Players | Notes |
|---|---|---|
classic_mvp |
5-12 | Werewolf, Seer, Witch, Villager. Smallest playable loop. |
classic_plus |
5-12 | Adds Guard, Hunter, and Idiot. Good default free-play pack. |
advanced_12 |
12 | Adds Alpha Wolf, Nightmare, and Knight for an advanced table. |
apps/
server/ Fastify API, Socket.IO realtime, SQLite store, MCP endpoints
web/ Vite + React Web client and public visual assets
widget/ Vite + React ChatGPT Apps SDK widget bundle
packages/
engine/ deterministic game engine, visibility layer, win checks
shared/ shared DTO and Zod schema helpers
configs/
role_packs/ role-pack JSON configuration
roles/ role definition JSON configuration
scripts/
start-nightfall.sh local launcher
start-mcp-app-tunnel.sh HTTPS tunnel launcher for ChatGPT Apps SDK testing
smoke-mcp-widget.mjs Playwright widget smoke test
Data flow:
Raw GameState
-> deterministic engine
-> visibility layer
-> REST / Socket.IO / MCP / ChatGPT widget
Normal interfaces do not return raw GameState. Player views use getPlayerView; public views use getPublicView; narration providers receive trimmed public/private narration inputs.
/mcp exposes general Streamable HTTP MCP tools.
Read tools:
werewolf_get_my_viewwerewolf_get_public_statewerewolf_get_available_actionswerewolf_get_game_logwerewolf_get_public_replay
Write tools:
werewolf_create_roomwerewolf_join_roomwerewolf_start_gamewerewolf_submit_actionwerewolf_submit_votewerewolf_send_public_messagewerewolf_advance_phase
/mcp-app exposes ChatGPT Apps SDK tools:
nightfall_app_create_roomnightfall_app_join_roomnightfall_app_list_playersnightfall_app_select_playernightfall_app_bind_existing_playernightfall_app_get_my_viewnightfall_app_get_available_actionsnightfall_app_prepare_writenightfall_app_confirm_writenightfall_app_unbind_player
NightFall is designed so each entry point receives only the data it is allowed to see.
- The server is the authoritative game master.
- LLMs do not decide roles, deaths, votes, win conditions, or skill outcomes.
- Every write validates
roomId,playerId,playerToken, phase, alive state, role, and action availability. - ChatGPT App writes use prepare/confirm nonces.
- AI app binding does not expose raw player tokens.
- Public spectator and replay endpoints return sanitized public data.
- Production deployment enables CORS allowlists, rate limits, Helmet security headers, body limits, and strong secret checks.
- Monorepo: pnpm workspaces
- Language: TypeScript
- Web: Vite, React
- Widget: Vite, React, single-file Apps SDK resource
- Server: Fastify, Socket.IO, SQLite
- Engine: pure deterministic TypeScript package
- MCP: Streamable HTTP
- Tests: Vitest, Playwright smoke
- Deployment: Docker, Docker Compose, HTTPS reverse proxy or Cloudflare Tunnel
pnpm start:local # one-command local startup
pnpm tunnel:mcp-app # HTTPS tunnel and temporary dev OAuth
pnpm dev # server + web dev
pnpm db:push # prepare SQLite schema
pnpm test # run Vitest
pnpm smoke:widget # build and test ChatGPT widget
pnpm lint # ESLint
pnpm typecheck # TypeScript project references
pnpm build # build all apps/packages| Variable | Description | Default |
|---|---|---|
PORT / NIGHTFALL_API_PORT |
API and MCP server port | 8787 |
NIGHTFALL_WEB_PORT |
Vite web port used by start:local |
5173 |
DATABASE_PATH |
SQLite database path | data/werewolf.sqlite |
TOKEN_SECRET |
token hash secret | nightfall-local-dev-secret |
RNG_SALT |
deterministic role assignment salt | nightfall-local-rng |
AI_NARRATION_PROVIDER |
set to openai for remote narration |
unset |
OPENAI_API_KEY |
OpenAI narration API key, server-side only | unset |
APP_INTEGRATION_SECRET |
enables trusted adapter mode | unset |
APP_OAUTH_ISSUER |
OAuth issuer for ChatGPT App access tokens | unset |
APP_OAUTH_AUDIENCE |
OAuth audience for ChatGPT App access tokens | unset |
APP_OAUTH_JWKS_URL |
JWKS endpoint for RS256 token validation | unset |
APP_OAUTH_SCOPES |
required scopes | unset |
DEV_OAUTH_ENABLED |
enables built-in temporary OAuth provider for dev only | unset |
WEB_DIST_PATH |
web build directory served by API server in production | unset |
ALLOWED_ORIGINS |
comma-separated CORS allowed origins | unset |
RATE_LIMIT_MAX |
max requests per client | 120 |
RATE_LIMIT_WINDOW |
rate limit window | 1 minute |
BODY_LIMIT_BYTES |
max request body size | 1048576 |
The useful pattern here is not “AI replaces the app.” The useful pattern is splitting an application into a deterministic backend, embeddable UI, safe tools, and natural-language assistance.
For NightFall, that means the LLM can explain rules, assist with actions, and summarize public events, while the server keeps the game fair and private. The same pattern can apply to CRM, project management, analytics, education, and internal operations: expose capabilities as safe tools, render complex state as UI, and let users express intent in conversation without giving up correctness or control.
MIT. See LICENSE.



