mcp-poll
by yyjeqhc
README.md
# MCP Goal Continuation Lab
A small, self-contained research implementation of durable **Goal continuation for MCP Apps**.
The project distills a longer sequence of ChatGPT/MCP experiments into one readable implementation. It focuses on the final model-managed design: the model owns semantic progress and completion, the server owns durable state and wake serialization, and the App View is only a resumable controller/adapter.
This is a research probe, not a production task runner.
## What it demonstrates
The central rule is simple:
> **Elapsed time never means business work completed.**
A Goal is divided into model-defined semantic phases. The model explicitly commits a phase, optionally checkpoints real progress, and then either:
- **completes** it with bounded evidence;
- **yields** the same unfinished phase to another model turn; or
- disappears, in which case a recovery watchdog may wake the **same unfinished phase** without claiming success.
The current design also demonstrates:
- exact `invocationKey -> goalId` binding rather than a global-current Goal;
- durable JSON state with atomic replacement and restart rehydration;
- a Goal-level single-active-wake gate;
- sticky View ownership for one wake event;
- a durable `leased -> dispatching -> delivered -> consumed` handoff state;
- fail-closed duplicate suppression after the dispatch fence;
- a server-owned controller generation lease that fences stale/reused iframes;
- foreground recovery for failures that happened **before** the irreversible dispatch fence;
- model `consume` as stronger proof than a View delivery acknowledgement.
## Why this repository exists
The design came from a sequence of live-host failures rather than from a single up-front abstraction. The important progression was:
| Experiment | What changed |
| --- | --- |
| v0.5.0 | Durable Goal, terminal event, sticky resume lease, dispatch fence |
| v0.5.1 | Exact invocation binding; removed global-current rehydration |
| v0.5.2 | Single active wake and queued/coalesced continuation |
| v0.5.3 | Model-defined semantic phases |
| v0.5.5 | Real/model-managed phases; timers cannot produce success |
| v0.5.7 | Controller generation lease fences stale App Views |
| v0.5.8 | Resume becomes an execution handoff, not just a narrative hint |
| v0.5.9 | Safe retry of pre-dispatch background/foreground Host failures |
This repository keeps only the converged contract rather than all historical compatibility implementations.
## Architecture
```text
model turn
|
| commit/checkpoint/yield/complete/consume
v
+---------------------------+
| durable Goal state |
| - semantic phases |
| - terminal evidence |
| - recovery deadline |
| - active wake |
| - dispatch state |
| - controller generation |
+-------------+-------------+
|
| exact Goal/event projection
v
+---------------------------+
| MCP App controller View |
| - exact attach binding |
| - acquire wake |
| - update model context |
| - prepare dispatch fence |
| - send one ui/message |
| - delivery ack |
+-------------+-------------+
|
v
resumed model turn
|
| exact consume proves handoff
+-------------------------------> durable Goal state
```
The detailed state/authority model is in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
## Current MCP surface
The server intentionally advertises only the current 13-tool Goal surface:
- `ui_goal_phase_start_v055`
- `ui_goal_phase_controller_attach_v059`
- `ui_goal_phase_state_v055`
- `ui_goal_phase_commit_v055`
- `ui_goal_phase_checkpoint_v055`
- `ui_goal_phase_yield_v055`
- `ui_goal_phase_complete_v055`
- `ui_goal_phase_consume_v055`
- `ui_goal_phase_finish_v055`
- `ui_goal_phase_stop_v055`
- `ui_goal_phase_resume_acquire_v055`
- `ui_goal_phase_resume_prepare_v055`
- `ui_goal_phase_resume_ack_v055`
The `v055` names are deliberate: v0.5.5 established the stable model-managed business contract; v0.5.9 changes the controller/Host handoff without changing those business tool semantics.
## Run locally
Requires Node.js 22+.
```bash
npm install
npm run dev
```
Default endpoints:
```text
http://127.0.0.1:8790/healthz
http://127.0.0.1:8790/mcp
```
State defaults to `./data/goal-state.json`. Override it with:
```bash
MCP_GOAL_STATE_PATH=/some/private/path/goal-state.json npm run dev
```
Useful environment variables:
| Variable | Default | Purpose |
| --- | --- | --- |
| `MCP_GOAL_HOST` | `127.0.0.1` | Bind host |
| `MCP_GOAL_PORT` | `8790` | HTTP port |
| `MCP_GOAL_STATE_PATH` | `./data/goal-state.json` | Durable state file |
| `MCP_GOAL_RECOVERY_SECONDS` | `14400` | Default orphan/lost-turn fallback |
| `MCP_GOAL_WAKE_GRACE_MS` | `2000` | Delay before an App View may wake after terminal evidence |
| `MCP_APP_DOMAIN` | unset | Optional MCP App domain metadata for a deployed HTTPS origin |
## Validate
```bash
npm run typecheck
npm run smoke
```
The smoke test exercises the HTTP tool surface, controller-generation fencing, yield/same-phase continuation, explicit completion, dispatch fencing, consume semantics, final Goal completion, and restart persistence.
## What this does **not** prove
This lab deliberately leaves production concerns out of scope:
- the JSON store assumes a **single writer**;
- there is no tenant/account authorization model;
- the recovery watchdog is a heuristic because MCP Apps currently provide no authoritative "model turn ended" event;
- it does not execute arbitrary shell/code/business work itself;
- it does not replace a durable Job/Run activity lease in a real agent runtime;
- it does not prove exactly-once message transport; it instead makes duplicate automatic delivery fail closed after the durable dispatch fence.
Do not expose this raw probe directly to the public Internet without an authentication/authorization layer. Goal text and checkpoint/evidence summaries are durable user-supplied data.
## License
MIT. See [`LICENSE`](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues