MCP server that exposes confluence-markdown-exporter (CME) as a set of AI-agent tools. An orchestrating agent can configure CME per workspace, manage export sources, and trigger asynchronous Confluence exports over MCP Streamable HTTP.
Current coordinated release: 0.14.5.
agent-cme is the exporter only. One global instance serves all workspaces:
credentials, source manifests, and exports are isolated per workspace. When
managed by llm-wiki-manager, the active workspace is injected automatically
on every tool call — orchestrators never pass workspace explicitly. Each
workspace's export output lands directly in its raw/untracked/ directory.
It belongs to a three-repository toolchain:
| Repository | Role |
|---|---|
agent-cme |
Confluence Markdown exporter and MCP server |
llm-wiki |
Local wiki workspace engine that ingests Markdown and builds deliverables |
llm-wiki-manager |
Orchestrates several wiki workspaces; starts agents globally via wiki-workspace agents up |
Do not hard-code workspace paths inside agent-cme. The agent always works with
container paths (/data and /workspaces); the workspace tool argument
selects the target, validated by the guardian before any file is written.
Orchestrating agent (Claude or other)
│ MCP Streamable HTTP
▼
agent-cme MCP server (port 3000)
│
├── /data/<workspace>/cme/app_data.json ← CME credentials + connection settings
├── /data/<workspace>/sources-manifest.yaml ← export sources managed by agent
└── /workspaces/<workspace>/raw/untracked/ ← exported Markdown output
All runtime state lives in ./data/ on the host, mounted as a Docker volume.
No export source is versioned in Git. On first use for a workspace, agent-cme
creates ./data/<workspace>/sources-manifest.yaml if it does not already exist.
MCP edits are persisted there and are not overwritten on later restarts.
cd agent-external/agent-cme
WORKSPACES_ROOT=/path/to/workspaces docker compose up --buildThe MCP endpoint starts on http://localhost:3336/mcp/.
Opening that URL in a browser shows a status page. MCP clients use the same URL
for Streamable HTTP requests.
Authentication is disabled by default. With Docker Compose, set
CME_MCP_AUTH_TOKEN; it is mapped to the internal MCP_AUTH_TOKEN used by the
server. Clients must then send Authorization: Bearer <generated-local-token>.
When this repository is used alongside llm-wiki-manager, start all external
agents together from the manager directory:
# manager/.env must have WORKSPACES_ROOT and CME_MCP_AUTH_TOKEN set
wiki-workspace agents upThis uses agents.docker-compose.yml and starts CME, documents, and mailer as
a single stack. Register the endpoint in mcp.endpoints.json using ${VAR}
interpolation so the token is never hard-coded:
{
"mcpServers": {
"cme": {
"url": "http://host.docker.internal:${CME_MCP_PORT:-3336}/mcp/",
"headers": { "Authorization": "Bearer ${CME_MCP_AUTH_TOKEN}" }
}
}
}Credentials and source manifests are stored per workspace under
.agents-data/cme/<workspace>/. For example:
.agents-data/cme/my-project/cme/app_data.json
.agents-data/cme/my-project/sources-manifest.yamlEach cme_export_run() for the active workspace writes Markdown directly to
/workspaces/my-project/raw/untracked/.
Configure CME directly from the command line without going through an MCP agent:
# configure credentials interactively
CME_WORKSPACE=my-project docker compose run --rm cme-cli config
# run an export manually
CME_WORKSPACE=my-project docker compose run --rm cme-cli exportThe cme-cli service uses the cme binary from confluence-markdown-exporter and mounts the same ./data volume as cme-mcp. Set CME_WORKSPACE so credentials are written under ./data/<workspace>/cme/app_data.json; they are immediately visible to the MCP server for that workspace.
Register the running endpoint in llm-wiki-manager/mcp.endpoints.json as
cme; the manager and served chat UI load it as an external MCP endpoint.
On first start, CME has no credentials. The agent must call cme_setup once.
After that, the server is fully autonomous across restarts.
cme_setup is synchronous: it writes configuration and returns immediately. It
does not create _activity metadata and will not appear in an Activity panel.
An orchestrator should either call it in the same turn, ask for missing required
credentials, or report that the CME tool/server is unavailable. It should not
answer with a plain-text promise to call cme_setup later.
Via llm-wiki-manager — workspace is injected automatically by Donna;
the active /use <workspace> is set once and applies to every call below:
1. cme_status → "not_configured — call cme_setup"
2. cme_setup(...) → credentials + settings written for active workspace
3. cme_sources_list() → inspect runtime sources for active workspace
4. cme_source_add(...) → add/update sources if needed
5. cme_export_run() → async export started, returns JSON with `job_id` and `_activity`
6. cme_export_status(job_id=...) → monitor progress, returns JSON with `_activity`
7. cme_export_cancel(job_id=...) → cancel a running export when needed
Direct MCP / standalone — pass workspace explicitly on every call:
1. cme_status(workspace="my-project")
2. cme_setup(workspace="my-project", ...)
3. cme_sources_list(workspace="my-project")
4. cme_source_add(workspace="my-project", ...)
5. cme_export_run(workspace="my-project")
6. cme_export_status(job_id=...)
7. cme_export_cancel(job_id=...)
On subsequent restarts: cme_status returns configured and the agent skips straight to step 3+.
| Tool | Description |
|---|---|
cme_status |
Check if CME is configured for one workspace. Always call this first. |
cme_setup |
Workspace initialization: credentials + connection settings. |
cme_sources_list |
List configured export sources for one workspace. |
cme_source_add |
Add or update a workspace export source (space, page, or page-with-descendants). |
cme_source_remove |
Remove a workspace export source by name. |
cme_export_run |
Start an async workspace export. Returns JSON with job_id, status, sources, and _activity. |
cme_export_cancel |
Cancel a running export job. Files already written before cancellation are left in place. |
cme_export_status |
Check job progress, or show last-export summary. With job_id, returns _activity. |
Orchestration contract (used by llm-wiki-manager's generic orchestrator —
executor-only agent, canPlan: false):
| Tool | Description |
|---|---|
agent_describe |
Declare the external-source.export capability (defaultRequiresApproval: true), limits, health. |
agent_execute |
Execute one approved export task; idempotent via idempotencyKey (a retry returns the existing job). |
agent_status |
Report orchestrated task progress and final result. |
agent_cancel |
Cancel the job bound to one orchestrated task. |
Configuring credentials or sources never triggers an export: exports run only
as approved orchestrated tasks or explicit cme_export_run calls.
cme_export_run and cme_export_status(job_id=...) include additive
_activity metadata so shells/orchestrators can monitor jobs without knowing
CME-specific response details:
{
"job_id": "abc12345",
"status": "running",
"_activity": {
"id": "abc12345",
"source": "cme",
"kind": "export",
"status": "running",
"poll": {
"server": "cme",
"tool": "cme_export_status",
"args": { "job_id": "abc12345" },
"intervalMs": 2500
}
}
}| Parameter | Type | Required | Description |
|---|---|---|---|
workspace |
string | yes | Workspace name; config is stored under /data/<workspace>/cme/ |
base_url |
string | yes | Confluence base URL, e.g. http://confluence.example.com |
username |
string | yes | Confluence email address or login |
pat |
string | no | Personal Access Token (self-hosted) |
api_token |
string | no | API token (Atlassian Cloud) |
verify_ssl |
boolean | no | Verify SSL certificates (default: true) |
use_v2_api |
boolean | no | Use REST API v2 — Data Center 8+ or Cloud (default: false) |
Always provide username as the Confluence email/login. Provide either pat
for self-hosted Confluence, or api_token for Atlassian Cloud. base_url and
username alone store connection settings but do not make cme_status return
configured.
| Parameter | Type | Required | Description |
|---|---|---|---|
workspace |
string | yes | Workspace name |
name |
string | yes | Short identifier |
type |
string | no | space (default), page, or page-with-descendants |
base_url |
string | if type=space |
Confluence base URL |
space |
string | if type=space |
Confluence space key |
url |
string | if type=page* |
Full Confluence URL or Markdown link |
description |
string | no | Human description |
When type is omitted and url is provided, CME infers space or page.
Supported forms include /spaces/<key>, /spaces/<key>/pages/<id>,
/display/<key>, and legacy viewpage.action?pageId=... URLs.
To enable TLS, uncomment the SSL lines in docker-compose.yml and provide certificate files:
mkdir certs
# Place server.crt and server.key in agent-cme/certs/# docker-compose.yml — uncomment:
environment:
- MCP_SSL_CERTFILE=/certs/server.crt
- MCP_SSL_KEYFILE=/certs/server.key
volumes:
- ./certs:/certs:roThen update your MCP client URL from http:// to https://.
The server refuses to start if only one of the two SSL variables is set, or if a file is missing.
With Docker Compose:
cd agent-cme
docker compose up --buildclaude mcp add --transport http cme http://localhost:3000/mcp/If CME_MCP_AUTH_TOKEN is set in Docker Compose, or MCP_AUTH_TOKEN is set for
direct local development:
claude mcp add --transport http cme http://localhost:3000/mcp/ \
--header "Authorization: Bearer <generated-local-token>"For scoped HTTP access, set MCP_READ_TOKEN for status/list clients and
MCP_WRITE_TOKEN for clients allowed to configure sources or start/cancel
exports. MCP_AUTH_TOKEN remains a legacy full-access read+write token. Rate
limiting defaults to 120 requests per 60 seconds and can be tuned with
MCP_RATE_LIMIT_REQUESTS and MCP_RATE_LIMIT_WINDOW_SECONDS.
Without token:
{
"mcpServers": {
"cme": {
"type": "http",
"url": "http://localhost:3000/mcp/"
}
}
}With token:
{
"mcpServers": {
"cme": {
"type": "http",
"url": "http://localhost:3000/mcp/",
"headers": {
"Authorization": "Bearer <generated-local-token>"
}
}
}
}Register agent-cme as an MCP server:
Type: MCP (Streamable HTTP)
URL: http://localhost:3000/mcp/
Auth: None
If OpenWebUI itself runs in Docker, localhost means the OpenWebUI container,
not your host. Use one of these instead:
http://host.docker.internal:3000/mcp/
or, if OpenWebUI is on the same Compose network:
http://cme-mcp:8080/mcp/
The server auto-detects the local CME venv at .cme/. Install both CME and the
MCP HTTP server dependencies in that venv:
cd agent-cme
python3 -m venv .cme
.cme/bin/pip install --upgrade pip
.cme/bin/pip install confluence-markdown-exporter "mcp>=1.9.4" starlette uvicorn pyyaml
.cme/bin/python cme_mcp_server.py
# Starts on http://0.0.0.0:8080/mcp/ by default.
# Use MCP_PORT=3000 .cme/bin/python cme_mcp_server.py if you want local dev on port 3000.
# CME_DATA_DIR defaults to agent-cme/CME credentials are read from a workspace-specific CME_CONFIG_PATH during MCP
tool calls. If you run the underlying cme binary manually without setting it,
the binary falls back to its default OS path:
- macOS:
~/Library/Application Support/confluence-markdown-exporter/app_data.json - Linux:
~/.config/confluence-markdown-exporter/app_data.json
agent-cme/data/ ← mounted at /data in the container
└── my-project/
├── cme/
│ └── app_data.json ← CME credentials + settings
└── sources-manifest.yaml ← runtime export sources
data/ is gitignored — it contains credentials and generated content.
Use agent-cme to create exports from Confluence. In manager mode, exports land
directly in the target workspace, then run:
./wiki-workspace wiki <workspace> doctor
./wiki-workspace wiki <workspace> ingest
./wiki-workspace wiki <workspace> build --plan
./wiki-workspace wiki <workspace> build
./wiki-workspace wiki <workspace> exportThe workspace binding is provided by Docker mounts from llm-wiki-manager; keep
agent-cme itself workspace-agnostic.
Released under the PolyForm Noncommercial License 1.0.0. See LICENSE.