Content
<!-- Generated by https://github.com/flowgent-labs/mcpfather -->
# confluence-mcp
[](https://go.dev/)
[](https://modelcontextprotocol.io/)
[](https://prometheus.io/)
[](https://opentelemetry.io/)
[](https://openid.net/connect/)
[](https://ldap.com/)
[](https://helm.sh/)
> An Enterprise-grade MCP server, specifically designed for AI to call Confluence Data Center APIs, generated from MCPFather — exposes **176 native tools**. Each API operation becomes an AI-callable tool with typed schemas and customized aggregate virtual tools, auth forwarding, and observability built-in, and even use as regular CLI.
## Quick Start
```sh
make
# Or with OpenTelemetry distributed tracing (gRPC or HTTP)
make build-with-otel-grpc # OTLP gRPC exporter
make build-with-otel-http # OTLP HTTP/protobuf exporter
```
### Start in `HTTP` mode (example)
```sh
# Configure your upstream API
export MCP__UPSTREAM__ENDPOINT=https://api.example.com
export MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN=your-token
./bin/confluence-mcp --transport http --port 8080 &
```
- Inspection MCP frontend Authentication (OAuth2.1).
```bash
export MCP__AUTH__FRONTEND__OIDC__ENABLED=true
export MCP__AUTH__FRONTEND__OIDC__ISSUER="https://keycloak.example.com/realms/master"
export MCP__AUTH__FRONTEND__OIDC__AUDIENCE="confluence-mcp"
./bin/confluence-mcp -v 10 --transport http --port 8080
# Obtain the OAuth2.1 metadata.
curl -s https://iam.example.com/realms/master/.well-known/openid-configuration
curl -s localhost:8080/.well-known/oauth-protected-resource
#{"resource":"confluence-mcp","authorization_servers":["https://keycloak.example.com/realms/master"],"bearer_methods_supported":["header"]}
```
### Run in `CLI` mode (example)
```sh
./bin/confluence-mcp -v 1 -t cli list
./bin/confluence-mcp -v 1 -t cli <tool-name> [OPTIONS]
```
## Options
| Flag | Default | Description |
|------|---------|-------------|
| `-t, --transport` | `stdio` | `stdio` / `http` / `cli` |
| `-p, --port` | `8080` | HTTP server port |
| `--metrics-port` | `0` | Override mgmt port (0 = use config) |
| `-v, --verbose` | `0` | Log verbosity (0–10) |
| `--print-default-config` | — | Print default config.yaml to stdout |
## Authentication
Auth is split into two independent layers — neither shares credentials with the other:
| Layer | Config YAML path | Env var prefix | Role |
|---|---|---|---|
| **Frontend** | `auth.frontend.oidc` | `MCP__AUTH__FRONTEND__OIDC__*` | Validates AI agent bearer tokens (inbound) |
| **Backend OIDC** | `auth.backend.oidc` | `MCP__AUTH__BACKEND__OIDC__*` | Server's own OIDC client_credentials for upstream APIs |
| **Backend LDAP** | `auth.backend.ldap` | `MCP__AUTH__BACKEND__LDAP__*` | Server's own LDAP service-account bind for upstream APIs |
| **Backend Static** | `auth.backend.static` | `MCP__AUTH__BACKEND__STATIC__*` | Server's own static token/cookie for upstream APIs |
**Frontend (inbound)** — validates bearer tokens presented by AI agents (MCP clients).
Only applies to `--transport http`; stdio has no network boundary. Standard OIDC JWT
bearer validation per RFC 9728 / MCP Authorization spec.
**Backend (outbound)** — credentials the MCP server uses to call upstream APIs.
Token priority (first available wins):
1. **OIDC** client_credentials grant (`auth.backend.oidc.*`)
2. **LDAP** service account bind (`auth.backend.ldap.*`)
3. **Static** bearer token (`auth.backend.static.bearer_token`)
The AI agent's inbound token is **never** forwarded upstream (MCP spec: Token Passthrough Prohibition).
Cookie-based auth is also available via `auth.backend.static.cookie_token`.
## Configuration
Create `~/.confluence-mcp/config.yaml` (run `--print-default-config` for a full template):
```yaml
upstream:
endpoint: "" # upstream API base URL (e.g. https://api.example.com)
# When true, the MCP session ID is forwarded as X-MCP-Session-ID header.
enable_mcp_session_forward: true
runtime:
download_dir: ""
log_authorization: false
# ---- Authentication ----
# frontend = inbound: validates bearer tokens presented by MCP clients (AI agents).
# backend = outbound: credentials the server uses to call upstream APIs.
# The two are independent — the inbound token is never reused outbound
# (see MCP Authorization spec: Token Passthrough Prohibition).
auth:
frontend:
# Standard OAuth 2.1 / OIDC JWT bearer validation (Resource Server role, RFC 9728).
# Only enforced on the http transport; stdio has no network boundary.
oidc:
enabled: false
issuer: "" # e.g. https://idp.example.com/realms/myrealm
jwks_uri: "" # optional override (auto-discovered from issuer if empty)
audience: "" # expected JWT "aud" claim; also published as the RFC 9728 protected-resource identifier
# When true, validated frontend JWT claims (sub, email) are forwarded upstream
# as X-MCP-Client-Token-Sub and X-MCP-Client-Token-Email headers.
enable_client_token_claim_forward: true
# Additional JWT claims to forward as X-MCP-Client-Token-<Header-Name> headers.
# e.g. ["given_name", "family_name", "preferred_username"]
# additional_client_token_claim_forward: []
# Outbound: credentials the server uses to call upstream APIs.
backend:
oidc:
enabled: true
issuer: https://idp.example.com/realms/myrealm
client_id: my-client
client_secret: "${MCP__AUTH__BACKEND__OIDC__CLIENT_SECRET}"
scopes: openid
# token_url: "" # auto-discovered from issuer /.well-known
ldap:
enabled: false
url: ldaps://ldap.example.com
base_dn: dc=example,dc=com
bind_dn: cn=svc,dc=example,dc=com
bind_password: "${MCP__AUTH__BACKEND__LDAP__BIND_PASSWORD}"
static:
bearer_token: "${MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN}" # or set cookie_token
nativeTools:
expose:
register_all_tools_by_default: true # expose all native tools
# includes: [Tool1, Tool2] # or whitelist specific tools
# excludes: [Tool3] # hide noisy tools
```
All values can be set via `MCP__`-prefixed environment variables (e.g. `MCP__AUTH__BACKEND__OIDC__CLIENT_ID`).
## Management — Metrics, Tracing, Pprof
```yaml
mgmt:
enabled: true
host: 0.0.0.0
port: 9991
otel:
enabled: false
endpoint: localhost:4317
protocol: grpc
sample_rate: 1.0
metrics:
enabled: true
prometheus: true
export_interval: 30s
pprof:
enabled: false
server_bind: 0.0.0.0:6669
```
```sh
./bin/confluence-mcp --transport http --port 8080
curl http://localhost:9991/metrics # Prometheus endpoint
curl http://localhost:9991/health # health check
```
Trace spans are named `mcp.tool.<name>`. W3C `traceparent` is propagated to upstream calls.
## Available Tools (176)
| Tool | Description |
|------|-------------|
| `Add` | Add a category to a space - Adds a category the description of a given {@link Space} identified by spaceKey. Example request URI to add space category 'testCategory' to space with space key TEST: `https://example.com/confluence/rest/api/space/TEST/category/testCategory` |
| `AddContentWatcher` | Add content watcher - Create a new watcher for the given user and content id. User is optional. If not specified, currently logged-in user will be used. Otherwise, it can be specified by either user key or username. When a user is specified and is different from the logged-in user, the logged-in user needs to be a Confluence administrator. Example request URI(s): `http://example.com/confluence/rest/api/user/watch/content/131213` `http://example.com/confluence/rest/api/user/watch/content/131213?username=jblogs` `http://example.com/confluence/rest/api/user/watch/content/131213?key=ff8080815a58e24c015a58e263710000` |
| `AddLabels` | Add Labels - Adds a list of labels to the specified content. The body is the json representation of the list. |
| `AddSpaceWatch` | Add space watcher - Create a new watcher for the given user and space key. User is optional. If not specified, currently logged-in user will be used. Otherwise, it can be specified by either user key or username. When a user is specified and is different from the logged-in user, the logged-in user needs to be a Confluence administrator. Example request URI(s): `http://example.com/confluence/rest/api/user/watch/space/SPACEKEY` `http://example.com/confluence/rest/api/user/watch/space/SPACEKEY?username=jblogs` `http://example.com/confluence/rest/api/user/watch/space/SPACEKEY?key=ff8080815a58e24c015a58e263710000` `http://example.com/confluence/rest/api/user/watch/space/SPACEKEY?contentType=blogpost` |
| `Archive` | Archive space - Archive the given Space identified by spaceKey. This method is idempotent i.e., if the Space is already archived then no action will be taken. |
| `ByOperation` | Get all restrictions by Operation - Returns info about all restrictions by operation. |
| `CancelAllQueuedJobs` | Cancel all queued jobs - Cancels all queued jobs. Does not affect jobs that are being processed at the moment. |
| `CancelJob` | Cancel job - Cancels the job. If the job is already cancelled or failed, the method will do nothing. |
| `ChangePassword` | Change password - Change the password for the user identified by the username. **Validation rules** : - The new password should not be null or blank. |
| `ChangePassword1` | Change password - Change the password for the current user. Validation Rules: - New password supplied cannot be null or blank Example request URI(s): `http://example.com/confluence/rest/api/user/current/password` |
| `Children` | Get children of content - Returns a map of the direct children of a piece of Content. Content can have multiple types of children. For example, a Page can have children that are also Pages, but it can also have Comments and Attachments. The types of the children returned is specified by the `expand` query parameter in the request. This parameter can include expands for multiple child types. If no types are included in the `expand` parameter, the map returned will just list the child types that are available to be expanded for the content referenced by the `id` path parameter. |
| `ChildrenOfType` | Get children of content by type - Returns the direct children of a piece of Content, limited to a single child type.The types of the children returned is specified by the "type" path parameter in the request. |
| `CommentsOfContent` | Get comments of content - Returns the comments of a piece of Content. Example request URI(s): - `http://example.com/confluence/rest/api/content/1234/child/comment` - `http://example.com/confluence/rest/api/content/1234/child/comment?expand=body.view` - `http://example.com/confluence/rest/api/content/1234/child/comment?start=20&limit=10` - `http://example.com/confluence/rest/api/content/1234/child/comment?location=footer&location=inline&location=resolved` - `http://example.com/confluence/rest/api/content/1234/child/comment?expand=extensions.inlineProperties,extensions.resolution` |
| `Contents` | Get contents in space - Returns the content in this given space. Example request URI: `http://example.com/confluence/rest/api/space/TEST/content?expand=history` |
| `ContentsWithType` | Get trash contents of space - Returns the trash contents in this given space. Example request URI: `http://example.com/confluence/rest/api/space/TEST/trash?limit=100&cursor=content:false:612345` |
| … | *161 more tools not shown* |
## Virtual Tools
Compose native tools via a declarative pipeline DSL:
```yaml
virtualTools:
- name: MyVirtualTool
description: Chain multiple native tools together
inputSchema:
type: object
properties:
appId: { type: string }
required: [appId]
pipeline:
- id: getApp
kind: call
spec: { tool: GetApplication, args: { applicationId: "$input.appId" } }
- id: appName
kind: jq
spec: { from: "$getApp", expr: .name }
- id: done
kind: return
spec: { from: "$appName" }
```
Pipeline kinds: `call`, `jq`, `foreach`, `emit`, `return`.
## Agent Integration
### OpenCode
- `~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"confluence-mcp": {
"type": "local",
"command": ["bash", "-c", "/path/to/confluence-mcp"],
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
},
"enabled": true
}
}
}
```
### Claude Code
- `~/.claude.json`:
```json
{
"mcpServers": {
"confluence-mcp": {
"command": "/path/to/confluence-mcp",
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Codex CLI
- `~/.codex/config.toml`:
```toml
[mcp_servers.confluence-mcp]
url = "http://localhost:8080/mcp"
bearer_token_env_var = "MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN"
```
### Cursor
- `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"confluence-mcp": {
"command": "/path/to/confluence-mcp",
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### OpenCode (Remote)
- `~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"confluence-mcp": {
"type": "remote",
"url": "http://localhost:8080/mcp",
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Claude Code (Remote)
- `~/.claude.json` (User MCPs):
```json
{
"mcpServers": {
"confluence-mcp": {
"url": "http://localhost:8080/mcp",
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Codex CLI (Remote)
- `~/.codex/config.toml`:
```toml
[mcp_servers.confluence-mcp]
url = "http://localhost:8080/mcp"
bearer_token_env_var = "MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN"
```
### Cursor (Remote)
- `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"confluence-mcp": {
"url": "http://localhost:8080/mcp"
}
}
}
```
## Deployment — Docker
### Build
```sh
make build-image
```
Or manually:
```sh
docker build -t ghcr.io/<YOUR_ORG>/confluence-mcp:latest -f deploy/docker/Dockerfile .
docker push ghcr.io/<YOUR_ORG>/confluence-mcp:latest
```
### Run
Pull the pre-built image from GitHub Container Registry:
```sh
docker pull ghcr.io/<YOUR_ORG>/confluence-mcp:latest
```
First generate a default config, then mount it when running:
```sh
docker run --rm ghcr.io/<YOUR_ORG>/confluence-mcp:latest --print-default-config > ~/.confluence-mcp/config.yaml
```
- **Static bearer token**
```sh
docker run -d --name confluence-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN="YOUR_BEARER_TOKEN" \
-v ~/.confluence-mcp:/home/mcp/.confluence-mcp \
ghcr.io/<YOUR_ORG>/confluence-mcp:latest \
--transport http --port 8080
```
- **OIDC backend auth**
```sh
docker run -d --name confluence-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__OIDC__ENABLED=true \
-e MCP__AUTH__BACKEND__OIDC__ISSUER="https://idp.example.com/realms/myrealm" \
-e MCP__AUTH__BACKEND__OIDC__CLIENT_ID="my-client" \
-e MCP__AUTH__BACKEND__OIDC__CLIENT_SECRET="YOUR_CLIENT_SECRET" \
-v ~/.confluence-mcp:/home/mcp/.confluence-mcp \
ghcr.io/<YOUR_ORG>/confluence-mcp:latest \
--transport http --port 8080
```
- **LDAP backend auth**
```sh
docker run -d --name confluence-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__LDAP__ENABLED=true \
-e MCP__AUTH__BACKEND__LDAP__URL="ldaps://ldap.example.com" \
-e MCP__AUTH__BACKEND__LDAP__BASE_DN="dc=example,dc=com" \
-e MCP__AUTH__BACKEND__LDAP__BIND_DN="cn=svc,dc=example,dc=com" \
-e MCP__AUTH__BACKEND__LDAP__BIND_PASSWORD="YOUR_BIND_PASSWORD" \
-v ~/.confluence-mcp:/home/mcp/.confluence-mcp \
ghcr.io/<YOUR_ORG>/confluence-mcp:latest \
--transport http --port 8080
```
### Test with mcpclient.sh
After the container starts, use the bundled `mcpclient.sh` to verify.
> **Note:** `mcpclient.sh` only supports **backend static** token auth. If the server requires
> **frontend OIDC/LDAP** authentication, use a real AI-agent MCP client instead.
```sh
# Point to the mapped host port (include /mcp path)
export MCP_SERVER_ENDPOINT=http://localhost:18080/mcp
# Call any tool (example: Add)
./mcpclient.sh call Add '{"<param>":"<value>"}'
# List all available tools
./mcpclient.sh list-tools
```
> **Tip:** Map the host config directory for persistent configuration:
> `-v ~/.confluence-mcp:/home/mcp/.confluence-mcp`
## Deployment — Kubernetes (Helm)
First generate a default config, then mount it when running:
```sh
docker run --rm ghcr.io/<YOUR_ORG>/confluence-mcp:latest --print-default-config > ~/.confluence-mcp/config.yaml
```
- **Static bearer token**
```sh
helm upgrade -i confluence-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/confluence-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.bearerToken="YOUR_BEARER_TOKEN" \
--set config.upstream.endpoint="https://api.example.com"
```
- **OIDC authentication**
```sh
helm upgrade -i confluence-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/confluence-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.oidcClientSecret="YOUR_OIDC_CLIENT_SECRET" \
--set config.auth.backend.oidc.enabled=true \
--set config.auth.backend.oidc.issuer="https://idp.example.com" \
--set config.auth.backend.oidc.clientId="my-client" \
--set config.upstream.endpoint="https://api.example.com"
```
- **LDAP authentication**
```sh
helm upgrade -i confluence-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/confluence-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.ldapBindPassword="YOUR_LDAP_BIND_PASSWORD" \
--set config.auth.backend.ldap.enabled=true \
--set config.auth.backend.ldap.url="ldaps://ldap.example.com" \
--set config.auth.backend.ldap.bindDN="cn=svc,dc=example,dc=com" \
--set config.upstream.endpoint="https://api.example.com"
```
See `deploy/helm/values.yaml` for all configurable parameters.
## Contributing
We welcome contributions! Here's how you can help:
1. **Fork** the repository and create a feature branch
2. **Make your changes** — add features, fix bugs, or improve documentation
3. **Add tests** — every new feature or bug fix should include unit tests
4. **Run the test suite** — `make test` must pass with no regressions
5. **Submit a Pull Request** — describe what you changed and why
### Development Setup
```sh
git clone https://github.com/<YOUR_ORG>/confluence-mcp.git
cd confluence-mcp
make build
make test
```
### Code Style
- Follow standard Go conventions (`go fmt`, `go vet`)
- Keep functions small and focused
- Write clear commit messages
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines and our [Code of Conduct](CODE_OF_CONDUCT.md).
## License
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
## Contact
For questions, suggestions, or support:
- **GitHub Issues**: [Create an issue](../../confluence-mcp/issues)
- **Discussions**: [Start a discussion](../../confluence-mcp/discussions)
- **Email**: <jameswong1376@gmail.com>
## Acknowledgments
This MCP server was generated by [MCPFather](https://github.com/flowgent-labs/mcpfather), an enterprise-grade OpenAPI-to-MCP code generator.
Built with these excellent open-source projects:
- [mcp-go](https://github.com/mark3labs/mcp-go) — Go SDK for the Model Context Protocol
- [OpenTelemetry](https://opentelemetry.io/) — observability framework for metrics and tracing
- [Prometheus](https://prometheus.io/) — metrics collection and alerting
- [Viper](https://github.com/spf13/viper) — Go configuration management
- [go-jq](https://github.com/itchyny/gojq) — Pure Go implementation of jq
- [Keycloak](https://www.keycloak.org/) — Open Source Identity and Access Management
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available