Tetiva ships an MCP server called Tetiva DevTools. It exposes 33 tools that let an AI agent read your workspaces, collections, requests and environments, create new ones, and run a saved request with the same environments, scripts and history you get from pressing Send. Nothing has to be pasted into a chat: the agent talks to the app at 127.0.0.1:9300.
The server advertises tools only — no resources, no prompts — and it lives exactly as long as the app window does.
Turning it on#
Open settings (Cmd/Ctrl+,, or the gear at the bottom of the left bar) and go to the AI agents (MCP) section:
- Turn on the MCP server switch.
- Check Address —
127.0.0.1:9300by default. - Restart the app. The section shows "Restart Tetiva to apply the changes"; the server binds at startup.
After the restart the MCP server row reads Running, and the diagram at the top of the section shows the address it listens on.
MCP can also be driven by environment variables: TETIVA_MCP=1 enables the server and TETIVA_MCP_ADDR sets the address. The pre-rebrand names GOPHERCOURIER_MCP and GOPHERCOURIER_MCP_ADDR still work. When either is set, the settings fields lock and read "Managed by environment variables".
Connecting an agent#
The server asks for an access token. Copy config in settings puts a ready-made snippet on the clipboard for the client you pick — Claude Code, Cursor, or the bare URL — with the token already in it. Claude Code and Cursor send the token in a header, so it stays out of the URL. The Claude Code preset is an entry for .mcp.json:
{
"mcpServers": {
"tetiva": {
"type": "sse",
"url": "http://127.0.0.1:9300/sse",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Cursor's entry is the same without "type": "sse". The URL only preset gives just the address, with the token in the query string: http://127.0.0.1:9300/sse?token=YOUR_TOKEN.
Instead of .mcp.json, Claude Code can take the endpoint in one command:
claude mcp add --transport sse tetiva http://127.0.0.1:9300/sse \
--header "Authorization: Bearer YOUR_TOKEN"The copy button next to the token in settings puts the raw value on the clipboard, which is what this command wants; Copy config gives you a whole snippet instead.
Configs written before tokens existed stop working after the upgrade: the server answers 401 until the token is added. Copy a fresh config from settings and paste it over the old entry.
The app has to be running while the agent works — the tools talk to the live window, not to a database file on disk.
That is also why the agent's changes show up in the open app right away: a new request appears in the tree, an edit lands in its tab or its separate request window, and the tab of a deleted request closes. Unsaved edits in a tab survive when the agent changed other fields; in a field you both changed, the agent's version wins.
The 33 tools#
Without workspace_id, tools work on the Default Workspace — not necessarily the one open in the app. is_active in list_workspaces tells you which one is open.
Workspaces (3). list_workspaces, get_workspace, create_workspace. Listing reports the active, remote and sync state of each workspace; creation always makes a local one.
Collections (6). list_collections, get_collection, create_collection, update_collection, move_collection, delete_collection. Nesting goes through parent_id, moves are checked for cycles, deletes are soft and cascade to descendants. Updates use optimistic locking, so pass the current version. The description field is the collection description from the Overview tab — see Collections.
Requests (7). list_requests, get_request, create_request, update_request, move_request, send_request, delete_request. create_request accepts HTTP, gRPC, GraphQL and WebSocket; update_request leaves the protocol as it is. Both take headers, auth, scripts and a description field — the same description the Docs tab holds, see request description. send_request runs HTTP, gRPC and GraphQL requests and really hits your API: it resolves variables from the workspace's active environment, runs your scripts, returns status, headers and body, names the environment it used in environment, and appends an entry to history.
Environments (5). list_environments, get_environment, create_environment, delete_environment, set_active_environment. set_active_environment makes an environment the active one in its workspace. The app switches along with the agent, and from then on send_request resolves variables from the new environment.
Variables (3). list_variables, create_variable, delete_variable. Writing values is unrestricted; reading them is not — see below.
Sync (9). sync_status, sync_push, sync_pull, sync_queue_list, sync_queue_clear, sync_resync, sync_pause, sync_resume, sync_disconnect_stream. This is the debugging set for Tetiva Cloud: inspect engine state and the outbox, force a push or a pull, pause and resume a workspace, or drop the event stream once to exercise the backoff and reconnect path. Besides pending entries, sync_queue_list lists the ones sync holds back under stalled, each with its status (failed, parked or deferred), the reason and the next retry.
Security#
The token
The server requires a token. It is generated on first launch, stored in the app database alongside the rest of the MCP settings, and accepted two ways — Authorization: Bearer <token> or ?token=<token> — because not every client can express both. Anything else gets a 401 whose body says where the working config lives. Both the SSE endpoint and the message endpoint are checked, so a leaked session id on its own buys nothing.
Regenerate in settings rotates the token on the running server immediately; every agent config still carrying the old value has to be updated. Require token turns the check off. That is an escape hatch for a client that can send neither form, and a deliberate one: with it off, any process on the machine — a browser tab reaching 127.0.0.1 included — can read every collection and variable and send requests as you.
The default address is loopback, and a host-less :9300 is normalized to 127.0.0.1:9300 at startup, including for configs saved earlier. Set a network-facing address deliberately and the app keeps it, but writes a warning to the log and highlights the field in settings.
What the token does not protect against: anyone who can already read ~/.tetiva/data.db. That file holds the token itself along with everything else. Encrypting the local database is a separate matter.
What tools redact
auth_dataon requests and collections — always[redacted], leaving the auth type visible.- Values of variables flagged as secret — withheld from
list_variables,get_environmentand the create-variable response. - Values of credential headers, in stored requests and in
send_requestresponses:authorization,proxy-authorization,cookie,set-cookie,x-api-key,api-key,x-auth-token,x-access-token,x-csrf-token,x-session-token. - gRPC metadata under those same names — for a gRPC request that is where the token lives instead of a header.
- Credential query parameters in a request URL:
token,access_token,refresh_token,api_key,apikey,key,secret,password,sig,signature. - Script output in a
send_requestresult: when a failed test orconsole.logquotes an active-environment secret, the request's auth secrets (the encoded basic value and a cached OAuth 2.0 token included) or what the request actually went out with (a signed JWT, a token a pre-request script swapped in, cookies), that value is masked.
A value made only of {{variable}} references is left alone, also behind an auth scheme: Bearer {{token}} or {{a}}{{b}}. Those name variables rather than being the secret, and the agent needs to see which one is in play. A value with any literal text in it, such as session=abc; theme={{ui}}, is redacted whole, because the literal part is still a secret.
Deliberately not redacted: request and response bodies — without them send_request is useless for debugging, so a token returned in a login response comes through in full — pre- and post-request scripts, and variables not flagged as secret.
Redaction does not cost you data. update_request and update_collection apply just the arguments you pass, so a field the agent leaves out keeps its stored value — name included, which no longer has to be sent. Some fields cannot be left out — changing one header means resending the whole array — so every [redacted] that comes back on a write is matched against what is stored under the same name and swapped for the real value before anything is saved. That means [redacted] can be sent back unchanged and the secret stays as it was; to change a secret, pass the new value. The response lists what was restored in kept_secrets: auth_data, headers.<name>, url.query.<param>, grpc_metadata.<key>. When nothing was restored, the field is left out.
A mask with nothing behind it is refused rather than written: create_request and create_collection reject [redacted] outright, and on an update a mask under a name that has no stored counterpart — a header the agent has just added, say — comes back as an error asking for the real value. Sync is a separate story, described in full on the Sync page.
Data directory#
The tools read and write the same database as the window: ~/.tetiva/data.db. The directory is overridden by TETIVA_DATA_DIR (the older GOPHERCOURIER_DATA_DIR still works as a fallback). That is the clean way to give an agent a second Tetiva instance with its own database and its own MCP port, well away from your real collections. See Settings for the full data layout.