confluence-mcp-server
# Confluence MCP Server
[](https://github.com/pankaj28843/confluence-mcp-server/actions/workflows/ci.yml)
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that connects AI assistants to your Confluence instance. It exposes two tools — **search** and **fetch** — letting any MCP-compatible client (Claude Desktop, Cursor, Windsurf, etc.) query and read Confluence pages in real time.
## Features
- 🔍 **Fuzzy search** — splits multi-word queries into parallel CQL searches and merges results by relevance
- 📄 **Full page hydration** — retrieves storage body, version info, labels, and ancestors as clean Markdown
- ⚡ **Disk cache** — search and content responses are cached locally to reduce API load
- 🔒 **Read-only** — all tools are annotated `readOnlyHint: true`; nothing is ever written to Confluence
- 🐳 **Docker-ready** — one-command deployment via `deploy_confluence_mcp.py`
---
## Prerequisites
- Python 3.11+ and [uv](https://docs.astral.sh/uv/) **or** Docker
- A Confluence instance accessible over HTTP(S)
- A [Confluence Personal Access Token](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html)
---
## Quickstart
### 1. Configure credentials
```bash
cp .env.example .env
```
Edit `.env`:
```env
CONFLUENCE_URL=https://confluence.example.com
CONFLUENCE_PERSONAL_ACCESS_TOKEN=your_token_here
```
### 2a. Run with uv (stdio — for Claude Desktop / Cursor)
```bash
uv sync
uv run python -m confluence_search.fastmcp_app
```
### 2b. Run with Docker (HTTP)
```bash
python deploy_confluence_mcp.py # builds image + starts container on :43043
```
Or manually:
```bash
docker build -t confluence-mcp-server .
docker run -d --name confluence-mcp-server \
--env-file .env \
-p 43043:43043 \
confluence-mcp-server
```
---
## Connecting an MCP client
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"confluence": {
"command": "uv",
"args": ["run", "python", "-m", "confluence_search.fastmcp_app"],
"cwd": "/path/to/confluence-mcp-server",
"env": {
"CONFLUENCE_URL": "https://confluence.example.com",
"CONFLUENCE_PERSONAL_ACCESS_TOKEN": "your_token_here"
}
}
}
}
```
### Cursor / Windsurf (HTTP transport)
Point your MCP client at `http://127.0.0.1:43043/mcp` after starting the Docker container.
---
## MCP Tools
### `search_confluence`
Execute a CQL search against Confluence.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | **required** | Natural language query (translated to CQL) |
| `limit` | int (1–25) | `5` | Maximum results to return |
| `spaces` | string[] | `null` | Filter by space keys (e.g. `["ENG", "OPS"]`) |
| `labels` | string[] | `null` | Filter by page labels |
| `titles_only` | bool | `false` | Search titles only (faster, lower recall) |
| `fuzzy` | bool | `true` | Parallel term-split search for better recall |
| `modified_after` | string | `"2y"` | Recency filter. Shorthands: `1d` `7d` `30d` `90d` `6M` `1y` `2y` `5y` or ISO date. Set to `null` for all time |
| `created_after` | string | `null` | Filter by creation date (same format) |
Returns a ranked list of matching pages with title, URL, space, excerpt, and matched labels.
### `fetch_confluence_page`
Hydrate a single page by its Confluence content ID.
| Parameter | Type | Description |
|-----------|------|-------------|
| `content_id` | string | Numeric content ID returned by `search_confluence` |
Returns the page as a Markdown document including metadata header (URL, space, version, last-modified date, labels, ancestor breadcrumb) followed by the full page body.
---
## Configuration reference
All settings are read from environment variables or `.env`:
| Variable | Default | Description |
|----------|---------|-------------|
| `CONFLUENCE_URL` | — | Base URL of your Confluence instance |
| `CONFLUENCE_PERSONAL_ACCESS_TOKEN` | — | Bearer token for authentication |
| `CONFLUENCE_VERIFY_TLS` | `true` | Enforce TLS certificate validation |
| `CONFLUENCE_REQUEST_TIMEOUT` | `20` | HTTP timeout in seconds |
| `CONFLUENCE_MAX_RETRIES` | `3` | Retry attempts for transient failures |
| `CONFLUENCE_CACHE_ENABLED` | `true` | Enable/disable disk cache |
| `CONFLUENCE_CACHE_DIR` | `.cache/confluence_mcp` | Cache directory path |
| `CONFLUENCE_CACHE_TTL_SEARCH` | `300` | Search cache TTL (seconds) |
| `CONFLUENCE_CACHE_TTL_CONTENT` | `900` | Page content cache TTL (seconds) |
### Transport variables (HTTP mode)
| Variable | Default | Description |
|----------|---------|-------------|
| `CONFLUENCE_MCP_TRANSPORT` | `stdio` | `stdio`, `http`, `sse`, or `streamable-http` |
| `CONFLUENCE_MCP_HOST` | `0.0.0.0` | Bind address (HTTP mode) |
| `CONFLUENCE_MCP_PORT` | `43043` | Listen port (HTTP mode) |
---
## Development
```bash
uv sync
uv run pytest -m unit --cov=confluence_search/ --cov-report=term-missing
uv run ruff check confluence_search/ tests/
uv run ruff format confluence_search/ tests/
```
The test suite requires **100% coverage** — enforced in CI.
---
## License
MIT
TDQS
Scored across 2 tools
Fetch and search are completely distinct operations: one retrieves a specific page by ID, the other finds pages via CQL. There is no ambiguity between the two.
Both names follow a verb_prefix pattern in snake_case, but 'fetch_confluence_page' includes an extra object while 'search_confluence' does not. This is a minor inconsistency, but still predictable.
Only two tools for a Confluence server feels too thin. A typical integration requires at least page creation, updates, and space management, not just fetch and search.
The server covers only read-oriented operations (fetch a page, search) and lacks all write capabilities such as create, update, delete, or list spaces. This is a significant gap for a domain like Confluence.