waiting-on
Provides an MCP server that tracks open loops (pending decisions or replies) across email, agent sessions, and other channels, enabling agents to query what is waiting on whom.
README
waiting-on
A local-first, tenant-separated open-loops ledger for agentic workflows: it tracks who has the ball, per domain, across email, agent sessions, and other channels.
An "open loop" is anything waiting on a decision or a reply. waiting-on reads the places those loops accumulate (an email account, your Claude Code and Codex session transcripts, a voice queue, manually entered leads), normalizes each one to a single record type, and stores it in a per-domain local SQLite database. It then answers one question from several surfaces: what is still waiting on you, and what are you waiting on someone else for.
The ledger core reads and tunes your data; it never sends anything. The one
write it can make to an outside system is an explicit, opt-in draft-creation
tool: waiting_on_draft_reply (and its draft --create-gmail-draft CLI
equivalent) can create a Gmail draft through an external Gmail MCP server, and
that is the only write path off the local store. There is no send path anywhere
in the code. waiting-on holds no credentials of its own and stores snippets
rather than full message bodies.
Why it is built this way
The interesting parts are the guarantees, not the CRUD.
-
Fail-closed tenant walls. Each domain maps to its own SQLite database file, with a fail-closed profile allow-list enforced in application code (the
Ledgerfacade), not an OS-level sandbox. A session runs under a profile that names the domains it may open; an unknown or unmapped profile resolves to no domains rather than to everything. TheLedgerfacade raisesTenantWallErrorwhen a caller reaches for a domain outside its profile, andAmbiguousDomainErrorwhen a bareline_idexists in more than one allowed domain, so it refuses to guess which tenant you meant instead of silently writing the wrong one. -
Optimistic-concurrency writes with named failure modes. Lead writes are versioned compare-and-swap operations against a WAL-mode database. A losing write raises
LeadWriteConflict, and a lock-contention retry-exhaustion raisesLeadWriteBusy(a subclass). The failure each one guards against is spelled out in its docstring, for example a completed lead being silently reopened by a stale writer. -
Pluggable adapters over one contract. Email, agent-threads, voice, and manual-lead collectors each normalize their source into a single
LineObservation. Adding a channel means writing one adapter, not touching the ledger. The deterministic file-based collectors (agent-threads, voice, manual leads) degrade to empty results when their source is absent: they yield nothing rather than raising. The email path is the exception: it shells out to an external Gmail MCP server, and a failure of that subprocess surfaces as aGmailMcpErrorrather than being swallowed. -
Deterministic core, zero-LLM collectors. The agent-threads and voice adapters classify state with deterministic heuristics and replay, not a model call, so their output is reproducible and testable. An injected clock makes all age math deterministic under test.
-
Four surfaces over one ledger. An MCP server (for agent tool use), a CLI, an fzf-driven tuning TUI, and a static self-contained HTML board all read the same store.
The test suite covers the tenant walls, the concurrency failure modes, each
adapter's normalization and fail-soft behavior, and the render surfaces. Run it
with python3 -m pytest from the repo root; it is 156 tests today.
Architecture
email account -> GmailMcpClient -+
Claude/Codex sessions -> AgentThreadsAdapter -+-> LineObservation -> per-domain
voice queue (JSONL) -> VoiceAdapter -+ (one contract) SQLite DB
manual leads (JSON) -> ManualLeadAdapter -+ |
v
MCP server | CLI | TUI | HTML board
The tenant boundary is enforced where roots and accounts resolve to a domain, so an adapter can only ever read the sources authorized for the domain it is collecting.
Install
Requires Python 3.11 or newer. No required third-party dependencies for the core.
python3 -m pip install -e .
Optional extras: .[mcp] for the MCP server, .[parquet] for Parquet export.
The tuning TUI additionally needs textual, and the fzf TUI needs fzf.
You can also run straight from a checkout without installing, via the launchers
in bin/.
Quickstart
The repository ships a synthetic examples/demo-leads.json so you can see the
shape without any configuration. With no config file, waiting-on uses a single
default domain.
# Import the demo leads into the local ledger
bin/waiting-on lead seed examples/demo-leads.json
# See who has the ball (your side first)
bin/waiting-on lead list
# A compact open-loops pane, suitable for a terminal or a cmux column
bin/waiting-on pane
# Render a static, self-contained HTML board from the current leads
python3 scripts/render_lead_board.py --out board.html
To wire up real sources (email accounts, agent-session roots, domains, and
profiles), copy config.example.toml to ~/.config/waiting-on/config.toml and
edit it. The example file documents the tenant-wall and profile model inline.
Documentation
docs/open-loops-ledger-design.md: the design and phased plan, including the tenant-wall model.docs/agent-threads-adapter-spec.md: how the agent-session collector decides a session is a forgotten open loop.docs/cli-contract.md: the machine-readable CLI contract and exit codes.docs/security.md: what is and is not stored, and why there is no send path.docs/studio-spec.mdanddocs/calibration-and-panes.md: the TUI and the live-tuning panes.docs/agent-integration.md: driving waiting-on from an agent.
Status
Early and actively developed: this repository was extracted from a working personal deployment, so the interfaces are real but may still shift, and the demo leads shipped here are synthetic.
Known rough edges:
- The agent-threads classifier is a deterministic tail heuristic over transcript
events. It is conservative and tunable (idle-hours and max-age dials), but it
can still misjudge an unusual session; treat its
waiting_on_mecalls as strong hints, not verdicts. - The email adapter expects an external Gmail MCP server to be configured (its command is set in config). waiting-on deliberately does not hold Gmail credentials itself, so email refresh does nothing until that is wired up.
- Additional channels (for example iMessage) and an optional LLM enrichment pass are described in the design doc but not yet built. Today the collectors are deterministic and zero-LLM.
- The config example is intentionally minimal. Multi-domain setups work, but you configure the domains, accounts, and profiles by hand.
License
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。