Skip to main content

Type-safe Linear CLI built on Pydantic v2

Project description

clinear

Type-safe Linear CLI built on Pydantic v2 + httpx + Typer.

A Linear command-line interface designed for humans, agents, and CI/CD pipelines. Every API response is a validated Pydantic model. Every command works in shell pipelines. JSON output is the canonical contract; the pretty human tables sit on top of it.

clinear me                          # who am I?
clinear team list                   # all teams in workspace
clinear issue list --assignee me    # my issues
clinear issue create --team ENG --title "Fix login bug" --priority 1
clinear -o json issue list | jq '.[].title'    # pipe into anything

Why clinear?

  • Type-safe. Every response validated through Pydantic v2. No silent schema drift.
  • Agent-first. Stable JSON contracts; pipe-friendly -o ids / -o md / -o yaml.
  • Tiny attack surface. Only Pydantic + httpx + Typer + Rich. No npm chaos, no postinstall hooks.
  • Honest errors. Linear API errors surfaced verbatim with proper POSIX exit codes.
  • Built for automation. --dry-run for safe mutation previews, raw query escape hatch for any GraphQL.

Install

# From PyPI (recommended)
pip install clinear
# or
uv tool install clinear
# From source
git clone https://github.com/rinadelph/clinear.git
cd clinear
uv venv && source .venv/bin/activate
uv pip install -e .

Quick Start

1. Get a token

Generate a personal API key at https://linear.app/settings/api.

2. Set it up

export LINEAR_TOKEN="lin_api_..."
# Or persist a config:
clinear init

3. Verify

clinear me

4. Use it

# Read
clinear team list
clinear issue get ENG-123
clinear issue list --assignee me --state Todo

# Write
clinear issue create --team ENG --title "Fix login bug" --priority 1
clinear issue assign ENG-123 me
clinear issue state ENG-123 "In Progress"
clinear issue prio ENG-123 1
clinear comment add ENG-123 "Started on this — investigating now"

# Pipe
clinear -o ids issue list --assignee me | xargs -I{} clinear issue url {}
clinear -o json issue list --state Todo | jq '.[] | "\(.identifier): \(.title)"'

# Safety net
clinear --dry-run issue update ENG-123 --priority 2

Output Formats

Set with -o / --output before the subcommand:

Format Flag What you get
human default Pretty Rich tables with colors
json -o json Compact JSON, null-pruned
yaml -o yaml Hand-rolled minimal YAML
md -o md Markdown tables for PRs / reports
plain -o plain TSV — id<TAB>state<TAB>...
ids -o ids Just identifiers, one per line — great for xargs

Command Reference

clinear
├── me / auth status / auth whoami
├── init                          Create ~/.config/clinear/config.toml
├── team list / get / states / members
├── issue
│   ├── list   --team --state --assignee --label --priority --contains ...
│   ├── get <id>
│   ├── create --team --title [--description --priority --assignee --label ...]
│   ├── update <id> [--title --state --assignee --priority --label ...]
│   ├── state <id> <state-name>
│   ├── assign <id> <user>
│   ├── prio <id> <0-4>
│   ├── url <id>
│   └── search <query>
├── project list / get
├── cycle current <team> / list <team>
├── comment list / add / edit / delete
├── label list / create / delete
└── raw query <graphql>           Escape hatch — arbitrary GraphQL

Run clinear <command> --help for full flags on any subcommand.


Configuration

Default location: ~/.config/clinear/config.toml. Override with $CLINEAR_CONFIG.

[auth]
token_env = "LINEAR_TOKEN"  # read from this env var

[defaults]
team = "ENG"
output = "human"

[display]
color = true
table_max_width = 120

Run clinear init to scaffold the file.


Exit Codes (stable across versions)

Code Meaning
0 Success
1 Generic error
2 Usage error (bad flags)
3 Auth error (missing/invalid token)
4 Not found
5 Validation error (response didn't match model)
6 API error (Linear returned errors)
7 Network error (timeout, DNS, TLS)
8 Rate limited

Security

  • Token read from $LINEAR_TOKEN, --token flag, or config.toml. Never logged in plaintext.
  • HTTPS-only. TLS verification mandatory.
  • No telemetry. Zero outbound calls except to api.linear.app.
  • Pre-commit hook blocks committing tokens, .log files, .env files. See scripts/pre-commit.sh.

Development

See AGENTS.md for the contributor guide — architecture, testing, version bumping, and release process.

git clone https://github.com/rinadelph/clinear.git
cd clinear
bash scripts/install-hooks.sh    # install pre-commit hook
uv venv && uv pip install -e ".[dev]"
export LINEAR_TOKEN="lin_api_..."
bash scripts/e2e-test.sh         # 36/36 should pass

License

MIT — see LICENSE.


For AI agents — Skill & MCP server

clinear ships with two artifacts specifically for AI coding agents:

1. Agent skill bundle

A Swarm/Claude-style skill that teaches an agent both the mechanics of clinear and the behavior of working through Linear (search before create, use --output json for pipes, chain commands in shell instead of writing Python, etc.).

Install:

git clone https://github.com/rinadelph/clinear.git
cd clinear
bash skills/install.sh

This symlinks skills/clinear/ into BOTH:

  • ~/.swarmos/skills/clinear/ (for Swarm OS)
  • ~/.claude/skills/clinear/ (for Claude Code / Claude Desktop)

Flags: --swarm-only, --claude-only, --copy (no symlinks), --uninstall.

2. MCP server (clinear-mcp)

An optional Model Context Protocol server that exposes the same teaching content as an MCP tool, plus read-only Linear resources and prompt templates for common workflows.

What it exposes:

  • 1 toolclinear_guide(topic) returns structured teaching content. Topics: overview, commands, workflows, filters, output-formats, examples.
  • 7 read-only resourcesclinear://me, clinear://issue/{id}, clinear://team/{key}, clinear://project/{id_or_slug}, clinear://cycle/current/{team_key}, clinear://issues/mine, clinear://issues/team/{team_key}.
  • 6 promptstriage, daily_standup, create_from_error, hand_off, cycle_review, issue_investigate.

The server exposes NO mutation tools by design. Mutations are performed via the clinear CLI through the agent's Bash tool — the MCP tool's response and every prompt body include a behavioral reminder reinforcing this rule.

Step 1 — Install

The server is an optional extra. Pick one:

# Option A — pip
pip install 'clinear[mcp]'

# Option B — uv (recommended; isolated tool install)
uv tool install --with mcp 'clinear==0.3.0'

# Option C — pipx
pipx install 'clinear[mcp]'

After install, you should have both binaries on PATH:

which clinear           # /home/you/.local/bin/clinear
which clinear-mcp       # /home/you/.local/bin/clinear-mcp
clinear --version       # clinear 0.3.0

Step 2 — Set up the Linear token

The MCP server reads the same token as the CLI. Pick one:

# Option A — env var (simplest; works for all clients)
export LINEAR_TOKEN="<YOUR_LINEAR_TOKEN>"
# Persist it in your shell rc:
echo 'export LINEAR_TOKEN="<YOUR_LINEAR_TOKEN>"' >> ~/.bashrc
# Or ~/.zshrc, ~/.config/fish/config.fish, etc.

# Option B — config file (good for desktop apps that don't inherit shell env)
clinear init                          # writes ~/.config/clinear/config.toml
# Then edit ~/.config/clinear/config.toml:
#   [auth]
#   token = "<YOUR_LINEAR_TOKEN>"

Get your token at https://linear.app/settings/api (create a personal API key). Verify it works:

clinear me            # should print your Linear profile
clinear auth status   # shows which token source resolved

Note for desktop MCP clients (Claude Desktop, Cursor, etc.): GUI apps usually do not inherit your shell's exports. Either put the token in the config file (Option B) or pass it via the MCP server entry's env block (shown below for each client).

Step 3 — Register with your MCP client

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "clinear": {
      "command": "clinear-mcp",
      "env": {
        "LINEAR_TOKEN": "<YOUR_LINEAR_TOKEN>"
      }
    }
  }
}

Claude Code (CLI) — add via the claude mcp command:

claude mcp add clinear clinear-mcp --env LINEAR_TOKEN=<YOUR_LINEAR_TOKEN>

Or edit ~/.claude.json directly:

{
  "mcpServers": {
    "clinear": {
      "command": "clinear-mcp",
      "env": { "LINEAR_TOKEN": "<YOUR_LINEAR_TOKEN>" }
    }
  }
}

Cursor (~/.cursor/mcp.json):

{
  "mcpServers": {
    "clinear": {
      "command": "clinear-mcp",
      "env": { "LINEAR_TOKEN": "<YOUR_LINEAR_TOKEN>" }
    }
  }
}

Swarm OS — drop the same config into your Swarm MCP config (~/.swarmos/mcp/servers.json or via swarm mcp add):

swarm mcp add clinear --command clinear-mcp --env LINEAR_TOKEN=<YOUR_LINEAR_TOKEN>

Codex CLI (~/.config/codex/mcp.json):

{
  "servers": {
    "clinear": {
      "command": "clinear-mcp",
      "env": { "LINEAR_TOKEN": "<YOUR_LINEAR_TOKEN>" }
    }
  }
}

If clinear-mcp is not on the system PATH of the GUI app (common on macOS), use the absolute path:

{
  "mcpServers": {
    "clinear": {
      "command": "/home/you/.local/bin/clinear-mcp",
      "env": { "LINEAR_TOKEN": "<YOUR_LINEAR_TOKEN>" }
    }
  }
}

Find the absolute path with which clinear-mcp.

Step 4 — Verify the wiring

Restart the MCP client and confirm the server connects. From inside the client you should see:

  • 1 tool: clinear_guide
  • 2 concrete resources: clinear://me, clinear://issues/mine
  • 5 resource templates: clinear://issue/{id}, clinear://team/{key}, clinear://project/{id_or_slug}, clinear://cycle/current/{team_key}, clinear://issues/team/{team_key}
  • 6 prompts: triage, daily_standup, create_from_error, hand_off, cycle_review, issue_investigate

If you have the MCP Inspector installed, you can also smoke-test from a terminal:

npx @modelcontextprotocol/inspector clinear-mcp

This launches a browser UI that lets you list tools/resources/prompts and invoke them interactively.

For a quick stdio sanity check without the Inspector:

(echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}'
 echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'
 echo '{"jsonrpc":"2.0","id":2,"method":"tools/list"}') | clinear-mcp | head -3

You should see a JSON-RPC initialize response followed by tools/list returning clinear_guide.

Troubleshooting

Symptom Fix
clinear-mcp: command not found in GUI app Use absolute path in the MCP config (which clinear-mcp)
Server starts but resource reads return auth error Token not visible to the server — pass it via the config's env block instead of relying on shell export
pip install 'clinear[mcp]' fails with quoting error Some shells eat the brackets — quote the whole spec or escape: pip install clinear\[mcp\]
Tool list is empty in client Restart the MCP client after editing the config; some clients cache tools/list
Stdout looks corrupted Don't run clinear-mcp interactively — it speaks JSON-RPC on stdin/stdout. Logs go to stderr.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clinear-0.3.1.tar.gz (157.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clinear-0.3.1-py3-none-any.whl (69.4 kB view details)

Uploaded Python 3

File details

Details for the file clinear-0.3.1.tar.gz.

File metadata

  • Download URL: clinear-0.3.1.tar.gz
  • Upload date:
  • Size: 157.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for clinear-0.3.1.tar.gz
Algorithm Hash digest
SHA256 857ba385834406fbcfec48a0c6eff719c7bfa5e0b5f8aa49cc40d0e3c48d7f7d
MD5 0f9f5865882ec90e09ffc7984bee373e
BLAKE2b-256 ce338112f8a5b605f087796c47e13783e17a5face5a8c515098b1767bdb49a8f

See more details on using hashes here.

File details

Details for the file clinear-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: clinear-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 69.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for clinear-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e109f596cb2701aa41f9a210d5a5b16610b6acfc7da0d589ef6a04ffdb4016ac
MD5 cbd329185c4454869db7f9554fd85646
BLAKE2b-256 f1268d78d5c126c386c8b95cf2a90bb0a0a463bed4406fd4c3032988545de39a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page