Skip to main content
Glama

Linear Streamable MCP Server

by iceener
metadata.ts11.9 kB
export const serverMetadata = { title: "Linear", instructions: `Use these tools to list and manage Linear issues, projects, teams, and users. Quick start - Call 'workspace_metadata' first to fetch canonical identifiers you will reuse across tools. It will include your viewer id: use that as 'assigneeId' when you want to assign items to yourself. - Then use 'list_issues' with teamId/projectId and filters (or q/keywords) to locate targets. Use 'list_issues' for both discovery and precise lookups (including by id/identifier) — prefer orderBy='updatedAt'. - To modify, use 'update_issues' / 'update_projects', then verify with 'list_issues'. - For teams with cycles enabled, use 'list_cycles' to browse planning cycles. Default recency window - If a date range is not provided when listing issues, default to the current week in the viewer's timezone (Mon 00:00 → Sun 23:59:59.999), using updatedAt for recency. Mention the timezone surfaced by the client in your reasoning and outputs. Account identifiers (returned by 'workspace_metadata') - viewer: { id, name, email, displayName, avatarUrl, timezone, createdAt } - teams: Array<{ id, key, name, description?, defaultIssueEstimate? }> - workflowStatesByTeam: Record<teamId, Array<{ id, name, type }>> - labelsByTeam: Record<teamId, Array<{ id, name, color?, description? }>> - projects: Array<{ id, name, state, teamId?, leadId?, targetDate?, createdAt }> How to chain safely - teamId: filter in 'list_issues'; pass to 'create_issues'; use workflowStatesByTeam[teamId] to find stateId for 'update_issues'. - projectId: filter in 'list_issues'; pass to create/update issue payloads. - stateId: set via 'update_issues'. Resolve by name → id using workflowStatesByTeam. - labelIds: pass to create/update; resolve from labelsByTeam. Handling assignees and failures - To assign to yourself, prefer using your viewer id from 'workspace_metadata' as 'assigneeId'. - If a create or update fails with 'assigneeId ... could not be found', either: - Re-run 'workspace_metadata' and verify the correct team/project and user id, or - Use 'list_users' to fetch users and pick the right id. Filtering (list_issues) - 'filter' supports GraphQL-style comparators and relationship fields. Comparators: { eq, neq, lt, lte, gt, gte, in, nin, containsIgnoreCase, startsWith, endsWith, null }. Common examples: - Team/project: { team: { id: { eq: teamId } } } or { project: { id: { eq: projectId } } } - State type: { state: { type: { eq: "started" } } } - Assignee email: { assignee: { email: { eqIgnoreCase: "name@acme.com" } } } - Title case-insensitive contains: { title: { containsIgnoreCase: "search" } } - Labels: { labels: { name: { in: ["Bug", "Defect"] } } } - Or use q/keywords to match title tokens automatically (case-insensitive OR of tokens). Pagination - List tools return { cursor, nextCursor, limit }. Pass nextCursor to fetch the next page. - Prefer small limits and refine filters instead of broad scans. Safety & writes - Do not guess ids. Always take ids from 'workspace_metadata' or a read tool. - Batch writes default to sequential; keep batches small and verify with 'list_issues'. - 'update_issues' ignores empty strings (e.g., dueDate: ""): only valid fields are sent. `, } as const; export const toolsMetadata = { workspace_metadata: { name: "workspace_metadata", title: "Discover IDs (Use First)", description: "Use this to discover workspace entities and canonical IDs (viewer, teams, workflow states, labels, projects, favorites). Use this FIRST whenever you don't know ids. Inputs: include? (profile|teams|workflow_states|labels|projects|favorites), teamIds?, project_limit?, label_limit?.\nReturns: viewer, teams[] (with estimation settings and cyclesEnabled), workflowStatesByTeam, labelsByTeam, projects[], favorites?. Next: Use teamId/projectId to filter 'list_issues'; use workflowStatesByTeam[teamId][].id as stateId for 'update_issues'; use labelsByTeam ids for label operations. If a team has cyclesEnabled=false, avoid cycle-related tools.", }, list_issues: { name: "list_issues", title: "List Issues", description: 'List issues in the workspace with powerful filtering and ordering. Inputs: teamId?, projectId?, filter? (IssueFilter), q?/keywords? for title tokens, includeArchived?, orderBy?(updatedAt|createdAt|priority), limit?, cursor?, fullDescriptions?.\nDefaults: If no date filter is provided, default to the current week in the viewer\'s timezone (Mon 00:00 → Sun 23:59:59.999) using updatedAt for recency.\nTitle search: pass q: "payment timeout" (split into tokens with case-insensitive OR), or keywords: ["payment", "timeout"]. Filter is merged with these tokens.\nReturns: { items[], cursor?, nextCursor?, limit? }. Items include id, title, description?, stateId, projectId?, assigneeId?, labels[]. Text output shows a short description snippet by default; set fullDescriptions=true to include full descriptions. Next: Use \'list_issues\' again to fetch details by id (UUID) or by number+team.key/team.id (limit=1); pass nextCursor to paginate; refine filters using comparators (eq/neq/lt/lte/gt/gte/in/nin, containsIgnoreCase, startsWith/endsWith, null) and relationship fields (e.g., assignee.email, labels.name). Use ids discovered via \'workspace_metadata\'.', }, // removed get_issue (single); use get_issues (batch) get_issues: { name: "get_issues", title: "Get Issues (Batch)", description: "Fetch detailed issues in batch by ids (UUIDs or short ids like ENG-123). Inputs: { ids: string[] }.\nReturns: { results: Array<{ index, ok, id?, identifier?, issue? }>, summary }. Each issue includes assignee, state, project, labels, attachments, and branchName when available. Next: Call 'update_issues' to modify fields or 'list_issues' to discover more.", }, list_my_issues: { name: "list_my_issues", title: "List My Issues", description: "List issues assigned to the current user with filtering and ordering. Inputs: filter?, q?/keywords?, includeArchived?, orderBy?, limit?, cursor?, fullDescriptions?.\nDefaults: If you don't provide a date window, the CLIENT should default to the current week (Mon 00:00 → Sun 23:59:59.999) using updatedAt in the viewer's timezone.\nTitle search: pass q: \"text\" or keywords: [\"word1\", \"word2\"] (case-insensitive OR on title).\nReturns: Same shape as 'list_issues' for the current viewer's assigned issues (items include description?). Text output shows a short description snippet by default; set fullDescriptions=true to include full descriptions. Next: Use 'list_issues' (by id or by number+team.key/team.id, limit=1) for details or 'update_issues' to change state/assignee.", }, create_issues: { name: "create_issues", title: "Create Issues (Batch)", description: "Create multiple issues in one call. Inputs: { items: Array<{ teamId: string; title: string; description?; stateId?; labelIds?; assigneeId?; projectId?; priority?; estimate?; dueDate?; parentId?; allowZeroEstimate? }>; parallel?; dry_run? }.\nBehavior: Only send fields you intend to set. If 'assigneeId' is omitted, THIS TOOL DEFAULTS it to the current viewer id (from your authenticated context). Invalid numbers are ignored (priority<0 is dropped; estimate<=0 is dropped unless allowZeroEstimate=true or team allows zero estimates). Returns: per-item results with created id/identifier and a summary. Next: verify with 'list_issues' (filter by id or by number+team.key/team.id, limit=1).", }, update_issues: { name: "update_issues", title: "Update Issues (Batch)", description: "Update issues in batch (state, labels, assignee, metadata). Inputs: { items: Array<{ id: string; title?; description?; stateId?; labelIds?; addLabelIds?; removeLabelIds?; assigneeId?; projectId?; priority?; estimate?; dueDate?; parentId?; archived?; allowZeroEstimate? }>; parallel?; dry_run? }.\nBehavior: Only send fields you intend to change. Empty strings are ignored; priority<0 is ignored; estimate<=0 is ignored unless allowZeroEstimate=true or the issue's team allows zero estimates (auto‑detected). add/removeLabelIds adjusts labels incrementally. This prevents common assignment failures (e.g., invalid estimate=0 when team disallows zero).\nExample: Reassign only → { items: [{ id: 'ISSUE_ID', assigneeId: 'VIEWER_ID' }] }. Returns: per-item results and a summary. Next: 'get_issues' for verification; 'list_issues' to confirm filters/states.", }, list_projects: { name: "list_projects", title: "List Projects", description: "List projects with filtering and pagination. Inputs: filter? (ProjectFilter: id/state/team/lead/targetDate), includeArchived?, limit?, cursor?. For a single project, set filter.id.eq and limit=1.\nReturns: { items[], cursor?, nextCursor?, limit? } where items include id, name, state, leadId?, teamId?, targetDate?, description?. Next: Use 'update_projects' to modify or 'list_issues' with projectId to find issues.", }, get_project: { name: "get_project", title: "Get Project", description: "Deprecated in favor of 'list_projects' with filter.id.eq and limit=1.", }, create_projects: { name: "create_projects", title: "Create Projects (Batch)", description: "Create multiple projects in one call. Inputs: { items: Array<{ name: string; teamId?: string; leadId?: string; description?: string; targetDate?: string; state?: string }> }.\nNotes: team association uses teamIds internally; provide teamId to attach initially. Returns: per-item results and a summary.", }, update_projects: { name: "update_projects", title: "Update Projects (Batch)", description: "Update multiple projects in one call. Inputs: { items: Array<{ id: string; name?: string; description?: string; targetDate?: string; state?: string; leadId?: string; archived?: boolean }> }.\nReturns: per-item results and a summary. Next: verify with 'list_projects' (filter.id.eq, limit=1); discover via 'list_projects'.", }, list_teams: { name: "list_teams", title: "List Teams", description: "List teams in the workspace. Inputs: limit?, cursor?.\nReturns: { items: Array<{ id, key?, name }>, cursor?, nextCursor?, limit? }. Next: Use team ids with 'workspace_metadata' (workflowStatesByTeam) and 'list_issues'.", }, list_users: { name: "list_users", title: "List Users", description: "List users in the workspace. Inputs: limit?, cursor?.\nReturns: { items: Array<{ id, name?, email?, displayName?, avatarUrl? }>, cursor?, nextCursor?, limit? }. Next: Use user ids in 'update_issues' (assigneeId).", }, list_comments: { name: "list_comments", title: "List Comments", description: "List comments for an issue. Inputs: { issueId, limit?, cursor? }.\nReturns: { items[], cursor?, nextCursor?, limit? } where items include id, body, url?, createdAt, updatedAt?, user{id,name?}. Next: Use add_comments to add context or mention teammates.", }, add_comments: { name: "add_comments", title: "Add Comments (Batch)", description: "Add one or more comments to issues. Inputs: { items: Array<{ issueId: string; body: string }>, parallel?, dry_run? }.\nReturns: per-item results and a summary. Next: Use list_comments to verify and retrieve the comment URLs.", }, list_cycles: { name: "list_cycles", title: "List Cycles", description: "List cycles for a team (only if team.cyclesEnabled=true). Inputs: { teamId, includeArchived?, orderBy?(updatedAt|createdAt), limit?, cursor? }.\nReturns: { items[], cursor?, nextCursor?, limit? } where items include id, name?, number?, startsAt?, endsAt?, completedAt?, teamId, status?. Next: Use teamId from 'workspace_metadata' to target the right team; avoid this tool if cyclesEnabled=false.", }, } as const;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iceener/linear-streamable-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server