Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions docs/ai-coding-agents/launchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ your Hub catalog and binds it to one connection-pinned Hub-backed flight root.

## Models

Hub is the only inference plane. `--model` names a catalog id: a pooled model
For Codex, opencode, and pi, Hub is the only inference plane. Claude Code
defaults to split mode (below), which adds Hub's catalog beside your own Claude
models. `--model` names a catalog id: a pooled model
such as `@you/qwen3:8b`, served by your own connected relays or by a pool shared
with you (see [`tap relay`](relay.md)). Each id is one model however many relays
back it; Hub routes each request to one of them. Without `--model` the launch
Expand All @@ -27,7 +29,7 @@ each one:

| Harness | Protocol | Wiring |
| --- | --- | --- |
| Claude Code | Anthropic Messages | `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, and every `ANTHROPIC_*_MODEL` slot set to the model. A session-only `--settings` `modelPicker` replaces `/model`'s Claude lineup with your catalog. An inherited `ANTHROPIC_API_KEY` is unset. |
| Claude Code | Anthropic Messages | Split mode (default): `ANTHROPIC_BASE_URL` set to the forwarder's per-launch path, and a session-only `--settings` `modelPicker` adding your catalog to `/model` with `replaceBuiltInOptions: false`. `--hub`: `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, and every `ANTHROPIC_*_MODEL` slot set to the model, the picker replaced by your catalog, and an inherited `ANTHROPIC_API_KEY` unset. `--subscription`: no model wiring at all. |
| Codex | OpenAI Responses | A `foldwise` model provider passed with `-c`, with `wire_api = "responses"` and its key in `TAP_LAUNCH_KEY` |
| opencode | OpenAI chat completions | A `foldwise` provider in `OPENCODE_CONFIG_CONTENT` listing the whole catalog |
| pi | OpenAI chat completions | A generated extension loaded with `-e` that registers a `foldwise` provider for the whole catalog. Your `~/.pi/agent` is untouched. |
Expand All @@ -39,6 +41,53 @@ per-launch key, and attaches your Hub credential to each request, refreshing a
harness. `--dry-run` prints the invocation with placeholders for the
forwarder's address, key, and file directory.

### Claude Code: split, hub, and subscription

Claude Code takes one base URL, so split mode routes inside the forwarder.
Claude Code sends every model request to
`http://127.0.0.1:<port>/t/<launch key>/anthropic`. A request naming a Claude
model (`claude-*`, or an alias such as `sonnet`) goes on to Anthropic exactly
as Claude Code sent it, with your own Claude login or `ANTHROPIC_API_KEY`;
`tap launch` adds nothing to it, and that credential never reaches Hub. Any
other model goes to Hub with your Hub credential, which never reaches
Anthropic or Claude Code. Without `--model` or `--agent` the session starts on
Claude Code's own default model, and background work stays on its small Claude
model. If the launching shell set `ANTHROPIC_BASE_URL`, Claude models go there
instead of `https://api.anthropic.com`. If Hub cannot list your models, the
launch warns and continues with Claude models only.

The per-launch key sits in the path because Claude Code's credential in split
mode is your Claude login, not the key. Outside that path the forwarder still
requires the key, and the path opens no routes beyond the inference ones.

`--hub` restores Hub-only inference: every slot on the selected Hub model.
`--subscription` leaves Claude Code's models alone, so Hub supplies only tools
and agents.

### Claude Code: relayed tools and subagents

Relayed MCP tools come through the tapper plugin's `tap mcp`, not through the
launch. The session's `tap mcp` sees `TAP_AGENT`, so Hub offers it the relayed
tools the `--agent` allows (`relay:tools`). Claude Code names them
`mcp__plugin_tapper_tapper__mcp__<owner>__<server>__<tool>`. See
[Relay: Where relayed tools appear](relay.md#where-relayed-tools-appear).

The agent's subagents become Claude Code subagents through `--agents`, named
`<namespace>-<name>`. `--subagents all` offers every agent you can see instead,
and `--subagents none` offers none. Each subagent gets:

- its Hub tools, as `mcp__plugin_tapper_tapper__<tool>` (every hosted tool when
its list is empty), using `runtime_tools`, so the organization's tool policy
applies;
- its relayed tools, as `mcp__plugin_tapper_tapper__<name>`, when both it and
the launch agent allow them;
- the built-ins in `--subagent-builtins`, by default `Read,Grep,Glob`;
- its model, when the launch can reach it; otherwise it inherits the session's.

Hub still checks every call against the launch agent, because the MCP sessions
run as that agent. A subagent's list can narrow what it is offered but never
widen it.

The child also gets `TAP_HARNESS` and `TAP_MODEL`. Tapper reports them in
orientation and telemetry as the session's identity, and they select nothing.

Expand Down
56 changes: 43 additions & 13 deletions docs/ai-coding-agents/mcp-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ explicitly configured root failed to initialize.
| Tool | Description |
| --- | --- |
| `node_create` | Atomically create 1–100 nodes from `nodes[]`, each a markdown `content` document plus an optional YAML `meta` document; unique keys support forward/backward `{{node:key}}` content references |
| `node_edit` | Read with `node_read`, then atomically replace `content`, `meta`, or both for 1–100 nodes from `nodes[]`; every item requires that node's `expected_hash`, and one hash covers content and metadata together |
| `node_delete` | Read with `node_read`, then atomically delete 1–100 `nodes[]`; every item requires its own `expected_hash` |
| `node_move` | Read with `node_read`, then move a node using its required `expected_hash` |
| `node_edit` | Read with `node_read`, then atomically replace `content`, `meta`, or both for 1–100 nodes from `nodes[]`; supplied `content` requires `expected_content_hash` (the read's `content_hash`) and supplied `meta` requires `expected_meta_hash` (its `meta_hash`); editing one part never invalidates the other's hash |
| `node_delete` | Read with `node_read`, then atomically delete 1–100 `nodes[]`; every item requires its own `expected_hash` (the combined `hash` from `node_read`) |
| `node_move` | Read with `node_read`, then move a node using its required `expected_hash` (the combined `hash` from `node_read`) |
| `keg_settings_edit` | Read the full document with `keg_settings_read`, then replace it using its required `expected_hash`; requires admin flight authority and editor/admin KEG access |

Mutation inputs are array-only, every batch tool takes its items under `nodes`,
Expand All @@ -121,10 +121,10 @@ and each array contains 1–100 items:
```json
// node_create
{"nodes":[{"key":"plan","content":"# Plan\n\nSee [task](../{{node:task}})\n","meta":"type: plan\n"}]}
// node_edit — content only, metadata only, or both under one hash
{"nodes":[{"node_id":"12","content":"# Revised\n","expected_hash":"..."}]}
{"nodes":[{"node_id":"12","meta":"type: plan\n","expected_hash":"..."}]}
{"nodes":[{"node_id":"12","content":"# Revised\n","meta":"type: plan\n","expected_hash":"..."}]}
// node_edit — content only, metadata only, or both; each part under its own hash
{"nodes":[{"node_id":"12","content":"# Revised\n","expected_content_hash":"..."}]}
{"nodes":[{"node_id":"12","meta":"type: plan\n","expected_meta_hash":"..."}]}
{"nodes":[{"node_id":"12","content":"# Revised\n","meta":"type: plan\n","expected_content_hash":"...","expected_meta_hash":"..."}]}
// node_delete
{"nodes":[{"node_id":"12","expected_hash":"..."},{"node_id":"13","expected_hash":"..."}]}
// snapshot_create
Expand All @@ -134,21 +134,28 @@ and each array contains 1–100 items:
Read metadata with `node_read` and `meta_only`; take snapshots with `snapshot_create`
before a large or destructive edit.
Mutation results preserve request order and report `node_id`, the resulting
hash or snapshot revision, and advisory schema validation details when
hash or snapshot revision (`node_edit` results also carry the new
`content_hash` and `meta_hash`, so a follow-up edit needs no re-read), and advisory schema validation details when
applicable. A failed batch returns no partial results and commits none of its
changes.

Reads are self-contained: each `node_read` row in `structuredContent.nodes[]` pairs
`node_id` and `hash` with that node's `content` and `meta`, matching the fields
`node_edit` accepts, so a read result can be modified and sent back without parsing
the human-readable rendering.
`node_id`, the combined `hash`, and the per-part `content_hash` and `meta_hash`
(present for the parts the read mode returned) with that node's `content` and
`meta`, matching the fields `node_edit` accepts, so a read result can be modified
and sent back without parsing. Each document is sent once, in `nodes[]`; the
text message is only a summary. `node_edit` takes `content_hash` as
`expected_content_hash` and `meta_hash` as `expected_meta_hash`; editing one part
never invalidates the other's hash. `node_delete` and `node_move` take the
combined `hash`.

Every protected mutation names the read that supplies its token: `node_read` for
node edits, metadata updates, moves, and removals; `keg_settings_read` for settings;
`schema_read` for schema edits/deletes; and `flight_read` for flight
edits/deletes. A conflict performs no operation and returns the current hash
and, when practical, current content. Merge or refetch, then retry with that
current hash.
and, when practical, current content; node conflicts also return
`currentContentHash` and `currentMetaHash`. Merge or refetch, then retry with
the current hash for what you are writing.

### Index, Diagnostics, And Safety

Expand Down Expand Up @@ -246,6 +253,29 @@ Both transports also publish `tapper://orient` and two resource templates:
Either template takes `?keg=` with a URL-escaped keg target to override the
server default.

### Relayed Tools

`tap mcp` also serves the MCP tools your relays forward to Hub (see
[Relay](relay.md#where-relayed-tools-appear)), under Hub's names:
`mcp__<owner>__<server>__<tool>`, where `<owner>` is the username of the
person whose relay runs it. Claude Code shows them with the plugin's prefix, as
`mcp__plugin_tapper_tapper__mcp__<owner>__<server>__<tool>`.

- It connects to the selected Hub's `/mcp/relay` with your Hub token in the
background, so startup never waits on it and an unreachable Hub only means
no relayed tools until it answers.
- The list refreshes every 60 seconds and whenever Hub announces a change; a
tool whose relay or share went away is removed.
- Without `TAP_AGENT` you get the servers granted to your personal namespace:
your own enabled servers and the shares you accepted. With `TAP_AGENT` (set
by `tap launch --agent`) Hub offers the servers granted to that agent's
namespace, as far as its `relay:tools` allows; the agent's tool allowlist
lets relayed names through, since Hub has already gated them.
- Calls go to Hub, which forwards them to the relay serving the tool.
- `TAP_RELAY_TOOLS=off` turns relayed tools off for that server. Agents the
relay's coding runners start get it, along with `TAP_RUNNER_DEPTH`, which
also turns them off.

## Keg Targeting

Every KEG-oriented tool accepts an optional `keg` parameter to override the
Expand Down
Loading
Loading