{
  "markdown": "<!-- mcp-name: io.github.AytuncYildizli/yapitalism -->\n<!-- The MCP Registry verifies PyPI package ownership by finding this\n     string in the package description, which is this README. It must\n     match server.json's name exactly. -->\n\n# Yapitalism\n\n**Drive terminal coding agents by voice, and never let the answer claim more than it proved.**\n\nYou are away from your desk. You speak; an agent in a terminal does the work; you get a spoken\nreply. That reply is the only thing you have — you cannot see the screen. So the worst failure is\nnot a crash, it is the voice saying *\"done\"* while your text sits unread in a prompt box.\n\nYapitalism is a local MCP server that lets a voice client reach your coding agents, plus a receipt\nlayer that decides what the voice is allowed to say. If you are at your desk, use your screen —\nthis exists for the hours you are not.\n\n> Public pre-alpha. Local-only. No production deployment, no external messaging, and no automation\n> of closed-source clients.\n\n## How it fits together\n\n```\nMCP client (Codex · Hermes · Claude Code · Claude Desktop · Cursor)\n    │  MCP over loopback — no public endpoint, no OAuth, no relay\n    ▼\nyapitalism MCP server          panes_list · pane_read · pane_send\n    │                          pane_await · pane_task\n    │                          panes_name · panes_unname\n    │                          panes_create · panes_resume · pane_clear\n    ├── superset backend       local host-service over tRPC (127.0.0.1:48900)\n    └── tmux backend           capture-pane / send-keys\n```\n\nThe agents it addresses: **codex, claude, kimi, opencode** — recognised by measurement (a\nprocess tree on tmux, the host's own agent registry on Superset), never by a caller's claim. A\npane running anything else is refused, because typing into a shell and pressing Enter is running\na command.\n\nThe voice client never reaches your machine directly: it drives a local agent session, and that\nsession talks to this server over `127.0.0.1`. Nothing is exposed to the network.\n\n## The part that matters: receipts\n\n`pane_send` returns a verdict, not a shrug.\n\n| | meaning |\n| --- | --- |\n| **GREEN** | the agent echoed a one-time marker. It demonstrably received and processed the text — which is not a claim the work it then did is correct; no receipt can promise that, and this one does not try. |\n| **YELLOW** | the write landed; processing was **not** proven. Never round this up. |\n| **RED** | the backend refused the write. Nothing reached the terminal. |\n\n`YELLOW` is the whole point. Text left unsubmitted in an agent's input box looks identical to work\nin progress from outside — same spinner, same scrolling output, same HTTP 200. A voice that rounds\nthat up to \"done\" costs you hours before you notice.\n\nExplicitly **not** acceptance: an HTTP 2xx, a PTY write returning, terminal output changing, a\nrevision advancing, or the prompt echoing your own words back.\n\n### Waiting is not the same as failing\n\nA fixed deadline reports on the clock, not on the agent. An agent that thinks for a minute and\nthen answers correctly was verified all along, and calling that YELLOW teaches an operator to\nignore YELLOW. So the wait is an **idle** timeout: it restarts whenever the pane changes, bounded\nby a hard ceiling.\n\nPane movement decides only whether to keep waiting. It is never evidence of acceptance — that\nstays the canary alone. A YELLOW therefore says which kind it is:\n\n- `canary_timeout_pane_moving` — the pane's text was still changing. Named after what was\n  measured: a spinner, a clock, a log tail or a second agent sharing the pane all produce this\n  without the intended agent doing anything. It is a hint that looking again may be worth it,\n  never a claim that the agent is working.\n- `canary_timeout_pane_still` — nothing moved at all.\n\nWhat remains irreducible: if an agent silently ignores the text and prints nothing, no mechanism\nhere can distinguish that from an agent that never received it. Verification needs the agent to\nemit something.\n\n### The second receipt: how the turn ended\n\nDelivery is table stakes; the expensive failure is an agent that has been sitting on a yes/no\nquestion for forty minutes while you thought it was working. `pane_await` watches a pane until the\nagent's **turn** ends and says how; `pane_task` is send-then-await in one call — instruct, walk\naway, come back to one of:\n\n| turn | meaning |\n| --- | --- |\n| `ended` | prompt idle, screen stopped changing. The turn is over — spoken with \"not that the work is correct\" attached, never as \"done\". |\n| `waiting_input` | a blocking dialog owns the screen (trust / login / confirmation). You are the blocker, and the payload's `tail` carries the question so it can be quoted. |\n| `agent_error` | a known failure line owns the screen — `Please run /login`, a 401, a rate limit — named, not timed out. |\n| `exited` | the pane no longer runs an agent. |\n| `running` | still changing when time ran out. Unproven; offers to look. |\n\nAn `ended` verdict re-verifies the agent process is still alive before it is claimed: the frozen\nprompt of a dead pane must not be celebrated as a finished turn.\n\n### Who may write, by transport\n\nThe bearer token answers *who is calling*; it never answered *what they may do*. Since 0.5.0:\nstdio clients write, always — the OS made that trust decision when it let the client spawn the\nprocess. **Writes over HTTP are off by default** and every refusal names the fix:\n`yapitalism authority allow-http-writes`, once, on that machine (`yapitalism setup` asks the same\nquestion while printing the HTTP registration lines). Reading, watching and `doctor` work on every\ntransport regardless, and `yapitalism-mcp --read-only` turns writes off everywhere — install the\nwatcher first, allow writes when it has earned them. Every write result carries its `origin`.\n\nOn tmux, the send path also pins the **identity** of the admitted agent (pid + start time) and\nre-verifies it immediately before Enter. An agent that exits into a shell between typing and\nsubmit gets the text staged but never submitted — typing is recoverable, Enter into a shell is\ncommand execution.\n\n### Where the checks happen\n\nMost of the time this does not change what you do, and it is not read out loud. It is in the\npayload and in `yapitalism setup`, for when something goes wrong and you want to know what was\nactually checked before the write.\n\n| | meaning |\n|---|---|\n| `host` | the host refuses the write itself — the check and the write are one operation |\n| `client` | this process checks, then writes; real against what it covers, not atomic |\n| `none` | nothing checks |\n\n```json\n{ \"status\": \"GREEN\",\n  \"client_guarantees\": [\"idempotent_dispatch\", \"empty_prompt_check\"],\n  \"missing_guarantees\": [\"optimistic_revision\"] }\n```\n\n`yapitalism setup` prints the table for your machine. Today both backends land in `client` for most\nof it: the checks happen here, a moment before the write, rather than inside the host atomically\nwith it. tmux cannot do the revision check at all.\n\n**No shipped Superset enforces these itself, and this tool used to claim it did.** It concluded\n\"guarded\" from the fact that a procedure named `terminal.send` was routed. The `terminal.send`\nSuperset actually ships takes `{terminalId, workspaceId, text, submit}` and guards nothing. The\nlevels are now read from what the host's own validator *requires* — the difference between a name\nand a contract.\n\nBoth backends reach GREEN, and GREEN means the same thing either way: the agent emitted the\none-time marker, so it received the text and processed it. The table is for diagnosing a machine,\nnot something to reason about on every send.\n\n## Reviewing this repo\n\nWhether you are a person or someone's agent asked \"should I install this,\" the\nclaims worth checking and where each one lives — verify, do not take our word:\n\n- Authority and its boundaries, including the shared-machine exposure and the\n  fail-closed bearer gate: [SECURITY.md](SECURITY.md).\n- What a caller can never do (closed launcher tables, non-agent refusal,\n  occupied-prompt refusal, per-pane serialisation): `src/yapitalism/mcp/tmux.py`\n  and `src/yapitalism/mcp/backends/`, each guard with a test named after the\n  failure it prevents.\n- What GREEN claims and does not claim: the receipts section above, and\n  `tests/test_canary.py` for why an echo cannot satisfy it.\n- This project's own past overclaims, retracted by name: [CHANGELOG.md](CHANGELOG.md),\n  0.2.0 through 0.2.4.\n\n## Machines are panes too\n\nA peer is another machine's yapitalism server, mounted under its own namespace:\nthe codex pane `tmux:%0` on the machine `studio` is `studio:tmux:%0` here, in the\nsame `panes_list`, addressable by the same `pane_send`. Receipts pass through\nverbatim — the peer proved them next to its own terminals, and this side only\ngives the ids their machine name back.\n\n```bash\n# on the peer (reachable over your tailnet):\nYAPITALISM_MCP_HOST=100.x.y.z yapitalism-mcp   # tailnet bind requires the bearer gate\n\n# on this machine:\nyapitalism peers add studio http://100.x.y.z:8792/mcp --token-file <peer's token>\n```\n\n`peers add` verifies the peer answers as a yapitalism server before writing\nanything. A peer on a public address is refused unless `allow_public` is said\nexplicitly; the tailnet is the transport, not the trust story — the peer's\nbearer token still decides who may call. Configured peers show up in\n`yapitalism doctor` with live pane counts.\n\nAgents can be **started and resumed** on a peer too: `panes_create` and\n`panes_resume` take a `machine` argument naming one. The peer runs its own\nfull gate chain — and starting an agent remotely is more authority than typing\ninto one, so it has its own switch, off by default:\n`yapitalism authority allow-remote-create`, once, on the peer. To keep a\npeer's server alive across reboots, `yapitalism service --host <its tailnet\nIP>` prints a filled launchd plist or systemd unit; installing it stays your\ndecision.\n\n### Say names, not ids\n\nVoice cannot say `mbp3:tmux:%2`. Bind a word to a pane once —\n\n```\npanes_name(\"billing\", \"tmux:%4\")\n```\n\n— and every tool accepts `billing` from then on. The binding records what the\npane runs at name-time and is re-verified against a live listing on every use:\ntmux reuses pane ids, so a name whose pane vanished or changed runtime is\n**refused with the reason**, never silently retargeted. No fuzzy matching —\n\"probably the right codex\" is not a target. Names appear on their panes in\n`panes_list`.\n\n## Driving it well\n\nThe bridge agent behaves best with standing instructions — speak receipts, never\nresend a YELLOW, name panes like a person. The exact prompt we run is in\n[docs/operator-prompt.md](docs/operator-prompt.md); paste it into your voice\nclient's custom instructions.\n\n## Recipes\n\nThe six tools compose into watchers worth having: a permission watch (nothing\nwaits on a dialog silently — its first real catch was an agent silently holding a\nwallet-transaction approval), a provider-outage watch, an end-of-day digest.\nWorking prompts in [docs/recipes.md](docs/recipes.md).\n\n## Install\n\nNeeds Python 3.11+ and `tmux`, on macOS or Linux — the full send-and-prove loop is exercised on\na bare `python:3.11-slim` container per release, not just assumed portable. Everything runs on\nyour machine; nothing is exposed to the network.\n\n```bash\n# 1. install\npipx install yapitalism        # or: uv tool install yapitalism / pip install yapitalism\n\n# 2. start the server (loopback only — it refuses to bind anything else)\nyapitalism-mcp\n\n# 3. point your voice client's agent at it, in another shell\ncodex mcp add yapitalism --url http://127.0.0.1:8792/mcp\n\n# 4. ask the tool what your machine can actually do\nyapitalism setup\n```\n\n`setup` interviews the machine rather than printing \"installed successfully\": which backends are\nusable, which agent CLIs are on `PATH`, whether a Superset host is live and which build it is, and\nthe guarantee table above filled in for you. It writes nothing except, if you say yes, the Superset\nmanifest — and it names what is still missing with the exact command for each.\n\nThen talk to the voice app: *\"list my panes\"*, then *\"send this to the Codex pane\"*. Any MCP\nclient works the same way — Hermes, Claude Code, Claude Desktop and Cursor registrations are\nprinted by `yapitalism setup` for exactly the machine it is run on.\n\n`YAPITALISM_MCP_PORT` moves the port if 8792 is taken. `YAPITALISM_TMUX_SOCKET` targets a\nnon-default tmux server. The HTTP transport requires a bearer token by default — loopback\nis not a user boundary, so the server mints one on first start and `yapitalism setup`\nprints the registration lines that carry it. `YAPITALISM_MCP_TOKEN` overrides it;\n`YAPITALISM_MCP_INSECURE=1` opts out. stdio needs no token: the OS already decided who\nmay talk to a spawned process.\n\n### Clients that launch the server themselves\n\nCodex takes a URL. Claude Desktop, Cursor and most other MCP clients instead spawn the process\nand speak over stdin/stdout, so point them at `--stdio` and do not run a separate server:\n\n```json\n{\n  \"mcpServers\": {\n    \"yapitalism\": {\n      \"command\": \"yapitalism-mcp\",\n      \"args\": [\"--stdio\"]\n    }\n  }\n}\n```\n\nUse the absolute path from `command -v yapitalism-mcp` if the client does not inherit your\n`PATH` — GUI apps on macOS usually do not. `YAPITALISM_MCP_TRANSPORT=stdio` does the same as the\nflag, for clients that only let you set the environment.\n\nIn stdio mode nothing but protocol may reach stdout, so the server suppresses its own startup\nbanner. If you wrap it in a shell script, keep that script silent too.\n\n### Keeping it running\n\nThe voice route dies when the server does, so run it under your init system rather than a\nterminal. Both units run as **your user**, never root: the server can read every terminal you\ncan see.\n\n- **macOS** — `.agents/launchd/`. The plist is a template; its README has a `sed` line that\n  fills in the real binary path, because launchd searches neither `PATH` nor `~`.\n- **Linux** — `.agents/systemd/`, a `--user` unit. `systemctl --user enable --now yapitalism-mcp`.\n\nKill any shell instance first either way, or the two race for port 8792 and which one wins is\ndown to timing.\n\n### Superset terminals (optional)\n\nThe tmux backend needs nothing. Superset needs a `0600` manifest holding the host endpoint and\ntoken — and you do not write it by hand, because Superset already writes what it needs:\n\n```bash\nyapitalism superset setup            # shows what it found and would do\nyapitalism superset setup --confirm  # writes it\n```\n\nThat reads the app's own `~/.superset/host/<organizationId>/manifest.json`, proves the token against\nthe live host, picks a default terminal and writes\n`~/.cache/superset-watch-voice/yapitalism-manifest.json` (or `$YAPITALISM_SUPERSET_MANIFEST`). It\nrefuses rather than guesses: a source file looser than `0600`, a manifest naming a dead process, or\ntwo live organizations with no way to choose all stop it. The token is never printed.\n\nWithout a manifest, `panes_list` still returns your tmux panes and reports Superset in `errors` — a\nbackend that could not be reached is never silently reported as \"no terminals\".\n\n### Teaching the voice how to speak the receipts\n\n`.agents/skills/superset-operator/` holds the policy that stops a model rounding YELLOW up to\n\"done\", plus a drift check against the copy your agent actually loads.\n\n## The receipt core, on its own\n\nThe five-leg model is usable without the MCP server:\n\n1. `capture` — the intent was captured\n2. `dispatch` — a concrete target received a write attempt\n3. `accept` — the target proved acceptance by canary or explicit acknowledgement\n4. `work` — material agent progress was observed\n5. `deliver` — a final update reached the user\n\nEvidence carries provenance — `api`, `terminal_diff`, `ui_observation`, `user_report`, `inferred` —\nand `inferred` may never mark a leg succeeded.\n\nThe incident this project came from: the audio session stayed alive, spoken progress stopped, two\n180-second watchers saw the terminal frozen at revision `920118`, and the canary never arrived —\nwhile the voice kept implying work. The product is not \"fix the voice client\". It is **never fake\nGREEN**.\n\nCLI surface: `doctor`, `receipt show`, `ledger verify|manifest|migrate`, and `superset status|send`.\nA confirmed `superset send` requires reusing the exact `client-token` a dry run emitted, snapshots\nimmediately before dispatch, rejects a changed revision, and never retries an ambiguous POST.\n\n## Known limits\n\nStated plainly, because a receipt system that overclaims is worse than none:\n\n- **Tail truncation is undetectable.** Deleting the last ledger rows leaves a prefix that still\n  verifies. Catching it needs an anchor outside the file — see `docs/adr/0004`.\n- **The ledger attests to itself.** Hash verification proves rows were not edited or reordered; it\n  does not prove who wrote them, and a wholly fabricated ledger verifies fine.\n- **No backend can offer tmux an optimistic-revision guard.** It needs an expectation from the\n  caller — \"write only if the pane still looks as it did when I read it\" — and `pane_send` takes no\n  expected revision. Reading the pane twice and refusing if it moved would be a different guarantee\n  wearing that name, so it is reported `none` rather than approximated.\n- **The empty-prompt check outside a guarded host is a heuristic.** It reads the rendered screen, so\n  a false \"empty\" would append to somebody's half-typed text and submit the merge. It therefore\n  refuses on anything short of a confident empty, including screens it cannot read, and `pane_clear`\n  is the way through. Validated against real panes, not a large sample.\n- **The Superset it is measured against is the shipped, stock build** — the one whose\n  `terminal.send` guards nothing. Proven sends (canary observed, GREEN) have been completed against\n  that live host repeatedly since 0.2.4, driven both directly and through the full MCP surface. The\n  guarded-host branch is the one exercised only against recorded shapes, because no shipped build\n  carries those guards to test against.\n- **A guarded Superset host over-refuses Codex placeholder text,** counting the agent's own\n  suggestion line as staged input, so sends to such panes are refused and clearing cannot help. The\n  receipt says exactly that instead of advising a clear. The fix belongs in the host.\n- **A tmux pane's runtime can go stale** between the check and the write. Superset's comes from the\n  host's own registry, which is why its gate can run before the write rather than after it.\n- **A canary proves less than \"it worked\".** It proves the text reached something that then echoed a\n  one-time marker — delivery and acknowledgement. It does not prove the agent did the work, did only\n  the work, or that the pane was the one you meant. GREEN is a delivery receipt, not a work receipt.\n- **Concurrent sends to one pane are serialized, not queued.** The second waits for the first; it is\n  not held, ordered, or retried. A voice client that fires faster than a terminal accepts will block\n  its own turn rather than interleave.\n- Connector-in-voice behaviour in closed clients is undocumented and can change without notice.\n\n## Repository map\n\n```text\nsrc/yapitalism/mcp/       MCP server, backend registry, receipts, tmux driver\nsrc/yapitalism/adapters/  Superset host-service client\nsrc/yapitalism/           receipt core: model, canary, claims, ledger, CLI\ntests/                    deterministic unit, replay, and real-tmux tests\nfixtures/                 scrubbed incident replays\n.agents/skills/           voice policy + drift check\n.agents/launchd/          run the server as a login agent\ndocs/adr/                 load-bearing decisions\ndocs/architecture.md      component boundaries and evidence model\n```\n\n## Explicit non-goals\n\n- reverse-engineering or patching closed voice-client internals;\n- inventing background-turn or push APIs that do not exist;\n- multi-tenant SaaS;\n- automatic email, DM, or messaging delivery;\n- treating terminal revision movement as command acceptance.\n\n## Credits\n\nThe receipt-integrity core was written by [@liri-ha](https://github.com/liri-ha), whose commits\nare carried here unrewritten. See [CONTRIBUTORS.md](CONTRIBUTORS.md).\n\n## Security\n\nRaw transcripts, terminal text, and credentials do not belong in this repository. Ledgers store\nbounded metadata, hashes, and classifications. A manifest holding a bearer token lives outside the\nrepo at mode `0600` and is read only when its path is passed explicitly; it is never printed. See\n[SECURITY.md](SECURITY.md).\n",
  "bytes": 20595,
  "sha": "3b61248e712e27fc9815c350c339aaca6f25a4f451f5a7dd2f4ad4373e3ad9d0",
  "repo_slug": "aytuncyildizli/yapitalism",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_aytuncyildizli_yapitalism_7ccfad42/readme"
}