taktcli
Authorized GraphQL CLI for Takt — the full Takt GraphQL surface from the shell, for agents and humans. Everything the MCP server does, and more.
The core is stdlib-only (urllib + argparse + json): a thin
argv → GraphQL-over-HTTP client. Because arguments arrive as argv strings, it
structurally sidesteps the MCP client-serialization bug class (arrays-as-JSON-
strings, double-encoded results, the empty-string-drops-all-args bug) — an argv
string simply can't be double-JSON-encoded.
The package and console-script are both named taktcli (bare takt is taken on
PyPI).
Install
pip install taktcli # stdlib-only core
pip install 'taktcli[watch]' # + websockets, enables `taktcli watch`
pip install -e cli/ # from a monorepo checkout
Maestro/harness images ship pip install 'taktcli[watch]' — taktcli watch is
the supported replacement for the retired tools/wait_for_task.py.
Quickstart
taktcli login # device-auth, stores ~/.takt/credentials.json
taktcli whoami # nickname + claims + active credential source
taktcli board --project takt # show the board
taktcli claim takt-42 # READY → RUNNING, prints leaseId
taktcli run takt-42 # heartbeat the lease
taktcli done takt-42 # mark DONE
taktcli --help (or taktcli help) lists every command; taktcli <noun> --help
lists a group's verbs.
Auth
Token resolution, simplest-wins:
TAKT_TOKENenv → used verbatim. This is what harness containers are stuffed with; env always wins over any stored credential.- Else
~/.takt/credentials.json— plain JSON{ "token": "<jwt>", "url": "..." }, mode0600, written bytaktcli login. - Else error:
No Takt credentials found. Run: taktcli login(exit3).
URL resolution mirrors it: TAKT_URL env → url in the credentials file →
default https://api.takt.sh/graphql. The watch WS endpoint is derived by
swapping the scheme (https → wss).
taktcli login runs the OAuth 2.0 device-authorization grant: it prints a
verification URL + user code, polls until you approve in a browser, then persists
the token. logout removes the stored credential; whoami prints who you are
and which credential source is active.
taktcli login # default endpoint
taktcli login --url http://localhost:8454/graphql # override the endpoint
taktcli logout
taktcli whoami
Auth is the same JWT + claims used by MCP and GraphQL — taktcli adds no
privilege surface. There is no claims/permission verb anywhere; admin ops are
reachable via gql only if the caller's token already holds the admin claim.
Long-lived bot tokens stay minted out-of-band (tools/jwt_cli.py) and dropped
into TAKT_TOKEN; login is for humans and dev machines.
Passthrough — reaches 100% of GraphQL
taktcli gql + taktcli schema are a complete superset of every MCP tool: any
GraphQL op MCP never wrapped is reachable here.
taktcli gql -q 'query { me { nickname claims } }'
taktcli gql -f query.graphql --var id=takt-42 --field-file description=desc.md
taktcli schema --sdl # SDL introspection (--json for JSON)
Big markdown flows through --field-file NAME=PATH (NAME=- = stdin) — this
sidesteps shell quoting and the empty-string-args bug class.
Shared I/O flags
--var KEY=VALUE(repeatable; value parsed as JSON when valid, else string)--vars JSON/--vars-file PATH(-= stdin) — bulk variables--field-file NAME=PATH(NAME=-= stdin) — inject a file's contents as a variable; the canonical path for large markdown--raw— full{data, errors, extensions}envelope (default is pretty.data)--compact— single-line JSON
Variable precedence (lowest → highest):
--vars-file < --vars < --var < --field-file.
Command surface
The CLI uses a positional namespace — taktcli <noun> <verb> [args] — so the
entity is always the first token. This fixes the MCP create_wiki (Wiki) vs
wiki_create (page) collision at the CLI layer. Run taktcli <noun> --help for a
group's verbs.
Every MCP tool maps to a verb:
| MCP tool | taktcli verb |
|---|---|
get_task |
get <id> / task get <id> |
list_tasks |
task list |
search_tasks |
task search <q> |
get_board |
board |
create_task |
task create |
update_task |
task update <id> |
update_status |
status <id> <STATUS> (or run / done / fail) |
claim_task |
claim <id> |
release_claim |
release <id> |
force_release_claim |
gql (admin claim required) |
transition_task |
task transition <id> <action> |
get_dependencies |
task deps <id> |
add_comment |
task comment <id> |
get_task_comments |
task comments <id> |
attach_result |
task result <id> |
get_task_results |
task results <id> |
delete_result |
task result-delete <id> |
wiki_read |
wiki read <path> |
wiki_search / wiki_semantic_search |
wiki search <q> (--semantic) |
wiki_grep |
wiki grep <pattern> |
wiki_create |
wiki create <path> |
wiki_update |
wiki update <path> |
wiki_edit |
wiki edit <path> |
wiki_move |
wiki move <path> <new-path> |
wiki_verify |
wiki verify <path> |
wiki_delete |
wiki delete <path> |
wiki_bulk_update_status |
wiki bulk-status <folder> <status> |
find_related_wiki_pages |
wiki related <task-id> |
list_wiki_feedback |
wiki feedback list |
resolve_wiki_feedback |
wiki feedback resolve <id> |
wiki_list |
wikis list |
create_wiki |
wikis create |
get_project_wiki |
wikis get <project> |
request_upload_url_and_headers + confirm_upload |
file upload <path> (composite) |
get_file_url |
file url <id> |
list_files |
file list |
attach_file_to_task |
file attach <id> <task-id> |
detach_file_from_task |
file detach <id> <task-id> |
attach_file_to_wiki |
file attach-wiki <id> <path> |
update_label / delete_label |
label update <name> / label delete <name> |
| (none — CLI-only) | label create |
list_notifications |
notif list |
mark_notification_read |
notif read <id> |
mark_all_notifications_read |
notif read-all |
unread_notification_count |
notif count |
| (none — CLI-only) | project {list,get,create,rename,member-add,member-remove} |
taskStatusChanged / taskChanged / resultAdded / commentAdded |
watch (needs [watch] extra) |
Notes:
label createis CLI-only (the MCP server never wrappedcreateLabel, but the GraphQL mutation exists).label update/deleteare keyed by name and resolve the id client-side via thelabelsquery.- The
wiki/wikisverbs take a global--wiki SLUG(default$TAKT_WIKI) and stream big markdown through--content-file/--old-file/--new-file(-= stdin). - The wiki-container verbs are
wikis list/create/get(entity = Wiki). The oldworkspace list/create/getverbs are a hard error naming the replacement for one release — workspace is being reclaimed to mean the tenant, and nothing else. project renameis name-only — slug changes are blocked on the backend.
Output & exit codes
Default output is pretty JSON of the unwrapped .data payload. --compact =
single-line; --raw = full {data, errors, extensions} envelope; watch =
NDJSON (one event per line, flushed per event).
| Code | Meaning |
|---|---|
0 |
success |
1 |
GraphQL error (server reached; errors[] non-empty) |
2 |
transport error (DNS/TCP/TLS/HTTP non-2xx/timeout/WS drop) |
3 |
config or usage error (no token, bad URL, unknown verb, missing flag) |
extensions.code (e.g. LEASE_EXPIRED, ALREADY_CLAIMED) is preserved in error
output so loops can branch on it.
Task monitoring — taktcli watch
Replaces the retired tools/wait_for_task.py. Streams subscription events as
NDJSON and reconnects with exponential backoff on drops (e.g. deploys). Needs the
[watch] extra.
taktcli watch --project takt --timeout 3600 # status changes, any task
taktcli watch --task takt-42 --task takt-43 # specific tasks
taktcli watch --project takt --event resultAdded # other subscriptions
--event defaults to taskStatusChanged. Unlike the old one-shot script,
watch is a continuous stream — read NDJSON lines as they arrive and act on the
first matching one; no restart-per-event. Tasks already READY when the watcher
starts are not detected — check taktcli board --project takt first.
MCP coexistence
taktcli and the MCP server run side-by-side over the same GraphQL API,
authenticating identically (JWT, same claims). Use either or both in one session.
The MCP freeze/removal timeline lives in PRD 002 §8.
Documentation
- Usage reference:
takt/context/taktcliin the Takt wiki. - Design & rationale:
takt/dev/prd-002-taktcli(PRD 002). - Contract placement:
takt/contract§4.3 — the CLI as a first-class integration interface alongside GraphQL + MCP.
Release
Push a cli-v* tag (e.g. cli-v0.1.0) — .github/workflows/cli.yaml builds
cli/ and OIDC trusted-publishes to PyPI. The tag must match the version in
cli/pyproject.toml (CI asserts this). The CLI has its own CI job alongside
backend/plugin/frontend (npm run test:cli).
Release files for taktcli 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| taktcli-0.6.0.tar.gz | 69.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| taktcli-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 122.4 kB
Release files / taktcli-0.6.0.tar.gz
| Download URL | taktcli-0.6.0.tar.gz |
|---|---|
| Size | 69.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10951fd7c1b052a6a909d6dace88601c6a682ad0e9d351663f533c76b18271f5
|
|
BLAKE2b-256 checksum How to use checksums |
324be0db36049077080dc9053b3ff3f975e354d525f841242d7b2af889eeedae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency logRelease files / taktcli-0.6.0-py3-none-any.whl
| Download URL | taktcli-0.6.0-py3-none-any.whl |
|---|---|
| Size | 53.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9653db4d764f211d61d5a87510e679ef0687c7373a7d20603a42535663cb15f7
|
|
BLAKE2b-256 checksum How to use checksums |
6711e4f07f492ffa583ada1f1364ab7c449fd6ce1eae579895d3d5cd9925f696
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency log