| name | linear |
| description | Read and write Linear issues, comments, projects, cycles, users, documents, initiatives, milestones, and more via the Linear MCP server. Attach chat-uploaded screenshots to issues. |
| license | MIT |
| allowed-tools | mcp_call skill_run read_skill |
| metadata | {"gini":{"version":"1.0.0","author":"Gini","platforms":["macos","linux"],"requires":{"credentials":["LINEAR_API_KEY"]}}} |
Linear
Linear is reachable through the mcp_call tool. Each call hits the hosted Linear MCP server; the runtime resolves the Authorization header from the connected Linear token. You never see the token or the endpoint — you pass server: "linear" and a tool name.
Every Linear call has the same shape:
mcp_call({
server: "linear",
tool: "<tool name>",
arguments: { ... }
})
The response is a JSON string. Parse it before reporting back to the user — Linear sometimes returns nested pageInfo or arrays you should summarize, not dump.
Discovering what's available
The full inventory of Linear MCP tools is in your system prompt under "Configured MCP servers — linear — tools: ...". That list is authoritative; do not assume a tool doesn't exist just because it isn't called out below. The categories below give you defaults and taste; reach for tools from the inventory list whenever the user asks for something outside them.
When you're unsure of a tool's argument shape, either:
- Call
mcp_call({ server: "linear", tool: "search_documentation", arguments: { query: "..." } }) to query Linear's own docs, or
- Just try the call — the server returns a clear validation error on bad args, which is recoverable.
Do not refuse a Linear-related ask unless you've actually checked. "Linear doesn't support that" is a claim that requires evidence.
Defaults and common shapes
Issues — list / read / save
save_issue handles both create and update — with an id it updates, without one it creates.
mcp_call({ server: "linear", tool: "list_issues",
arguments: { assignee: "me", state: "started" } })
mcp_call({ server: "linear", tool: "get_issue",
arguments: { id: "LIN-123" } })
mcp_call({ server: "linear", tool: "save_issue",
arguments: { team: "ENG", title: "Login fails on Safari 17",
description: "Steps to reproduce…" } })
Useful argument quirks Linear's tool descriptions sometimes leave terse:
assignee: "me" resolves to the authenticated viewer — no user-id lookup needed.
team accepts the team key (ENG) or UUID.
state accepts either the workflow state id or its display name ("In Progress", "Done"). Workflow state groups for filtering: backlog, unstarted, started, completed, cancelled.
priority is 0|1|2|3|4 where 1 is urgent, 4 is low, 0 is no priority.
list_issues truncates description for compactness. Follow up with get_issue when the user needs the full body.
- Paginate by passing the
cursor from pageInfo.endCursor back into the same call.
Attaching screenshots / images
When the user provides images in chat and asks you to file or update a Linear issue, attach them after you know the issue identifier. The flow is three steps; the middle step (PUT bytes to Linear's signed URL) lives in the attachments skill — read_skill name='attachments' for the full reference. Linear-specific args are documented here.
-
Prepare (Linear-specific) — mcp_call({ server: "linear", tool: "prepare_attachment_upload", arguments: { issue, filename, contentType, size } }). The response carries uploadRequest.url, uploadRequest.headers, and assetUrl. The signed URL expires in 60 seconds; move immediately.
-
PUT the bytes (attachments skill) — skill_run({ skill: "attachments", script: "signed-upload", args: { uploadId, url: prep.uploadRequest.url, headers: prep.uploadRequest.headers } }). The script reads the upload off disk and PUTs it; the model never has to touch raw bytes. Returns { ok, status, bytesSent }.
-
Finalize (Linear-specific) — mcp_call({ server: "linear", tool: "create_attachment_from_upload", arguments: { issue, assetUrl, title, subtitle } }) with the assetUrl from step 1 and an optional title / subtitle.
Each user message that carries attachments ends with a system note listing each upload's id, mime type, and size:
Attached image uploads (in order):
- abc-123-... (image/png, 36116 bytes)
- def-456-... (image/jpeg, 24512 bytes)
Read the size and mimeType from that marker — prepare_attachment_upload rejects requests where size doesn't match the actual bytes (GCS returns EntityTooLarge / EntityTooSmall). One 3-step sequence per image.
If the skill_run step returns ok: false, the bytes did not land. Don't run the finalize step; either re-prepare (the signed URL is dead after 60s) or tell the user the upload failed and offer to paste the assetUrl into the issue body manually.
Reading attachments on existing issues
To inspect or describe an attachment that's already on a Linear issue: combine mcp_call(get_attachment) with the attachments skill's signed-download script to land the bytes as a Gini upload, then vision_query to ask the model about the image. The attachments skill documents the full pattern — read_skill name='attachments'.
Everything else (projects, cycles, comments, documents, initiatives, milestones, status updates, labels, diffs, users, teams)
Use the matching tool from the inventory list in your system prompt. The shapes are predictable: list_* returns an array with pageInfo, get_* takes an id, save_* creates without id / updates with id, delete_* takes an id. Examples:
mcp_call({ server: "linear", tool: "list_teams", arguments: {} })
mcp_call({ server: "linear", tool: "list_projects", arguments: { team: "ENG" } })
mcp_call({ server: "linear", tool: "list_cycles",
arguments: { team: "ENG", type: "current" } }) // also "next", "previous"
mcp_call({ server: "linear", tool: "list_comments",
arguments: { issueId: "LIN-123" } })
mcp_call({ server: "linear", tool: "save_comment",
arguments: { issueId: "LIN-123", body: "Reproduced on macOS 14." } })
mcp_call({ server: "linear", tool: "list_issue_labels", arguments: { team: "ENG" } })
mcp_call({ server: "linear", tool: "list_issue_statuses", arguments: { team: "ENG" } })
For label / status names passed to save_issue, confirm via list_issue_labels / list_issue_statuses first — Linear returns a generic validation error otherwise.
Limitations
- Existing attachment bytes can't be streamed back —
get_attachment returns metadata, not bytes. Outbound uploads (chat → Linear) work via the 3-step sequence above.
- No webhooks / subscriptions — the server is request/response. For live updates, point the user at Linear's native subscriptions.
- No batch tool — bulk operations loop client-side.
Rules
- Always invoke through
mcp_call with server: "linear" (or signed_upload for the PUT step of attachment uploads). Do not call any other tool to reach Linear.
- Never refuse a Linear ask without checking. If a plausible tool is in the inventory list and the user asked for that capability, try it. If you can't tell which tool, call
search_documentation first.
- Confirm destructive intent before deleting an issue, comment, attachment, or status update. Linear has no undo.
- When the user asks "what am I working on", default to
list_issues({ assignee: "me", state: "started" }) and summarize, not dump.
- Quote issue identifiers verbatim (
LIN-123, not 123). Linear's deeplinks resolve those directly.
- Never include the user's API token in a reply or in any tool argument — the runtime injects it server-side.