M16: an MCP server (illogical as tools for any agent) #5

Open
opened 2026-10-02 00:16:05 +00:00 by jhgaylor · 0 comments
Owner

Depends on: #4

Prerequisites already done: M3 (CLI/HTTP API), M3b/M3c (VMs), M6a, M6b, M6c, M7.

From PLAN.md, section "M16".


Any MCP client (Claude Code, Codex, Claude Desktop, an agent block) gets
illogical as typed tools: run commands in durable panes you can watch and
take over, spin up throwaway VMs, open a dev server next to its terminal,
start and supervise other agents, and search what happened yesterday. M3
already built the CLI and HTTP API, so M16 is a curated layer over them, not
new machinery.

Why it beats an agent's own Bash tool:

  • Work outlives the agent's turn. A build started through MCP runs in a
    real pane. You see it on the phone, scroll it, take it over. It survives
    the agent's session and daemon restarts, and the agent picks it up again
    with wait or read_output.
  • Sandboxes on demand: run with vm: true is an isolated machine that
    disappears afterwards.
  • Agents supervising agents: start_agent, wait until it's idle or
    asking, read its transcript, and answer its approvals and questions (M6c),
    or leave them for you.
  • Showing you things: open_port puts its dev server in a browser block
    beside its terminal.
  • Memory across sessions: history and search.
  • Permissions per tool in the MCP client, for example always allowing
    read_output and wait while asking before run.

Decisions (2026-10-01):

  • Both transports now: stdio and HTTP.
  • Scope: everything, like the CLI. An external MCP client can touch any
    pane on any host. The MCP client's own tool permissions are the guard,
    which makes tool annotations matter (below).
  • Injected into agent blocks automatically, scoped to the block's tab.

Transports (one implementation):

  • The daemon serves MCP over Streamable HTTP at /mcp, with the same
    auth as the API:
    • the owner over the tailnet (serve headers, or WhoIs on direct
      listeners);
    • per-client bearer tokens from illogical mcp token [--name n] [--scope …], revocable, for clients without tailnet identity;
    • the exact-Origin rule for browsers (the MCP spec requires this
      check). Non-browser clients send no Origin.
  • illogical mcp is a stdio bridge to that endpoint over the daemon's
    Unix socket, or to another daemon with --host. Claude Code and Codex
    configure it as a plain command:
    claude mcp add illogical -- illogical mcp
    
  • Implementation: the official Rust SDK (rmcp) in the daemon. S14
    checks it covers Streamable HTTP, resource subscriptions and progress
    notifications.

Tools. About a dozen, shaped for agents rather than mirroring every
endpoint. Each returns a short text summary plus structuredContent, with
output capped and pageable by offset, so a chatty pane can't flood the
agent's context.

Tool What it does Annotations
run Run a command in a new tab or split; cwd, vm/vm_tab/machine, host, policy, wait (with a timeout). Returns the pane, and with wait, its exit code and the last lines. not read-only, not idempotent
send_input Text (with optional Enter) or named keys (C-c, Up) to a pane not read-only
read_output A pane's output from an offset, or its last command (escape sequences stripped). Returns text and the next offset. read-only
capture_screen The visible screen as text read-only
wait Until command end, exit, a regex match, idle or needs-input, with a timeout. On timeout it returns "still running" and the offset, so the agent calls again. read-only
list Panes and blocks: type, host, cwd, command, attention state read-only
close Close a pane or block (and a pane-owned VM) destructive
history / search Commands across panes (failed, since, cwd), and full-text search of logs read-only
open_port A browser block on a port of the pane's machine, beside it not read-only
start_agent An agent block (Claude Code, Codex, a Fountain agent) with a prompt; returns the block not read-only
agent_respond Approve or deny a pending permission, or answer a pending question (M6c) not read-only
read_file A file on a pane's host or VM (M7's fs), capped read-only
  • Long calls: run --wait and wait send progress notifications. They
    also return before the client's MCP tool timeout (S14 measures Claude
    Code's) with a resumable "still running", so a long build never fails a
    tool call.
  • Errors are tool results (isError) with a sentence an agent can act
    on, for example "pane %7 is gone; it exited 2 at 14:03", not protocol
    errors.

Resources:

  • illogical://pane/%N/output (subscribable, so a client can follow a pane
    live), illogical://pane/%N/screen, illogical://block/%N (state), and
    illogical://history.
  • Resource templates, so clients can list them.

Agent blocks get it automatically, scoped to their tab:

  • session/new passes mcpServers with an illogical server. S13 showed
    claude-agent-acp uses MCP servers passed that way.
  • The scope is a token minted per block. The agent can create panes and
    blocks in its own tab (on the tab's machine, in a VM tab), read and drive
    what it created, and read the rest of its tab. It can't touch other tabs
    or hosts.
  • Local agents get the stdio bridge with that token.
  • VM agents need to reach the daemon from inside the VM. That means an
    HTTP endpoint on wisp's bridge address, or the bridge running host-side.
    S14 checks what the guest network allows.
  • Fountain agents can't reach the tailnet, so they don't get it.

Safety. External clients get full scope, so:

  • every tool carries honest annotations (readOnlyHint, destructiveHint,
    idempotentHint), which clients use to decide what to ask about;
  • the README recommends a Claude Code permission set: allow the read-only
    tools, ask for the rest;
  • every MCP call is logged with the client's name and token. The pane shows
    "started by mcp:", and history records it.

Done when:

  • Claude Code outside illogical, with illogical mcp, runs a long build in a
    VM pane. You watch it on the phone, Claude waits through it, reads the
    failure, fixes it and reruns, and the pane shows "started by
    mcp:claude-code".
  • An agent block starts its project's dev server in a pane in its own tab
    and opens it in a browser block beside itself. A try to touch another tab
    is refused.
  • One agent starts a second in an agent block, waits until it asks a
    question, and answers it.
  • "What failed in this repo yesterday?" is answered through history.
  • A client on another tailnet machine uses /mcp over HTTP with a token,
    and revoking the token cuts it off.
**Depends on:** #4 Prerequisites already done: M3 (CLI/HTTP API), M3b/M3c (VMs), M6a, M6b, M6c, M7. _From PLAN.md, section "M16"._ --- Any MCP client (Claude Code, Codex, Claude Desktop, an agent block) gets illogical as typed tools: run commands in durable panes you can watch and take over, spin up throwaway VMs, open a dev server next to its terminal, start and supervise other agents, and search what happened yesterday. M3 already built the CLI and HTTP API, so M16 is a curated layer over them, not new machinery. **Why it beats an agent's own Bash tool:** - **Work outlives the agent's turn.** A build started through MCP runs in a real pane. You see it on the phone, scroll it, take it over. It survives the agent's session and daemon restarts, and the agent picks it up again with `wait` or `read_output`. - **Sandboxes on demand:** `run` with `vm: true` is an isolated machine that disappears afterwards. - **Agents supervising agents:** `start_agent`, `wait` until it's idle or asking, read its transcript, and answer its approvals and questions (M6c), or leave them for you. - **Showing you things:** `open_port` puts its dev server in a browser block beside its terminal. - **Memory across sessions:** `history` and `search`. - **Permissions per tool** in the MCP client, for example always allowing `read_output` and `wait` while asking before `run`. **Decisions (2026-10-01):** - **Both transports now:** stdio and HTTP. - **Scope: everything, like the CLI.** An external MCP client can touch any pane on any host. The MCP client's own tool permissions are the guard, which makes tool annotations matter (below). - **Injected into agent blocks automatically, scoped to the block's tab.** **Transports (one implementation):** - **The daemon serves MCP over Streamable HTTP at `/mcp`,** with the same auth as the API: - the owner over the tailnet (serve headers, or `WhoIs` on direct listeners); - per-client bearer tokens from `illogical mcp token [--name n] [--scope …]`, revocable, for clients without tailnet identity; - the exact-`Origin` rule for browsers (the MCP spec requires this check). Non-browser clients send no `Origin`. - **`illogical mcp` is a stdio bridge** to that endpoint over the daemon's Unix socket, or to another daemon with `--host`. Claude Code and Codex configure it as a plain command: ``` claude mcp add illogical -- illogical mcp ``` - **Implementation:** the official Rust SDK (`rmcp`) in the daemon. S14 checks it covers Streamable HTTP, resource subscriptions and progress notifications. **Tools.** About a dozen, shaped for agents rather than mirroring every endpoint. Each returns a short text summary plus `structuredContent`, with output capped and pageable by offset, so a chatty pane can't flood the agent's context. | Tool | What it does | Annotations | |---|---|---| | `run` | Run a command in a new tab or split; `cwd`, `vm`/`vm_tab`/`machine`, `host`, `policy`, `wait` (with a timeout). Returns the pane, and with `wait`, its exit code and the last lines. | not read-only, not idempotent | | `send_input` | Text (with optional Enter) or named keys (`C-c`, `Up`) to a pane | not read-only | | `read_output` | A pane's output from an offset, or its last command (escape sequences stripped). Returns text and the next offset. | read-only | | `capture_screen` | The visible screen as text | read-only | | `wait` | Until command end, exit, a regex match, idle or needs-input, with a timeout. On timeout it returns "still running" and the offset, so the agent calls again. | read-only | | `list` | Panes and blocks: type, host, cwd, command, attention state | read-only | | `close` | Close a pane or block (and a pane-owned VM) | destructive | | `history` / `search` | Commands across panes (failed, since, cwd), and full-text search of logs | read-only | | `open_port` | A browser block on a port of the pane's machine, beside it | not read-only | | `start_agent` | An agent block (Claude Code, Codex, a Fountain agent) with a prompt; returns the block | not read-only | | `agent_respond` | Approve or deny a pending permission, or answer a pending question (M6c) | not read-only | | `read_file` | A file on a pane's host or VM (M7's `fs`), capped | read-only | - **Long calls:** `run --wait` and `wait` send progress notifications. They also return before the client's MCP tool timeout (S14 measures Claude Code's) with a resumable "still running", so a long build never fails a tool call. - **Errors** are tool results (`isError`) with a sentence an agent can act on, for example "pane %7 is gone; it exited 2 at 14:03", not protocol errors. **Resources:** - `illogical://pane/%N/output` (subscribable, so a client can follow a pane live), `illogical://pane/%N/screen`, `illogical://block/%N` (state), and `illogical://history`. - Resource templates, so clients can list them. **Agent blocks get it automatically, scoped to their tab:** - `session/new` passes `mcpServers` with an `illogical` server. S13 showed `claude-agent-acp` uses MCP servers passed that way. - The scope is a token minted per block. The agent can create panes and blocks in its own tab (on the tab's machine, in a VM tab), read and drive what it created, and read the rest of its tab. It can't touch other tabs or hosts. - **Local agents** get the stdio bridge with that token. - **VM agents** need to reach the daemon from inside the VM. That means an HTTP endpoint on wisp's bridge address, or the bridge running host-side. S14 checks what the guest network allows. - **Fountain agents** can't reach the tailnet, so they don't get it. **Safety.** External clients get full scope, so: - every tool carries honest annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`), which clients use to decide what to ask about; - the README recommends a Claude Code permission set: allow the read-only tools, ask for the rest; - every MCP call is logged with the client's name and token. The pane shows "started by mcp:<client>", and `history` records it. **Done when:** - Claude Code outside illogical, with `illogical mcp`, runs a long build in a VM pane. You watch it on the phone, Claude waits through it, reads the failure, fixes it and reruns, and the pane shows "started by mcp:claude-code". - An agent block starts its project's dev server in a pane in its own tab and opens it in a browser block beside itself. A try to touch another tab is refused. - One agent starts a second in an agent block, waits until it asks a question, and answers it. - "What failed in this repo yesterday?" is answered through `history`. - A client on another tailnet machine uses `/mcp` over HTTP with a token, and revoking the token cuts it off.
Sign in to join this conversation.
No description provided.