Skip to main content
RapidDev - Software Development Agency
mcp-tutorial

How to use MCP for project management (Jira, Linear)

Use MCP servers for Jira and Linear to let AI assistants manage project tickets through natural language. The AI creates issues, updates statuses, assigns team members, and queries sprint progress — all without leaving your IDE or chat interface. Configure community or custom MCP servers that wrap the Jira REST API or Linear GraphQL API and expose project management actions as MCP tools.

What you'll learn

  • How to configure Jira and Linear MCP servers for ticket management
  • How to create, update, and query issues through natural language
  • How to build a custom MCP server wrapping the Linear GraphQL API
  • How to integrate project management into AI-assisted development workflows
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate9 min read20-30 minMCP TypeScript SDK v1.x, Jira Cloud or Linear, Claude Desktop / CursorMarch 2026RapidDev Engineering Team
TL;DR

Use MCP servers for Jira and Linear to let AI assistants manage project tickets through natural language. The AI creates issues, updates statuses, assigns team members, and queries sprint progress — all without leaving your IDE or chat interface. Configure community or custom MCP servers that wrap the Jira REST API or Linear GraphQL API and expose project management actions as MCP tools.

Managing Jira and Linear Tickets via MCP

Switching between your code editor and project management tools breaks flow. MCP solves this by bringing ticket management into your AI assistant. Connect a Jira or Linear MCP server, and you can create bugs from error logs, update ticket statuses after completing features, and check sprint progress — all through natural language conversation. This tutorial covers configuring community servers and building a custom Linear MCP server.

Prerequisites

  • A Jira Cloud or Linear account with project access
  • API token for Jira or API key for Linear
  • Claude Desktop or Cursor with MCP support
  • Node.js 18+ for custom server development

Step-by-step guide

1

Configure a Jira MCP server for ticket management

Set up a Jira MCP server that connects to your Jira Cloud instance. The server provides tools to search issues with JQL, create new issues, update fields, add comments, and transition statuses. You need a Jira API token (create one at id.atlassian.com/manage-profile/security/api-tokens) and your Jira site URL.

typescript
1// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
2{
3 "mcpServers": {
4 "jira": {
5 "command": "npx",
6 "args": ["-y", "mcp-server-jira"],
7 "env": {
8 "JIRA_URL": "https://your-company.atlassian.net",
9 "JIRA_EMAIL": "you@company.com",
10 "JIRA_API_TOKEN": "your-jira-api-token"
11 }
12 }
13 }
14}
15
16// Tools typically available:
17// - search_issues: Search with JQL queries
18// - create_issue: Create a new ticket
19// - update_issue: Update fields on existing ticket
20// - add_comment: Add a comment to a ticket
21// - transition_issue: Change ticket status

Expected result: Jira MCP server connected with tools for searching, creating, and updating tickets.

2

Configure a Linear MCP server for modern project tracking

Linear's GraphQL API is well-suited for MCP integration. Several community MCP servers exist for Linear. Configure one with your Linear API key (Settings > API > Personal API keys). Linear's data model is cleaner than Jira's, making AI interactions more straightforward.

typescript
1// Claude Desktop config:
2{
3 "mcpServers": {
4 "linear": {
5 "command": "npx",
6 "args": ["-y", "mcp-server-linear"],
7 "env": {
8 "LINEAR_API_KEY": "lin_api_your_key_here"
9 }
10 }
11 }
12}
13
14// Common natural language interactions:
15// "Create a bug ticket: Login page returns 500 when password is empty"
16// "What issues are assigned to me this sprint?"
17// "Mark LIN-423 as done"
18// "Add a comment to LIN-150: Fixed in PR #78, deployed to staging"

Expected result: Linear MCP server connected with issue CRUD tools and sprint query capabilities.

3

Build a custom Linear MCP server with typed tools

For maximum control, build a custom MCP server that wraps Linear's GraphQL API. This lets you tailor tools to your team's specific workflows — creating issues with custom fields, filtering by team or label, and managing cycles. The Linear GraphQL API is simple and well-documented.

typescript
1// src/linear-server.ts
2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4import { z } from "zod";
5
6const LINEAR_API = "https://api.linear.app/graphql";
7const API_KEY = process.env.LINEAR_API_KEY!;
8
9async function linearQuery(query: string, variables: Record<string, unknown> = {}) {
10 const res = await fetch(LINEAR_API, {
11 method: "POST",
12 headers: {
13 "Content-Type": "application/json",
14 Authorization: API_KEY,
15 },
16 body: JSON.stringify({ query, variables }),
17 });
18 return res.json();
19}
20
21const server = new McpServer({ name: "linear-pm", version: "1.0.0" });
22
23server.tool("search_issues", "Search Linear issues with filters", {
24 query: z.string().optional().describe("Text search query"),
25 status: z.string().optional().describe("Filter by status: Todo, In Progress, Done"),
26 assignee: z.string().optional().describe("Filter by assignee name"),
27 limit: z.number().default(20),
28}, async ({ query: q, status, assignee, limit }) => {
29 const filters: string[] = [];
30 if (q) filters.push(`{ searchableContent: { contains: "${q}" } }`);
31 if (status) filters.push(`{ state: { name: { eq: "${status}" } } }`);
32 if (assignee) filters.push(`{ assignee: { name: { contains: "${assignee}" } } }`);
33
34 const filterStr = filters.length > 0 ? `filter: { and: [${filters.join(",")}] },` : "";
35 const result = await linearQuery(`{
36 issues(${filterStr} first: ${limit}, orderBy: updatedAt) {
37 nodes { identifier title state { name } assignee { name } priority createdAt }
38 }
39 }`);
40 return { content: [{ type: "text", text: JSON.stringify(result.data.issues.nodes, null, 2) }] };
41});
42
43server.tool("create_issue", "Create a new Linear issue", {
44 title: z.string(),
45 description: z.string().optional(),
46 teamKey: z.string().describe("Team key, e.g. ENG"),
47 priority: z.number().min(0).max(4).default(3).describe("0=none, 1=urgent, 2=high, 3=medium, 4=low"),
48 labelNames: z.array(z.string()).optional().describe("Labels to apply"),
49}, async ({ title, description, teamKey, priority, labelNames }) => {
50 const teamResult = await linearQuery(`{ teams(filter: { key: { eq: "${teamKey}" } }) { nodes { id } } }`);
51 const teamId = teamResult.data.teams.nodes[0]?.id;
52 if (!teamId) return { content: [{ type: "text", text: `Error: Team ${teamKey} not found` }], isError: true };
53
54 const result = await linearQuery(`mutation($input: IssueCreateInput!) {
55 issueCreate(input: $input) { success issue { identifier title url } }
56 }`, { input: { title, description, teamId, priority } });
57
58 const issue = result.data.issueCreate.issue;
59 return { content: [{ type: "text", text: `Created ${issue.identifier}: ${issue.title}\n${issue.url}` }] };
60});

Expected result: Custom Linear MCP server with search and create tools tailored to your team's workflow.

4

Integrate project management into your development workflow

The highest-value pattern is using project management MCP tools alongside code tools. When you finish a feature, tell the AI to update the ticket. When you hit a bug, create a ticket with the error details directly from your conversation. This keeps project tracking up to date without context switching. RapidDev teams use this pattern extensively to keep tickets synchronized with actual development progress.

typescript
1// Workflow examples:
2
3// After fixing a bug:
4// "Update LIN-423 status to Done and add a comment:
5// Fixed the null pointer in UserService.getProfile().
6// Root cause was missing null check on the session token.
7// PR #78 deployed to staging."
8
9// After finding a bug while coding:
10// "Create a bug ticket for the ENG team:
11// Title: API returns 500 when email contains special characters
12// Description: The /api/users/search endpoint throws an unhandled
13// error when the email query parameter contains + or & characters.
14// Priority: high"
15
16// Sprint planning:
17// "Show me all In Progress issues assigned to me"
18// "What high-priority bugs are unassigned in the ENG team?"
19// "How many story points are done vs remaining in the current sprint?"

Expected result: Seamless project management integrated into your AI-assisted development workflow.

Complete working example

src/linear-server.ts
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3import { z } from "zod";
4
5const API_KEY = process.env.LINEAR_API_KEY!;
6
7async function gql(query: string, variables?: Record<string, unknown>) {
8 const res = await fetch("https://api.linear.app/graphql", {
9 method: "POST",
10 headers: { "Content-Type": "application/json", Authorization: API_KEY },
11 body: JSON.stringify({ query, variables }),
12 });
13 return res.json();
14}
15
16const server = new McpServer({ name: "linear-pm", version: "1.0.0" });
17
18server.tool("search_issues", "Search Linear issues", {
19 query: z.string().optional(), status: z.string().optional(),
20 assignee: z.string().optional(), limit: z.number().default(20),
21}, async ({ query: q, status, assignee, limit }) => {
22 const f: string[] = [];
23 if (q) f.push(`searchableContent: { contains: "${q}" }`);
24 if (status) f.push(`state: { name: { eq: "${status}" } }`);
25 if (assignee) f.push(`assignee: { name: { contains: "${assignee}" } }`);
26 const filter = f.length ? `filter: { ${f.join(", ")} },` : "";
27 const r = await gql(`{ issues(${filter} first: ${limit}) { nodes { identifier title state { name } assignee { name } priority url } } }`);
28 return { content: [{ type: "text", text: JSON.stringify(r.data.issues.nodes, null, 2) }] };
29});
30
31server.tool("create_issue", "Create a new issue", {
32 title: z.string(), description: z.string().optional(),
33 teamKey: z.string(), priority: z.number().min(0).max(4).default(3),
34}, async ({ title, description, teamKey, priority }) => {
35 const t = await gql(`{ teams(filter: { key: { eq: "${teamKey}" } }) { nodes { id } } }`);
36 const teamId = t.data.teams.nodes[0]?.id;
37 if (!teamId) return { content: [{ type: "text", text: "Team not found" }], isError: true };
38 const r = await gql(`mutation($i: IssueCreateInput!) { issueCreate(input: $i) { success issue { identifier title url } } }`,
39 { i: { title, description, teamId, priority } });
40 const issue = r.data.issueCreate.issue;
41 return { content: [{ type: "text", text: `Created ${issue.identifier}: ${issue.title}\n${issue.url}` }] };
42});
43
44server.tool("update_status", "Update issue status", {
45 issueId: z.string().describe("Issue identifier like ENG-123"),
46 status: z.string().describe("New status: Todo, In Progress, Done, Canceled"),
47}, async ({ issueId, status }) => {
48 const i = await gql(`{ issueSearch(query: "${issueId}") { nodes { id } } }`);
49 const id = i.data.issueSearch.nodes[0]?.id;
50 if (!id) return { content: [{ type: "text", text: "Issue not found" }], isError: true };
51 const states = await gql(`{ workflowStates { nodes { id name } } }`);
52 const stateId = states.data.workflowStates.nodes.find((s: any) => s.name === status)?.id;
53 if (!stateId) return { content: [{ type: "text", text: `Status '${status}' not found` }], isError: true };
54 await gql(`mutation { issueUpdate(id: "${id}", input: { stateId: "${stateId}" }) { success } }`);
55 return { content: [{ type: "text", text: `Updated ${issueId} to ${status}` }] };
56});
57
58async function main() {
59 await server.connect(new StdioServerTransport());
60 console.error("Linear MCP server running");
61}
62main().catch(e => { console.error(e); process.exit(1); });

Common mistakes when using MCP for project management (Jira, Linear)

Why it's a problem: Using personal API tokens instead of service account tokens for shared MCP servers

How to avoid: Create a dedicated service account or bot user for the MCP server. Personal tokens stop working when the user leaves the organization.

Why it's a problem: Not validating team keys before creating issues, causing cryptic API errors

How to avoid: Look up the team ID first and return a clear error if the team key does not exist.

Why it's a problem: Hardcoding status names that differ between projects or teams

How to avoid: Query available workflow states dynamically and match by name. Different teams may have different status names.

Best practices

  • Use service account API tokens for MCP servers, not personal tokens
  • Include issue URLs in creation and update responses for easy access
  • Query workflow states dynamically instead of hardcoding status names
  • Add comment tools so the AI can document changes directly on tickets
  • Log all project management operations for audit purposes
  • Validate team keys and project IDs before creating issues
  • Return structured data so the AI can present it clearly to the user

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Build an MCP server for Linear project management in TypeScript. Include tools to search issues with filters, create new issues with team and priority, update issue status, and add comments. Use the Linear GraphQL API.

MCP Prompt

Create a Linear MCP server with search_issues, create_issue, and update_status tools. Use the Linear GraphQL API with proper team lookup and workflow state management. Include Zod schemas for all inputs.

Frequently asked questions

Should I use Jira or Linear MCP for my team?

Linear has a cleaner GraphQL API that is easier to work with from MCP. Jira has broader enterprise adoption and more complex workflows. Choose based on what your team already uses.

Can the AI automatically create tickets from errors?

Yes. Combine error monitoring (Sentry MCP) with project management (Linear MCP) to automatically create bug tickets when errors exceed a threshold.

How do I prevent the AI from making unwanted changes to tickets?

Configure the MCP server with read-only tools for querying, and require explicit confirmation before executing create or update operations.

Can RapidDev build custom project management MCP integrations?

Yes. RapidDev builds MCP integrations for Jira, Linear, Asana, Notion, and other project management tools, including custom workflows and automated ticket creation pipelines.

Does this work with Asana or Notion?

The pattern works with any project management tool that has an API. Community MCP servers exist for Notion, and Asana can be integrated with a custom MCP server wrapping their REST API.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.