#
tokens: 2274/50000 10/10 files
lines: off (toggle) GitHub
raw markdown copy
# 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:
--------------------------------------------------------------------------------

```
# Atlassian Credentials
JIRA_BASE_URL=https://your-domain.atlassian.net
CONFLUENCE_BASE_URL=https://your-domain.atlassian.net
BITBUCKET_BASE_URL=https://api.bitbucket.org/2.0
[email protected]
ATLASSIAN_API_TOKEN=your-api-token

# MCP Server Configuration
PORT=3000
```

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

```
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Dependency directories
node_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# Build / output directories
dist
build
out
```

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

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

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

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

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

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

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

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.test.ts"]
}
```

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

```json
{
  "name": "atlassian-cursor-mcp",
  "version": "1.0.0",
  "description": "Managed Code Plugin for Cursor IDE to integrate with Atlassian products",
  "main": "dist/index.js",
  "scripts": {
    "start": "ts-node src/index.ts",
    "build": "tsc",
    "serve": "node dist/index.js",
    "dev": "nodemon --exec ts-node src/index.ts",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["cursor", "mcp", "atlassian", "jira", "confluence", "bitbucket"],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "@types/express": "^5.0.0",
    "@types/node": "^22.13.9",
    "axios": "^1.8.2",
    "dotenv": "^16.4.7",
    "express": "^5.0.1",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.9"
  }
}
```

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

```typescript
import { MCPServer } from '@modelcontextprotocol/sdk';
import express from 'express';
import dotenv from 'dotenv';
import { jiraTools } from './mcp/jiraTools';
import { confluenceTools } from './mcp/confluenceTools';
import { bitbucketTools } from './mcp/bitbucketTools';

// Load environment variables
dotenv.config();

// Create Express app
const app = express();
const port = process.env.PORT || 3000;

// Create MCP server
const mcpServer = new MCPServer({
  name: 'Atlassian MCP',
  description: 'Managed Code Plugin for Cursor IDE to integrate with Atlassian products (JIRA, Confluence, BitBucket)',
  version: '1.0.0',
  tools: [...jiraTools, ...confluenceTools, ...bitbucketTools],
});

// Mount MCP server to Express app
app.use('/mcp', mcpServer.handler());

// Add health check endpoint
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok', message: 'Atlassian MCP server is running' });
});

// Start server
app.listen(port, () => {
  console.log(`Atlassian MCP server is running on port ${port}`);
  console.log(`MCP endpoint: http://localhost:${port}/mcp`);
  console.log(`Health check: http://localhost:${port}/health`);
});
```

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

```typescript
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import dotenv from 'dotenv';

dotenv.config();

export default abstract class AtlassianBaseService {
  protected client: AxiosInstance;
  protected baseUrl: string;

  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
    this.client = axios.create({
      baseURL: baseUrl,
      auth: {
        username: process.env.ATLASSIAN_EMAIL || '',
        password: process.env.ATLASSIAN_API_TOKEN || '',
      },
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    });

    // Add request interceptor for logging
    this.client.interceptors.request.use((config) => {
      console.log(`Making ${config.method?.toUpperCase()} request to ${config.baseURL}${config.url}`);
      return config;
    });

    // Add response interceptor for error handling
    this.client.interceptors.response.use(
      (response) => response,
      (error) => {
        console.error('API Error:', error.response?.data || error.message);
        return Promise.reject(error);
      }
    );
  }

  protected async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
    const response = await this.client.get<T>(url, config);
    return response.data;
  }

  protected async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    const response = await this.client.post<T>(url, data, config);
    return response.data;
  }

  protected async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    const response = await this.client.put<T>(url, data, config);
    return response.data;
  }

  protected async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
    const response = await this.client.delete<T>(url, config);
    return response.data;
  }
}
```

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

```typescript
import AtlassianBaseService from './AtlassianBaseService';
import dotenv from 'dotenv';

dotenv.config();

interface JiraIssue {
  id: string;
  key: string;
  self: string;
  fields: {
    summary: string;
    description?: string;
    status?: {
      name: string;
    };
    issuetype?: {
      name: string;
    };
    priority?: {
      name: string;
    };
    assignee?: {
      displayName: string;
      emailAddress: string;
    };
    reporter?: {
      displayName: string;
      emailAddress: string;
    };
    created?: string;
    updated?: string;
    [key: string]: any;
  };
}

export interface CreateJiraIssuePayload {
  fields: {
    project: {
      key: string;
    };
    summary: string;
    description?: string;
    issuetype: {
      name: string;
    };
    [key: string]: any;
  };
}

export default class JiraService extends AtlassianBaseService {
  constructor() {
    super(process.env.JIRA_BASE_URL || '');
  }

  async searchIssues(jql: string, startAt: number = 0, maxResults: number = 50): Promise<{
    issues: JiraIssue[];
    total: number;
  }> {
    return this.get('/rest/api/3/search', {
      params: {
        jql,
        startAt,
        maxResults,
      },
    });
  }

  async getIssue(issueIdOrKey: string): Promise<JiraIssue> {
    return this.get(`/rest/api/3/issue/${issueIdOrKey}`);
  }

  async createIssue(payload: CreateJiraIssuePayload): Promise<{ id: string; key: string; self: string }> {
    return this.post('/rest/api/3/issue', payload);
  }

  async updateIssue(issueIdOrKey: string, payload: any): Promise<void> {
    return this.put(`/rest/api/3/issue/${issueIdOrKey}`, payload);
  }

  async assignIssue(issueIdOrKey: string, assignee: string): Promise<void> {
    return this.put(`/rest/api/3/issue/${issueIdOrKey}/assignee`, {
      accountId: assignee,
    });
  }

  async getTransitions(issueIdOrKey: string): Promise<any> {
    return this.get(`/rest/api/3/issue/${issueIdOrKey}/transitions`);
  }

  async transitionIssue(issueIdOrKey: string, transitionId: string, fields?: any): Promise<void> {
    return this.post(`/rest/api/3/issue/${issueIdOrKey}/transitions`, {
      transition: {
        id: transitionId,
      },
      fields,
    });
  }

  async addComment(issueIdOrKey: string, comment: string): Promise<any> {
    return this.post(`/rest/api/3/issue/${issueIdOrKey}/comment`, {
      body: {
        type: 'doc',
        version: 1,
        content: [
          {
            type: 'paragraph',
            content: [
              {
                type: 'text',
                text: comment,
              },
            ],
          },
        ],
      },
    });
  }

  async getProjects(): Promise<any[]> {
    return this.get('/rest/api/3/project');
  }

  async getIssueTypes(): Promise<any[]> {
    return this.get('/rest/api/3/issuetype');
  }
}
```