Skip to main content
Glama
README.md
# linear-mcp

A [Linear](https://linear.app) MCP server for Dedalus.

## Prerequisites

- Python 3.11+
- [`uv`](https://docs.astral.sh/uv/)
- A Linear workspace

## Setup

```bash
cd linear-mcp
cp .env.example .env   # then fill in your values
uv sync
```

### Environment variables

Linear uses OAuth2 for authentication. The Dedalus platform (DAuth) handles the
OAuth flow. The MCP server declares the secret name it needs (e.g. `LINEAR_ACCESS_TOKEN`).

OAuth provider configuration (consumed by the Dedalus platform):

| Variable | Description |
| --- | --- |
| `OAUTH_ENABLED` | `true` |
| `OAUTH_AUTHORIZE_URL` | `https://linear.app/oauth/authorize` |
| `OAUTH_TOKEN_URL` | `https://api.linear.app/oauth/token` |
| `OAUTH_CLIENT_ID` | Your Linear OAuth app client ID |
| `OAUTH_CLIENT_SECRET` | Your Linear OAuth app client secret |
| `OAUTH_SCOPES_AVAILABLE` | `read,write,issues:create,comments:create` |
| `OAUTH_BASE_URL` | `https://api.linear.app` |

Dedalus client configuration (for `_client.py` testing):

| Variable | Description |
| --- | --- |
| `DEDALUS_API_KEY` | Your Dedalus API key (`dsk_*`) |
| `DEDALUS_API_URL` | Defaults to `https://api.dedaluslabs.ai` |
| `DEDALUS_AS_URL` | Defaults to `https://as.dedaluslabs.ai` |

## Run the server

```bash
uv run src/main.py
```

## Test locally

```bash
uv run src/_client.py
```

This opens an interactive agent loop. On first use, DAuth will prompt you to
complete the Linear OAuth flow in your browser.

## Lint and typecheck

```bash
uv run --group lint ruff format src/
uv run --group lint ruff check src/ --fix
uv run --group lint ty check src/
```

## Available tools

| Tool                      | R/W | Description                                |
| ------------------------- | --- | ------------------------------------------ |
| `linear_get_issue`        | R   | Get issue by ID or identifier (ENG-123)    |
| `linear_list_issues`      | R   | List issues with filters                   |
| `linear_create_issue`     | W   | Create a new issue                         |
| `linear_update_issue`     | W   | Update an existing issue                   |
| `linear_list_comments`    | R   | List comments on an issue                  |
| `linear_create_comment`   | W   | Add a comment to an issue                  |
| `linear_get_project`      | R   | Get project by ID                          |
| `linear_list_projects`    | R   | List projects                              |
| `linear_create_project`   | W   | Create a new project                       |
| `linear_update_project`   | W   | Update an existing project                 |
| `linear_list_cycles`      | R   | List cycles for a team                     |
| `linear_get_cycle`        | R   | Get a cycle by ID                          |
| `linear_active_cycle`     | R   | Get the current active cycle for a team    |
| `linear_list_teams`       | R   | List all teams                             |
| `linear_get_team`         | R   | Get a team by ID                           |
| `linear_list_team_states` | R   | List workflow states (statuses) for a team |
| `linear_whoami`           | R   | Get authenticated user profile             |
| `linear_list_users`       | R   | List workspace members                     |
| `linear_list_labels`      | R   | List labels                                |
| `linear_create_label`     | W   | Create a new label                         |
| `linear_search_issues`    | R   | Full-text search across issues             |

**21 tools** (14 read, 7 write)

## Architecture

Linear uses a single GraphQL endpoint (`POST /graphql`) for all operations. The
request layer in `src/linear/request.py` dispatches queries through Dedalus's HTTP
enclave, which injects OAuth credentials transparently.

```plaintext
src/
├── linear/
│   ├── config.py      # Connection definition (OAuth)
│   ├── request.py     # GraphQL dispatch + coercion helpers
│   └── types.py       # Typed dataclass models
├── tools/
│   ├── issues.py      # Issue CRUD
│   ├── comments.py    # Comment operations
│   ├── projects.py    # Project CRUD
│   ├── cycles.py      # Cycle queries
│   ├── teams.py       # Team + workflow states
│   ├── users.py       # User queries
│   ├── labels.py      # Label operations
│   └── search.py      # Issue search
├── server.py          # MCPServer setup
├── main.py            # Entry point
└── _client.py         # Interactive agent loop (DAuth)
```

## Notes

- Linear's API is **GraphQL-only**. Every tool dispatches a GraphQL query or mutation.
- Workflow states (statuses) vary per team. Use `linear_list_team_states` to discover
  valid state IDs before creating or transitioning issues.
- Issue identifiers (e.g. `ENG-123`) can be used interchangeably with UUIDs in most tools.
- Priority values: 0 = No priority, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low.
- Archived resources are hidden by default. Some tools accept `include_archived` to surface them.
- Authentication uses OAuth2 via DAuth. Personal API keys are **not** supported in this server.