Tapline
English | 简体中文

Capture, inspect, rewrite and replay HTTP, HTTPS, HTTP/2, HTTP/3, gRPC, WebSocket and
SSE traffic without leaving VS Code. Integrated terminals and debug sessions are routed
through a local proxy — one port that accepts both HTTP CONNECT and SOCKS5 — which
decrypts configured hosts with the Tapline root CA. By default all hosts are selected;
excluded hosts pass through unchanged.
Install from the VS Code Marketplace
or Open VSX.

Install and trust the CA, capture an httpbin request, edit & resend it, diff it against
the original, decode grpcbin fields with the workspace .proto.
MP4 · recording notes
Features
- Traffic panel — a Structure tree (host → path → request) in the sidebar and one
panel with a sortable, virtualised Sequence table (status/method chips, host view,
multi-select, resizable columns) and an inspector below or beside it.
- Inspector — request and response side by side: Overview with timing waterfall,
Raw, Params (query, cookies, form, multipart), Headers and trailers, Body as JSON tree
/ XML / Text / Hex / image with find-in-body. gzip, deflate, br and zstd bodies are
decoded. JWTs found in headers are decoded to their registered claims, with local
times and a valid / not yet valid / expired state.
- Streams — WebSocket frames, SSE events and gRPC messages arrive live; search,
copy, pause and follow, filter by direction, resend a WebSocket message on the open
connection.
- gRPC — length-prefixed messages (gzip/deflate, gRPC-Web) decoded with the
workspace's
.proto files or, without a schema, by field number; grpc-status colours
the row.
- Filter query — terms such as
status:5xx, method:post, host:api.*,
path:/v1, type:json, type:js, proto:h2, size>10k, dur>500, ip:10.0.,
body:"not found", header:x-id=1, rule:any and negations like -status:2xx; plain
words match URL, method or status. Quick chips filter by status (2xx/3xx/4xx/5xx/errors),
media type (JSON, JS, HTML) and WebSocket. Statistics summarises the filtered rows per host.
- Rules — breakpoints, rewrite, map local, map remote, block and throttle
(below).
- Native context menu, compose and compare — right-click requests to replay,
Edit & Resend, copy as cURL, open as text, compare, mark, annotate or delete;
write a request or paste a curl command; compare any two rows, or a replay with its
original, in the native diff editor.
- Automatic capture — new terminals and debug sessions (
node, python, go,
java, …; selected automatically by debug type) get HTTP(S)_PROXY plus the CA variables of common tools
(SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE,
GIT_SSL_CAINFO, JAVA_TOOL_OPTIONS, …). Copy Proxy Environment gives any other
program one line of https_proxy, http_proxy and all_proxy (SOCKS5), quoted for
the shell you pick.
- Root certificate — install, trust and uninstall the CA in the OS store on macOS,
Windows and Linux with one click (below).
- One capture per window — every VS Code window has its own traffic, controls and
proxy port (
tapline.port, 3606 by default, falling back to a free port when it is
busy); windows in the same extension storage share one sing-box process and CA, and
the last window to close shuts them down.
- MCP server — Copilot Chat, Claude Code, Cursor and other assistants can list,
search, read, replay and send captured requests (below).
- Context menus, copy as cURL, export HAR, status-bar controls, English and 简体中文 UI.
Compose requests
Open Tapline: Compose Request, paste a cURL command into the composer or use its
cURL import button. Parsing uses curlconverter. Multiline commands and UTF-8 data
files such as curl -d @data.json https://example.com/api are supported; review import
warnings before sending. Missing files, multipart file references and unsupported
transport options produce warnings. For binary uploads, select the file in Body.
- Params and Headers: edit key/value rows and check which entries to send;
unchecked entries remain available in the editor. Headers also support bulk editing.
- Authorization: configure Basic, Bearer or a custom Authorization header; the
header the request will send is shown below the fields.
- Body: choose
none, form-data, x-www-form-urlencoded, raw, binary or
GraphQL. Multipart supports text and file fields; URL-encoded forms support
checkboxes, descriptions and bulk editing. Binary sends the selected file's bytes.
GraphQL has Query, Variables and Operation Name fields and validates variables JSON.
- Send with ⌘↩ / Ctrl+↩, then inspect the captured result or compare it with the
original request.
The ReqBin coverage report records tested examples and
import limitations.
Settings
Tapline: Settings (or the gear in the traffic panel) opens a settings tab with search.
Settings and rules live in the extension's global storage and are shared by every project
in the same VS Code profile; settings.json is not used. The same tab can point VS Code's
own proxy at the live capture port, and take it back off again.
| Setting |
Default |
Purpose |
tapline.autoStart |
false |
Start capture when VS Code opens |
tapline.port |
3606 |
Capture port; falls back to a free one when busy, 0 always asks the OS |
tapline.terminal.profiles |
openssl, git |
Variables injected into new terminals |
tapline.ssl.hosts / ssl.noHosts |
["*"] / [] |
Which hosts are decrypted, and which are excluded |
tapline.ssl.insecureUpstream |
true |
Accept any upstream certificate on decrypted connections |
tapline.ssl.noProxy |
[] |
Hostnames appended to NO_PROXY in captured environments |
tapline.maxEntries / tapline.maxBodyKiB |
2000 / 512 |
Requests kept and body bytes retained |
tapline.mcp.enabled / tapline.mcp.port |
true / 3607 |
MCP endpoint for AI assistants |
tapline.grpc.protoFiles |
["**/*.proto"] |
Schemas for decoding gRPC messages |
tapline.rules |
[] |
Interception rules, edited with Tapline: Rules… |
Rules
Rules apply in order to every request whose URL matches the wildcard pattern (*
matches anything, a pattern without * is a prefix, empty matches all) and, optionally,
one of the listed methods. Open the editor with the ruler button or Tapline: Rules…;
Break on This URL in a row's context menu adds a breakpoint. Example rule data:
[
{
"kind": "breakpoint",
"url": "https://api.example.com/v1/orders*",
"request": true,
"response": true
},
{
"kind": "rewrite",
"url": "*/v1/*",
"request": { "headers": { "X-Debug": "1", "Authorization": null } },
"response": {
"status": 500,
"bodyReplace": { "pattern": "\"ok\":true", "replacement": "\"ok\":false" }
}
},
{ "kind": "mapLocal", "url": "*/users.json", "file": "mocks/users.json" },
{ "kind": "mapLocal", "url": "*/feature-flags", "body": "{\"beta\": true}", "status": 200 },
{ "kind": "mapRemote", "url": "https://api.example.com/*", "to": "http://localhost:8080" },
{ "kind": "block", "url": "*://telemetry.*", "status": 403 },
{ "kind": "throttle", "url": "*", "latencyMs": 800, "kbps": 256 }
]
| Kind |
Effect |
breakpoint |
Holds the request and/or response; the inspector shows an editor with Continue and Abort. |
rewrite |
method, url (regex → replacement), status, headers (null removes), body or bodyReplace. |
mapLocal |
Answers with file (relative to the workspace or absolute) or body; contentType is guessed if unset. |
mapRemote |
Sends the request to to (origin, optional path prefix), keeping the path and query; Host follows. |
block |
Refuses with status (default 403) without contacting the server. |
throttle |
Delays forwarding by latencyMs and paces both bodies at kbps. |
Rewritten and edited bodies are sent uncompressed with Content-Encoding removed;
text/event-stream responses are never buffered, so only their status and headers can
change. Requests a rule touched show a pencil in the table and the rule names in the
overview.
Root certificate
The CA lives in the extension's global storage (Tapline: Copy Root Certificate Path).
Capture starts only once the OS trusts it; the sidebar and status bar offer to install
it, and Tapline: Uninstall Root Certificate removes it.
| Platform |
Store |
| macOS |
login keychain via security (system password dialog) |
| Windows |
current-user Trusted Root via certutil (confirmation dialog) |
| Linux |
distribution anchor directory + update command, run with sudo |
Firefox and snap/flatpak browsers keep their own stores and need a manual import. Set
tapline.ssl.hosts to [] to capture without decryption or any certificate.
TLS interception follows host inclusion/exclusion settings. Unlike Charles's
opt-in default, Tapline keeps tapline.ssl.hosts at ["*"] (all hosts). Set it to
[] or exclude a host: its HTTPS/WSS connections pass through unchanged and are
marked Undecrypted. Only connection metadata and byte counts are recorded;
clients see the original server certificate, including when they use certificate pinning.
With no decryption hosts configured, terminal/debug environments inject only proxy
settings, preserving the client's native CA trust.
Add a hostname or wildcard to tapline.ssl.hosts to decrypt its traffic; * opts in
all hosts. tapline.ssl.noHosts and ! patterns exclude hosts and take precedence.
Existing saved patterns are preserved. Changes apply to new connections, so reconnect
or restart capture to replace existing tunnels. Composer/Replay explicitly inspects its
own request without changing the host policy for other clients.
tapline.ssl.insecureUpstream is enabled by default: decrypted connections accept
self-signed, expired and hostname-mismatched upstream certificates. Disable it to
verify the upstream server certificate. Explicitly saved settings are preserved.
An opted-in connection that fails TLS is not automatically retried or switched to
passthrough. Exclude pinned hosts before connecting. Tapline uses Go TLS rather than
Charles's TLS implementation: malformed certificate acceptance is not guaranteed to
match, even with ssl.insecureUpstream enabled.
MCP server
The capture agent serves an MCP endpoint at
http://127.0.0.1:3607/mcp (Streamable HTTP, loopback only, no extra process) while any
window with Tapline is open. Tools: list_sessions, status, list_requests, search,
get_request, get_body, replay, send, export_har, start_capture,
stop_capture, set_recording, clear, delete; resources
tapline://sessions/{sessionId}/requests/{id}. With window isolation each window is a
session: list_sessions shows them, and the other tools take sessionId (required
once more than one window is open).
Tapline: Configure MCP Server… gives one-click install in Cursor, the URL, an
mcp.json snippet or a claude mcp add command.
How it works
A Node agent drives a bundled, patched sing-box:
sing-box terminates TLS with leaf certificates minted from the Tapline CA and streams
bodies through the agent, which records them, applies rules and passes them on.
The interception engine is the separate Go module in engine; the
patch series only adds the glue that embeds it in sing-box.
Development
Requires Node 24 LTS, Go 1.27+ and git.
git clone --recurse-submodules https://github.com/fqix/tapline.git && cd tapline
nvm use # Node 24 LTS (.nvmrc)
npm ci --ignore-scripts # use the WASM parser; skip unused native addons
npm rebuild esbuild
npm run core:build # patch and build sing-box into core/<platform>-<arch>/
npm run build # esbuild → dist/
npm test # vitest: unit tests plus integration tests against the core
npm run test:e2e # the extension inside a real VS Code (downloads it on first run)
npm run package # VSIX for this platform (package:all for all six)
Press F5 to launch the extension development host. CI builds, tests and packages every
platform on each push and runs the end-to-end suite on one runner per OS.
Releasing
Push/PR CI only builds and tests. Add the version to CHANGELOG.md, bump, push the tag
and publish a GitHub release:
npm version patch && git push --follow-tags
The release workflow rebuilds all platforms, publishes to
the VS Code Marketplace (Azure managed identity over OIDC, no PAT) and Open VSX (trusted
publishing over OIDC, no PAT), and attaches the VSIX files to the release.
Licence
MIT. The bundled sing-box core is GPL-3.0; its source revision, patches and notices ship
next to each binary.