Agent Tooling · 2026 active

Livewall

A live status surface your agents program themselves. Send a board spec over MCP, HTTP or the CLI and watch it update in the browser over SSE. No dashboard to build, because the board is the interface.

Livewall is a live status board your AI agent writes while it works.

An agent running a long job has two bad options: go silent, or spam you with status messages. Livewall is the third. The agent sends a small JSON board spec and a browser renders it live. That spec might be a progress bar for a migration, a table of test results, a log tail, a fleet status wall. None of it needs per-use-case UI code.

Livewall rendering a board that uses every block type — heading, progress, gauge, bar, sparkline, line chart, table with status pills, key/value rows, list, log and code

Every block type on one board, in whichever theme you are reading this. The images are generated by scripts/make_screenshot.py, so they cannot drift from what the client actually renders.

Livewall on a phone: the same board, stacked into one column Livewall on a phone, dark mode

On a phone the same board stacks into one column, light and dark.

Why you’d want it

How it works

   your agent
       │   MCP tool calls: set_board / update_block / update_board / …
       ▼
  mcp/server.py ───────────────► backend/app.py
  (agent-facing tools)           (boards, TTL,
                                  SSE broadcast)
                                        │
                                        │  SSE  (GET /api/stream)
                                        ▼
                              browser (React: renders blocks)
  1. The agent calls an MCP tool with a board spec: title, status, blocks, optional tab and TTL. (The bundled CLI drives the same API from a shell, for callers that are not an MCP host.)
  2. The backend stores the board and validates the spec against a shared contract.
  3. Every connected browser gets a full snapshot over SSE; the React client renders the selected board’s blocks live.

See docs/architecture.md for why it is shaped this way.

Quick start

git clone https://github.com/ashuio/livewall livewall && cd livewall
cp .env.example .env          # optional: every value has a working default
docker compose up --build     # http://127.0.0.1:8470

That builds the client, starts the backend, and serves both on one origin.

Running without Docker:

make install                  # .venv (backend + MCP + test deps) + web/node_modules
make build                    # client -> web/dist
make backend                  # http://127.0.0.1:8470

make install builds ONE virtualenv at .venv/ holding every dependency the backend, the MCP server and the test suites need; make test uses it.

For client development, run Vite alongside the backend. It proxies /api:

cd web && npm run dev         # http://127.0.0.1:5183

Guides

New to Livewall? These walk the whole path:

The full reference set is indexed in docs/README.md.

Wiring it to an agent

Add the MCP server to your client. Most MCP clients — Claude Desktop, Cursor, Claude Code (.mcp.json), and others — read a mcpServers JSON object:

{
  "mcpServers": {
    "livewall": {
      "command": "/path/to/livewall/.venv/bin/python",
      "args": ["/path/to/livewall/mcp/server.py"],
      "env": { "LIVEWALL_URL": "http://127.0.0.1:8470" }
    }
  }
}

Use the interpreter that has fastmcp and httpx — the one make install builds at .venv/bin/python. If your host uses a different config shape, adapt the same command and args; some hosts read a YAML mcp_servers map instead:

mcp_servers:
  livewall:
    command: /path/to/livewall/.venv/bin/python
    args: ['/path/to/livewall/mcp/server.py']

The agent then gets 11 tools (set_board, update_block, describe_blocks, and so on) and can program a board mid-task. describe_blocks is how it learns the vocabulary at runtime. See docs/mcp.md for every tool, or mcp/README.md for a shorter reference.

Teach your agent the workflow

Tools tell an agent what it can call; they do not tell it when to reach for Livewall or which traps cost a cycle. skills/livewall/ is an Agent Skill — a small SKILL.md an agent loads when the task fits, covering when to use a board, the workflow shape, and the mistakes that are easy to make (a running board is pruned after an hour of silence, an existing id is a 409, list items are scalars, and so on).

Drop the folder where your agent looks for skills, or point it at the file. It carries no block vocabulary on purpose — that is describe_blocks’ job, so the skill cannot drift out of date.

A board spec is small:

{
  "title": "Migration",
  "status": "running",
  "ttl": 1800,
  "tab": {"label": "Migration", "accent": "violet",
          "indicator": {"kind": "bar", "value": 6836, "max": 6950}},
  "blocks": [
    {"id": "p", "type": "progress", "label": "Rows copied",
     "value": 6836, "max": 6950, "unit": "rows"},
    {"id": "c", "type": "callout", "tone": "ok", "text": "No errors so far"},
    {"id": "l", "type": "log", "title": "recent", "lines": ["batch 41 ok"]}
  ]
}

Later, update one block without resending the board:

{"id": "p", "set": {"value": 6950}}

Command-line client

An MCP host is not the only way to drive a board. The bundled CLI wraps the same API, so a shell script, a cron job, or a CI step can program one — and you can see it working in a single command:

pipx install ./cli            # stdlib only; installs the `livewall` command
livewall demo                 # creates a sample board — open the web client
livewall update livewall-demo --block p --set '{"value": 4}'
livewall watch livewall-demo  # follow it from the terminal

livewall --help lists every command (ls, get, set, update, reset, rm, config, watch, demo, health). It reads LIVEWALL_URL and LIVEWALL_TOKEN from the environment. See cli/README.md.

Block types

heading · text · stat · stats · progress · gauge · bar · sparkline · chart · line · table · log · kv · list · callout · code · divider

That is 17 block types rendered through 15 components (chart is an alias of sparkline; stat/stats share one chip component). The exact field list of each is in docs/blocks.md, and call describe_blocks over MCP for the runtime copy.

The charts are interactive: line and sparkline snap a crosshair to the nearest point and read out the value there, so a chart shows its numbers without you eyeballing the axis.

Numbers are exact by default: 6836, not 6.8K. Compaction is a deliberate per-value choice. Send a string ("1.5M") for a pre-formatted label, or "fmt": "compact" on a progress/gauge/bar whose ratio still needs a real number. The full rule is in docs/blocks.md.

Layout

backend/   FastAPI: boards, SSE, TTL, pruning; streams each board's blocks
web/       React client: renders the blocks, live
mcp/       MCP server: the agent-facing tool surface
cli/       command-line client (stdlib only; `pipx install ./cli`)
skills/    an Agent Skill (SKILL.md) that teaches an agent the workflow
examples/  small runnable scripts that program a board (copy-and-run), plus a pusher
scripts/   the test runner (`make test` boots a throwaway backend)
docs/      the adopter's manual (API, blocks, MCP, deployment, pushers, protocol)

Protocol

The board spec is the wire format: the server streams each board’s own blocks exactly as stored, and the client renders them directly. There is no intermediate UI protocol, so the stream is inspectable and any client that can render the block vocabulary can draw the board. The wire shape and the render contract are in docs/protocol.md.

Configuration

All via environment variables; the MCP server also loads a repo-root .env (see .env.example). Defaults: loopback bind, port 8470, finished boards expire after 1800s, running boards after 3600s idle, at most 12 boards. Runtime config changes persist to disk (LIVEWALL_CONFIG_FILE) so a restart does not revert them.

The backend binds loopback by default. Setting LIVEWALL_TOKEN turns on bearer-token auth: every /api/* endpoint then requires Authorization: Bearer <token> (or ?token=<token>), while /health and the static client stay open. Unset, the API is open — the quickstart default. The backend, the CLI and the MCP server read the token from the process environment (the MCP server also reads a repo-root .env, and docker compose substitutes it from .env into the container). The browser client never reads an env var — it takes the token from ?token=<token> once and stores it. See docs/deploying.md.

The client’s view is also driven by the URL, and these params compose with ?token= (the token is stripped; the others survive):

The layout is responsive across three bands — phone (<=600px), tablet (601–1024px) and desktop (>=1025px) — verified at 390×844, 820×1180 and 1280×800: no horizontal overflow, the app bar and tab bar stay usable, and the freshness chip and error alarm remain visible. On the phone band the app bar and its action group wrap instead of overflowing, and the app-bar buttons and tabs grow to 42px touch targets. Kiosk is unaffected at every width.

When there are many boards the tab strip is a cramped horizontal scroller. The board overview — the grid button in the app bar, or the g key — shows every board at once (title, status, indicator, age and the error alarm) and selecting one selects it and writes ?board= as usual; Esc closes it. It is hidden with a single board and in kiosk (?kiosk=1): kiosk is a chrome-free single-board wall, and an overview would both add chrome and let a passer-by navigate away.

Keyboard

The wall is fully operable without a mouse, and every control shows a visible focus ring:

All shell animation — the update pulse, the breathing live dot, the blinking error dot and the pull spinner — is disabled under prefers-reduced-motion: reduce.

Status / maturity

Version 0.0.1 — the first release. See CHANGELOG.md. It is a young project. Every push to main and every pull request runs the full test suite plus a client lint and a Docker build in CI (.github/workflows/ci.yml).

What it does have:

It has not been load-tested. It is also not a metrics store: boards live in memory and are deliberately short-lived.

Tests

make test          # builds the client, boots a throwaway backend, runs every suite

make test needs no running server: scripts/run_tests.py boots the backend on a free port with a temp config file, runs each suite against it, and exits non-zero if anything fails. Each suite also runs on its own (and boots its own backend):

make test-backend   # edge cases, SSE/stream, config persistence, auth, store concurrency, pusher, vocabulary parity
make test-cli       # the command-line client, end to end
make test-mcp       # MCP contract + write lifecycle
make test-web       # Node unit tests, render + transport + accessibility suites

The counts below are what a full run prints on a clean checkout:

SuiteChecks
backend/tests/edge_test.py142 API edge cases (validation, TTL, prune-during-await, hostile field values, numeric magnitude, board-id collision)
backend/tests/stream_test.py22 SSE/concurrency cases (bounded queue, disconnect leak, keepalive, reaper)
backend/tests/config_persist_test.py58 config round-trip and fallback cases
backend/tests/auth_test.py22 auth-gate cases (enforcement in both directions: open, 401, accepted, every method, /health liveness-only when unauthenticated, static exempt)
backend/tests/store_concurrency_test.py3 store-concurrency cases (route handlers never mutate the store off the event loop)
backend/tests/pusher_test.py20 checks on examples/pusher.py’s value-resolution path (pure functions, no backend needed)
backend/tests/vocab_parity_test.py92 vocabulary-parity checks (the MCP tool descriptions and the client renderer read exactly what contract.py declares)
cli/test_cli.py51 CLI end-to-end checks (every command, collision, bad input, token, unreachable backend)
mcp/test_live.py43 MCP contract + lifecycle checks (11 tools)
web/test/*.test.ts67 Node unit tests on a fake clock (transport, number formatting, token resolution, freshness/TTL)
web/scripts/verify_render.py60 rendered strings across all 17 block types, both themes, 16 component classes
web/scripts/verify_transport.py98 browser transport/lifecycle checks
web/scripts/verify_a11y.py46 accessibility checks (roles and names, keyboard operation, focus, contrast, reduced motion, reflow, clipboard)

Two details worth knowing:

License

Apache License 2.0. See LICENSE.

Apache-2.0 was chosen over MIT for its explicit patent grant. Livewall’s own dependencies are all permissive (Apache-2.0 / MIT / BSD).

Copyright 2026 Ashutosh Varma.

← All projects