Skip to main content
Glama
OpenProject MCP Server

OpenProject MCP Server

by zilligm
README.md
# OpenProject MCP Server

[![CI](https://github.com/zilligm/openproject-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/zilligm/openproject-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)

An [MCP](https://modelcontextprotocol.io) server that gives AI agents — Claude, GitHub
Copilot, Cursor, Codex, Gemini, or any other MCP client — structured access to
[OpenProject](https://www.openproject.org): projects, work packages, sprints and
versions, relations, time tracking, attachments, and progress reports.

Ask your agent things like:

- *"What's assigned to me and overdue in the Website project?"*
- *"Break the checkout redesign into phases and tasks, with dependencies."*
- *"Move #1234 to In progress and log 2 hours against it."*
- *"Write a status report for Sprint 7."*

## Features

**Work management**
- Find work packages by assignee, status, type, version, dates, or text — open work by
  default, everything on request — and read one in full, with custom fields and history.
- Create single work packages or whole hierarchies in one call; update dates, priority,
  assignee, effort, progress, and custom fields; move status through the workflow.
- Maintain a living status section inside a description, post notes, and bulk-edit many
  work packages at once.

**Planning and tracking**
- Sprints and releases as versions; dependencies (`precedes`, `blocks`, `relates`, …)
  between work packages; project health status.
- Time entries: log, review, correct, and delete.
- Progress reports for a project or sprint: completion, overdue and blocked work, effort
  against estimates.
- Read project documents and work package attachments; upload text attachments.

**Safety**
- Destructive and bulk tools are dry runs until called with `dry_run=False, confirm=True`.
- Edits use OpenProject's optimistic locking and are retried against fresh state, so a
  concurrent human edit is never overwritten.
- Names (`priority="High"`, `version="Sprint 4"`) are resolved and checked; an unknown name
  fails with the list of valid ones instead of guessing.
- An optional allowlist confines every tool to chosen projects — a guard against agent
  mistakes and instructions injected through ticket content.

**Deployment**
- Runs locally over stdio, or as a shared HTTP service where each person authenticates
  with their own OpenProject token and acts as themselves.
- Switch off whole tool categories you do not use, so the model has fewer tools to
  choose between.
- Built-in diagnostics, structured logs on stderr, optional usage telemetry and a
  read-only usage dashboard.
- Two agent skills for operating the tools and planning work, installable as a Claude
  Code plugin.

---

## Contents

- [Features](#features)
- [Setup guide](#setup-guide)
  1. [Get an OpenProject instance](#1-get-an-openproject-instance)
  2. [Create an API token](#2-create-an-api-token)
  3. [Install the server](#3-install-the-server)
  4. [Connect your AI client](#4-connect-your-ai-client)
  5. [Verify](#5-verify)
- [Running as a shared HTTP service](#running-as-a-shared-http-service)
- [Configuration](#configuration)
- [Tools](#tools)
- [Agent skills](#agent-skills)
- [Development](#development)
- [Documentation](#documentation)

---

## Setup guide

### 1. Get an OpenProject instance

Any OpenProject **16.3 or later** works — self-hosted or
[OpenProject Cloud](https://www.openproject.org/enterprise-edition/#hosting-options).
If you already have one, skip to step 2.

To try it locally, the official all-in-one container is the quickest route
([full instructions](https://www.openproject.org/docs/installation-and-operations/installation/docker/)):

```bash
docker run -d --name openproject -p 8080:80 \
  -e SECRET_KEY_BASE="$(openssl rand -hex 64)" \
  -e OPENPROJECT_HOST__NAME=localhost:8080 \
  -e OPENPROJECT_HTTPS=false \
  -e OPENPROJECT_DEFAULT__LANGUAGE=en \
  -v openproject_pgdata:/var/openproject/pgdata \
  -v openproject_assets:/var/openproject/assets \
  openproject/openproject:17
```

First start takes a few minutes. Then open <http://localhost:8080>, sign in as
`admin` / `admin`, and set a new password when prompted. Create a project to play with.

> This is a **local, plain-HTTP** setup for evaluation. For anything shared, follow
> OpenProject's production guides (HTTPS, a real `SECRET_KEY_BASE`, backups).

### 2. Create an API token

The server acts **as the user whose token it holds**, with exactly that user's
permissions — so consider a dedicated user that is a member of only the projects the
agent should touch.

1. In OpenProject, open your avatar → **Account settings** → **Access tokens**.
2. Under **API**, click **+ API token**, give it a name, and **Create**.
3. Copy the token now — it is shown only once.

If the API section is missing, an administrator needs to enable it under
*Administration → API and webhooks*. Check the token works:

```bash
curl -s -u apikey:YOUR_TOKEN http://localhost:8080/api/v3/users/me | head -c 200
```

A JSON body with your name means you are ready. `401` means the token is wrong.

### 3. Install the server

You need **Python 3.11+** and [**uv**](https://docs.astral.sh/uv/getting-started/installation/).

```bash
git clone https://github.com/zilligm/openproject-mcp.git
cd openproject-mcp
uv sync
cp example.env .env
```

Edit `.env` and set the two required values:

```env
OPENPROJECT_URL=http://localhost:8080        # your instance's base URL, no /api/v3
OPENPROJECT_TOKEN=your_api_token_here
```

`.env` is gitignored. Never commit it — the token carries every permission its user has.

### 4. Connect your AI client

Most clients launch the server themselves over **stdio**: no port is opened, and the
credentials come from the `.env` in the repository. In every snippet below, replace
`/path/to/openproject-mcp` with the absolute path of your clone.

#### Claude Code

```bash
claude mcp add openproject -- uv run --directory /path/to/openproject-mcp server.py --transport stdio
```

Add `--scope user` to make it available in every project, not just the current one.

#### Claude Desktop

Open *Settings → Developer → Edit Config* and add to `claude_desktop_config.json`, then
restart Claude Desktop:

```json
{
  "mcpServers": {
    "openproject": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/openproject-mcp", "server.py", "--transport", "stdio"]
    }
  }
}
```

If Claude Desktop cannot find `uv`, use its absolute path (`which uv`) as `command`.

#### Other clients

The same command works in any MCP client that can launch a local server.
[docs/clients.md](docs/clients.md) has ready-to-paste configurations for:

| Client | Where the config goes |
|---|---|
| VS Code (GitHub Copilot agent mode) | `.vscode/mcp.json` |
| Cursor | `~/.cursor/mcp.json` |
| OpenAI Codex CLI | `~/.codex/config.toml` |
| Gemini CLI | `~/.gemini/settings.json` |
| Anything else | `uv run --directory /path/to/openproject-mcp server.py --transport stdio` |

It also covers connecting to a server over HTTP, the Claude Code plugin, and why
cloud-hosted connectors (claude.ai custom connectors, ChatGPT) cannot reach a server on
your own network.

### 5. Verify

Ask your agent:

> *Call get_operational_diagnostics.*

It is read-only, and reports whether OpenProject is reachable, its version, and which
user the token acts as. Then try *"List my OpenProject projects."*

Something wrong? Without any AI client, the
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) lets you call tools
by hand:

```bash
npx @modelcontextprotocol/inspector uv run --directory /path/to/openproject-mcp server.py --transport stdio
```

Common causes are in [Troubleshooting](docs/operations.md#troubleshooting).

---

## Running as a shared HTTP service

For a team, run one long-lived server and let each person connect with **their own**
OpenProject token. The server holds no secret of its own: it verifies each caller's
bearer token with OpenProject and acts as that person — their permissions, their name on
every edit.

A multi-arch image (x86-64 and arm64) is published to GitHub Container Registry:

```bash
cp example.env .env        # set OPENPROJECT_URL; keep MCP_TRANSPORT=streamable-http
docker compose up -d       # or: docker compose up -d --build, to build this checkout
```

The endpoint is `http://<host>:8001/mcp`. Clients send:

```
Authorization: Bearer <their OpenProject API token>
```

For Claude Code:

```bash
claude mcp add --transport http openproject http://your-host:8001/mcp \
  --header "Authorization: Bearer YOUR_OPENPROJECT_API_TOKEN"
```

Without Docker: `uv run server.py --transport streamable-http --port 8000`.

> Expose the port only on networks you trust, or put it behind an HTTPS reverse proxy.
> If OpenProject runs on the same host as the container, `localhost` inside the container
> is the container itself — use the host's address in `OPENPROJECT_URL`.

[docs/operations.md](docs/operations.md#first-deployment-on-a-host) covers deployment
end to end: verifying it, updating it, private CAs, and the optional usage dashboard.

---

## Configuration

All settings are environment variables, read from `.env`. The ones you are likely to
touch:

| Variable | Default | Purpose |
|---|---|---|
| `OPENPROJECT_URL` | — | Base URL of the instance (required) |
| `OPENPROJECT_TOKEN` | — | API token; required for stdio |
| `OPENPROJECT_ALLOWED_PROJECTS` | unset | Confine every tool to these project IDs or identifiers |
| `OPENPROJECT_TOOLSET_*` | `true` | Switch off whole tool categories you do not use |
| `OPENPROJECT_VERIFY_SSL` | `true` | Prefer `SSL_CERT_FILE` for a private CA over turning this off |
| `MCP_TRANSPORT` | `streamable-http` | `stdio` or `streamable-http`; `--transport` overrides it |
| `MCP_PORT` | `8000` | HTTP port; `--port` overrides it |

Every variable is described in [`example.env`](example.env) and in
[docs/operations.md](docs/operations.md#environment-variables).

---

## Tools

**36 tools** covering projects, work packages (dates, priority, versions, effort,
progress, custom fields), versions (sprints and releases), notes and history, relations,
members, time tracking, progress reporting, documents, attachments, and diagnostics.
The full reference, generated from the code, is in [docs/tools.md](docs/tools.md).

### Tool selection guide

| Situation | Tool to use |
|-----------|-------------|
| Check the connection and which user the token acts as | `get_operational_diagnostics` |
| Find a project | `list_projects(search=...)` |
| Valid statuses, types, priorities, versions for a project | `get_work_package_options` |
| Find work: mine, overdue, in a sprint, by text | `list_work_packages` (open only unless `status="all"`) |
| Read one work package in full | `get_work_package` |
| Create one work package | `create_work_package` |
| Break work down into a hierarchy | `create_work_package_tree` |
| Reschedule, reprioritize, reassign, unset fields | `update_work_package` |
| Change status (workflow-checked) | `update_work_package_status` |
| Same change on many work packages | `bulk_update_work_packages` (dry run by default) |
| Create or close a sprint / release | `create_version` / `update_version(status="closed")` |
| Progress note on a work package | `add_work_package_note` |
| Living status section in a description | `update_work_package(description_section_heading=...)` |
| What happened on a work package | `list_work_package_activities` |
| Log or review time | `create_time_entry` / `list_time_entries` |
| Project or sprint status report | `generate_project_progress_report` |
| Record project health | `update_project(status=...)` |
| Delete anything | `delete_*` tools (dry run by default) |

The server also exposes two MCP prompts, `openproject_usage_guide` and
`openproject_safe_mode_guide`, for clients that support them.

---

## Agent skills

[`skills/`](skills/) holds two agent skills that teach a model to use these tools well:
`openproject` for day-to-day operation, and `project-planning` for structuring work into
phases, milestones and tasks. In Claude Code, the repository installs as a plugin that
brings the skills along:

```bash
claude plugin marketplace add zilligm/openproject-mcp
claude plugin install openproject-mcp@openproject-tools
```

The plugin connects over HTTP, so it expects a [running server](#running-as-a-shared-http-service).
See [skills/README.md](skills/README.md) for installing the skills on their own, alongside
a stdio setup.

---

## Development

```bash
uv sync
uv run pytest -m "not integration"     # unit tests, no live instance needed
uv run ruff check .                    # lint
```

[docs/development.md](docs/development.md) covers conventions, the live integration suite,
and adding a tool. Read [docs/architecture.md](docs/architecture.md) first.

---

## Documentation

| Document | What it covers |
|----------|----------------|
| [docs/clients.md](docs/clients.md) | Configuration for each MCP client, over stdio and HTTP |
| [docs/tools.md](docs/tools.md) | Full tool reference (generated) |
| [docs/operations.md](docs/operations.md) | Configuration, deployment, troubleshooting, known limitations |
| [docs/architecture.md](docs/architecture.md) | How the server is built and the conventions tools follow |
| [docs/development.md](docs/development.md) | Adding a tool, testing, documentation map |
| [skills/](skills/) | Agent skills for using these tools and for planning work with them |
| [CHANGELOG.md](CHANGELOG.md) | What changed and when |
| [docs/history/](docs/history/) | Archived build records — not maintained |

## Disclaimer

This is an independent, community project. It is not affiliated with, endorsed by, or
supported by OpenProject GmbH; "OpenProject" is a trademark of its respective owner and
is used here only to describe what this software connects to.

The server lets an AI agent read and **change real data** in your OpenProject instance,
with every permission of the API token it is given. Language models can misread requests
and make mistakes, and the safeguards described above reduce that risk but do not remove
it. You are responsible for how you deploy and use this software and for any actions
taken through it. Use a token with only the permissions you need, try it against a
non-critical project first, and keep backups of data you cannot afford to lose.

The software is provided "as is", without warranty of any kind; see the [license](LICENSE)
for the full terms.

## License

[MIT](LICENSE)