Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
.DS_Store
.idea/
.idea/
firmware/main/apps/app_agent_tasks/agent_tasks_local_defaults.h
firmware/build-host-tests/

# 本机网关环境、运行记录和构建缓存
.env
.env.*
!.env.example
__pycache__/
*.pyc
.venv/
.pytest_cache/
.ruff_cache/
*.egg-info/
captures/
/user-defaults.toml
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# StackChan 官方固件与 Agent 看板

本项目以 `m5stack/StackChan` 为固件基础。`firmware/` 保持官方 ESP-IDF 6.1、Mooncake、HAL 与 Setup 架构;`gateway/` 是来自 `kisaragi-mochi/stackchan-mcp` 的独立 Python 进程,保留上游许可证。

## 架构与检查

- Codex/Claude 适配器经 MCP 写入网关快照;设备每秒通过 Wi-Fi HTTP 读取 `/agent-dashboard`。
- 网关其它 WebSocket 设备控制功能依赖其上游替代固件,不代表本项目官方固件支持这些功能。
- 网关检查:`cd gateway && uv run --locked pytest && uv run --locked ruff check .`。
- 固件宿主检查:`cmake -S firmware/tests -B firmware/build-host-tests && cmake --build firmware/build-host-tests && ctest --test-dir firmware/build-host-tests --output-on-failure`。
- 固件构建、设备配置及已验收范围见 `docs/agent-tasks-setup.zh-CN.md`、`docs/agent-tasks-validation.zh-CN.md`。
- 面向单用户家庭局域网,优先验证实机主要路径;审查建议应友好,样式问题标为非阻断。
- 不提交 `.env`、令牌、本机默认头文件、会话数据、设备备份或构建产物;日志不输出令牌或聊天正文。

## StackChan 待办同步

本机已安装 Agent 看板时,Codex 主会话在制定、调整、完成工作计划时同步短标题与状态。使用 `~/.local/share/stackchan-agent-dashboard/.venv/bin/python -m stackchan_mcp.agent_adapters.runner codex-plan`,通过 stdin 提交 JSON:`{"stage":"实现","plan":[{"step":"短任务标题","status":"in_progress"}]}`。会话 ID 使用 `CODEX_THREAD_ID`,最多 12 项,状态为 `pending`、`in_progress`、`completed`;缺少命令或会话 ID 时继续工作,不伪造 ID。

监视器负责合并计划与生命周期后经 MCP 上报;不要同时用完整快照覆盖同栏。子 Agent 不提交主栏计划。只提交短标题和状态,不上传提示词、正文、路径、工具参数或推理内容。
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,62 @@
# StackChan · Agent 待办看板

基于 M5Stack 官方固件扩展:在独立 `AGENT TASKS` 菜单中,左侧显示 Codex、右侧显示 Claude 的状态、阶段与待办,顶部独立显示完成百分比,支持进行中优先排序、转圈图标和动态省略号。

- [中文安装与使用说明](docs/agent-tasks-setup.zh-CN.md)
- [验证记录与已知限制](docs/agent-tasks-validation.zh-CN.md)
- `firmware/`:官方 ESP-IDF / Mooncake 固件及看板 App。
- `gateway/`:独立 Python MCP 网关与 Agent 适配器,读取本机会话状态,经 MCP 更新快照。
- `scripts/`:macOS 常驻服务安装与本机固件默认配置脚本。

设备通过 Wi-Fi HTTP 拉取快照;网关的其它 WebSocket 设备控制功能面向其上游替代固件,本项目官方固件不提供那些设备控制功能。公开源码不含个人令牌。网关源码及第三方许可见 [gateway/LICENSE](gateway/LICENSE) 与 [gateway/LICENSE-THIRD-PARTY](gateway/LICENSE-THIRD-PARTY)。

![双栏看板预览(设计示意)](docs/design/agent-tasks-official-preview.png)

## 让 Codex 和 Claude 在设备上显示进度

### 1. 启动共享网关

在 macOS 上安装 Python 3.12 和 `uv`,从仓库根目录运行以下命令,并将 IP 换成电脑当前的局域网 IPv4 地址:

```bash
python3 scripts/install-agent-dashboard-macos.py --host-ip 192.168.1.20
```

安装器会启动一个共享 MCP 网关,以及 Codex、Claude 的本机会话监视器。设备连接地址和令牌保存在 `~/.config/stackchan-agent-dashboard/设备连接信息.txt`。在 StackChan 上打开 `SETUP` → `Apps` → `Agent 看板`,填写其中的地址和令牌,测试并保存;设备需先连接同一局域网 Wi-Fi。详细的手动启动、网关地址变化和固件配置方法见[中文配置说明](docs/agent-tasks-setup.zh-CN.md)。

### 2. 提交 Codex 待办

Codex 监视器自动报告桌面主会话的工作状态;桌面目前没有可用的原生计划清单事件,因此 Codex 需要把当前计划显式提交给看板。使用主会话的 `CODEX_THREAD_ID`,通过仓库的 `AGENTS.md` 约定调用:

用户可以先对 Codex 说:

> 请把这项工作拆成不超过 12 条短待办,开始执行前同步计划;每当任务开始、完成或计划变化时,都更新状态并按本仓库 AGENTS.md 的 StackChan 待办同步约定提交完整清单。只同步任务标题和状态。

```bash
~/.local/share/stackchan-agent-dashboard/.venv/bin/python \
-m stackchan_mcp.agent_adapters.runner codex-plan <<'JSON'
{"stage":"实现看板","plan":[{"step":"完成界面","status":"completed"},{"step":"验证设备显示","status":"in_progress"},{"step":"更新使用文档","status":"pending"}]}
JSON
```

每次提交的是该会话的完整待办清单,最多 12 项;状态使用 `pending`、`in_progress`、`completed`。计划变化时提交更新后的清单。不要使用子 Agent 的会话 ID。

### 3. 让 Claude 创建任务

Claude 监视器读取本机 Claude Code 主会话记录。Claude 成功执行 `TaskCreate`、`TaskUpdate` 或 `TodoWrite` 后,监视器会同步任务标题和状态;结束回合后保留最后清单。用户可以对 Claude 说:

> 请先用任务清单把工作拆成不超过 12 项;执行时使用任务工具创建待办,并在开始、完成或调整任务时及时更新状态,这样我可以在 StackChan 上查看进度。

没有任务工具记录时,右栏仍会显示状态和阶段,但任务区为空。

### 设备显示内容

打开 Launcher 中的 `AGENT TASKS`:左栏为 Codex,右栏为 Claude。每栏展示 Agent 状态、当前阶段和最多 12 项任务,进行中的任务排在前面;栏标题右侧独立显示该 Agent 的整数完成百分比,计算方式为“已完成数 ÷ 当前清单总数”,无任务时显示 `--%`。工作时会显示转圈和动态省略号。网关或 Wi-Fi 暂时断开时,设备保留最近一次成功取得的快照。

看板只接收短阶段、任务标题、状态和会话生命周期,不上传提示词、对话正文、文件路径或工具参数。Codex 的原生计划事件与 Claude 新会话任务采集限制,以及设备验证记录,见[看板配置与验证文档](docs/agent-tasks-setup.zh-CN.md)和[验证记录](docs/agent-tasks-validation.zh-CN.md)。

以下保留官方项目介绍。

# StackChan Open-Source

<img src="https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1205/K151_stack_chan_main_pictures_01.webp" width="60%">
Expand Down
151 changes: 151 additions & 0 deletions docs/178-daemon-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Issue #178 Daemon Setup

The gateway now has two supported MCP server modes:

- **stdio mode**: `stackchan-mcp` with no subcommand. This is the original
single-client mode used by existing MCP client configs.
- **daemon mode**: `stackchan-mcp serve --transport streamable-http`. This
starts one long-lived gateway process, keeps one ESP32 WebSocket connection,
and accepts multiple MCP clients over Streamable HTTP.

Use daemon mode when more than one MCP client or automation needs to share the
same StackChan device. Keep stdio mode for simple local setups, compatibility
testing, and existing clients that do not yet support Streamable HTTP.

## Start the daemon

```bash
cd gateway
uv run stackchan-mcp serve --transport streamable-http
```

The daemon starts the same ESP32 WebSocket listener and capture server as stdio
mode, then exposes the MCP endpoint at:

```text
http://127.0.0.1:8767/mcp
```

The zero-subcommand stdio entry point remains supported and unchanged:

```bash
stackchan-mcp
```

`stackchan-mcp serve --transport stdio` is equivalent to the zero-subcommand
stdio path.

## Environment variables

| Variable | Default | Purpose |
|---|---:|---|
| `STACKCHAN_TOKEN` | unset | Bearer token shared with the ESP32 firmware and, when set, required by HTTP MCP clients. |
| `BEARER_TOKEN` | unset | Legacy alias used only when `STACKCHAN_TOKEN` is unset. |
| `MCP_HTTP_HOST` | `127.0.0.1` | Bind host for the Streamable HTTP MCP endpoint. |
| `MCP_HTTP_PORT` | `8767` | Bind port for the Streamable HTTP MCP endpoint. |
| `MCP_HTTP_ALLOWED_HOSTS` | unset | Comma-separated Host / Origin allowlist for non-loopback clients, for example `192.168.1.10,stackchan.local:8767`. |
| `STACKCHAN_COMMAND_QUEUE_SIZE` | `32` | Maximum queued ESP32-bound tool calls before the daemon returns backpressure. |

The existing gateway variables (`HOST`, `WS_PORT`, `CAPTURE_PORT`,
`VISION_HOST`, `VISION_URL`, `VISION_TOKEN`, and audio hook settings) keep the
same meaning in daemon mode.

## Bind safety

Loopback binds (`127.0.0.1`, `::1`, or `localhost`) may run without an HTTP
bearer token for local development.

Non-loopback binds, such as `0.0.0.0` or a LAN IP address, require
`STACKCHAN_TOKEN` or `BEARER_TOKEN`. Startup is refused otherwise:

```text
stackchan-mcp: refusing non-loopback MCP_HTTP_HOST without STACKCHAN_TOKEN or BEARER_TOKEN
```

When a token is configured, every request to `/mcp` and `/status` must include:

```text
Authorization: Bearer <token>
```

`/healthz` is intentionally unauthenticated so local process monitors can check
whether the daemon is alive. Host and Origin headers are still validated on all
daemon endpoints.

For wildcard binds such as `MCP_HTTP_HOST=0.0.0.0`, loopback Host values
(`127.0.0.1`, `localhost`, `::1`) are accepted by default. LAN clients usually
send the machine's LAN IP or DNS name in the `Host` header, so add those values
to `MCP_HTTP_ALLOWED_HOSTS`:

```bash
STACKCHAN_TOKEN=your-secret-token-here \
MCP_HTTP_HOST=0.0.0.0 \
MCP_HTTP_ALLOWED_HOSTS=192.168.1.10,stackchan.local:8767 \
stackchan-mcp serve --transport streamable-http
```

## MCP client configuration

Use a Streamable HTTP MCP client pointed at `/mcp`. Example
`mcp.config.json`:

```json
{
"mcpServers": {
"stackchan-mcp": {
"type": "streamable-http",
"url": "http://127.0.0.1:8767/mcp",
"headers": {
"Authorization": "Bearer your-secret-token-here"
}
}
}
}
```

If no `STACKCHAN_TOKEN` or `BEARER_TOKEN` is configured and the daemon is bound
to loopback, omit the `headers` block.

## Operational endpoints

`GET /healthz` returns daemon-local health:

```json
{
"ok": true,
"esp32_connected": true,
"queue_depth": 0,
"queue_capacity": 32,
"owner_id": "stackchan-mcp-1234abcd"
}
```

`GET /status` returns the same gateway status as the `get_status` MCP tool,
plus `queue_depth`, `queue_capacity`, and `connected_clients`.

## Migration notes

Existing stdio configs do not need to change. The following remains valid:

```json
{
"mcpServers": {
"stackchan-mcp": {
"type": "stdio",
"command": "stackchan-mcp"
}
}
}
```

To migrate, start the daemon as an independent process first, then point each
Streamable HTTP-capable MCP client at `http://127.0.0.1:8767/mcp`. Once a
client uses the daemon, it should not also spawn `stackchan-mcp` in stdio mode
for the same device, because the daemon is the process that owns the ESP32
WebSocket.

The daemon keeps a single bounded FIFO command queue for ESP32-bound tool
calls. `get_status` bypasses the queue because it reads gateway-local state.
When the queue is full, the daemon returns a JSON-RPC error with code `-32000`
and message `stackchan command queue is full`; it does not buffer commands
without a limit.
Loading
Loading