An MCP (Model Context Protocol) server built in TypeScript that authenticates users via Atlassian OAuth 2.0 (3LO) with PKCE. Once authenticated, the server exposes Jira tools that operate under the user's own Atlassian permissions — every API call respects the same access controls as the Atlassian UI.
┌──────────────┐ ┌──────────────────────┐ ┌────────────────┐
│ MCP Client │ │ This MCP Server │ │ Atlassian │
│ (Rovo Dev, │ Bearer │ (Express + MCP SDK) │ OAuth │ Cloud APIs │
│ Claude, │ token │ │ token │ (Jira, │
│ VS Code, │────────▶│ /mcp endpoint │───────▶│ Confluence) │
│ Cursor) │◀────────│ │◀───────│ │
└──────────────┘ JSON └──────┬───────────────┘ JSON └────────────────┘
│
/oauth/login ──▶ Atlassian consent screen
/oauth/callback ◀── Authorization code
Flow:
- User visits
GET /oauth/loginin their browser - Atlassian shows the consent screen; user approves
- Atlassian redirects to
/oauth/callbackwith an authorization code - Server exchanges the code for access/refresh tokens (with PKCE)
- Server fetches the user's profile and accessible cloud sites
- Server creates a session and displays the session token
- User configures their MCP client with
Authorization: Bearer <token> - All subsequent MCP tool calls use the user's Atlassian token
| Tool | Description | Read-only |
|---|---|---|
jira_whoami |
Show the authenticated user's identity | ✅ |
jira_search_issues |
Search issues with JQL | ✅ |
jira_get_issue |
Get detailed info for a single issue | ✅ |
jira_create_issue |
Create a new issue in a project | ❌ |
jira_add_comment |
Add a comment to an issue | ❌ |
jira_list_projects |
List accessible projects | ✅ |
jira_transition_issue |
Change an issue's workflow status | ❌ |
jira_get_transitions |
List available transitions for an issue | ✅ |
- Node.js 18+ (uses native
fetch) - An Atlassian Cloud site (Jira and/or Confluence)
- An Atlassian OAuth 2.0 (3LO) app (see setup below)
-
Click Create → OAuth 2.0 integration
-
Give it a name (e.g. "My MCP Server")
-
Under Authorization → Add → select OAuth 2.0 (3LO)
-
Set the Callback URL to:
http://localhost:3000/oauth/callback -
Under Permissions, add the following scopes:
Jira API:
read:jira-workwrite:jira-workread:jira-user
User identity:
read:me
Confluence API (optional):
read:confluence-content.allsearch:confluence
-
Copy your Client ID and Client Secret from the app's Settings page
# Clone or unzip the project
cd atlassian-mcp-server
# Install dependencies
npm install
# Create your environment file
cp .env.example .envEdit .env with your values:
ATLASSIAN_CLIENT_ID=your-client-id-here
ATLASSIAN_CLIENT_SECRET=your-client-secret-here
PORT=3000
SERVER_URL=http://localhost:3000
SESSION_SECRET=generate-a-random-string-hereTo generate a session secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"# Build TypeScript
npm run build
# Start the server
npm startYou should see:
╔══════════════════════════════════════════════════════════════╗
║ Atlassian MCP Server running ║
╠══════════════════════════════════════════════════════════════╣
║ MCP endpoint: http://localhost:3000/mcp ║
║ Login: http://localhost:3000/oauth/login ║
║ Health: http://localhost:3000/health ║
╚══════════════════════════════════════════════════════════════╝
- Open
http://localhost:3000/oauth/loginin your browser - You'll be redirected to Atlassian — log in and approve the permissions
- After approval, you'll see a page with your session token and ready-to-use configuration snippets
Edit ~/.rovodev/mcp.json:
{
"mcpServers": {
"atlassian-custom": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer YOUR_SESSION_TOKEN_HERE"
},
"transport": "http"
}
}
}Then in Rovo Dev interactive mode, type /mcp to verify the server is connected.
Edit claude_desktop_config.json:
{
"mcpServers": {
"atlassian-custom": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer YOUR_SESSION_TOKEN_HERE"
}
}
}
}In your .vscode/mcp.json or the MCP settings panel:
{
"servers": {
"atlassian-custom": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer YOUR_SESSION_TOKEN_HERE"
}
}
}
}# List available tools
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN_HERE" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}' | jq
# Call a tool (e.g. jira_whoami)
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN_HERE" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "jira_whoami",
"arguments": {}
}
}' | jq
# Search for issues
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN_HERE" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "jira_search_issues",
"arguments": {
"jql": "assignee = currentUser() ORDER BY updated DESC",
"max_results": 5
}
}
}' | jqTo make this server available to Rovo cloud agents, you need to:
- Deploy to a public HTTPS endpoint (e.g. AWS, GCP, Railway, Fly.io)
- Update
SERVER_URLin.envto your public URL - Update the OAuth callback URL in your Atlassian app to
https://your-domain.com/oauth/callback - In Atlassian Administration → Apps → Sites → your site → Connected apps
- Click the dropdown beside "Explore apps" → "Add external MCP server"
- Select "Custom MCP server" and provide your server's URL
Note: For the MCP Gallery path, Rovo sends requests from its own infrastructure. You may need to adapt the auth model to accept org-level API keys rather than per-user OAuth sessions. See the "Production Considerations" section below.
atlassian-mcp-server/
├── package.json
├── tsconfig.json
├── .env.example
├── README.md
├── src/
│ ├── index.ts # Express app, OAuth routes, MCP endpoint
│ ├── config.ts # Environment variable loading
│ ├── types.ts # Shared TypeScript interfaces
│ ├── middleware/
│ │ └── auth.ts # Bearer token auth middleware
│ ├── services/
│ │ ├── atlassian-oauth.ts # OAuth 2.0 (3LO) flow with PKCE
│ │ ├── atlassian-api.ts # Jira/Confluence REST API client
│ │ └── session-store.ts # In-memory session management
│ └── tools/
│ └── jira-tools.ts # Jira MCP tool definitions
└── dist/ # Compiled JavaScript (after npm run build)
This implementation is designed as a working starting point. For production use, consider:
Session Storage:
Replace the in-memory SessionStore with Redis or a database. The current store loses all sessions on server restart.
Token Refresh:
The AtlassianOAuthService.refreshTokens() method is implemented but not automatically called. Add middleware to detect expired tokens and refresh them transparently.
HTTPS: Always use HTTPS in production. Atlassian OAuth requires HTTPS callback URLs for non-localhost deployments.
CORS: If your MCP clients make requests from browsers, add appropriate CORS headers.
Rate Limiting:
Atlassian Cloud APIs have rate limits. Add request throttling and backoff logic to AtlassianApiClient.
Multi-tenancy:
If supporting multiple Atlassian sites, let users select their target site during or after auth (the sites array in the session contains all accessible sites).
Rovo Gallery Auth: When registered in the Rovo MCP Gallery, Rovo's infrastructure (not individual users) calls your server. You'll likely need to support a service-account / API-key auth mode alongside OAuth for this path.
To add tools (e.g. Confluence tools):
- Create a new file in
src/tools/(e.g.confluence-tools.ts) - Follow the same pattern as
jira-tools.ts:- Import
setCurrentSession/requireSessionhelpers - Use
AtlassianApiClient.confluenceRequest()for API calls - Register tools on the
McpServerinstance
- Import
- Import and call your registration function in
src/index.ts
MIT