
Upload a file and attach it to a Linear issue. filereference must be a file staged through the platform's file endpoint (not a public URL); issueid takes a UUID or 'ENG-142'. Max 25 MB. To attach a web link instead, use createattachment. Remove with deleteattachment.
List comments from Linear, optionally scoped to one issue via issueid (accepts a UUID or an identifier like 'ENG-142'; use getissues to find issues). Filter server-side by bodycontains (substring) or createdafter. Supports cursor pagination.
Get a single Linear comment by id. Use get_comments to find comment ids on an issue.
Add a comment to a Linear issue. issueid accepts a UUID or an identifier like 'ENG-142'; use getissues to find issues. Body is Markdown.
Edit the body of an existing Linear comment. Use get_comments to find comment ids.
Resolve a Linear comment thread by comment id. Use get_comments to find comment ids.
Reopen a resolved Linear comment thread by comment id. Use get_comments to find ids.
Delete a Linear comment by id (permanent). Use getcomments to find comment ids. To reopen a resolved thread instead of deleting it, use unresolvecomment.
List Linear custom views (saved, named filters) with cursor pagination. Filters server-side by name substring, teamid (use getteams) and shared. Returns each view's id for updatecustomview/deletecustomview, plus its issue filter and view-preferences ids.
Create a Linear custom view (a saved issue filter). Requires name; optional filterdata (Linear IssueFilter JSON), teamid (getteams), projectid (getprojects; such views are not listed by listcustomviews), shared, icon, color. Returns the view id for updatecustom_view.
Update a Linear custom view by id (use listcustomviews): name, description, icon, color, shared, teamid, ownerid, or filterdata/projectfilterdata, which replace the existing filter entirely. Only the fields you pass change; to clear them use unsetteam or unset_description.
Delete a Linear custom view by id, the inverse of createcustomview. Use listcustomviews to find ids. Deleting a view never deletes the issues it matched.
List Linear customer needs (customer requests linked to issues) with cursor pagination. Filters server-side by customerid (listcustomers), issueid (getissues) and priority. Returns the need ids that updatecustomerneed and deletecustomerneed take.
Get one Linear customer need by id: its content, priority, source attachment url, customer and issue. Use listcustomerneeds to find need ids.
Create a Linear customer need: record that a customer wants something on an issue. Requires customerid (listcustomers) and issueid (getissues). Optional: Markdown body, priority 0/1 (1 = important), attachment_url. Returns the need with its id.
Update a Linear customer need by id (listcustomerneeds): reassign customerid, move to another issueid, body, priority 0/1, attachmenturl. Only passed fields change. To CLEAR the body use unsetbody; to drop the attachment use clear_attachment.
Delete a Linear customer need by id, the inverse of createcustomerneed. Use listcustomerneeds to find ids. Set keep_attachment=true to keep its source attachment on the issue.
List Linear customers (organizations that file requests) with cursor pagination. Filters server-side by name substring, domain, externalid, ownerid (getusers), statusid (listcustomerstatuses) and tierid (listcustomertiers). Returns the customer ids updatecustomer, deletecustomer and createcustomer_need take.
Get one Linear customer by id, with its status, tier, owner and need count. Use listcustomers to find ids, or a customer need's customer.id. Use listcustomer_needs to see the customer's needs.
List the workspace's customer statuses (e.g. Active, Churned) with ids. Call this before createcustomer or updatecustomer to get a valid statusid, or listcustomers to filter by status.
List the workspace's customer tiers (e.g. Enterprise, SMB) with ids. Call this before createcustomer or updatecustomer to get a valid tierid, or listcustomers to filter by tier.
Create a Linear customer. Requires name. Optional: domains, externalids, mainsourceid, logourl, ownerid (getusers), revenue, size, slackchannelid, statusid (listcustomerstatuses), tierid (listcustomertiers). Returns the customer with its id.
Update a Linear customer by id (listcustomers): name, domains (replaces list), externalids (adds), owner, revenue, size, statusid, tierid, Slack channel. Only passed fields change. To UNLINK the Slack channel use unsetslackchannel.
Delete a Linear customer by id, the inverse of createcustomer. May also remove the customer's needs, so check listcustomerneeds first. Use listcustomers to find ids.
List cycles (sprints) from Linear, filterable by teamid (use getteams) and isactive. Cycles belong to one team. Use this to discover cycle ids for createissue/update_issue.
Update a Linear cycle (sprint): name, description, startsat, endsat, or completedat. Use listcycles to find cycle ids. Only the fields you pass change. Linear does not support creating cycles through its API.
Share a Linear document with workspace members (userids from getusers) as view, comment or edit (default edit). Document ids come from get_documents. Users who already have access move to the given role.
Stop sharing a Linear document with the given userids (use getusers), the inverse of sharedocument. The owner can remove anyone; a collaborator can only remove themselves. Document ids come from getdocuments.
List documents from Linear. Filter server-side by title (substring, case-insensitive), project, or creator; supports cursor pagination and includearchived. Use this to discover document ids for other tools. Omits each document's Markdown body; use getdocument for content. To search document text use search_documents. Keep the default limit (50): large limits can exceed Linear's complexity cap.
Get a single Linear document by id, including its full Markdown content. Use get_documents to search by title when the id is unknown.
Create a Linear document. Requires title and a container: projectid (use getprojects) or issueid (use getissues) — Linear has no standalone documents, so provide at least one. Optional: content (full Markdown body), icon (emoji), color (hex).
Update a Linear document by id: title, content (Markdown body), icon, color, or its project/issue association. To CLEAR the body set unsetcontent=true. To delete the document use deletedocument. Use get_documents to find document ids.
Delete a Linear document by id (moves it to the trash, Linear's delete). Reversible with restoredocument. Use getdocuments to find document ids.
Restore a trashed Linear document by id, the inverse of deletedocument. Call getdocuments with include_archived=true to find ids of trashed documents.
List the authenticated user's Linear sidebar favorites (pinned issues, projects, views, folders, etc.) with cursor pagination. Returns the favorite ids that updatefavorite and deletefavorite need, and folder ids for parent_id.
Pin something to the user's Linear sidebar. Pass exactly one of issueid, projectid, customviewid, cycleid, documentid, labelid, teamid, userid, customerid or initiativeid. Errors if the entity is already a favorite, so check listfavorites first. For a folder use createfavoritefolder. Returns the favorite id for updatefavorite/deletefavorite.
Create a new folder in the user's Linear sidebar to group favorites, e.g. 'Q4 launch'. Every call creates another folder, even with the same name. Move favorites in with updatefavorite (parentid). Returns the folder favorite id; remove the folder with delete_favorite using that id, which also removes the favorites inside it.
Move or reorder a Linear sidebar favorite, or rename a favorite folder: parentid (folder id), movetotoplevel (out of its folder), sortorder, foldername. Use list_favorites to find favorite ids. Only the fields you pass change.
Remove a favorite from the user's Linear sidebar by favorite id, the inverse of createfavorite. Use listfavorites to find ids. The favorited entity itself is untouched.
List Linear initiatives (strategic goals that group projects) with cursor pagination. Filters server-side by name substring, status, and ownerid (getusers). Omits the Markdown content: use getinitiative for it. Returns the initiative ids that getinitiative, updateinitiative and archiveinitiative take.
Get one Linear initiative by id, including its full Markdown content. Use list_initiatives to find ids.
Create a Linear initiative. Requires name. Optional: description, Markdown content, status, priority 0-4, ownerid (getusers), leadteamid (getteams), targetdate with targetdateresolution, color, icon. Returns the new initiative with its id.
Update a Linear initiative by id (listinitiatives): name, description, content, status, priority, owner, lead team, target date, color, icon. Only passed fields change. To CLEAR the lead team or target date use unsetleadteam / unsettarget_date.
Archive a Linear initiative by id. It is hidden from default views but can be restored with unarchiveinitiative. Use listinitiatives to find ids.
Restore an archived Linear initiative by id, the inverse of archiveinitiative. Pass includearchived=true to list_initiatives to find archived initiative ids.
List initiative status updates from Linear, optionally scoped to one initiative via initiativeid (use listinitiatives to find ids). Supports cursor pagination and include_archived.
Get a single Linear initiative update by id. Use listinitiativeupdates to find update ids.
Post a status update on a Linear initiative. Requires initiativeid (use listinitiatives) and a Markdown body; optional health: onTrack, atRisk, or offTrack.
Edit an existing Linear initiative update's body or health by id. Use listinitiativeupdates to find update ids.
Archive a Linear initiative update by id. Use listinitiativeupdates to find ids.
Restore an archived Linear initiative update by id. Use listinitiativeupdates with include_archived=true to find archived update ids.
List issue labels from Linear, with server-side name (substring) and teamid filters plus cursor pagination. Use this to discover valid label ids before setting labelids on createissue/updateissue; only non-group (leaf) labels can be applied to issues.
Create a Linear issue label. Requires a name; teamid (use getteams) scopes it to one team, omit for a workspace-level label. Optional hex color and description.
Edit a Linear issue label: rename it, change its color or description, or nest it under a group label (parentid) or remove it from its group (unsetparent=true). Use getissuelabels to find label ids. Only the fields you pass change.
Delete a Linear issue label by id (permanent), the inverse of createissuelabel. Use getissuelabels to find label ids. Deleting a label removes it from every issue that carries it.
Set a reminder on a Linear issue: Linear notifies the authenticated user at reminderat (ISO 8601, in the future). Takes an issue id or identifier like 'ENG-142' (use getissues). Linear's API cannot cancel a reminder once set.
List workflow states (issue statuses) from Linear, filterable by teamid (use getteams) and state category. State ids are team-specific — use this to discover valid stateid values before setting them on createissue/update_issue.
List issues from Linear with server-side filters (team, assignee, project, project milestone, state, priority, title text, label, updated-after date) and cursor pagination. Use getteams/getusers/getprojects/listprojectmilestones/listissuestatuses to find filter ids. For one known issue use getissue instead. Text filter matches TITLES ONLY; to search descriptions/comments use search_issues.
Get a single Linear issue by id (UUID) or identifier like 'ENG-142'. Use get_issues to search when the id is unknown.
Create a Linear issue. Requires teamid (use getteams). Optional: description, priority 0-4, assigneeid (getusers), stateid (listissuestatuses), labelids (getissuelabels, leaf labels only), projectid (getprojects), cycleid (listcycles), projectmilestoneid (listprojectmilestones), parentid, duedate, subscriber_ids.
Update a Linear issue by id or identifier like 'ENG-142': title, description, priority 0-4, estimate, assigneeid (getusers), stateid (listissuestatuses), labels (getissuelabels), project/cycle/projectmilestone/parent, subscriberids, duedate, team transfer. To CLEAR a field use its unset_* flag, not null.
Archive a Linear issue by id or identifier like 'ENG-142'. The issue is hidden from default views but kept, and unarchiveissue brings it back. Use deleteissue only when the user explicitly asked to delete.
Delete a Linear issue by id or identifier like 'ENG-142'. By default, Linear soft-deletes it for a 30-day grace period; restore it during that period with unarchiveissue. Use getissues(includearchived=true) to find archived or trashed issue ids. permanentlydelete=true (admins only) skips the grace period and is irreversible. Prefer archive_issue unless the user asked to delete.
Restore an archived or soft-deleted Linear issue by id or identifier like 'ENG-142'. This is the inverse of archiveissue and also restores soft-deleted issues during Linear's 30-day grace period. Use getissues(include_archived=true) to find archived or trashed issue ids.
List Linear project milestones (dated checkpoints inside a project) with cursor pagination. Filters server-side by projectid (use getprojects), name substring, target-date range, and updatedafter. Linear has no server-side status filter — read each milestone's status field instead. For a milestone's issues call getissues with projectmilestoneid.
Get one Linear project milestone by id (UUID): name, description, target date, status, and progress. Use listprojectmilestones to find milestone ids. For the issues in the milestone call getissues with projectmilestone_id.
Create a Linear project milestone (a dated checkpoint inside a project). Requires projectid (use getprojects) and name; optional description and targetdate (YYYY-MM-DD). Returns the milestone id to pass as projectmilestoneid to createissue/update_issue.
Update a Linear project milestone: name, description, targetdate, or move it to another project (projectid). Use listprojectmilestones to find milestone ids. Only the fields you pass change; to CLEAR the target date use unsettargetdate, not null (a description cannot be cleared).
Delete a Linear project milestone by id, the inverse of createprojectmilestone. Use listprojectmilestones to find ids. Check getissues(projectmilestone_id=...) first: its issues may be left without a milestone.
List projects from Linear. Filter server-side by name (substring, case-insensitive), state, health, or lead; supports cursor pagination and includearchived. Use this to discover project ids for other tools. Omits each project's Markdown body; use getproject for content. To search project text use search_projects. Keep the default limit (50): large limits can exceed Linear's complexity cap.
List the workspace-defined project statuses (e.g. Backlog, Planned, In Progress). Use this to find status ids for createproject/updateproject's statusid; each status also shows its fixed type, which is what getprojects' state filter matches.
Get a single Linear project by id, including its full Markdown content. Use get_projects to search by name when the id is unknown.
Create a Linear project. Requires name and teamids (use getteams to find team ids). Optional: description (short summary), content (full Markdown body), leadid/memberids (getusers), startdate, target_date (YYYY-MM-DD).
Update a Linear project by id: name, description, content (Markdown body), lead, status, priority, teams, or dates. To CLEAR the body set unsetcontent=true. Use getprojects to find project ids.
Archive a Linear project by id. Archiving hides the project from default views; it can be restored in the app. Use get_projects to find project ids.
Restore an archived Linear project by id, the inverse of archiveproject. Pass includearchived=true to get_projects to find archived project ids.
List project status updates from Linear, optionally scoped to one project via projectid (use getprojects to find project ids). Supports cursor pagination and include_archived.
Get a single Linear project update by id. Use getprojectupdates to find update ids.
Post a status update on a Linear project. Requires projectid (use getprojects) and a Markdown body; optional health: onTrack, atRisk, or offTrack.
Edit an existing Linear project update's body or health by id. Use getprojectupdates to find update ids.
Archive a Linear project update by id. Use getprojectupdates to find update ids.
Restore an archived Linear project update by id. Use getprojectupdates with include_archived=true to find archived update ids.
Add an emoji reaction to a Linear comment (commentid, from getcomments), issue (issueid, from getissues), project update or initiative update. Pass exactly one target. Returns the reaction id, which delete_reaction needs.
Remove an emoji reaction from Linear by reaction id, the inverse of createreaction. The id comes from createreaction's result or the reactions list on getcomments / getissue; only reactions you created can be removed.
Link two Linear issues, e.g. mark one as blocking another or as a duplicate. Both ids accept a UUID or an identifier like 'ENG-142'; use getissues to find issues. Linear adds the inverse edge automatically, so create the relation once, in one direction only. Use getissue_relations to see existing links first.
List the relations on a Linear issue in BOTH directions: what it blocks, what blocks it, duplicates and related links. Each result carries a direction field, so type='blocks' with direction='incoming' means the issue you asked about is blocked by the other one. issueid accepts a UUID or an identifier like 'ENG-142'. Returns relation ids for deleteissue_relation.
Remove a link between two Linear issues. Takes the relation's own id, not an issue id; use getissuerelations to find it. Removing a relation also removes the inverse edge Linear created with it.
Attach a link to a Linear issue, e.g. a PR, doc or dashboard. issueid accepts a UUID or an identifier like 'ENG-142'. The url is the attachment's identity: reusing a url already on the issue updates that attachment rather than adding a second one. Use getattachments to see what is already attached.
List the links attached to a Linear issue, such as PRs, docs and dashboards. issue_id accepts a UUID or an identifier like 'ENG-142'.
Delete a link attached to a Linear issue, the inverse of createattachment. Takes the attachment's own id, not an issue id; use getattachments to find it.
Full-text search of Linear issues by term, ranked by relevance. Searches titles and descriptions, and comments when includecomments=true. Returns totalcount of all matches (null when filter is set). Pass filter (IssueFilter) to narrow by team, state, assignee or label; teamid only boosts ranking. Use getissues when there is no search term.
Full-text search of Linear documents by term, ranked by relevance; set includecomments=true to match comment text. Returns totalcount. Use get_document with a returned id to read the body.
Full-text search of Linear projects by term, ranked by relevance; set includecomments=true to match comment text. Returns totalcount. Use getprojects for exact filters; use getproject with a returned id for the full body.
Natural-language (semantic) search across Linear issues, projects, documents and initiatives in one call, matching meaning rather than exact words. Returns type, id, title and url per hit; follow up with the matching get_* tool. Not paginated.
List teams from Linear. Filter server-side by name (substring, case-insensitive) or key (the exact short code like 'ENG' that prefixes issue identifiers — never pass search text as key). Supports cursor pagination. Use this to discover team ids for other tools.
Get a single Linear team by id, including its members. Use get_teams to find team ids (or to search by name/key).
Create a Linear team. Requires a name; the short key (max 7 chars, e.g. 'ENG') is auto-generated when omitted.
Update a Linear team's name, key, or description by team id. Use get_teams to find team ids.
List the workspace's Linear templates (issue, project and document), optionally filtered by type or teamid. Returns at most 250 (truncated=true when capped), with the pre-filled templatedata. Not paginated.
Get one Linear template by id, including its templatedata. Use listtemplates to find ids.
Create a Linear template of type issue, project or document with pre-filled templatedata (use gettemplate on an existing one to see the shape). Optional teamid (use getteams) scopes it to a team. Cannot configure recurring-issue schedules.
Update a Linear template's name, templatedata, team, description, icon or color. Only fields you pass change; templatedata replaces the whole data; unsetteam=true makes it workspace-wide. Use listtemplates to find ids.
Delete a Linear template by id. Existing issues made from it are not affected. Use list_templates to find ids.
List Linear triage responsibilities: who is on triage duty per team (currentuser), the action (assign or notify) and the rotation. Optional teamid to find one team's. Supports cursor pagination.
List Linear time schedules (on-call rotations) with their shifts. Use it to find the id to pass as timescheduleid to createtriageresponsibility or updatetriageresponsibility. Supports cursor pagination.
Get one Linear triage responsibility by id. Use listtriageresponsibilities to find ids.
Configure triage duty for a Linear team: action 'assign' or 'notify', plus either a manualuserids rotation (use getusers) or a timescheduleid (use listtimeschedules). Requires teamid (use getteams). One per team; use updatetriage_responsibility to change it.
Change a Linear triage responsibility's action, manual rotation (manualuserids, assignmentindex) or timescheduleid (use listtimeschedules), not both; unsettimeschedule=true detaches the schedule. Only fields you pass change. Use listtriage_responsibilities to find ids.
Delete a Linear triage responsibility by id, ending that team's triage rotation. Use listtriageresponsibilities to find ids.
List users in the Linear workspace. Filter server-side by name (substring), email (exact), active status, or is_me=true for the authenticated user. Supports cursor pagination. Use this to discover user ids for assignee/lead/member fields. Linear has no user create/update API.
Get a single Linear user by id. Use getusers to find user ids (or filter isme=true for the authenticated user).
Validate Linear credentials by fetching the authenticated user. Verifies credentials during setup.
Create Linear view preferences (layout, grouping, ordering) for at most one scope: team, project, initiative, label, project label, initiative label or custom view (ids from getteams/getprojects/listinitiatives/listcustom_views); no scope id for workspace-wide views like myIssues. For the user or workspace. Replaces conflicting preferences.
Delete Linear view preferences by id, resetting that scope to Linear's defaults; the inverse of createviewpreferences. Ids come from createviewpreferences or listcustomviews.

In an mcp.json file, add the configuration below, and restart Cursor.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "url": "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
5 "headers": {
6 "Authorization": "Bearer yMt*****"
7 }
8 }
9 }
10}
11Open your Claude Desktop configuration file and add the server configuration below. You'll also need to restart the application for the changes to take effect.
Make sure Claude is using the Node v20+.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "yMt*****"
14 }
15 }
16 }
17}Open your Windsurf MCP configuration file and add the server configuration below.
Click on the refresh button in the top right of the Manage MCP server page or in the top right of the chat box in the box icon.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api.merge.dev/api/v1/tool-packs/<tool-pack-id>/registered-users/<registered-user-id>/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "<ah-production-access-key>"
14 }
15 }
16 }
17 }In Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows), run "MCP: Open User Configuration".
You can then add the configuration below and press "start" right under servers. Enter the auth token when prompted.
Learn more in the official documentation ↗
1{
2 "inputs": [
3 {
4 "type": "promptString",
5 "id": "agent-handler-auth",
6 "description": "Agent Handler AUTH_TOKEN", // "yMt*****" when prompt
7 "password": true
8 }
9 ],
10 "servers": {
11 "agent-handler": {
12 "type": "stdio",
13 "command": "npx",
14 "args": [
15 "-y",
16 "mcp-remote@latest",
17 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
18 "--header",
19 "Authorization: Bearer ${input:agent-handler-auth}"
20 ]
21 }
22 }
23}Merge automates the full authentication lifecycle. This includes:
For Linear specifically, users authenticate through Merge’s Link flow, and Merge manages OAuth tokens before executing any MCP tool calls.
Yes. Merge’s Security Gateway supports both default and fully custom DLP rules. You can configure rules to:
These rules apply to both tool inputs and outputs.
Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now