OpenProject MCP Server
Allows interaction with OpenProject's API, providing tools for managing work packages, projects, statuses, types, priorities, comments, time logging, and assignments, with support for dry-run and protected transitions.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OpenProject MCP Serverlist my work packages"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
openproject-agent
A pnpm monorepo that connects OpenProject to Claude Code:
Package | What it is |
OpenProject API v3 client — typed errors, HAL→DTO mapping, pagination, retry/backoff, lockVersion handling. | |
An MCP (stdio) server exposing OpenProject as tools Claude Code can call. | |
A CLI that pulls work packages, runs headless Claude Code ( |
The design goal is guarded automation: dry-run by default, protected status transitions blocked, file access scoped to the target repo, and the API key kept out of logs and out of the agent's environment. These are guardrails, not a sandbox: read the Security model before pointing the orchestrator at a real repository.
Unofficial community project. Not affiliated with, endorsed by, or sponsored by OpenProject GmbH or Anthropic PBC. OpenProject, Claude and Claude Code are trademarks of their respective owners.
Prerequisites
Node.js 20+ (developed on 24)
pnpm (
npm i -g pnpm)Claude Code CLI (
claude) available on yourPATH— the orchestrator shells out to it.An OpenProject instance and an API key.
Related MCP server: OpenProject MCP Server
Install & build
pnpm install
pnpm build # tsc --build across all packages
pnpm test # vitest (mocked; no network)
pnpm lint # eslintGet an OpenProject API key
In OpenProject: My account → Access tokens → API → Generate. The key is used as the
HTTP Basic password with the fixed username apikey
(Authorization: Basic base64("apikey:" + KEY)).
Configure
Copy the env template and fill it in (never commit the real
.env):cp .env.example .env # edit .env -> OPENPROJECT_URL, OPENPROJECT_API_KEYEdit
openproject-agent.config.json. Key fields:project— your project id or identifier.targetRepo— absolute path to the repo Claude Code should work in.filters— which work packages to pull (types,statuses,assignee: "me", ...).statusFlow—{ start, success, blocked }status names the orchestrator advances to.protectedTransitions— statuses that always require human approval (defaultClosed,Rejected).claude.allowedTools/disallowedTools— the tools spawned Claude Code may use; built-in tools not listed inallowedToolsare not available at all. The default scopes file tools to the target repo (Read(./**),Edit(./**), ...); a bareReadorEditwould allow every file your OS user can access.claude.addDirswidens the scope.claude.settingSources— which Claude Code settings files (user,project,local) the agent loads; default[](none). See Security model.claude.maxTurns— turn cap (see note below).commentFooter— Markdown appended to every result comment (defaults to an English "produced by an AI agent, must be reviewed by a human" note;""omits it).
apiKeyis written as"env:OPENPROJECT_API_KEY"and resolved from the environment; the raw key is never stored in the config file.
Use the MCP server with Claude Code
Build first (pnpm build). Then register the stdio server — prefer -s user scope
(one registration, available in every project, immune to the Windows drive-letter-casing
pitfall described in Troubleshooting):
Windows (PowerShell — single line):
claude mcp add openproject -s user -e OPENPROJECT_URL=https://openproject.example.com -e OPENPROJECT_API_KEY=your-api-key -- node C:/absolute/path/to/openproject-mcp/packages/mcp/dist/bin.jsmacOS/Linux (bash):
claude mcp add openproject -s user \
-e OPENPROJECT_URL=https://openproject.example.com \
-e OPENPROJECT_API_KEY=your-api-key \
-- node /absolute/path/to/openproject-mcp/packages/mcp/dist/bin.js⚠️ Start a NEW Claude Code session after registering. MCP tools are snapshotted at session start — an already-open chat will never gain the
mcp__openproject__*tools, even ifclaude mcp listsays Connected.
Both env vars are mandatory (OPENPROJECT_URL, OPENPROJECT_API_KEY) — but if either
is missing from the registration, the server falls back to a .env file, searched in this
order: $OPENPROJECT_ENV_FILE, ./.env (cwd), the package's .env, this repo's .env.
Set OPENPROJECT_NO_ENV_FILE=1 to disable the fallback. Optional env:
OPENPROJECT_READONLY=true (block all writes),
OPENPROJECT_PROTECTED_TRANSITIONS=Closed,Rejected, and OPENPROJECT_START_STATUS="In progress"
(the status start_work_package moves a work package to).
Or add a project-scoped .mcp.json (checked into a repo Claude Code runs in). Use
${VAR} expansion so the key never lands in a committed file, and note the server must
also be approved via enabledMcpjsonServers in that repo's .claude/settings.local.json:
{
"mcpServers": {
"openproject": {
"command": "node",
"args": ["C:/absolute/path/to/openproject-mcp/packages/mcp/dist/bin.js"],
"env": {
"OPENPROJECT_URL": "https://openproject.example.com",
"OPENPROJECT_API_KEY": "${OPENPROJECT_API_KEY}",
"OPENPROJECT_READONLY": "true"
}
}
}
}Set the referenced variable once per machine (Windows:
setx OPENPROJECT_API_KEY "...", then open a new terminal). Never commit a literal key. Pick exactly ONE registration path (user scope or.mcp.json) — duplicate registrations across scopes make failures very hard to diagnose. When you remove a.mcp.jsonentry, also remove the stale name fromenabledMcpjsonServers.
Troubleshooting
First step, always: run the built-in self-test in a real terminal —
node C:/absolute/path/to/openproject-mcp/packages/mcp/dist/bin.js --doctorIt prints which OPENPROJECT_* vars are set (masked), where .env was searched/loaded,
performs a live auth + project-listing round-trip, and ends with a one-line fix hint.
"Failed to connect" — the server process crashed at startup. Almost always a missing
OPENPROJECT_URL/OPENPROJECT_API_KEYin the registration env (and no.envfound).--doctorshows exactly what is missing.Tools missing in an open session — tools load at session start; start a new session.
claude mcp listsays Connected but the session says "Server not found" (Windows) — drive-letter casing split-brain:~/.claude.jsoncan hold two project entries for the same folder (C:/...andc:/...), and the registration may have landed in the one your session doesn't use. Fix: register with-s user(project-independent), and remove the duplicates (claude mcp remove openproject -s localrun from the affected project).Verifying inside a session — run
/mcpin Claude Code to see connected servers and their tools.
Tools exposed
Read: list_projects, list_work_packages, get_work_package, get_next_work_package,
list_statuses, list_types, list_priorities, get_allowed_status_transitions,
get_journal (full change history incl. "Status changed from X to Y" lines),
get_work_package_schema (field types, required/writable, custom fields),
get_attachment (images are returned as viewable image content; text files are inlined).
Boards: list_boards, get_board, list_board_column, get_next_board_card
(boards are status-based Kanban boards; each column is a saved query).
Write (each supports a dryRun flag and honors global read-only mode):
add_comment, update_work_package_status, start_work_package (move a WP to the start
status, default "In progress" — call when you begin work), create_work_package,
assign_work_package, log_time.
Critical transitions in protectedTransitions are refused with a "human approval required"
error, and work-package deletion is never exposed.
Use the orchestrator
# Show the queue without running anything
node packages/orchestrator/dist/cli.js list
# Dry run (DEFAULT): Claude Code runs, but OpenProject is NOT written
node packages/orchestrator/dist/cli.js run --wp 1234
# Really write results back to OpenProject
node packages/orchestrator/dist/cli.js run --write --yes --limit 3
# State summary / reset
node packages/orchestrator/dist/cli.js status
node packages/orchestrator/dist/cli.js resetrun flags: --project <id>, --types bug,feature, --limit <n>, --wp <id>,
--board <name>, --column <name>, --dry-run (default), --write, --yes (no per-WP
prompt), --max-turns <n>, --verbose, --config <path>, --template <path>.
Board mode
Instead of filters.statuses, you can drive the queue from a board column. Add a
board block to the config (or pass --board/--column):
"board": { "name": "Kanban", "sourceColumn": "New" }The queue is pulled from that column via its saved query (preserving board order), then
still narrowed by filters.types / filters.assignee. Because these are status-based
boards, advancing a card across columns is just a status change — so statusFlow already
moves cards on the board. Board names are unique only within a project, so the board is
resolved within config.project.
Per work package the orchestrator: fetches full detail → renders
templates/work-package.md → runs claude -p --output-format stream-json in targetRepo (with this MCP server wired in via --mcp-config) → logs the
stream to .openproject-agent/logs/wp-<id>.jsonl → parses the agent's JSON result block →
in --write mode, posts a review comment and advances status per statusFlow → records
progress in .openproject-agent/state.json. Runs are sequential (concurrency 1) and
resumable (already-finished work packages are skipped).
Security model
Read this before running the orchestrator. It feeds OpenProject content (subject,
description, assignee name, comments, relation titles, attachment names, and whatever the
agent later reads through attachments or the openproject tools) into an autonomous Claude
Code session that edits files in targetRepo. Anyone who can write content the API key can
read — work packages, comments or attachments, including related work packages in other
projects — can try to steer that agent (prompt injection). Assume they can run code on the
orchestrator host:
The default
allowedToolsincludeEdit,WriteandBash(npm test:*).npm testruns whatever the repo's test script and test files say, and the agent can edit both, so an allowed test command amounts to arbitrary code execution as your OS user.Dry-run does not prevent this. It only stops writes to OpenProject; the agent still edits files in
targetRepoand runs its allowed commands.In
--writemode every field of the agent's result block (summary,changedFiles,openQuestions,testsRun) — or up to 4000 characters of its final message when there is no result block — is posted as a comment everyone in the project can read. That is a possible channel for leaking file contents.All work packages in one run share the same
targetRepoworking tree, so changes a poisoned run leaves behind (e.g. toCLAUDE.mdor tests) carry into the next run.
Recommended setup:
Run the orchestrator in a disposable container, VM or devcontainer with no other credentials and restricted network egress, against a throwaway clone or worktree. Keep the API key out of files on that machine where you can (see below).
Use a dedicated low-privilege OpenProject account whose API key can only see the target project.
Make sure only trusted people can write the content that gets queued: a project where only trusted users may create, edit, comment on or attach to work packages, or a status/type in the workflow that only trusted roles can set.
filters.assigneeand board columns choose which work packages run, not who wrote their content.Keep
allowedToolsminimal and path-scoped, stay in dry-run until you trust the setup, and review the diff after each work package (e.g.--limit 1against a clean checkout) when the queue is not fully trusted.
What the orchestrator does to limit the blast radius:
Fenced input. Work-package text embedded in the prompt is wrapped in
<untrusted-content>tags, and the prompt tells the agent to treat those, attachments andopenprojecttool results as data. This lowers the risk of prompt injection; it does not remove it.Only allowed tools exist, scoped to the repo. Built-in tools not listed in
claude.allowedToolsare removed from the session (--tools),disallowedTools(defaultBash(git:*)) are denied, and the default file-tool rules (Read(./**)etc.) refuse paths outsidetargetRepoandclaude.addDirs. Commands run through an allowedBashrule are not confined this way.Isolated settings.
claude.settingSourcesdefaults to[], so nothing from your user, project or local Claude Code settings files applies to unattended runs: no hooks, plugins or allow-rules — but also no deny rules, sandbox or model settings, and the repo'sCLAUDE.mdis not auto-loaded (the default template tells the agent to read it). Put restrictions inclaude.disallowedTools, which always applies. Adding a source (e.g.["user"]when your Claude Code auth comes fromapiKeyHelperorenvin a settings file) brings back that file's hooks, plugins and rules too. Never addprojectorlocalfor an untrusted queue: the agent can edit those files insidetargetRepo.Read-only OpenProject access for the agent. Its MCP server always runs with
OPENPROJECT_READONLY=true; every OpenProject write is made by the orchestrator itself, and only in--writemode.OPENPROJECT_*variables (and any variable holding the key) are removed from the agent's environment, so commands it runs do not inherit the API key. Code running as your OS user can still read it from the files that hold it — the temporary MCP config file and your.env— hence the recommendations above.Masking. The API key is masked in the orchestrator's log output, in the
.openproject-agent/logs/wp-<id>.jsonltranscripts and in posted comments. Only the exact key is caught: this stops accidental leaks, not a steered agent that encodes it. Transcripts otherwise contain repository content verbatim; treat them as sensitive.Protected transitions (default
Closed,Rejected) are never performed automatically, by either the MCP server or the orchestrator.No git. The prompt forbids git and the runner denies
Bash(git:*). This keeps a well-behaved agent out of git; it is not a boundary against code run through another allowed command.Unknown results (no structured result block) are routed to human review, not auto-advanced.
Note on maxTurns
Claude Code has no --max-turns CLI flag (verified against v2.1.186). The orchestrator
enforces claude.maxTurns itself by counting assistant turns in the stream-json output
and terminating the child process if it is exceeded; a wall-clock timeoutMs is a safety net.
Project layout
packages/core/ OpenProject API client, DTOs, retry, errors
packages/mcp/ MCP stdio server (uses core)
packages/orchestrator/ CLI, prompt rendering, Claude runner, state store
templates/work-package.md
openproject-agent.config.jsonTesting
pnpm test runs the full suite with msw-mocked HTTP and an in-memory MCP client — no
live OpenProject or Claude Code process is needed. pnpm build (tsc) and pnpm lint must
also pass.
License
MIT © 2026 Kürşat Kurt
This server cannot be deployed
Maintenance
Related MCP Connectors
Read and write Mission Control state via MCP — projects, tasks, subtasks, templates, status updates.
Project management MCP for AI agents with safe task reads and writes.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Run field service from Claude, ChatGPT or Copilot: dispatch, billing, customer messages, photos, and change the software itself, with a confirm step before every change. This address is the India region; US East, Canada, Norway and NZ addresses are at fieldproxy.ai/mcp.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI assistants to manage OpenProject work packages, projects, and time tracking. It provides comprehensive tools for creating, updating, and querying tasks and project metadata through the OpenProject API.1141 npm1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with OpenProject's APIv3 for autonomous project management, including task tracking, member administration, and project configuration. It supports comprehensive operations for managing work packages, projects, and reference data like statuses and priorities.41 npm2MIT
- AlicenseAqualityDmaintenanceExposes the OpenProject REST API as MCP tools for project management, including creating and managing projects, work packages, relations, attachments, users, notifications, watchers, boards, and reference data.3741 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables LLM applications to interact with OpenProject for project management, work package tracking, and task creation.84MIT