---
title: Agent integrations
description: The harnesses Tapes captures, which lane each one uses, and how to point a generic provider client at the transparent proxy.
editUrl: https://github.com/papercomputeco/tapes/edit/main/docs/integrations.md
sourceRepo: papercomputeco/tapes
sourcePath: docs/integrations.md
lastUpdated: 2026-09-29
---

Tapes captures an agent in one of three ways. The client launches the agent
under a just-in-time capture proxy that dies with the process. For an agent
that launches itself, the client binds the address the agent was installed
against and captures whatever runs in that window. For Cursor, the client
records the structured output the agent's own print mode emits.

| Harness | Lane | Plugin needed first |
| --- | --- | --- |
| `claude` | `tapesctl start claude` | none |
| `codex` | `tapesctl start codex` | none |
| `pi` | `tapesctl start pi` | `tapesctl plugin install pi` |
| `cursor` | `tapesctl start cursor` | none |
| `codex-app` | `tapesctl capture codex-app` | `tapesctl plugin install codex-app` |

Capture commands address the **private ingest API**, `:8082` by default — not
the read API on `:8081`. A capture pointed at the read port reports success and
stores nothing.

Start the server and its local dependencies before these examples:

```bash
tapes local up
tapes serve
```

Then install the client:

```bash
curl -sSfL https://download.tapes.dev/tapesctl/install | bash
```

The agent brings its own provider credentials. `tapes auth` stores credentials
for the server-side features that call a provider themselves — span embedding
and skill generation — and is not part of capturing an agent.

## Claude Code

```bash
tapesctl start claude --ingest-url http://localhost:8082
```

`tapesctl` starts a loopback capture proxy, sets Claude Code's `ANTHROPIC_BASE_URL` to it, launches `claude`, and ships the captured turns to the server. Pass Claude flags after `--`:

```bash
tapesctl start claude --ingest-url http://localhost:8082 -- --worktree
```

Claude sessions also produce transcripts on disk, which carry the subagent
structure the wire traffic alone cannot show. `start` tails them live. For a
session no capture was running for, sweep them afterwards:

```bash
tapesctl sync --ingest-url http://localhost:8082
```

`sync` sweeps the last seven days by default; `--since-days 0` sweeps
everything. Re-pushing is safe — the server deduplicates.

For a manually managed, fixed-port proxy instead of the just-in-time one:

```bash
tapes serve --provider anthropic --upstream https://api.anthropic.com
ANTHROPIC_BASE_URL=http://localhost:8080 claude
```

## Gateway capture protocols

The Tapes extproc adapter captures Anthropic Messages, OpenAI Responses, and
OpenAI Chat Completions (`/v1/chat/completions`). Chat Completions supports JSON
and SSE responses, text/refusals, streamed function-call arguments (including
parallel calls), tool-result history, completion usage and cached input tokens.
It keeps the provider's model ID after any gateway routing rewrite.
Explicit Chat Completions tool exchanges are derived as conversation calls
even when non-streaming, so tool results link back to their function calls.

Responses and Chat Completions use distinct shared reducers. The gateway
adapter selects the reducer by provider and endpoint, so an OpenAI-compatible
`/v1/chat/completions` turn is not sent through the Responses parser. When
reducing stored raw-only turns or recovering a missing reduction, the captured
request selects the format (`input` versus `messages`). Deploy the updated
Tapes ingest/derive image before enabling raw-only capture with the updated
extproc image; `dual` retains the adapter's reduction as well as the original
wire bytes.

A stream missing `[DONE]`, a finish reason, or valid frames is retained as
partial, not represented as a completed answer. Invalid function arguments
remain attached to the tool call without a partially decoded object; valid
argument numbers retain their JSON precision. Choice zero is the canonical
answer; additional choices are preserved in response metadata rather than
merged. If zero is absent, only the indexed alternatives are retained and the
response is marked partial. Null optional fields are treated as absent.
Audio and custom-tool streaming deltas are not yet normalized; they are marked
partial and require the raw lane for full-fidelity replay. Request parameters
are forwarded unchanged; this adds capture, not API translation or execution.

The wire contract follows the [OpenAI Chat Completions reference](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create).

## Codex

The terminal CLI is launched like Claude:

```bash
tapesctl start codex --ingest-url http://localhost:8082
```

The ChatGPT desktop app launches itself, so it is captured through lifecycle
hooks instead. Install the plugin once, then run a capture window and start a
session in the app:

```bash
tapesctl plugin install codex-app
tapesctl capture codex-app --ingest-url http://localhost:8082
```

`plugin install codex-app` writes the handoff file and points the app's Codex
configuration at the capture address; `capture` reads that handoff and binds it.
Running `capture` first fails and tells you to install. Unlike `start`, `capture`
prints no turn counts when it stops — it reports the number of sessions it saw.

`plugin uninstall codex-app` removes the provider entry and the handoff, but the
plugin stays registered with Codex; the command prints the `codex plugin remove`
line that finishes the job.

## pi

pi is captured by an installed extension, so the install is a prerequisite
rather than a convenience:

```bash
tapesctl plugin install pi
tapesctl start pi --ingest-url http://localhost:8082
```

`start pi` refuses to run when the extension is absent, before anything is bound
or launched. pi redirects several providers to one endpoint, so it is the one
harness that takes `--schema`:

```bash
tapesctl start pi --ingest-url http://localhost:8082 --schema openai
```

`--schema` on `claude` or `codex` is an error rather than a silent no-op: each
speaks exactly one schema, taken from the harness.

## Cursor

Cursor's CLI, the `agent` binary, has no provider base-URL setting, so it is
captured from its `stream-json` output instead of through a proxy:

```bash
tapesctl start cursor --ingest-url http://localhost:8082 -- --trust "summarize this repo"
```

`start cursor` copies the stream to your terminal, saves it, and uploads it as
a transcript when the run ends. It never adds `--trust`, so in an untrusted
directory Cursor refuses and the run exits with Cursor's status. If the upload
fails, the file stays under `~/.tapes/transcripts/cursor`. Sweep it later:

```bash
tapesctl sync cursor --ingest-url http://localhost:8082
```

Turn counts come from derived traces, which include prompts, assistant
responses, and tool calls. Tool outputs are retained even when no assistant
response follows them. Cursor capture does not report token usage.
Use `--since-days 0` to retry recordings older than seven days.

## Ollama and generic clients

With default configuration, Tapes forwards Ollama-compatible traffic to `http://localhost:11434`:

```bash
tapes serve
curl http://localhost:8080/api/chat \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen3-coder:30b","messages":[{"role":"user","content":"hello"}],"stream":false}'
```

Pull a chat model separately; `tapes local up` pulls the embedding model, not every completion model:

```bash
ollama pull qwen3-coder:30b
```

For another Anthropic-, OpenAI-, or Ollama-compatible application, configure its base URL as `http://localhost:8080` and run `tapes serve` with the matching `--provider` and `--upstream`. Preserve the path convention expected by the client and provider.

## Verify and stop

The read API health endpoint is separate from the proxy and from ingest:

```bash
curl http://localhost:8081/ping
tapes status
tapesctl sessions list --api-url http://localhost:8081
```

A captured session appears in that list. `start` prints the harness's own
session id on exit, which is a different id from the one `tapesctl sessions get`
takes — find the session in the list rather than pasting the printed id.

Stop the foreground `tapes serve` process with `Ctrl-C`. `tapes local down` removes bootstrap containers but keeps PostgreSQL data unless `--wipe` is supplied.