Your agent, this morning
“I can’t see your project tracker. Could you paste the ticket in here?”
“You have 3 work packages in Acme. #1284, the login redirect loop, is in progress.”
Your agent, patched through to OpenProject.
It reads your work, updates your tasks, and follows your team’s rules.
curl -fsSL https://operator.daer.dev/install.sh | sh macOS or Linux, x64 or arm64. No Node, no sudo. It checks the SHA-256, then starts the wizard.
npx operator-mcp setupNode 22 or newer. Also the way in on Windows and Alpine.
Ask, and it answers from real work.
No more pasting tickets into the chat. Your agent looks the work up, logs the time, and moves the status itself, following the rules your team wrote down.
you what’s on my plate?
my_work
3 work packages assigned to you in Acme:
| ID | Type | Status | Subject |
|---|---|---|---|
| #1284 | Bug | In progress | Fix login redirect loop |
| #1301 | Task | New | Migrate invoice export |
| #1312 | Task | New | Review Q3 access audit |
you log 2h on #1284, I paired on the redirect fix
log_timeworkPackageId 1284 · hours 2 · comment “Paired on the redirect fix”
Logged 2 h on #1284 Fix login redirect loop for today, with your comment. It shows on the work package’s time entries.
you start on #1301
conventions
get_work_packageid 1301
update_work_packageid 1301 · status “In progress”
Your manual names branches {type}/{wp-id}-{slug}, so I made task/1301-migrate-invoice-export.
The branch exists now, so #1301 moved to In progress, as the manual asks.
A scripted conversation. Acme and its work packages are made up.
About 13 seconds of machine time. The rest is you, pasting a key.
Measured from curl to the wizard’s last card: 4.8 s on a local run, plus about 8 s to download the 49 MiB binary at 50 Mbit/s.
Install
One line downloads a single binary, checks its SHA-256 against the release, and puts it in ~/.local/bin. Run it again to upgrade; it says what it found.
Answer two questions
Where your OpenProject lives (a bare openproject.acme.com is fine) and your API key, checked against your instance before anything is saved. Then search for a default project among the ones you can write to.
Ask
Pick your agents with the arrow keys and each one is patched in. Restart yours and ask it what’s on your plate.
Write the house rules once. Every agent follows them.
Each workplace has a short markdown manual that the agent reads at the start of every session. Change the branch rule below and watch the answer change with it.
~/.config/operator-mcp/default/conventions.md
## Git / GitLab
``
- Commit subject: `[#1234] <imperative summary>`
- MR title must contain `#1234` so GitLab links back.
## Workflow
- Move to `In progress` only once a branch exists.
- Never close a work package yourself. QA closes it.
Fill-ins: {type} {wp-id} {slug} {user}. Or try one:
you start on #1284
conventions
Your manual says how to name it, so:
$ git switch -c bug/1284-fix-login-redirect-loop
#1284 moves to In progress now that the branch exists.
20 tools. Each does one job.
This list is what the server itself advertises over MCP, pulled with a real tools/list call when the page was built.
20 of 20 tools
Identity
whoamiIdentity and connection state.Identity and connection state. Call this first: it tells you which user you are acting as, which OpenProject instance you are on, the default project, whether writes are permitted, and whether this workplace has a conventions manual.
Discovery
list_projectsProjects visible to you.Projects visible to you. Use the `identifier` value wherever a tool asks for a project.
list_usersPeople you can assign work to.People you can assign work to. Pass a project to get exactly its assignable members; omit it to search everyone visible to you.
get_work_packageFull detail for one work package, including its description and the lockVersion an update needs.Full detail for one work package, including its description and the lockVersion an update needs.
search_work_packagesFind work packages by project, assignee, status, type, priority, text, or recency.Find work packages by project, assignee, status, type, priority, text, or recency. Descriptions are omitted; use get_work_package for one item's full detail.
my_workWork packages assigned to you, soonest due first.Work packages assigned to you, soonest due first. The usual starting point for a working session.
Tasks
create_work_packageCreate a work package in a project.Create a work package in a project. Type and subject are required; everything else is optional.
update_work_packageChange any subset of a work package's fields.Change any subset of a work package's fields. The optimistic lock is handled for you. Pass null for assignee or parent to clear it.
comment_work_packageAdd a markdown comment.Add a markdown comment. Everyone watching the work package gets a notification.
list_activitiesComments and field changes for one work package, oldest first.Comments and field changes for one work package, oldest first.
link_work_packagesCreate a relation between two work packages, such as one blocking another.Create a relation between two work packages, such as one blocking another.
Notifications
list_notificationsYour in-app notification feed, newest first.Your in-app notification feed, newest first. Each row carries the work package it concerns, so you can triage without a second lookup.
mark_notifications_readMark specific notifications read by id, or every notification matching a filter with all: true.Mark specific notifications read by id, or every notification matching a filter with all: true. Use this after triaging so the feed stays meaningful.
watch_work_packageControl whether you get notified about a work package's future activity.Control whether you get notified about a work package's future activity.
Time & metadata
list_metadataIds and names for work package types, statuses, priorities, or time entry activities.Ids and names for work package types, statuses, priorities, or time entry activities. Call this when a write is rejected for an unknown value, or when you need the exact wording this instance uses.
log_timeRecord time spent on a work package.Record time spent on a work package. Hours are given as a decimal number; 2.5 means two and a half.
Conventions
conventionsThe rules this workplace expects you to follow in OpenProject: branch and commit naming, workflow states, required fields.The rules this workplace expects you to follow in OpenProject: branch and commit naming, workflow states, required fields. These are also delivered in the server instructions; re-read them here whenever you are unsure.
add_conventionAppend a rule to the workplace manual so it survives this session.Append a rule to the workplace manual so it survives this session. Use it when the user states a working rule you would otherwise forget. Appending never removes anything.
remove_conventionDelete a rule from the workplace manual.Delete a rule from the workplace manual. Set confirm only when the user has explicitly asked you, in this conversation, to remove that rule — never on your own initiative. The file is backed up before any change.
Escape hatch
api_requestEscape hatch for anything the other tools do not cover.Escape hatch for anything the other tools do not cover. DELETE is refused unless the user set OPENPROJECT_ALLOW_DELETE=1. Returns the raw HAL+JSON response, unflattened. Prefer a dedicated tool when one exists — this one requires you to know APIv3 request shapes, and its responses are large.
No tool matches that. api_request reaches the rest of OpenProject’s API, and it still can’t delete unless you allow it.
Your agent can’t delete anything. By design.
For whoever has to approve the install: each promise below links to the code that keeps it. Read it before you say yes.
- Enforced in
There is no delete tool.
Count them: 20 tools, and not one deletes. An agent can’t remove a work package because it misread a vague instruction.
src/tools/ - Enforced in
The escape hatch refuses DELETE.
api_requestreaches the rest of the API, but it refusesDELETEunless you setOPENPROJECT_ALLOW_DELETE=1in the server’s environment. An agent can’t set that for itself.src/tools/raw.ts - Enforced in
Read-only means read-only.
With
OPENPROJECT_READ_ONLY=1, every create, update, comment, time entry and watch change is refused before a request leaves your machine.src/client.ts - Enforced in
It only talks to your instance.
Every request is built under your instance’s
/api/v3root, and any link that resolves outside it is refused.src/client.ts - Enforced in
Your key never travels in plain text.
A plain
http://address is refused unless the host is your own machine, or you setOPENPROJECT_ALLOW_INSECURE_HTTP=1yourself. An address without a scheme is taken ashttps://.src/config.ts - Enforced in
Credentials stay in one private file.
The key is written with
0600permissions under~/.config/operator-mcp, never into your agent’s config, so that file stays safe to commit.src/config.ts - Enforced in
The installer checks before it installs.
The binary is checked against the release’s
SHA-256sums. If they don’t match, nothing is installed.install/install.sh
Works with the agent you already use.
The wizard finds the agents on your machine, lets you pick them with the arrow keys, and patches each one in, backing up any file it touches.
Patched in by the wizard
- Claude Code
- Claude Desktop
- Codex CLI
- Gemini CLI
- Cursor
- opencode
Claude Desktop on macOS and Windows; there is no Linux build of it.
One config snippet away
- VS Code
- Windsurf
- Cline
- Zed
- anything that speaks MCP over stdio
Two workplaces at once.
Each workplace is a profile with its own key and its own manual. Each registers under its own server name, so both can be connected at the same time and your agent never mixes up one instance with the other.
$ npx operator-mcp setup --profile acme
$ npx operator-mcp setup --profile personalQuestions, answered.
Does my API key leave my machine?
Only to talk to your own OpenProject. It lives in ~/.config/operator-mcp/<profile>/config.json with 0600 permissions, and never appears in your agent’s MCP config.
Do I need Node.js?
Not for the curl install: it fetches a single binary for macOS or Linux, x64 or arm64. The npx route needs Node 22 or newer.
Does it work on Windows?
Yes, through npx operator-mcp setup with Node 22 or newer. The same goes for Alpine.
Can an agent report without changing anything?
Set OPENPROJECT_READ_ONLY=1 in the server’s entry in your MCP config. Reads work normally, and every write is refused before a request leaves your machine.
My agent isn’t on the list.
Any client that runs an MCP server over stdio works. Point it at npx with the arguments -y operator-mcp, or at the installed binary’s absolute path. The README has snippets for VS Code and the common JSON shape.
Can the whole team share one manual?
Set OPENPROJECT_CONVENTIONS_PATH to a file in your repository. The rules are then versioned, reviewed in merge requests, and the same for everyone.
How do I check it’s working, or update it?
operator-mcp doctor runs the same checks as the wizard. To update, run the install line again: it says what it found (an older binary, or a setup that ran through npx), the wizard offers to keep your connection, and each agent is moved onto the binary.
Does it speak Spanish?
The installer, setup, install, doctor and --help do, when your locale is Spanish or you pass --lang es. What your agent reads, tool descriptions and errors, stays in English.
What does it cost? Is it made by OpenProject?
It’s free and MIT-licensed. It is an independent project, not affiliated with OpenProject GmbH.
Patch your agent through.
Run the line, answer two questions, ask what’s on your plate.
curl -fsSL https://operator.daer.dev/install.sh | sh macOS or Linux, x64 or arm64. No Node, no sudo. It checks the SHA-256, then starts the wizard.
npx operator-mcp setupNode 22 or newer. Also the way in on Windows and Alpine.