linLinear CLI
lin - the Linear CLI

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|table and --fields shape 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-run on every mutation. Learn one command and the rest follow.
  • Discoverable at runtime. lin describe emits 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.

ApproachTokens 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 unmasked

Response 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:

ControllinLinear MCP
Choose the fields returned--fields identifier,title,statenot exposed
Preview a mutation--dry-run on every mutationnot exposed
Choose the output shape--output json|ndjson|tablefixed
Guard destructive calls--confirm=<id>not exposed
Follow every page--alldrive the cursor yourself
Branch on failuresysexits exit codesread 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-run

An 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:

On this page