Skip to main content
Glama
README.md
# Atlassian MCP Server

[![CI](https://github.com/lizamir/atlassian-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/lizamir/atlassian-mcp-server/actions/workflows/ci.yml)

An [MCP](https://modelcontextprotocol.io) server that gives AI agents safe, audited access to **Jira** and **Confluence** Cloud. Point Claude (or any MCP client) at it and ask things like *"what's open in the OPS project?"* or *"find our VPN setup guide and summarize it"*.

It is built the way an enterprise IT team would want an AI integration to behave:

- **Read-only by default.** The tools that change data are not even registered unless you turn them on.
- **Every tool call is audited** as one JSON line: which tool, which arguments, outcome, duration.
- **Credentials stay in the environment.** They never appear in tool results, errors or logs.
- **Runs anywhere**: locally over stdio, or in Docker over HTTP behind an MCP gateway.

## Tools

| Tool | What it does | Mode |
|---|---|---|
| `jira_search_issues` | Search issues with JQL | read |
| `jira_get_issue` | One issue with description, status, people, dates | read |
| `confluence_search` | Search pages with CQL | read |
| `confluence_get_page` | Page content as plain text | read |
| `jira_create_issue` | Create an issue | write, opt-in |
| `jira_add_comment` | Comment on an issue | write, opt-in |

Write tools appear only when `ATLASSIAN_ALLOW_WRITES=true`.

## How it works

```
MCP client (Claude, agent, gateway)
        │  stdio  or  HTTP POST /mcp + bearer token
        ▼
  src/server.ts    tool definitions, input validation (zod), audit log
        ▼
  src/atlassian.ts REST client: auth, timeouts, readable errors
        ▼
  Jira Cloud REST v3  ·  Confluence Cloud REST
```

Jira's rich-text format (ADF) and Confluence's storage markup are converted to plain text before they reach the model, so it reads words instead of JSON and HTML.

## Quick start

You need an Atlassian Cloud site and an [API token](https://id.atlassian.com/manage-profile/security/api-tokens).

```bash
cp .env.example .env   # then fill in the three ATLASSIAN_ values
```

### With Docker (HTTP)

Set `MCP_AUTH_TOKEN` in `.env` to a long random string, then:

```bash
docker compose up --build
```

The server listens on `http://localhost:3000/mcp`. Clients must send `Authorization: Bearer <MCP_AUTH_TOKEN>`. `GET /healthz` is open for liveness probes.

### With Claude Desktop or Claude Code (stdio)

```bash
npm ci && npm run build
```

Then add the server to your MCP config:

```json
{
  "mcpServers": {
    "atlassian": {
      "command": "node",
      "args": ["/absolute/path/to/atlassian-mcp-server/dist/index.js"],
      "env": {
        "ATLASSIAN_BASE_URL": "https://your-site.atlassian.net",
        "ATLASSIAN_EMAIL": "you@example.com",
        "ATLASSIAN_API_TOKEN": "your-token"
      }
    }
  }
}
```

## Configuration

| Variable | Required | Default | Meaning |
|---|---|---|---|
| `ATLASSIAN_BASE_URL` | yes | | `https://your-site.atlassian.net` |
| `ATLASSIAN_EMAIL` | yes | | Account that owns the API token |
| `ATLASSIAN_API_TOKEN` | yes | | Atlassian API token |
| `ATLASSIAN_ALLOW_WRITES` | no | `false` | `true` registers the write tools |
| `MCP_TRANSPORT` | no | `stdio` | `stdio` or `http` |
| `PORT` | no | `3000` | HTTP port |
| `MCP_AUTH_TOKEN` | with `http` | | Bearer token clients must send |

## Security notes

- **Least privilege.** The server can do what its Atlassian account can do, and no more. Use a dedicated service account that sees only the projects and spaces the agent should reach.
- **Input validation.** Issue keys, project keys and page ids are checked against strict patterns before any request is made.
- **Audit trail.** Audit lines go to stderr, so `docker logs` or any log collector picks them up:
  ```json
  {"time":"2026-11-12T18:18:52.824Z","event":"tool_call","tool":"jira_get_issue","args":{"issueKey":"OPS-1"},"outcome":"ok","durationMs":212}
  ```
- **Container hardening.** The image runs as a non-root user; the compose file adds a read-only filesystem and drops all Linux capabilities.
- **HTTP auth** is a static bearer token compared in constant time. In production, put the server behind a gateway that handles OAuth/SSO.

## Development

```bash
npm ci
npm test          # unit tests, no network needed (fetch is mocked)
npm run typecheck
npm run build
```

CI (GitHub Actions) runs typecheck, tests and build, then builds the Docker image, starts the container and checks that it is healthy, refuses requests without a token, and exposes only the read tools.

## Possible next steps

- OAuth 2.0 (3LO) so each user acts with their own Atlassian permissions
- Jira transitions and Confluence page creation
- Usage metrics per tool (calls, errors, latency) for KPI reporting

## License

MIT