Introduction
The Linear CLI for humans and AI agents - one fast native binary over the Linear API.
lin is the Linear CLI: a single, fast, native binary that turns Linear's
GraphQL API into a predictable, scriptable command surface. It is first-class
for two audiences at once - a person at a terminal, and an AI agent running
headless.
What you get
- 8.4x lighter than the MCP. Measured, not asserted: ~8,300 tokens for a 40-turn agent session, against ~69,800 through a lazy MCP client and ~808,500 through an eager one. See Why a CLI, not just the MCP.
- JSON-first. Data on stdout, messages on stderr.
--output json|ndjson|tableand--fieldsshape the payload; a machine is always assumed to be reading. - Predictable over clever. Resource-first grammar (
lin <resource> <verb>), sysexits exit codes, cursor pagination, and--dry-runon every mutation. Learn one command and the rest follow. - Discoverable at runtime.
lin describeemits the whole surface as JSON; these docs are generated from it, so they never drift from the binary. - Comprehensive through codegen. Commands are typed GraphQL operations compiled against a vendored schema, so coverage is broad without being brittle.
- Safe with secrets. API keys come from the environment or an XDG config file, never from flags, and are masked in output. Mutations only ever change the fields you name; unset never means "clear".
- Runs anywhere. One native binary in a terminal, a cron job, or a CI step - no MCP server hop, no runtime to install, no MCP-aware client required.
Why lin exists
Linear has an excellent API but no first-class command line. Scripts, cron jobs,
CI pipelines, and coding agents that want to file an issue, attach a screenshot,
or move a card otherwise hand-roll GraphQL or drive a browser. lin fills that
gap: one binary, the same shapes everywhere, safe to re-run.
Why a CLI, not just the MCP
The Linear MCP server is the easy way to give an agent Linear access, but it has a measurable cost. We benchmarked it: 40-turn session, 23 Linear operations, response sizes measured against a real workspace.
| Approach | Tokens for the session |
|---|---|
| MCP, eager client (Claude Desktop, most frameworks) | ~808,500 |
| MCP, lazy client (loads tools on demand) | ~69,800 |
| lin | ~8,300 |
That is 8.4x cheaper than a well-implemented lazy MCP client, and ~98x
cheaper than one that keeps all 57 tool schemas resident. The 8.4x figure is the
honest one to judge lin by: it compares against the MCP at its best.
Nearly all of that gap comes from one capability difference. No Linear MCP read
tool exposes field selection - not list_issues, not get_issue, not any of
the twelve we checked. get_issue can only add to the payload
(includeRelations, includeReleases). There is no way to ask the MCP for
less. lin ships --fields on every read command:
lin issue list --team ENG --limit 20 --fields identifier,title,state
# ~1,200 tokens, against ~11,300 for the same 20 issues unmaskedResponse volume dominates any real agent session, and it is the one axis where the MCP currently offers no lever.
The schemas cost nothing either: no tool definitions sit in context. The agent
learns the whole grammar once from AGENTS.md (~1,150 tokens) and calls
lin describe <command> only when it needs a reminder.
The full methodology, raw payloads, and scripts are in
bench/ - including where
lin measures worse (request-side syntax on filtered list calls). See
For AI agents for the invariants to load.
Controls the MCP does not expose
The token gap is a symptom. The underlying difference is that lin hands the
caller levers over how an operation runs, and the Linear MCP does not expose
them at all. Checked across twenty of its tools:
| Control | lin | Linear MCP |
|---|---|---|
| Choose the fields returned | --fields identifier,title,state | not exposed |
| Preview a mutation | --dry-run on every mutation | not exposed |
| Choose the output shape | --output json|ndjson|table | fixed |
| Guard destructive calls | --confirm=<id> | not exposed |
| Follow every page | --all | drive the cursor yourself |
| Branch on failure | sysexits exit codes | read the error prose |
Two of these change how an agent can behave, not just what it costs.
--fields decides what enters the context. An agent triaging a backlog
needs identifiers and titles, not description bodies, timestamps, and relation
graphs for twenty issues. Asking for three fields instead of everything is the
difference between ~1,200 and ~11,300 tokens on a single call, and the context
it saves is context left for the actual task.
--dry-run makes a mutation inspectable before it happens. Every lin
mutation can print the operation and variables exactly as they would go on the
wire, resolve every human reference (team key or name, me, state and label names), and
then send nothing:
lin issue update ENG-123 --state Done --add-labels regression --dry-runAn agent can show that to a human, or check its own work, before anything is written. Through the MCP the only way to find out what a call does is to make it.
Get started
Install the binary, authenticate with a Linear API key, and run your first commands:
- Installation - Homebrew,
go install, or from a clone - Authentication - your Linear personal API key
- Quickstart - the first commands and the grammar behind them
- Command reference - every command, grouped by resource