#
tokens: 3076/50000 10/10 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .env.example
├── .gitignore
├── package.json
├── README.md
├── src
│   ├── index.ts
│   ├── mcp
│   │   ├── bitbucketTools.ts
│   │   ├── confluenceTools.ts
│   │   └── jiraTools.ts
│   └── services
│       ├── AtlassianBaseService.ts
│       └── JiraService.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
1 | # Atlassian Credentials
2 | JIRA_BASE_URL=https://your-domain.atlassian.net
3 | CONFLUENCE_BASE_URL=https://your-domain.atlassian.net
4 | BITBUCKET_BASE_URL=https://api.bitbucket.org/2.0
5 | [email protected]
6 | ATLASSIAN_API_TOKEN=your-api-token
7 | 
8 | # MCP Server Configuration
9 | PORT=3000
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Logs
 2 | logs
 3 | *.log
 4 | npm-debug.log*
 5 | yarn-debug.log*
 6 | yarn-error.log*
 7 | lerna-debug.log*
 8 | .pnpm-debug.log*
 9 | 
10 | # Dependency directories
11 | node_modules/
12 | 
13 | # TypeScript cache
14 | *.tsbuildinfo
15 | 
16 | # Optional npm cache directory
17 | .npm
18 | 
19 | # dotenv environment variable files
20 | .env
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 | .env.local
25 | 
26 | # Build / output directories
27 | dist
28 | build
29 | out
```

--------------------------------------------------------------------------------
/src/mcp/jiraTools.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const jiraTools = [];
```

--------------------------------------------------------------------------------
/src/mcp/bitbucketTools.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const bitbucketTools = [];
```

--------------------------------------------------------------------------------
/src/mcp/confluenceTools.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const confluenceTools = [];
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "commonjs",
 5 |     "outDir": "./dist",
 6 |     "rootDir": "./src",
 7 |     "strict": true,
 8 |     "esModuleInterop": true,
 9 |     "skipLibCheck": true,
10 |     "forceConsistentCasingInFileNames": true,
11 |     "resolveJsonModule": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules", "**/*.test.ts"]
15 | }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "atlassian-cursor-mcp",
 3 |   "version": "1.0.0",
 4 |   "description": "Managed Code Plugin for Cursor IDE to integrate with Atlassian products",
 5 |   "main": "dist/index.js",
 6 |   "scripts": {
 7 |     "start": "ts-node src/index.ts",
 8 |     "build": "tsc",
 9 |     "serve": "node dist/index.js",
10 |     "dev": "nodemon --exec ts-node src/index.ts",
11 |     "test": "echo \"Error: no test specified\" && exit 1"
12 |   },
13 |   "keywords": ["cursor", "mcp", "atlassian", "jira", "confluence", "bitbucket"],
14 |   "author": "",
15 |   "license": "MIT",
16 |   "dependencies": {
17 |     "@modelcontextprotocol/sdk": "^1.6.1",
18 |     "@types/express": "^5.0.0",
19 |     "@types/node": "^22.13.9",
20 |     "axios": "^1.8.2",
21 |     "dotenv": "^16.4.7",
22 |     "express": "^5.0.1",
23 |     "ts-node": "^10.9.2",
24 |     "typescript": "^5.8.2"
25 |   },
26 |   "devDependencies": {
27 |     "nodemon": "^3.1.9"
28 |   }
29 | }
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { MCPServer } from '@modelcontextprotocol/sdk';
 2 | import express from 'express';
 3 | import dotenv from 'dotenv';
 4 | import { jiraTools } from './mcp/jiraTools';
 5 | import { confluenceTools } from './mcp/confluenceTools';
 6 | import { bitbucketTools } from './mcp/bitbucketTools';
 7 | 
 8 | // Load environment variables
 9 | dotenv.config();
10 | 
11 | // Create Express app
12 | const app = express();
13 | const port = process.env.PORT || 3000;
14 | 
15 | // Create MCP server
16 | const mcpServer = new MCPServer({
17 |   name: 'Atlassian MCP',
18 |   description: 'Managed Code Plugin for Cursor IDE to integrate with Atlassian products (JIRA, Confluence, BitBucket)',
19 |   version: '1.0.0',
20 |   tools: [...jiraTools, ...confluenceTools, ...bitbucketTools],
21 | });
22 | 
23 | // Mount MCP server to Express app
24 | app.use('/mcp', mcpServer.handler());
25 | 
26 | // Add health check endpoint
27 | app.get('/health', (req, res) => {
28 |   res.status(200).json({ status: 'ok', message: 'Atlassian MCP server is running' });
29 | });
30 | 
31 | // Start server
32 | app.listen(port, () => {
33 |   console.log(`Atlassian MCP server is running on port ${port}`);
34 |   console.log(`MCP endpoint: http://localhost:${port}/mcp`);
35 |   console.log(`Health check: http://localhost:${port}/health`);
36 | });
```

--------------------------------------------------------------------------------
/src/services/AtlassianBaseService.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
 2 | import dotenv from 'dotenv';
 3 | 
 4 | dotenv.config();
 5 | 
 6 | export default abstract class AtlassianBaseService {
 7 |   protected client: AxiosInstance;
 8 |   protected baseUrl: string;
 9 | 
10 |   constructor(baseUrl: string) {
11 |     this.baseUrl = baseUrl;
12 |     this.client = axios.create({
13 |       baseURL: baseUrl,
14 |       auth: {
15 |         username: process.env.ATLASSIAN_EMAIL || '',
16 |         password: process.env.ATLASSIAN_API_TOKEN || '',
17 |       },
18 |       headers: {
19 |         'Content-Type': 'application/json',
20 |         'Accept': 'application/json',
21 |       },
22 |     });
23 | 
24 |     // Add request interceptor for logging
25 |     this.client.interceptors.request.use((config) => {
26 |       console.log(`Making ${config.method?.toUpperCase()} request to ${config.baseURL}${config.url}`);
27 |       return config;
28 |     });
29 | 
30 |     // Add response interceptor for error handling
31 |     this.client.interceptors.response.use(
32 |       (response) => response,
33 |       (error) => {
34 |         console.error('API Error:', error.response?.data || error.message);
35 |         return Promise.reject(error);
36 |       }
37 |     );
38 |   }
39 | 
40 |   protected async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
41 |     const response = await this.client.get<T>(url, config);
42 |     return response.data;
43 |   }
44 | 
45 |   protected async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
46 |     const response = await this.client.post<T>(url, data, config);
47 |     return response.data;
48 |   }
49 | 
50 |   protected async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
51 |     const response = await this.client.put<T>(url, data, config);
52 |     return response.data;
53 |   }
54 | 
55 |   protected async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
56 |     const response = await this.client.delete<T>(url, config);
57 |     return response.data;
58 |   }
59 | }
```

--------------------------------------------------------------------------------
/src/services/JiraService.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import AtlassianBaseService from './AtlassianBaseService';
  2 | import dotenv from 'dotenv';
  3 | 
  4 | dotenv.config();
  5 | 
  6 | interface JiraIssue {
  7 |   id: string;
  8 |   key: string;
  9 |   self: string;
 10 |   fields: {
 11 |     summary: string;
 12 |     description?: string;
 13 |     status?: {
 14 |       name: string;
 15 |     };
 16 |     issuetype?: {
 17 |       name: string;
 18 |     };
 19 |     priority?: {
 20 |       name: string;
 21 |     };
 22 |     assignee?: {
 23 |       displayName: string;
 24 |       emailAddress: string;
 25 |     };
 26 |     reporter?: {
 27 |       displayName: string;
 28 |       emailAddress: string;
 29 |     };
 30 |     created?: string;
 31 |     updated?: string;
 32 |     [key: string]: any;
 33 |   };
 34 | }
 35 | 
 36 | export interface CreateJiraIssuePayload {
 37 |   fields: {
 38 |     project: {
 39 |       key: string;
 40 |     };
 41 |     summary: string;
 42 |     description?: string;
 43 |     issuetype: {
 44 |       name: string;
 45 |     };
 46 |     [key: string]: any;
 47 |   };
 48 | }
 49 | 
 50 | export default class JiraService extends AtlassianBaseService {
 51 |   constructor() {
 52 |     super(process.env.JIRA_BASE_URL || '');
 53 |   }
 54 | 
 55 |   async searchIssues(jql: string, startAt: number = 0, maxResults: number = 50): Promise<{
 56 |     issues: JiraIssue[];
 57 |     total: number;
 58 |   }> {
 59 |     return this.get('/rest/api/3/search', {
 60 |       params: {
 61 |         jql,
 62 |         startAt,
 63 |         maxResults,
 64 |       },
 65 |     });
 66 |   }
 67 | 
 68 |   async getIssue(issueIdOrKey: string): Promise<JiraIssue> {
 69 |     return this.get(`/rest/api/3/issue/${issueIdOrKey}`);
 70 |   }
 71 | 
 72 |   async createIssue(payload: CreateJiraIssuePayload): Promise<{ id: string; key: string; self: string }> {
 73 |     return this.post('/rest/api/3/issue', payload);
 74 |   }
 75 | 
 76 |   async updateIssue(issueIdOrKey: string, payload: any): Promise<void> {
 77 |     return this.put(`/rest/api/3/issue/${issueIdOrKey}`, payload);
 78 |   }
 79 | 
 80 |   async assignIssue(issueIdOrKey: string, assignee: string): Promise<void> {
 81 |     return this.put(`/rest/api/3/issue/${issueIdOrKey}/assignee`, {
 82 |       accountId: assignee,
 83 |     });
 84 |   }
 85 | 
 86 |   async getTransitions(issueIdOrKey: string): Promise<any> {
 87 |     return this.get(`/rest/api/3/issue/${issueIdOrKey}/transitions`);
 88 |   }
 89 | 
 90 |   async transitionIssue(issueIdOrKey: string, transitionId: string, fields?: any): Promise<void> {
 91 |     return this.post(`/rest/api/3/issue/${issueIdOrKey}/transitions`, {
 92 |       transition: {
 93 |         id: transitionId,
 94 |       },
 95 |       fields,
 96 |     });
 97 |   }
 98 | 
 99 |   async addComment(issueIdOrKey: string, comment: string): Promise<any> {
100 |     return this.post(`/rest/api/3/issue/${issueIdOrKey}/comment`, {
101 |       body: {
102 |         type: 'doc',
103 |         version: 1,
104 |         content: [
105 |           {
106 |             type: 'paragraph',
107 |             content: [
108 |               {
109 |                 type: 'text',
110 |                 text: comment,
111 |               },
112 |             ],
113 |           },
114 |         ],
115 |       },
116 |     });
117 |   }
118 | 
119 |   async getProjects(): Promise<any[]> {
120 |     return this.get('/rest/api/3/project');
121 |   }
122 | 
123 |   async getIssueTypes(): Promise<any[]> {
124 |     return this.get('/rest/api/3/issuetype');
125 |   }
126 | }
```