OpenProject CE MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| OPENPROJECT_URL | No | Instance root URL, e.g. https://openproject.example.com. A trailing /api/v3 is tolerated and stripped. | |
| OPENPROJECT_API_KEY | No | API key from My account → Access tokens. Sent as HTTP Basic apikey:<token>. | |
| OPENPROJECT_MCP_OTEL | No | Reserved for OpenTelemetry tracing. Accepted but not yet wired to anything in this release. | false |
| OPENPROJECT_MCP_DISABLE | No | Comma-separated group tags to remove whole tool groups at startup. | |
| OPENPROJECT_OAUTH_TOKEN | No | OAuth bearer token, as an alternative to the API key. | |
| OPENPROJECT_MCP_INSECURE | No | Allow --transport http to start without auth tokens. Local development only. | false |
| OPENPROJECT_MCP_CACHE_TTL | No | TTL in seconds for the metadata cache (statuses, types, priorities, schemas). | 300 |
| OPENPROJECT_MCP_CA_BUNDLE | No | Path to a CA bundle (PEM) for instances behind a private CA. TLS is always verified. | system trust store |
| OPENPROJECT_MCP_HTTP_HOST | No | Bind address for --transport http. | 127.0.0.1 |
| OPENPROJECT_MCP_HTTP_PORT | No | Port for --transport http. | 8000 |
| OPENPROJECT_MCP_LOG_LEVEL | No | DEBUG, INFO, WARNING, ERROR or CRITICAL (case-insensitive). | INFO |
| OPENPROJECT_MCP_READ_ONLY | No | Serve read tools only: every write, destructive and admin tool is removed at startup. | false |
| OPENPROJECT_MCP_LOG_BODIES | No | Log request/response bodies — only at DEBUG level, with credentials redacted. Development use only. | false |
| OPENPROJECT_MCP_LOG_FORMAT | No | text or json. Logs always go to stderr. | text |
| OPENPROJECT_MCP_ADMIN_TOOLS | No | Expose the three admin-gated membership write tools (hidden by default). | false |
| OPENPROJECT_MCP_AUTH_TOKENS | No | Comma-separated bearer tokens accepted by --transport http. Every request must carry Authorization: Bearer <token>. | |
| OPENPROJECT_MCP_MAX_RETRIES | No | Retry budget for idempotent requests. | 3 |
| OPENPROJECT_MCP_DOWNLOAD_DIR | No | Directory where download_attachment writes files (created if missing; default is relative to the server's working directory). | ./openproject-downloads |
| OPENPROJECT_MCP_POOL_TIMEOUT | No | Seconds to wait for a free connection from the pool. | 5 |
| OPENPROJECT_MCP_READ_TIMEOUT | No | Seconds to wait for response data. | 30 |
| OPENPROJECT_MCP_WRITE_TIMEOUT | No | Seconds to wait while sending request data (uploads). | 60 |
| OPENPROJECT_MCP_ACCEPT_LANGUAGE | No | Sent as the Accept-Language header; OpenProject localizes validation messages accordingly. | |
| OPENPROJECT_MCP_CONNECT_TIMEOUT | No | Seconds to wait for a TCP/TLS connection to OpenProject. | 10 |
| OPENPROJECT_MCP_MAX_CONNECTIONS | No | Connection pool size toward OpenProject. | 10 |
| OPENPROJECT_MCP_MAX_DOWNLOAD_MB | No | Size cap for attachment downloads, in MiB. | 100 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| extensions | {
"io.modelcontextprotocol/ui": {}
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| search_work_packagesA | Find work packages by text when you do not know their ids. Use this first whenever a user names a ticket instead of numbering it, then feed the
returned Returns the standard list envelope: compact rows (id, subject, type, status, priority,
assignee, project, dates, progress) plus Pitfalls: search filters, it does not rank, so a broad query returns a lot — narrow it
with For structured filtering use |
| list_work_packagesA | List work packages with structured filters — the workhorse read tool. Handles "assigned to me", "overdue" via parameters, not separate
tools: overdue → Returns the standard list envelope: rows plus Pitfalls: returns open work packages only unless For text lookups use |
| get_work_packageA | Read one work package in full: description, dates, custom fields, parent and progress. This is the tool to call once a search or list has given you an id, and the only way to
read a work package's description text. The Returns every core field, Pitfalls: includes are capped at 20 — a truncated For the comment thread use |
| create_work_packageA | Create a work package, validated through OpenProject's own form endpoint first. Use it for new tasks, bugs, subtasks ( Returns the created work package in full detail, including its new Pitfalls: To change it afterwards use |
| update_work_packageA | Change any writable field of a work package, with optimistic locking done properly. Use it to assign or unassign, move a status forward, re-schedule, re-parent, set progress, or write custom fields — one tool instead of many. Returns the updated work package in full detail, including the new Pitfalls: omitted parameters are left alone, while passing null clears a field
(assignee, responsible, version, sprint, parent, dates, description). A 409 error means
somebody else changed the work package first — the error carries the fresh Ids come from |
| delete_work_packageA | Permanently delete a work package and everything attached to it. Use only on explicit user instruction. Deletion removes the work package with its comments, attachments, time entries and relations, and OpenProject offers no API-side undo. Returns a small confirmation object once OpenProject accepts the deletion. Pitfalls: children are not deleted with the parent, so check
|
| bulk_update_work_packagesA | Preview or apply up to 50 work-package updates, each with its own changes. First call with dry_run=true (default). Review each before/after diff and validation error, then submit the same updates with dry_run=false and each preview's lock_version. Omitted fields remain untouched; null clears clearable fields; target_versions=[] clears version assignments. Status/type/priority accept names or ids, as in update_work_package. All items are validated before any update. If any preflight fails, nothing is applied; correct those items or submit a valid subset. Execution is sequential and NOT atomic: concurrent edits can still conflict after validation. Each item reports its result; partial_failure is true when some apply and others fail. Item notes flag values OpenProject will ignore or ignored, such as story_points without Backlogs. An unknown outcome means a connection/server failure may have happened after committing: re-read that item before retrying. Writes are never automatically retried or rolled back. Never rerun successful items. Notifications use the same notify flag for the whole batch. |
| list_work_package_commentsA | Read the comment thread and change history of a work package. Use this whenever the question is "what did people say about this
ticket" or "what changed on it": it returns the full activity journal —
comment entries (author, markdown text, internal flag, timestamps) and
field-change entries whose details are parsed into
Returns the standard list envelope: Pitfalls. OpenProject's activities endpoint is unpaginated — this
tool fetches the entire journal on every call and pages it here, so
Cross-references: post a comment with |
| add_work_package_commentA | Post a comment on a work package. Use this to reply in a ticket's thread, record a decision, or leave a
handover note. Returns the created journal entry (activity id, author,
markdown text, internal flag, timestamps) — the same shape
Pitfalls. Every call creates a new comment; it is not idempotent, so do
not retry blindly after a timeout — read the thread first.
Cross-references: read the thread with |
| edit_work_package_commentA | Rewrite the text of an existing work-package comment. Use this to fix a typo, correct a wrong statement, or extend a note you
just posted. Returns the updated journal entry (activity id, author,
markdown text, internal flag, timestamps) in the same shape
Pitfalls. Only comment entries are editable: the journal also holds field-change entries ("Status changed from New to In progress"), which OpenProject records automatically and refuses to alter — this tool rejects those locally, before any write. Editing needs the edit-work-package-comments permission (or edit-own for your own comments); a 403 means the account may read the thread but not rewrite it. The edit replaces the text entirely and OpenProject keeps no API-visible history of the previous version, so do not use it to "undo" — post a correcting comment when the record matters. Editing does not notify anyone. Cross-references: read the thread and get activity ids with
|
| add_work_package_watcherA | Subscribe a user to a work package's notifications. Use this when someone should be kept in the loop on a ticket without being assigned to it. Watchers receive OpenProject's notifications for comments and changes. Returns the watcher (user id and name), the watch state after the call, and whether this call actually changed anything. Pitfalls. The user must already be able to see the work package;
OpenProject answers 422 with a violation on Cross-references: |
| remove_work_package_watcherA | Unsubscribe a user from a work package's notifications. Use this to stop notifying someone who no longer needs the updates. Returns the user, the watch state after the call (always not watching) and a confirmation message. Pitfalls. OpenProject answers the same 204 whether or not the user was
watching, so Cross-references: |
| create_work_package_relationA | Link two work packages (blocks, follows, duplicates, relates, ...). Use this to record a dependency the schedule or the reader needs to know about: "ship the client layer follows design sign-off", "this duplicates #4321". Returns the created relation with its id, type, reverse_type, both work packages, lag and description. Pitfalls. OpenProject stores one canonical direction per pair, so the
passive spellings are rewritten on save: creating Cross-references: |
| update_work_package_relationA | Change an existing relation's type, lag or description. Use it to widen the gap between a predecessor and its successor, to correct a link that was created with the wrong type, or to explain why two work packages are connected. Returns the updated relation. Pitfalls. At least one of type, lag or description must be given.
Relations carry no lock version, so this is a plain overwrite with no
conflict detection — a concurrent edit is silently replaced; re-read the
relation if that matters. The two work packages cannot be changed here:
delete the relation and create a new one instead. Raising the lag on a
Cross-references: |
| delete_work_package_relationA | Remove the link between two work packages. Use it when a dependency no longer holds. Neither work package is touched, only the relation between them. Returns a small confirmation object. Pitfalls. Deleting a Cross-references: |
| toggle_comment_reactionA | React to a work-package comment with an emoji, or take your reaction back. Use this for the lightweight acknowledgement a comment does not deserve:
👍 on a decision, 👀 to say you are looking at it, 🎉 when something
shipped. Returns the comment's full reaction state afterwards — every
emoji on it with the people who picked it — plus Pitfalls. This is a toggle, not an add: calling it twice with the
same reaction leaves the comment exactly as it started, so it is not safe
to retry blindly after a timeout — read Cross-references: |
| set_work_package_reminderA | Set, change or clear your personal reminder on a work package. Use this when something should resurface later: "remind me about this on
Monday", "ping me an hour before the release". Reminders are private —
only you see yours, and only you are notified. The tool upserts: it looks
for your active reminder on the work package and creates one if there is
none, updates it if there is. Returns Passing Pitfalls. OpenProject allows exactly one active reminder per work package
per person, so a second "create" becomes an update of the first — there
is no way to stack two. Cross-references: |
| list_remindersA | List your own upcoming work-package reminders. Use this to answer "what have I asked to be reminded about", to check
whether a reminder is already set before creating another one, or to find
the work packages you deferred. Returns the standard list envelope:
Pitfalls. Reminders are personal — this only ever shows the ones the
authenticated account created, never a colleague's, and there is no way
to list someone else's. It only shows reminders that are still
upcoming: once one has fired (or was completed) OpenProject drops it
from this collection, so an empty result does not mean nothing was ever
scheduled. The work package each reminder points at is in
Cross-references: |
| execute_custom_actionA | Run an instance-defined one-click action on a work package. Custom actions are shortcuts an administrator configured — "Accept and
assign to me", "Reject", "Move to review" — that apply several field
changes at once, sometimes under conditions (role, status, project).
Use one when Pitfalls. The action decides what changes; this tool cannot influence it,
and OpenProject does not report which fields it touched — compare the
returned row with what you read before, or call Cross-references: |
| list_attachmentsA | List the files attached to one container. Containers are work packages, wiki pages, meetings, documents, budgets and comments. Use this to discover attachment ids before calling download_attachment, or to check what a work package already carries. The upstream collection is not paginated, so it is fetched in full: the envelope always reports has_more=false and a total equal to the row count. Returns the standard list envelope; each row has id, file_name, size_bytes, content_type, description, author, created_at and status. status is the virus-scan state — 'uploaded' and 'scanned' are downloadable, 'quarantined' files are not, and anything else is still being scanned and is readable only by its uploader. Pitfalls: container_id identifies the container, not the file. A 404 means the container does not exist or the module providing it (meetings, budgets, documents) is not enabled on this instance. Forum posts are a valid API container but have no discovery path here. Related: download_attachment fetches the bytes for one row, upload_attachment adds a file to the same containers, and get_work_package(include=['attachments']) returns these rows inline for a single work package. |
| download_attachmentA | Download an attachment's bytes to a file on the machine running this server. Use it once list_attachments (or get_work_package(include=['attachments'])) has given you an attachment_id. Metadata is read first, then the bytes are streamed to disk in chunks with progress notifications, so a large file neither stalls the call nor buffers in memory. Returns path, file_name, size_bytes, content_type and the SHA-256 of the bytes (use it to verify or de-duplicate). With return_image=true an image of at most 1 MB comes back as an inline image block as well. Pitfalls: the file is written on the server's machine, which is the user's machine only in a local (stdio) deployment — tell the user the returned path rather than assuming they can see it. Quarantined attachments fail with attachment_quarantined and are never fetched. An attachment whose virus scan is unfinished answers 401 for everyone except its uploader. Transfers above OPENPROJECT_MCP_MAX_DOWNLOAD_MB (default 100) are refused up front and aborted mid-stream, leaving no partial file. A name collision in the target directory saves as 'name (2).ext' and says so in notes. Related: list_attachments produces attachment_id; upload_attachment is the reverse direction. |
| upload_attachmentA | Attach a local file to a work package, wiki page, meeting, document, budget or comment. Use it when a file that already exists on the server's machine should be added to an existing container. The file's existence and its size against this instance's maximumAttachmentFileSize are checked locally first, so an oversized file fails instantly instead of after the transfer. Returns the created attachment row (id, file_name, size_bytes, content_type, description, author, created_at, status) — the id feeds download_attachment. Pitfalls: uploading to a container needs edit permission on that container, so to give a brand-new work package its files use create_work_package(attachment_paths=[...]) instead, which uploads the files unattached and claims them on create. Instances may restrict extensions; a rejected type comes back as validation_failed with the allowlist hint and nothing is stored. The stored name comes from file_name (or the path's basename), never from the multipart part. Related: list_attachments shows what a container already holds; download_attachment is the reverse direction. |
| delete_attachmentA | Permanently delete one attached file from OpenProject. Use it only on explicit user instruction, for example to remove a file uploaded to the wrong work package or a superseded document. The attachment's metadata is read first so the result names the file and the container it was attached to, and so an unknown id fails before anything is removed. Returns the attachment id, the file name, its container and a confirmation message. Pitfalls: this deletes the file itself, not a link to it — every work package, wiki page or comment that embedded it loses the image or download. Deleting needs edit permission on the container (or authorship for a file that has no container yet), so a 403 can follow a successful read. A 404 means the id is unknown or already deleted; a second call on the same id answers 404 rather than succeeding. Removing a file does not remove the comment or work package that referenced it. Related: list_attachments shows the ids and file names of everything a container holds; upload_attachment adds a replacement; download_attachment saves a copy first if the bytes are still wanted. |
| list_file_linksA | List the external-storage files (Nextcloud, OneDrive/SharePoint) linked to a work package. File links are OpenProject's other kind of file: instead of living inside OpenProject like an attachment, the document stays in a connected storage and the work package points at it. Use this to answer "which documents belong to this ticket" — and pair it with list_attachments, because the two lists are disjoint and neither implies the other. Returns the standard list envelope, fetched in full (has_more is always false). Each row carries file_name, the storage it lives on, the file's origin_id inside that storage, mime_type, the creator and — the useful part — open_url and download_url. Those are absolute OpenProject URLs that redirect to the storage once OpenProject has resolved the link, so hand them to the user: they need the user's own OpenProject login, this server cannot fetch the bytes, and download_attachment does not work on them. Pitfalls: this needs the storages module and a storage connected to the project. When it is missing (404) or this account may not read the links (403) the call still succeeds with an EMPTY list and a note explaining which — read notes before saying a ticket has no documents. An empty list is never proof either: an account lacking the 'view file links' permission gets an empty 200 rather than a 403, which is exactly what that note says. permission carries the storage's own wording — 'View allowed' means the URLs will work, 'View not allowed', 'Not found' and 'Error' mean they will not, and null means the storage said nothing. Creating and deleting file links, and browsing the remote storage, are out of scope for this server — do them in the OpenProject UI. Related: list_attachments covers files stored inside OpenProject, download_attachment fetches those bytes, and get_work_package gives the ticket the links belong to. |
| list_projectsA | List projects, filtered server-side, one page at a time. Use this to turn a project name into the id or identifier that every other tool
consumes, to enumerate the sub-projects of a parent, or to review which projects are
off track. It is the id-producing path for every Returns the standard list envelope: Pitfalls: For a single project's description and status explanation use |
| get_projectA | Read one project in full. Use it after Returns Pitfalls: For the ids valid inside this project (types, versions, categories, time-entry
activities) call |
| create_projectA | Create a project, validated through OpenProject's own form endpoint first. Use it for a new workspace or, with Returns the created project: Pitfalls: creating projects usually requires the 'create project' permission or
admin rights, so a 403 here is about the account, not the payload. The new project
starts with the instance's default modules and types enabled — check
Cross-references: |
| update_projectA | Change a project's name, description, visibility, parent, status or archived state. Use it to record a status change with its explanation ("at_risk because the vendor
slipped"), to rename or re-parent a project, to publish it, or to archive it with
Only the parameters you pass are sent — omitted fields are never rewritten, so two
agents editing different fields do not clobber each other. Projects carry no
Returns the updated project in the same shape as Pitfalls: Cross-references: |
| delete_projectA | Schedule the permanent deletion of a project and everything inside it. Use only on an explicit, specific instruction. Deletion CASCADES: every subproject,
work package, comment, attachment, time entry, version, wiki page and membership of
this project goes with it, and OpenProject offers no API-side undo. If the goal is
only to get the project out of the way, Deletion is ASYNCHRONOUS upstream: OpenProject accepts the request and runs it as a
background job. This tool therefore returns Pitfalls: deleting normally requires admin rights (403 otherwise). A 404 means the
id or identifier is wrong, or the project was already deleted. Because the work runs
in the background, a later failure inside the job is not visible here; confirm with
Cross-references: |
| copy_projectA | Copy a project — its settings, and optionally its work packages — into a new one. Use it to spin a new engagement or release off a template project, which is the only
way to reproduce a project's members, versions, categories and enabled modules in one
call. The request goes through Copying is ASYNCHRONOUS: OpenProject queues a job and answers immediately. This tool
therefore returns Pitfalls: only Cross-references: |
| get_job_statusA | Check whether a background job (a project copy, a scheduled deletion) has finished. OpenProject runs copies, deletions and exports asynchronously and hands back a job id.
This is the only way to learn what happened to one: call it after Returns Pitfalls: a 200 does not mean the job worked — read Cross-references: |
| set_project_favoriteA | Add or remove a project from the authenticated user's favorites (OpenProject 17+). Favorites are per user, not per project: this changes what the account behind OPENPROJECT_API_KEY sees starred on its own overview page, and nothing about the project itself or about anybody else's view. Use it when the user asks to pin, star or favorite a project they work in. Returns Pitfalls: this endpoint only exists from OpenProject 17. When the instance reports a version older than that the call is REFUSED before anything is sent, with the detected version in the message, because there is no downgrade that would achieve the same thing — favorite the project in the web UI instead. When it reports no version at all the request IS sent, since an unreported version says nothing about the endpoint. A 404 is ambiguous on purpose in the hint: it means either the project does not exist for this account or the endpoint is missing. This is not project 'status' and not a work-package watcher. Cross-references: |
| list_project_phase_definitionsA | List the instance-global phase definitions — the vocabulary of the project life cycle. Use it to learn which phases (Initiating, Planning, …) and gates this instance
defines, and to get the definition id or name that Returns the standard list envelope of Pitfalls: definitions are the instance-wide catalog, not any project's actual
phases — a project may deactivate phases or set no dates. Phase dates are not
exposed by the API at all. Requires OpenProject 16.1+ and the
Cross-references: |
| get_project_phaseA | Read one project's phase record: name, active flag and its definition. Use it after Returns Pitfalls: the API has no phases index — ids only come from work packages'
Cross-references: |
| list_queriesA | List the saved work-package views (queries) this user can open. Use it to discover what a team already tracks — "Sprint board", "My open bugs",
"Overdue in Platform" — before hand-building filters: running someone's saved view
with Returns the standard list envelope: rows of Pitfalls. Query ids are instance-wide, not per project — never guess one, take it from here. This lists definitions only; it never runs them, so nothing here says how many work packages a query returns. Cross-references: run one with |
| run_queryA | Run a saved view and get its work packages — the fastest way to answer with a team's own definition of "the sprint" or "our bugs". OpenProject queries run on read: this returns the rows as they are right now, in the
stored order and grouping. The result is the standard list envelope — Pitfalls. Cross-references: find query ids with |
| save_queryA | Save a filter set as a reusable OpenProject view. Use it to keep a filter combination — "Overdue in Platform", "My open bugs" — instead
of rebuilding it each session: the view shows up in the OpenProject UI too, and
Validated via Pitfalls. If OpenProject keeps fewer filters than were sent, Cross-references: |
| list_notificationsA | Read the authenticated user's OpenProject inbox. Use this to answer "what needs my attention?", "was I mentioned anywhere?" or "what changed on the things I watch?" — it is the only tool that sees notifications, and it always reports the inbox of the token owner, never another user's. Returns the standard list envelope: Pitfalls. Several changes to the same work package are aggregated into
one notification, so the count is not a count of events. Reading a
notification here does not mark it read — that is
Cross-references: mark specific rows read with |
| mark_notificationsA | Mark specific notifications read (or unread) in one bulk request. Use it after you have actually handled what a notification was about, so
the user's inbox reflects reality. It is the id-consuming counterpart of
Returns Pitfalls. Marking is idempotent, so a retry after a timeout is safe. Pass
the notification id, not Cross-references: get the ids from |
| mark_all_notifications_readA | Mark everything matching the filters as read — the whole inbox by default. Use it for "clear my notifications" or "I have dealt with everything in
project X". Called with no arguments it marks every unread notification
of the token owner as read, across all projects; Returns This tool only ever marks read. There is deliberately no
"mark everything unread" twin: that is a mistake with no upside, and the
reverse direction stays available per-id through
Cross-references: preview or page the inbox with |
| list_time_entriesA | List logged time, filtered server-side, with an optional accurate total. Answers "how much time went into this ticket?" or "how much did the team spend on
project X in June?". Filters combine with AND, so Returns the standard list envelope: Pitfalls. Without view-all-time-entries you see only your own entries, so a small
total may mean "not allowed to see" rather than "nobody booked time". Cross-references: book time with |
| log_timeA | Book time against a work package or a project. Use it when the user says "log 2 hours on #1234" or "book half a day to project X". The call is validated through OpenProject's own form endpoint first, so an activity this project does not allow, a missing permission or a closed cost-reporting period comes back as a typed error listing what would be accepted — nothing half-written is left behind. Returns the created entry: Pitfalls. This is not idempotent — calling it twice books the time
twice, so never blind-retry after a timeout; list the day's entries
first. The time is always booked for the token owner; you cannot log
time on someone else's behalf through this tool. Logging time does not
change the work package's status, estimate or progress — those are
separate fields, and on instances that derive progress from work the
percentage is read-only anyway. The work package's Cross-references: the activities and ids this project accepts come from
|
| update_time_entryA | Correct an existing time entry. Use it for the everyday fixes: wrong duration, wrong day, wrong activity, a comment that needs to say what actually happened. Only the parameters you pass are written; everything else is left exactly as it is. Returns the updated entry in the same shape Pitfalls. What can be moved between entries is limited: the work package
and the project a time entry belongs to are not editable here —
delete the entry and log it again where it belongs. Some instances
report a Cross-references: find the id with |
| delete_time_entryA | Permanently delete a logged time entry. Use only on explicit user instruction, and only for genuinely wrong entries. Deletion removes the booked hours from every cost report and from the work package's aggregated spent time, with no API-side undo. Returns a small confirmation object once OpenProject accepts the deletion. Pitfalls. If the entry is merely on the wrong day, has the wrong
duration or the wrong activity, Cross-references: find the id with |
| list_versionsA | List versions (releases, milestones, sprints) you can assign work packages to. Use it to turn "Sprint 12" or "release 2.1" into the version id that
Returns the standard list envelope: Pitfalls: Cross-references: |
| create_versionA | Create a version (release, milestone or sprint) inside a project. Use it to open a new sprint or plan a release before assigning work packages to it.
The call goes through Returns the created version Pitfalls: Cross-references: |
| update_versionA | Change a version's name, dates, description, status or sharing. Use it to move a sprint's dates, to close a finished release ( Only the parameters you pass are sent, so concurrent edits to other fields survive.
Versions carry no Returns the updated version in the same shape as Pitfalls: Cross-references: |
| delete_versionA | Permanently delete a version. Use it only for a version created by mistake. For a finished release or sprint,
Returns a small confirmation once OpenProject accepts the deletion. Work packages are NOT deleted — they simply lose their version — but that only happens on instances that allow the deletion at all. Pitfalls: OpenProject refuses (422) to delete a version that work packages still reference; the error hint explains how to find them. Deleting a shared version affects every project that used it. The 'manage versions' permission is required, so a 403 is about the account. Cross-references: |
| list_sprintsA | List sprints (Scrum timeboxes) you can plan work packages into. Use it to turn "Sprint 11" into the sprint id that
Returns the standard list envelope: Pitfalls: sprints are not versions (17.3+): Cross-references: |
| get_sprintA | Read one sprint in full: dates, workspace and lifecycle status. Use it once Pitfalls: sprint ids and version ids are different id spaces — a
version id from Cross-references: |
| search_principalsA | Find users, groups and placeholder users, and get their ids. This is the id-producing tool for every principal parameter in this
server: Use it to answer "who is Grace Hopper's account", "which groups exist",
"who is a member of the demo project". Returns the standard list
envelope: Pitfalls. Cross-references: |
| get_userA | Read one user's profile: name, login, email, admin flag and status. Use it after Returns Pitfalls. Cross-references: |
| list_membershipsA | List who has access to which project, and with which roles. Use it before granting or revoking access ("does she already have a role here?"), to audit a project's member list, or to see which projects a principal can reach. Called with no arguments it pages through every membership the authenticated account may see, which on a large instance is a lot — filter. Returns the standard list envelope: Pitfalls. The Cross-references: |
| list_rolesA | List the roles this instance defines, with their ids. This is the id-producing tool for Returns the standard list envelope with Pitfalls. Role names are configurable per instance, so do not assume
'Member' exists — read the list. Some roles are not assignable to a
project membership (global and work-package roles live in the same
collection); the membership form rejects those with the assignable set
listed. Not every OpenProject version exposes permission arrays on this
endpoint: when Cross-references: |
| get_instance_infoA | Check the OpenProject connection and report what this instance supports. Call this first when anything fails in an unexplained way, when the user asks "am I connected / who am I", or before using a version-gated parameter. It is the server's connection test: it authenticates on every call rather than answering from cache. Returns the core version and instance name, the attachment size ceiling
( Pitfalls: For per-project ids (types, statuses, priorities, versions, categories, activities)
use |
| get_project_metadataA | List the ids and names that are actually valid on this instance. This is the one-call answer to "what do I pass for type / status / priority / version / category / activity". Call it before any create or update, before filtering by ids, and whenever a write fails with an allowed-values error. Nothing here is hardcoded — priority ids and activity ids differ per instance. Without Pitfalls: results are cached (default 300 s) — pass For the writable fields and custom fields of one project+type combination use
|
| get_work_package_schemaA | Show which fields a work package of this type accepts in this project. Call it before Returns Pitfalls: Ids for both parameters come from |
| list_permissionsA | List what the authenticated user is allowed to do, globally or in one project. Use it before attempting a write that might 403, to explain to a user why an action failed, or to pick between tools ("can I add a member here, or should I ask an admin?"). It reads the real capabilities API for the current user — it does not return a user profile and it never guesses from the admin flag. Returns the standard list envelope whose CAVEAT, straight from the API: OpenProject exposes only a SUBSET of its permissions as capabilities. An action missing from this list is not proof that the user lacks the permission — it may simply not be modelled. Treat a hit as reliable and a miss as "unknown, try it and read the 403". Pitfalls: the capabilities API has no Cross-references: |
| enable_tool_groupA | Re-enable a tool group hidden by OPENPROJECT_MCP_PROFILE=core, for this session. Use it when the user needs meetings, wiki, news, documents, budgets, git activity or reporting and those tools are not in your list. It never calls OpenProject. Returns Pitfalls: it only lifts the core profile. It cannot override read-only mode (write
tools stay hidden, see Cross-references: |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| triage_inbox | Group the current user's unread OpenProject notifications by reason and suggest what to do with each group. Reads the unread inbox server-side and renders one section per reason (mentioned, assigned, watched, …) with the work packages behind it, so triage is a single pass instead of one notification at a time. |
| groom_backlog | Sweep a project's open backlog for the three things that rot it: work with no assignee, work with no estimate, and work nobody has touched in weeks. The open set is read oldest-changed first and classified here, because "has no estimate" is not expressible as an OpenProject filter. Counts always come from the server, so a capped scan understates the lists but never the backlog. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 59 tools
Most tools target a distinct resource+action, and descriptions explicitly cross-reference alternatives (search_work_packages vs list_work_packages vs run_query; list_versions vs list_sprints; get_project_metadata vs get_work_package_schema). A few pairs remain easy to confuse at a glance—sprints versus versions, saved queries versus ad-hoc filters, and project phase definitions versus project phases—but the descriptions resolve them.
All 59 tools use snake_case verb_noun names (get_work_package, update_project, list_time_entries, delete_attachment, mark_notifications), with predictable list/get/create/update/delete patterns across domains. Minor outliers like run_query, save_query, log_time and toggle_comment_reaction still follow the verb_noun convention.
At 59 exposed tools this is a very heavy surface for an agent to navigate, spanning work packages, comments, watchers, relations, reminders, reactions, attachments, file links, projects, phases, queries, notifications, time entries, versions, sprints, principals, roles, memberships and metadata. The breadth is justified by OpenProject's domain, but 59 tools exceeds a comfortable selection budget and risks misselection.
Core lifecycle coverage is strong: work packages, projects, time entries, versions and comments each have full CRUD, with supporting metadata, schema, permission, job-status and notification tools. Gaps such as no edit/delete for saved queries, read-only file links and sprints, and admin-gated membership writes are explicitly documented but still leave a few dead ends.