@kkaminsk/linear-mcp
v1.0.0
Published
Model Context Protocol server for Linear
Readme
Linear MCP Server
An MCP server for Linear built in TypeScript. It exposes a structured tool surface for issues, projects, workflow metadata, attachments, portfolio entities, webhooks, and agent workflows.
Current status
- Default runtime is stdio. Set
LINEAR_MCP_TRANSPORT=streamto expose MCP streamable HTTP atLINEAR_MCP_PATH(default/mcp). The server does not expose/sse. - Auth supports
LINEAR_API_KEY, theLINEAR_ACCESS_TOKENalias, and the tool-driven OAuth flow throughlinear_authandlinear_auth_callback. OAuth callbackstatevalues are single-use. - Advertised tool schemas are enforced at runtime before handler dispatch, so malformed tool payloads fail with structured validation errors at the MCP boundary.
- Advertised tool schemas avoid top-level
oneOf/allOf/anyOfcombinators so Claude-compatible MCP clients can ingest the full tool catalog. linear_get_capabilitiesreports transport details together with server build provenance so clients can confirm the packaged runtime name/version they are connected to.- Each MCP tool request emits structured stderr telemetry with tool name, transport, duration, and sanitized failure metadata for troubleshooting.
linear_get_runtime_diagnosticsreports live low-cardinality request counters, recent failure context, and active stream session state without exposing secrets.linear_search_issuesis the query-backed issue search path. The built server advertises a requiredquerystring and keeps the free-text query separate from optional list-style filters.- OAuth token exchange and the shared Linear request boundary enforce explicit time budgets. Approved safe reads use bounded retry/backoff on retryable failures, while non-idempotent writes stay single-attempt by default.
- Release validation is gated by
npm run verify:release, which rebuilds the server, audits guarded Linear issue contracts, checks the built tool catalog, runs critical issue workflow smoke tests, and smoke-tests a fresh packaged install.
What it supports
Core work management
- Issues: get, create one issue with
linear_create_issue, batch create withlinear_create_issues, bulk update, list, search, delete, hierarchy viaparentId, and general issue relations - Projects: create, update, delete, get, list, search, create-with-issues, project updates, and initiative association via
initiativeId - Comments: get a single comment, list comments globally or by issue, create threaded replies with
parentId, update, delete, resolve, and unresolve threads - Project milestones: create, update, delete, get, search, list, bulk create
Workflow and discovery
- Teams: get, list
- Users: viewer, get, list, search
- Workflow states: list
- Labels: list, create, update, delete
- Cycles: get, list, current cycle
Integrations and advanced surfaces
- Attachments: get, list, create, update, delete
- Webhooks: get, list, create, delete
- Portfolio entities: initiatives and customers
- Agents: agent sessions and agent activities
- Capabilities: runtime capability discovery
- Runtime diagnostics: live troubleshooting counters and session state through
linear_get_runtime_diagnostics
Runtime-aware behavior
- Subscription tools are only advertised when the runtime reports streaming transport support.
- If a subscription tool is invoked on stdio, the server returns a structured capability-limitation error instead of a generic failure.
- The default runtime is stdio. Set
LINEAR_MCP_TRANSPORT=streamto expose a remote MCP streamable HTTP endpoint instead. - Stream mode uses MCP streamable HTTP at
LINEAR_MCP_PATH(default/mcp). The server does not expose a legacy/sseendpoint. linear_get_capabilitiesreportsauthScopeasserverfor stdio andsessionfor stream transport.- Structured per-tool telemetry is emitted to stderr so stdio protocol traffic on stdout stays untouched.
Authentication
API key
Set a personal API key in either supported environment variable:
LINEAR_API_KEY=your_api_key
# or
LINEAR_ACCESS_TOKEN=your_api_keyIf both are set, the server uses LINEAR_API_KEY. This is the simplest way to run the server locally.
OAuth
The OAuth flow is available through MCP tools:
- Call
linear_authwithclientId,clientSecret, andredirectUri. - Open the returned
authorizationUrl. - Call
linear_auth_callbackwith bothcodeand the exact returnedstate.
Notes:
- OAuth uses
actor=app. - Callback state is single-use and validated against the issued authorization request. If a link goes stale, call
linear_authagain to get a fresh URL and state. - The generated authorization URL uses only Linear-supported parameters. The server does not request offline-only OAuth parameters.
- Token refresh is awaited before handlers resolve the active client.
- In stream mode, each MCP session gets its own isolated auth context. Pending OAuth state, active tokens, and auth configuration do not leak across remote sessions.
Webhook security
Webhook management tools only register webhooks with Linear. They do not host a public receiver for you.
If you create a webhook:
- store the webhook secret outside the repository,
- verify the Linear signature on every delivered payload,
- reject unsigned or invalid payloads before processing them.
Development
npm install
npm run build
npm test
npm startAdditional commands:
npm run dev
npm run test:coverage
npm run test:integration
npm run verify:linear-api-contracts
npm run verify:issue-workflowsMCP setup
Example MCP configuration:
{
"mcpServers": {
"linear": {
"command": "node",
"args": ["C:\\path\\to\\linear-mcp\\build\\index.js"],
"env": {
"LINEAR_API_KEY": "your_personal_access_token"
}
}
}
}Use either LINEAR_API_KEY or LINEAR_ACCESS_TOKEN in MCP client config. If both are present, LINEAR_API_KEY wins.
Runtime transports
Stdio
- Default mode for local clients such as Cline.
- No remote HTTP endpoint is available in stdio mode.
- Startup diagnostics report the packaged server build as
Build: linear-mcp@<version>before auth/setup messages.
Streamable HTTP
Set these environment variables before starting the server:
LINEAR_MCP_TRANSPORT=stream
LINEAR_MCP_HOST=127.0.0.1
LINEAR_MCP_PORT=3000
LINEAR_MCP_PATH=/mcp- Clients should connect to
http://127.0.0.1:3000/mcpby default. /sseis not a supported endpoint; use the configured streamable HTTP path instead.- If you need a public tunnel for a remote client, expose the configured port and forward the
/mcppath. For example,ngrok http 3000should target the stream endpoint, not/sse. - Stream transport auth is session-scoped. Each connected MCP session keeps its own pending OAuth state and credentials.
Issue workflows
Hierarchy vs. relations
- Use
linear_create_issuefor a single issue andlinear_create_issuesfor one or more issues submitted through the batch-create contract. linear_create_project_with_issuescreates the project first and reuses the same batch-create contract for its follow-on issues.linear_create_project_with_issuesstops before issue creation if project creation does not return a usable project ID, and it reports compensation or partial-state details when downstream issue creation fails.- Use
parentIdonlinear_create_issueandlinear_bulk_update_issuesfor parent-child hierarchy. - Use
linear_create_issue_relationandlinear_delete_issue_relationfor non-hierarchical relationships such as blocked-by or duplicate links. linear_get_issueis the canonical hierarchy read and returns bothparentandchildrenreferences when they exist.linear_delete_issuesreturns deterministicdeletedIdsand failure details when some requested issue deletions do not complete.
Example hierarchy update:
{
"issueIds": ["ENG-124"],
"update": {
"parentId": "ENG-100"
}
}Existing issue project assignment
Use linear_bulk_update_issues for the released existing-issue update path, even when updating a single issue:
{
"issueIds": ["ENG-123"],
"update": {
"projectId": "project-abc"
}
}- Set or change a project assignment by sending a project ID.
- Clear an existing project assignment explicitly with
"projectId": null. - Omitting
projectIdleaves the current project unchanged.
Project and initiative workflows
- Initiative lifecycle tools are available through
linear_get_initiative,linear_list_initiatives,linear_create_initiative, andlinear_update_initiative. - Project create and update workflows accept
initiativeIdso clients can attach a project to an initiative. - Clear an existing initiative association explicitly with
"initiativeId": nullonlinear_update_project. - Project reads and mutation responses include linked initiative summary data when the association exists.
Example project update:
{
"id": "project-abc",
"initiativeId": "initiative-42"
}Search semantics
linear_list_issuesis the filter-and-pagination path.linear_search_issuesis the free-text search path and always requiresquery.- Optional
teamId,projectId,assigneeId,stateId,states,priority, andcycleIdfilters are applied alongside the text query. stateIdandstatesare mutually exclusive on both list and search requests.linear_search_issuesdoes not accept a genericfilterobject ororderBy; those list-style controls stay onlinear_list_issues.- The search path sends
querythrough Linear's search backend instead of encoding it as an issue filter field. - Exact issue identifiers like
POL-431resolve through a deterministic team-key + issue-number lookup before falling back to ranked search results. - Current regression coverage covers both query-only and query-plus-filter search behavior.
Guarded issue workflow contracts
linear_create_issueuses the audited raw GraphQL single-create helper so the MCP tool does not inherit SDK mutation-shape regressions.linear_create_issuesremains on the SDK-backed batch-create path.- Raw GraphQL single-create stays on
IssueCreateInput!and batch-create stays onIssueBatchCreateInput!; the repo guards against reintroducing array-shaped single-create input. - Raw GraphQL bulk delete stays on
issueDelete(ids: $ids)and the repo audits that helper contract separately from the MCP handler's deterministic per-ID result shaping. linear_search_issueskeeps free-text queries on the rawsearchIssues(term: ...)GraphQL path, with exact identifier lookups handled separately so issue references likePOL-431stay reliable.- Run
npm run verify:linear-api-contractsto audit those guarded contracts locally.npm run verify:releaseincludes the same audit before packaging.
Critical issue workflow smoke coverage
npm run verify:issue-workflowsruns credential-free MCP-boundary smoke tests forlinear_create_issue,linear_create_issues, andlinear_search_issues.- The smoke harness boots the real MCP server against a fake Linear backend, so the tool boundary is exercised without live Linear credentials or network access.
npm run verify:releaseincludes the smoke suite so critical issue workflows cannot drift silently between source and released builds.
Marketplace and packaged installs
- The published package exposes the
linear-mcpexecutable frombuild/index.js. - Release verification uses:
npm run verify:tool-catalog
npm run verify:package-installTroubleshooting
- Run
linear_get_capabilitiesto confirm the runtime is reporting the expected packaged server name/version before debugging tool behavior. - Run
linear_get_runtime_diagnosticsfor a live snapshot of request totals, failure counters, recent sanitized failure context, and active stream session count. - Inspect stderr telemetry records to correlate a tool name, transport, duration, outcome, retryability, and upstream request ID without exposing raw headers or request bodies.
- If
linear_search_issuesfails with anIssueFilter.searchGraphQL error, the client is still connected to a stale build or package that predates the raw search fix. Rebuild or reinstall the package, restart the MCP server, and re-checklinear_get_capabilities. - If startup logs say
Auth: no LINEAR_API_KEY or LINEAR_ACCESS_TOKEN detected, the package installed correctly and you only need to finish auth setup. - If a remote client hangs on
/sse, switch it to the configured streamable HTTP endpoint such as/mcp, or run the server in stdio mode for local clients. - If a request reaches its time budget, the returned failure identifies the timed-out operation and keeps the retryable timeout classification (
TIMEOUT/ HTTP 408) in the structured GraphQL error payload. - Safe read operations may retry within the bounded request policy before failing. Non-idempotent writes do not auto-retry, so rerun those manually only after confirming the upstream state.
- Before publishing, run
npm run verify:releaseto rebuild, validate the built tool catalog, and smoke-test a fresh package install.
Comment tools
linear_get_commentreturns a direct comment payload with stable IDs, author metadata, issue context, parent linkage, and resolution state when present.linear_list_commentsandlinear_get_issue_commentsboth accept Linear-style collection controls:first,after,last,before,filter,includeArchived, andorderBy.linear_get_issue_commentsaccepts either a Linear issue ID or a human-friendly issue identifier such asPOL-431.linear_create_commentandlinear_update_commentuse markdownbodyas the primary content field.bodyDataremains optional advanced structured content for clients that need it.
Released tool surface (Version 1.0.0)
Version 1.0.0 ships the released issue-update and comment workflow surface:
linear_bulk_update_issueslinear_get_commentlinear_list_commentslinear_get_issue_commentslinear_create_commentlinear_update_commentlinear_delete_commentlinear_resolve_commentlinear_unresolve_comment
Implementation notes
- Tool schemas in
src/core/types/tool.types.tsuse standard JSON Schema. - Tool responses return machine-readable
structuredContent. - Comment tools use markdown-first
bodyfields;bodyDatais optional advanced structured content. - The server uses both raw GraphQL operations and the current
@linear/sdksurface. - GraphQL errors preserve status, request correlation, retryability, and normalized error details without forwarding raw upstream headers.
