The threa command line.
Search, read, post, and run delegations from a shell. The same binary is an MCP server, so agent hosts like Claude Desktop can connect too.
Install
The CLI is published to npm as @threahq/cli and needs
Node.js 22.13 or newer. Install it globally to put threa on
your PATH:
npm install -g @threahq/cli
Or run it without installing with npx -y @threahq/cli. The
source is in the open-source repo at
github.com/threahq/threa
under packages/cli.
Configure
The tool binds one workspace with one API key. Create the key in Workspace Settings → API Keys (see Authentication), then set environment variables or write a config file. Environment variables win when both exist.
export THREA_API_KEY=threa_uk_...
export THREA_WORKSPACE_ID=
threa whoami {
"apiKey": "threa_uk_...",
"workspaceId": "",
"output": "text"
}
A user key acts as you and shows a via-API badge on what it posts; a
workspace key acts as its bot. The key's scopes decide which commands work.
Start with threa whoami to confirm the binding.
Commands
Commands group by resource, verb second. threa --help lists
everything; each command documents its own flags with --help.
threa whoami
threa search "release plan" --what messages|memos|attachments
threa streams list --type channel
threa streams read "#general" --members
threa streams archive stream_...
threa streams unarchive stream_...
threa conversations list --stream "#general"
threa conversations read conv_...
threa messages send "#general" "Hello from the CLI"
threa messages edit msg_... "Edited"
threa messages delete msg_...
threa messages find-by-metadata source=ci
threa memos list --stream "#general"
threa memos get memo_...
threa attachments list --stream "#general"
threa attachments get att_... --url
threa attachments download att_... ./out.png
threa labels list
threa labels add triage "#general"
threa labels remove triage "#general"
threa users list
threa delegations list
threa delegations get dlg_...
threa delegations claim dlg_... --label "My machine"
threa delegations update dlg_... --note "tests passing"
threa delegations release dlg_...
threa delegations finish dlg_... --outcome complete --result - < report.md
threa delegations request-access dlg_...
threa e2e unlock
threa e2e status
threa e2e lock
threa mcp serve
Two commands worth calling out. search is the one retrieval
entry point: --what memos searches the memory Threa builds
from your conversations, and an empty memo query browses the most recent.
messages send can open a conversation with
--new-conversation and continue one with
--conversation conv_..., which is how an external agent keeps
a thread of work going across runs.
Sealed streams
An end-to-end-encrypted stream stores only ciphertext, so reading or posting
needs the key that opens it. threa e2e unlock fetches the
encrypted bundle holding your identity key, opens it with your passphrase and
files the key on this machine. Type the passphrase at the prompt, where it is
not echoed, or pipe it on stdin. Neither it nor the key leaves the machine.
threa e2e unlock # prompts, then: unlocked uik_... into the OS keychain
threa e2e status # holding uik_... (current)
threa streams read stream_... # bodies opened here, with that key
threa messages send stream_... "on it" # sealed here, then sent
threa e2e lock # forget this machine's copy
The key goes in the OS keychain by default, reached through the keychain's
command-line tool so a reinstall does not lose it.
--key-store file writes a 0600 file under
~/.threa/e2e-keys instead, and --key-dir moves that
directory. Both also read from keyStore and keyDir in
the config file, or THREA_E2E_KEY_STORE and
THREA_E2E_KEY_DIR in the environment; a flag wins over both, which
is how a runtime hands its own settings to the CLI it launches. Every command
that touches a sealed stream takes the same two flags.
threa e2e status compares the key this machine holds against the
one the workspace has active for you. They differ after the web app rotates
your key, which is when sealed streams stop opening here until you unlock
again. threa e2e lock removes the copy on this machine and revokes
nothing.
Reads and writes fail loudly rather than degrade. Without a key,
streams read says so instead of printing the placeholder the
server stores, and messages send refuses rather than posting in
the clear. A single message sealed to a key generation you were never wrapped
to comes back with null content and its own reason, alone among the page. A
sealed stream takes no --metadata and no conversation flags: both
would travel in the clear, so they are refused instead of quietly dropped.
Referring to things
Anywhere a command takes a stream you can pass the raw
stream_... id or a #channel-slug. User slugs work
as @user-slug. Lookups are exact and cached; a miss or an
ambiguous match fails with an error that names the candidates and how to
find the id.
Output and exit codes
The CLI prints short human-readable tables by default, piped or not.
-o json (or --json) switches to JSON and works
anywhere in the command line, so threa --json streams list | jq
and threa streams list -o json both behave the way you expect.
An "output": "json" entry in the config file sets the default;
an explicit flag wins. Errors go to stderr as a single JSON object with a
code, a message, and often a hint.
Exit codes: 0 for success, 1 for an API or reference error, 2 for a usage
mistake.
The MCP server
threa mcp serve exposes the same operations as 24 typed MCP
tools over stdio, for hosts that speak MCP rather than a shell. Register it
with Claude Code:
claude mcp add threa \
--env THREA_API_KEY=threa_uk_... \
--env THREA_WORKSPACE_ID= \
-- threa mcp serve
Sealed streams work over MCP the same as in the shell: read_stream
opens bodies and send_message seals them, with the key
threa e2e unlock filed on this machine. A workspace with no sealed
streams never touches the key store.
Other MCP hosts use their own registration format with the same command
and environment. The tool names mirror the CLI surface:
search, read_stream, send_message,
get_delegation, claim_delegation,
release_delegation, and so on.
Using it from agents
For a coding agent with shell access the CLI is usually the better
integration: no per-session registration, output that pipes into other
tools, and no tool schemas taking up context. The repo ships an agent
skill teaching effective use; threa skill install copies it to
~/.claude/skills/threa-cli/ for Claude Code.
The delegation commands close the loop described in
Recipes: a Threa persona
hands work to your machine, your agent inspects it before claiming, posts
progress or a manual heartbeat while working, and completes, fails, or releases
the claim. Claim tokens persist in ~/.threa/state.json; successful
completion or failure clears the stored token. A successful release clears only
the matching stored token and preserves a replacement; a failed request leaves
the token available for recovery.