Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
OPENPROJECT_TIMEOUTNoRequest timeout in seconds12
OPENPROJECT_BASE_URLYesBase URL of your OpenProject instance, e.g. https://op.example.com
OPENPROJECT_API_TOKENYesPersonal API token
OPENPROJECT_LOG_LEVELNoCRITICAL, ERROR, WARNING, or INFOWARNING
OPENPROJECT_TEXT_LIMITNoCharacter cap for description previews in list results (max 50000)500
OPENPROJECT_VERIFY_SSLNoVerify TLS certificatestrue
OPENPROJECT_MAX_RESULTSNoHard cap on total results returned by a tool100
OPENPROJECT_MAX_PAGE_SIZENoHard cap on results per request50
OPENPROJECT_READ_PROJECTSNoReadable projects; comma-separated identifiers, names, or glob patterns; * allows all visible projects; empty denies all project-scoped reads
OPENPROJECT_WRITE_PROJECTSNoWritable projects; same syntax; empty disables all project-scoped writes; always intersected with OPENPROJECT_READ_PROJECTS
OPENPROJECT_ATTACHMENT_ROOTNoAbsolute directory local attachment uploads are confined to; empty disables uploads
OPENPROJECT_DEFAULT_PAGE_SIZENoDefault results per page10
OPENPROJECT_ENABLE_ADMIN_READNoInstance-wide users, groups, principals and storage connections (admin)false
OPENPROJECT_ENABLE_BOARD_READNoBoardstrue
OPENPROJECT_ENABLE_ADMIN_WRITENoUser, group and storage connection management (admin); explicit opt-in onlyfalse
OPENPROJECT_ENABLE_BOARD_WRITENoBoard create/update/delete. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_HIDE_CUSTOM_FIELDSNoCustom field names or keys to omit; * wildcards supported
OPENPROJECT_ENABLE_MEETING_READNoMeetings, agenda items, sections, outcomes, recurring meetingstrue
OPENPROJECT_ENABLE_PROJECT_READNoProjects, documents, phases, project storagestrue
OPENPROJECT_ENABLE_VERSION_READNoVersionstrue
OPENPROJECT_ENABLE_EXTENDED_READNoRarely used metadata tools (query schemas, render_text, help texts, working days)false
OPENPROJECT_ENABLE_MEETING_WRITENoMeeting, agenda item, section, outcome and recurring meeting create/update/delete. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_ENABLE_PERSONAL_READNoYour own preferences and notificationsfalse
OPENPROJECT_ENABLE_PROJECT_WRITENoProject create/update/delete, news, documents, grids. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_ENABLE_VERSION_WRITENoVersion create/update/delete. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_ENABLE_PERSONAL_WRITENoUpdate your own preferences, mark notifications read; requires OPENPROJECT_ENABLE_PERSONAL_READfalse
OPENPROJECT_ENABLE_MEMBERSHIP_READNoMemberships, roles, current user, actions, capabilitiestrue
OPENPROJECT_ENABLE_MEMBERSHIP_WRITENoProject membership create/update/delete. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_ENABLE_WORK_PACKAGE_READNoWork packages, search, relations, attachments, watchers, sprintstrue
OPENPROJECT_ENABLE_USER_SCHEDULE_READNoPer-user non-working times and working hours (OpenProject 17.3+)false
OPENPROJECT_ENABLE_WORK_PACKAGE_WRITENoWork package create/update/delete, comments, relations, attachments, time entries. Has no effect without a non-empty OPENPROJECT_WRITE_PROJECTS.true
OPENPROJECT_ENABLE_USER_SCHEDULE_WRITENoCreate/update/delete user non-working times and working hours; requires OPENPROJECT_ENABLE_USER_SCHEDULE_READfalse

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

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
list_projectsA

List visible projects with optional name or identifier search.

Under a restrictive OPENPROJECT_READ_PROJECTS, total reflects only the allowed projects already scanned to fill this page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_projectA

Get a project by id, identifier or name, including its ancestor chain.

description/status_explanation are returned in full by default (single projects are not truncated). Pass text_limit to cap them at that many characters; when cut, description_truncated/status_explanation_truncated are true and description_length/status_explanation_length report the real length.

get_project_admin_contextC

Return project admin metadata such as lifecycle statuses, parent options, and writable fields.

get_project_configurationA

Return a project's configuration: get_instance_configuration's fields plus enabled_internal_comments.

list_sprintsA

List Backlogs sprints, optionally filtered by name search.

Omit project to list every sprint visible to the current token across all projects; pass it to list only sprints for that project.

Requires the OpenProject Backlogs module and OpenProject 17.3 or newer; unavailable instances return a clear not-found message.

total is only the count of allowed sprints returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_sprintB

Get a Backlogs sprint by id. Requires OpenProject 17.3 or newer.

list_backlog_bucketsA

List Backlogs backlog buckets, optionally filtered by name search.

Omit project to list every backlog bucket visible to the current token across all projects; pass it to list only backlog buckets for that project.

Requires the OpenProject Backlogs module and OpenProject 17.6 or newer; unavailable instances return a clear not-found message.

total is only the count of allowed backlog buckets returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_backlog_bucketB

Get a Backlogs backlog bucket by id. Requires OpenProject 17.6 or newer.

list_documentsA

List documents, optionally filtered to a single project or by title search.

total is only the count of allowed documents returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

text_limit caps each document's description at that many characters (default: the server's configured OPENPROJECT_TEXT_LIMIT, NOT unlimited -- unlike get_document's single-item default). When text is cut, description_truncated is true and description_length reports the real length.

get_documentA

Get a single document by id.

The description is returned in full by default (single documents are not truncated). Pass text_limit to cap it at that many characters; when the text is cut, description_truncated is true and description_length reports the real length.

list_project_storagesA

List a project's links to configured external file storages, optionally filtered to one project.

Read-only in OpenProject's API -- no create/update/delete endpoint exists for this resource; manage the underlying connection with the storages tools instead.

get_project_storageB

Get a single project's link to a configured external file storage by id.

list_newsA

List news entries, optionally filtered by project or title/summary search.

total is only the count of allowed news entries returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

text_limit caps each entry's summary/description at that many characters (default: the server's configured OPENPROJECT_TEXT_LIMIT, NOT unlimited -- unlike get_news's single-item default). When text is cut, description_truncated is true and description_length reports the real length.

get_newsA

Get a single news entry by id.

The description is returned in full by default (single news entries are not truncated). Pass text_limit to cap it at that many characters; when the text is cut, description_truncated is true and description_length reports the real length.

get_wiki_pageA

Get a single wiki page's metadata (id, title, project) by id.

OpenProject's REST API v3 does not expose a wiki page's body text at all -- GET /api/v3/wiki_pages/{id} returns only id/title/project, and there is no other route that returns the page content. This tool therefore cannot return wiki page text; use the OpenProject web UI to read a page's content.

get_postA

Get a single forum post by id.

OpenProject's API exposes exactly one route for posts: GET /api/v3/posts/{id}. There is no collection/list endpoint for posts or forums at all in OpenProject's REST API -- a post's id must therefore come from elsewhere (e.g. a work package's activity/journal referencing a forum post, or a link copied from the OpenProject web UI). This MCP does not and cannot provide a list_posts or list_forums tool.

list_viewsA

List saved OpenProject views, optionally filtered by project, view subtype, or name search.

total is only the count of allowed views returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_viewC

Get a single OpenProject view by id.

list_gridsA

List dashboard grids, optionally filtered by scope (page path).

total is only the count of allowed grids returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_gridC

Get a single dashboard grid by id.

list_categoriesA

List work-package categories configured for a project.

get_categoryA

Get a single category by id.

project is optional: when given, it's cross-checked against the category's real project and a mismatch raises a not-found error, rather than being the sole source of authorization.

list_project_phase_definitionsA

List available project lifecycle phase definitions exposed by OpenProject.

get_project_phase_definitionB

Get a single project lifecycle phase definition by id.

get_project_phaseC

Get a single project lifecycle phase by id.

get_my_project_accessB

Return the current user's membership and inferred access hints for a project.

get_project_work_package_contextB

Return project metadata and, optionally, the writable work-package schema for a given type.

get_instance_configurationA

Return instance-level OpenProject configuration and active feature flags.

get_job_statusB

Get the current status of a background job such as project copy.

list_work_packagesA

List work packages with structured filters and no free-text query requirement.

project accepts a numeric ID, exact identifier/slug, or project name; the parameter is named project, not project_id. There is no generic filters=[...] parameter; each filter is its own named argument (version, status, assignee, the date filters, etc.), listed below.

version_status filters by the status of a work package's assigned version: one of 'open', 'closed', or 'locked'. It requires OpenProject 16.4 or newer. A work package with several target versions matches version and version_status when any one of them matches.

assignee filters by any user (username, id, or "me"). assignee_me takes precedence.

status/priority filter by exact OpenProject status/priority name or numeric ID (case-insensitive), not meta-values like 'open'/'closed'; set open_only=true to restrict results to not-closed work packages instead.

Date filters accept YYYY-MM-DD format:

  • created_on/updated_on/due_on: exact date match

  • created_between/updated_between/due_between: inclusive date range [start, end] Cannot specify both _on and _between for the same field.

overdue_only=true restricts results to work packages with due_date before today that are not closed (OpenProject's own overdue? predicate; there is no dedicated overdue API filter, this composes a relative date filter with the open-status meta-filter). due_within_days=N restricts to due_date in [today, today+N days]. Neither can be combined with each other or with due_on/due_between (all four constrain the same field).

sort_by and group_by are checked against OpenProject's real sortable and groupable work-package columns (the parameters list them with the release each newer column needs); an unknown field raises a ValueError listing the valid set instead of only failing once OpenProject itself rejects the request. A column the instance's release lacks is rejected by OpenProject and the error names the release it needs. Dates, hours, subject, id and the other columns without a value per group sort fine but cannot be grouped by.

select restricts each result row to the given fields (e.g. ["id", "subject", "status"]); an invalid name returns the allowed set. Common fields: id, display_id, subject, type, status, priority, assignee, project, version, parent_id, parent_display_id, start_date, due_date, estimated_time, spent_time, created_at, updated_at, author, category, description, schedule_manually, derived_start_date, derived_due_date, percentage_done, derived_percentage_done, readonly, has_project_attributes, ignore_non_working_days, custom_fields, custom_fields_truncated, custom_comments, custom_comments_truncated. custom_fields/custom_comments are selectable/hideable only as a whole field, not by individual custom-field key (per-key filtering is OPENPROJECT_HIDE_CUSTOM_FIELDS' concern, not select's). parent_display_id is only populated on OpenProject 17.5+ (semantic mode); it stays null on older/classic instances even when parent_id is set.

total is the real matching count only when the query is provably restricted to OPENPROJECT_READ_PROJECTS server-side: scope is unrestricted, an explicit project was given, or (no project, restricted scope) a server-side filter for the resolved allowed project IDs was sent. Otherwise total falls back to this page's item count, and next_offset/truncated are based on whether this page came back full rather than the server's own total, so nothing here ever reveals how many matches exist in projects you can't see. Because of this, total can read 0 while next_offset is still non-null (a restrictive scope filtered out every match on this page, but the raw server page was full); that is not an inconsistency, keep paging via next_offset rather than stopping on a low/zero total. Page until next_offset is null either way.

include_sums=true adds server-computed aggregates instead of requiring client-side pagination and summation: groups (one entry per group_by value, with count and a sums dict of OpenProject's fixed summable fields: estimated_time, story_points, percentage_done, remaining_time, overall_costs, labor_costs, material_costs, plus any custom fields, as raw server-formatted values, e.g. ISO 8601 durations and currency strings) and total_sums (the same shape, across all matches). groups is only populated when group_by is also set; total_sums is populated either way (a sum over the whole filtered result set even without grouping). Same scope-safety rule as total above: groups/total_sums come back null whenever the query cannot be proven restricted to OPENPROJECT_READ_PROJECTS server-side. A group named after a version, sprint or backlog bucket defined in a project that OPENPROJECT_READ_PROJECTS does not cover keeps its count and sums under the label [outside read scope].

Version rollup recipe: group_by="status", version="", include_sums=true returns per-status progress/time sums for one version's work packages, replacing manual pagination + client-side summation.

custom_fields is a dict keyed by the RAW OpenProject key (e.g. "customField12") -- never a friendly name -- with values normalized by shape: plain scalars pass through; link-typed values (list/user/version format) become title-only strings (or a list of titles for a multi-value field), matching every other link field in this response; multi- paragraph "text"-format values are capped like description (this call's effective text_limit); a scalar string/link/date-format value is independently capped at ~255 characters. custom_fields_truncated is true when the dict was capped at 50 entries and/or any individual entry's value was itself capped. custom_comments (keyed the same way, holding a field's freeform comment text) and custom_comments_truncated follow the identical shape and caps, independently -- in practice custom_comments is always empty/null for work packages on OpenProject's stock CE behavior: only Projects opt into per-custom-field comments, work packages do not, so this field is present for forward compatibility only. Combined worst case is roughly 250 KB for custom_fields per work package, always finite regardless of how many custom fields exist or how large their values are. get_work_packages (batch) and list_my_open_work_packages return the same custom_fields/custom_comments shape and caps, since they reuse this same normalization. OPENPROJECT_HIDE_CUSTOM_FIELDS hides individual custom_fields entries by matching ONLY the raw key/wildcard (e.g. "customField12", "customField*") -- unlike the write path, which also accepts the custom field's friendly name, a read-side hide pattern written as a friendly name has no effect.

custom_field_filters filters results by custom field value(s) -- a dict keyed by "cf_" or "customField" (both forms accepted transparently and always normalized to "cf_" on the wire; "cf_" is CustomField#column_name, the actual OpenProject filter key, distinct from "customField" which is the JSON/PATCH key used by custom_fields above -- do not confuse the two). Each entry's value is {"operator": "", "values": [...]}, e.g. {"cf_12": {"operator": "=", "values": ["42"]}}. Learn a field's cf_ id from any prior get_work_package call's custom_fields dict keys (strip the "customField" prefix). Only raw cf_/customField keys are accepted -- friendly-name resolution is not supported (a list/search call has no single project+type context to resolve a name against safely; a friendly name is only meaningful for the write path's per-call project+type schema probe). At most 20 custom-field filters per call, at most 100 values per filter, each value at most 1000 characters.

Legal operators depend on the field's format on this instance -- see docs/filters.md's "Custom-Field Filters" section for the full format-to-operator matrix, verified against OpenProject CE source. This tool validates the key shape and the operator symbol locally (a recognized custom-field operator, not necessarily legal for this specific field's format) and rejects hidden fields (OPENPROJECT_HIDE_CUSTOM_FIELDS) before any network call; an operator/value that is syntactically valid but illegal for the field's actual format is rejected by OpenProject itself with a clear error (surfaced as a ValueError here, not a raw HTTP passthrough) rather than validated client-side against a live schema -- this keeps list/search calls at their existing single-request cost (no per-call schema probe). user/version-format custom-field filters additionally require project to be set (OpenProject only considers project-scoped custom fields of these two formats filterable at all; this is not checked locally -- a global, no-project user/version CF filter fails server-side with an unhelpful "filter not available" error, see docs/filters.md's "Global (no-project) filtering constraint" section).

search_work_packagesA

Search work packages by free text, optionally scoped to a project.

search matches only the work package subject and numeric ID (OpenProject's native subject_or_id full-text filter); it does NOT match version, category, description, or other linked-resource fields. To filter by version, use list_work_packages(version=..., project=...) instead.

In parallel with that text/id search, a search shaped like a work package reference (a numeric id or a display id like "PROJ-42", but not a date such as 2026-10-15) is also resolved directly the same way get_work_package does; other text is never looked up as an id. When that resolves to a work package that also satisfies every other filter given here (project/status/assignee/dates/ custom fields/etc.), it's returned separately as exact_match; never folded into results, and never counted toward total/count/pagination, since a single extra item can't be paginated consistently. Absent (not present in the response at all) when nothing resolves, when the resolved item fails a filter, or when it's already present in results via the text match. select applies to exact_match the same way it applies to each results row.

Without project, the search runs globally across every project readable under OPENPROJECT_READ_PROJECTS, not just one project; pass project explicitly to scope results to it.

Set status to restrict results to an exact OpenProject status name or numeric ID, not a meta-value like 'open'/'closed'. Set open_only=true to return only open (not-closed) work packages. Set assignee_me=true to return only work packages assigned to the current user.

assignee filters by any user (username, id, or "me"). assignee_me takes precedence.

priority filters by priority name or numeric ID (case-insensitive).

Date filters, overdue_only/due_within_days, select, pagination (offset/limit/total), include_sums, and each result's custom_fields/custom_comments (raw-key custom-field values/comments, capped and hide-matched exactly as documented) all work exactly as documented on list_work_packages; see that tool's docstring for the full field lists and semantics. sort_by and group_by take the same fields and are described by their own parameters. One difference: total is the real matching count only when scope is unrestricted or an explicit project was given (list_work_packages's server-side allowed-project filter for the no-project+restricted-scope case does not apply here); otherwise total/groups/total_sums fall back to this page's data, same safety guarantee either way.

custom_field_filters filters by custom field value(s); see list_work_packages's docstring for the full parameter documentation (identical shape and semantics on both tools).

get_work_packageA

Get a work package by id, including its full description.

The description is returned in full by default (single work packages are not truncated). Pass text_limit to cap it at that many characters; when the text is cut, description_truncated is true and description_length reports the real length. This same text_limit also caps any "text"-format custom field value in custom_fields (see list_work_packages's docstring for the full custom_fields/custom_comments shape and hide-matching rules) -- but a scalar string/link/date-format custom field value is capped independently at ~255 characters regardless of text_limit, including when text_limit=None (the default here): "single work packages are not truncated" applies to description and CF text-format values, not to that separate, always-on scalar cap.

select restricts the response to the given fields (e.g. ["id", "subject", "status"]); an invalid name returns the allowed set.

get_work_packagesA

Get several work packages by id in one batch call.

Fetches work packages in parallel and returns per-item results. Failed fetches are reported individually without stopping the batch. Maximum 100 IDs per batch.

ids: internal ids (e.g., 952) or display_ids (e.g., "PROJ-51"), not UI display numbers. Duplicate IDs are automatically deduplicated.

select restricts each result's work_package to the given fields (e.g. ["id", "subject", "status"]); an invalid name returns the allowed set. The id/success/error fields on each result are always included regardless of select, so you can still tell which items succeeded; listing success or error in select is accepted and changes nothing, while id doubles as a selectable work_package field.

For batches with many full-detail items, set text_limit and/or select proactively; an unbounded batch of large work packages can exceed the tool-result size limit and get redirected to a file.

Each item's work_package carries the same custom_fields/custom_comments shape and caps as get_work_package; see list_work_packages's docstring for the full details (raw-key values, per-field caps, and the OPENPROJECT_HIDE_CUSTOM_FIELDS key-only hide-matching asymmetry). With many items, an unbounded custom_fields/custom_comments per item adds to the same size-limit risk text_limit/select address above.

list_my_open_work_packagesA

List the current user's open assigned work packages.

This query has no project scope of its own, so total is the real matching count only when OPENPROJECT_READ_PROJECTS is unrestricted ("*"); under a restricted scope, total always falls back to this page's item count, and next_offset/truncated are based on whether this page came back full rather than the server's own total, so nothing here ever reveals how many matches exist in projects you can't see. Page until next_offset is null either way.

Each result row also carries custom_fields/custom_comments, selectable or hideable only as a whole field like every other field, with the same shape, caps, and OPENPROJECT_HIDE_CUSTOM_FIELDS key-only hide-matching as list_work_packages -- see that tool's docstring for the full details.

get_work_package_activitiesA

Get the activity log for a work package, most recent first.

Comments are returned in full by default (this is one work package's own history, not an open-ended multi-row list; same rationale as get_work_package). Pass text_limit to cap each comment at that many characters; when a comment is cut, comment_truncated is true and comment_length reports its real length.

When OPENPROJECT_READ_PROJECTS is not *, or a work package or custom field is hidden, change details are left out and details_omitted is true: OpenProject returns them as text in the API user's language with no structure to check against the read scope or the hidden fields, and the text can name parents, projects and versions of projects outside it and carry the values of hidden fields. Comments, user and time are unaffected.

list_work_package_reactionsA

List emoji reactions across a work package's comment activities.

One row per comment and reaction, without the activity id, so a reaction can repeat.

list_remindersA

List the current user's active reminders across all work packages. Requires OpenProject 16.1+.

get_work_package_relationsA

Get the relations of one work package (blocks, relates to, duplicates, etc.).

type/from_id/to_id reflect how OpenProject actually stored the relation, which does not depend on which work package's relations you query, but can differ from how it was originally requested, since OpenProject canonicalizes some relation types at creation time (e.g. a relation requested as 'precedes' is stored as 'follows' with from_id/to_id swapped; see create_work_package_relation). Use from_id/to_id together with type, not the request you expect to have made, to determine the actual direction.

Each result additionally carries queried_perspective, a caller-relative reading of the same relation from work_package_id's own side; never a replacement for type/from_id/to_id, which stay unchanged. queried_perspective.direction is "from" or "to" (which raw id equals work_package_id); queried_perspective.effective_type is the type as read FROM work_package_id's side (e.g. a stored "blocks" relation reads as effective_type="blocked" when work_package_id is the to_id side, "blocks" when it's the from_id side). queried_perspective.predecessor_id/successor_id are populated only for the precedes/follows type pair (OpenProject's own scheduling-relevant relation types); both stay null for every other type, since no other type has an equivalent first/second concept.

total is only the count of allowed relations returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

list_work_package_attachmentsA

List attachments on a work package.

include_total_size=true sums file_size_bytes across every attachment (independent of limit/offset); OpenProject's attachments endpoint always returns the full list in one response, so this costs no extra request in the common case. Null if file_size_bytes is hidden by server configuration, rather than leaking it indirectly through a sum.

include_images=true also reads the listed PNG/JPEG/GIF/WebP attachments and returns them as images the model can see, appended after the list. One shared byte budget covers the whole call (OPENPROJECT_ATTACHMENT_CONTENT_MAX_BYTES, 5 MB by default), taken in listing order; every attachment that was not inlined is reported in images with its reason. Use get_attachment_content for one specific attachment, or for text content.

get_attachmentC

Get a single attachment by id.

get_attachment_contentA

Read an attachment's actual content, not just its metadata.

Returns a JSON block describing the outcome, followed by the content itself as a native block when it can be inlined:

  • PNG/JPEG/GIF/WebP come back as an image the model can see

  • text-like content (text/*, JSON, XML) comes back as text, cut at the byte limit if it is long (outcome "text", truncated=true)

  • an image over the byte limit is refused whole rather than returned partially (outcome "too_large")

  • anything else returns metadata only (outcome "not_inline_supported"); no bytes, since no MCP client could display them; content_type there is the attachment's stored type, which names the file where the served header only says "not inlineable"

max_bytes may only LOWER the server's configured limit (OPENPROJECT_ATTACHMENT_CONTENT_MAX_BYTES, 5 MB by default), never raise it.

size_bytes is the size of the returned content block, and is null if file_size_bytes is hidden by server configuration, rather than leaking it indirectly under another name.

Nothing is written to disk. Use get_attachment for metadata alone.

list_work_package_file_linksA

List Nextcloud file links attached to a work package (Community Edition).

For files uploaded to OpenProject itself, use list_work_package_attachments.

list_work_package_watchersC

List watchers of a work package.

list_statusesA

List all available work package statuses.

Read-only: statuses cannot be created or modified via the OpenProject API (Community Edition); configure them in the web admin UI.

get_statusC

Get a single work package status by id.

list_prioritiesB

List all available work package priorities.

get_priorityB

Get a single work package priority by id.

list_typesA

List all available work package types, optionally filtered by project.

Read-only: types cannot be created or modified via the OpenProject API (Community Edition); configure them in the web admin UI.

get_typeA

Get a single work package type by id.

list_time_entry_activitiesB

List available time entry activities.

list_time_entriesA

List time entries with optional project, work package, user, and date filters.

total is only the count of allowed time entries returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

include_total_hours=true sums hours (as an ISO 8601 duration) across every matching entry, independent of limit/offset; this runs its own full walk of the filtered collection (bounded; see total_hours_truncated), since OpenProject has no server-side sum for time entries (unlike list_work_packages's include_sums). Leave false unless the total is actually needed: it costs extra requests on top of the page this call already returns.

get_time_entryA

Get a single time entry by id, including its full comment.

The comment is returned in full by default (single time entries are not truncated). Pass text_limit to cap it at that many characters; when the text is cut, comment_truncated is true and comment_length reports the real length.

get_cost_entryB

Get a single cost entry by id.

Cost entries are entirely read-only in OpenProject's API (Community Edition) -- there is no create/update/delete endpoint for this resource.

list_work_package_cost_entriesA

List all cost entries recorded against a work package.

Returns every cost entry for the work package in one call -- this endpoint is unpaginated on OpenProject's side (no offset/limit parameters exist).

get_work_package_costs_by_typeA

Get a work package's costs aggregated by cost type.

count is the number of distinct cost types with recorded spend on this work package, not a monetary total. Each result's spent_units is a quantity in that cost type's own unit (see get_cost_type for the unit name) -- there is no currency conversion or grand total computed here or by OpenProject's own API.

get_cost_typeA

Get a cost type by id.

Cost types are entirely read-only in OpenProject's API (Community Edition) -- there is no create/update/delete endpoint, and no collection GET either (no list_cost_types tool exists because the endpoint does not exist upstream).

get_github_pull_requestA

Get a single GitHub pull request by its own id.

GitHub pull requests are read-only mirror rows synced by OpenProject's own GitHub App integration -- never creatable via this API. An empty or 404 result can mean either the pull request doesn't exist, or the GitHub App integration isn't configured on this instance; OpenProject's API does not distinguish these cases.

Unlike work-package-scoped lookups in this domain, this global lookup relies on OpenProject's own visibility check (whether the linked work package is visible to the API token), not on this MCP's OPENPROJECT_READ_PROJECTS allowlist -- the pull request payload carries no project link for this MCP to check against.

list_work_package_github_pull_requestsA

List all GitHub pull requests linked to a work package.

Returns every linked pull request in one call -- this endpoint is unpaginated on OpenProject's side (no offset/limit parameters exist). An empty result can mean either no pull requests are linked, or the GitHub App integration isn't configured on this instance; OpenProject's API does not distinguish these cases.

list_work_package_gitlab_issuesA

List all GitLab issues linked to a work package.

Returns every linked issue in one call -- this endpoint is unpaginated on OpenProject's side (no offset/limit parameters exist). An empty result can mean either no issues are linked, or the GitLab integration isn't configured on this instance; OpenProject's API does not distinguish these cases. No single-item get_gitlab_issue tool exists because no such endpoint exists upstream -- only this work-package-scoped list.

list_work_package_gitlab_merge_requestsA

List all GitLab merge requests linked to a work package.

Returns every linked merge request in one call -- this endpoint is unpaginated on OpenProject's side (no offset/limit parameters exist). An empty result can mean either no merge requests are linked, or the GitLab integration isn't configured on this instance; OpenProject's API does not distinguish these cases. No single-item get_gitlab_merge_request tool exists because no such endpoint exists upstream -- only this work-package-scoped list.

list_relationsA

List all relations across the instance, optionally filtered by type (e.g. 'blocks', 'follows').

type/from_id/to_id reflect how OpenProject actually stored the relation (it does not change depending on which work package's relations you're viewing), which can differ from how it was originally requested; OpenProject canonicalizes some relation types at creation time (e.g. a relation requested as 'precedes' is stored as 'follows' with from_id/to_id swapped; see create_work_package_relation). Filtering by relation_type matches the stored (canonical) type, not necessarily the type a caller originally requested when creating it.

Each result's queried_perspective field is always null here; a caller-relative reading needs one anchor work package to read the relation FROM, and this instance-wide listing has none. Use get_work_package_relations instead when you need queried_perspective populated.

total is only the count of allowed relations returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

list_work_package_wiki_linksA

List wiki pages linked to a work package.

Requires OpenProject 17.5+; on an older server this returns a [not_found] naming the required version.

Known OpenProject server bug (17.5 to at least 17.9.1): this call returns a [server_error] when none of the work package's links resolves to a wiki page the user can see, and on 17.5/17.6 also when the work package has no links. A link to the internal wiki resolves when its identifier is the page's numeric id.

Only the work package is checked against OPENPROJECT_READ_PROJECTS: a link carries no project, so a link to a page in another project is listed. It shows the identifier, provider and author, never the page's title or text.

execute_queryA

Execute a saved OpenProject query by id and return its resolved work packages.

query_id: the query's own numeric id; obtain it from get_view/list_views's query_id field, or from list_boards/get_board (a board's id IS its underlying query id, since OpenProject Boards are Query resources).

Runs the query server-side (OpenProject resolves its stored filters/sort/ group_by and returns real work packages, not just the query's definition); no client-side filter translation happens here. Results are still filtered against this MCP's own OPENPROJECT_READ_PROJECTS allowlist before being returned, since the query itself executes with the API token's full server-side permissions.

list_project_membershipsB

List memberships for a project, including principal and role names.

get_membershipA

Get one project membership by id: the user or group, the project and the role names.

To list a project's members use list_project_memberships.

list_rolesB

List OpenProject roles visible to the current user.

get_current_userA

Return the currently authenticated user's profile.

list_actionsA

List the action ids OpenProject's API defines, e.g. "work_packages/create".

This is the vocabulary only; which user or group may perform an action in which project comes from list_capabilities.

list_capabilitiesA

List API capabilities exposed by OpenProject.

At least one of project or capability_id is required; there is no unfiltered global listing, since one would bypass the project read allowlist.

list_versionsA

List versions globally or for a specific project, optionally filtered by a case-insensitive name substring.

Without project, total is only the count of allowed versions returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_versionA

Get a version by id, including its full description.

The description is returned in full by default (single versions are not truncated). Pass text_limit to cap it at that many characters; when the text is cut, description_truncated is true and description_length reports the real length.

list_boardsA

List saved OpenProject boards/queries, optionally scoped to a project.

With project, search, or a restrictive OPENPROJECT_READ_PROJECTS, total is only the count of allowed boards returned on THIS page, not a full count of all matches; the search stops as soon as it has enough, so an exact total would need an extra full walk. Page until next_offset is null.

get_boardC

Get a saved OpenProject board/query by id.

list_meetingsA

List OpenProject meetings, optionally scoped to a project.

Requires OpenProject 17.4+; the meetings module's agenda/section/ participant shape used here does not exist on 16.6, which only exposes an incompatible legacy "meeting contents" representation.

Omit project to list across all readable projects.

get_meetingA

Get a single OpenProject meeting by id: schedule, state, participants and its lock_version.

Requires OpenProject 17.4+. Its agenda comes from list_meeting_sections and list_meeting_agenda_items.

list_meeting_agenda_itemsA

List the agenda items of one meeting.

Requires OpenProject 17.4+.

This list is unpaginated server-side (OpenProject returns every agenda item of the meeting in one response); offset/limit are applied client-side by this MCP.

text_limit caps each item's notes at that many characters (default: the server's configured OPENPROJECT_TEXT_LIMIT, NOT unlimited). When text is cut, notes_truncated is true and notes_length reports the real length.

list_work_package_meeting_agenda_itemsA

List the agenda items, across all meetings, that link a work package.

Requires OpenProject 17.7+.

Items of meetings in projects outside OPENPROJECT_READ_PROJECTS are left out.

This list is unpaginated server-side; offset/limit are applied client-side by this MCP.

text_limit caps each item's notes at that many characters (default: the server's configured OPENPROJECT_TEXT_LIMIT, NOT unlimited). When text is cut, notes_truncated is true and notes_length reports the real length.

get_meeting_agenda_itemA

Get a single meeting agenda item by id.

Requires OpenProject 17.6+.

list_meeting_outcomesA

List outcomes of a meeting agenda item.

Requires OpenProject 17.6+; the meeting_outcomes endpoint does not exist on 17.4/17.5.

This list is unpaginated server-side; offset/limit are applied client-side by this MCP.

text_limit caps each outcome's notes at that many characters (default: the server's configured OPENPROJECT_TEXT_LIMIT, NOT unlimited). When text is cut, notes_truncated is true and notes_length reports the real length.

get_meeting_outcomeB

Get a single meeting outcome by id.

Requires OpenProject 17.6+.

list_meeting_sectionsA

List sections of an OpenProject meeting.

Requires OpenProject 17.4+.

This list is unpaginated server-side; offset/limit are applied client-side by this MCP.

get_meeting_sectionA

Get a single meeting section by id: title, position and whether it is the backlog.

Requires OpenProject 17.6+. Its agenda items come from list_meeting_agenda_items.

list_recurring_meetingsA

List OpenProject recurring meeting series, optionally scoped to a project.

Requires OpenProject 17.4+.

Omit project to list across all readable projects.

get_recurring_meetingA

Get a single OpenProject recurring meeting series by id.

Requires OpenProject 17.4+.

list_recurring_meeting_occurrencesA

List virtual occurrences of a recurring meeting.

Requires OpenProject 17.4+.

filter: one of "upcoming", "past", "cancelled", "open". Occurrences are virtual (synthesized from the recurrence rule) except where a real Meeting has already been materialized via init_recurring_meeting_occurrence; this list has no offset/pagination, unlike list_meetings/list_recurring_meetings.

create_projectA

Create a project.

Omitted optional fields are not sent, so OpenProject's defaults apply. A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope.

update_projectA

Update a project.

Omitted fields stay unchanged. A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope.

delete_projectA

Delete a project and its subprojects (admin operation).

OpenProject archives them at once and deletes them in a background job, with their work packages, memberships, versions, time entries, news, wiki, forums, meetings, documents and storage links; this cannot be undone. OpenProject also tries to delete storage project folders managed automatically, with their files. Unless the project is already archived, rejected while work packages of other projects use one of their versions.

copy_projectA

Copy an existing project into a new project.

name, identifier and the optional fields describe the new project. A confirmed copy starts a background job; poll get_job_status with the returned job_status_id. A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope.

set_project_favoriteA

Mark or unmark a project as a favorite. Requires OpenProject 17.0 or later.

create_newsC

Create a news entry inside a project.

update_newsA

Update a news entry. Omitted fields stay unchanged.

delete_newsA

Delete a news entry with its comments and attachments; this cannot be undone.

update_documentB

Update a document. Omitted fields stay unchanged.

create_gridA

Create a dashboard grid on a page.

A grid is the widget layout of My Page, a project overview or a board. The grid starts with OpenProject's default widgets for that page; other widgets are added in OpenProject. A board's saved work package queries are created with create_board.

update_gridA

Rename or resize a dashboard grid; omitted fields stay unchanged.

The widgets stay as they are; a size that would leave a widget outside the grid is rejected.

delete_gridA

Delete a board grid; My Page and project overview grids cannot be deleted.

17.0+: its column queries are deleted too, as far as you may delete them. This cannot be undone.

create_work_packageA

Create a work package.

The tool validates the payload first. assignee: 'me' or numeric user id (e.g., 42). Call list_users to find ids. parent: internal id (e.g., 952) or display_id (e.g., "PROJ-51"), not UI display number to nest the new work package under a parent. estimated_time, remaining_time, duration accept ISO8601 duration strings (e.g., 'PT8H' for 8 hours, 'PT1H30M' for 1.5 hours, 'P1D' for 1 day, 'P2W' for 2 weeks). target_versions accepts a list of version names/ids to assign multiple target versions at once (OpenProject's multi-version feature); omit it, or pass [], to create with no target version assigned. target_versions and version write the same underlying data and cannot be used together in one call. due_date falling on a non-working day (e.g. a weekend) can be silently moved forward to the next working day by OpenProject; compare the request and the returned result.due_date if the exact calendar date matters. This server does not expose a way to opt out of that shift (OpenProject's own ignoreNonWorkingDays flag is not a write parameter here). A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope. If you issue multiple create_work_package/create_subtask calls concurrently, OpenProject assigns IDs in server completion order, not call order; use bulk_create_work_packages instead when relative ID/creation order across a batch matters.

create_subtaskA

Create a subtask under an existing work package.

The tool validates the payload first. Concurrent calls to this tool (or create_work_package) do not preserve call order in the resulting IDs; use bulk_create_work_packages when order across several new items matters. target_versions accepts a list of version names/ids to assign multiple target versions at once (OpenProject's multi-version feature). target_versions and version write the same underlying data and cannot be used together in one call.

update_work_packageA

Update a work package.

The tool validates the patch first. assignee: 'me' or numeric user id (e.g., 42). Call list_users to find ids. parent re-parents the work package (numeric id or a PROJ-123 reference); pass 'none' to remove the parent and make it top-level. version accepts a version name/id, or 'none' to unassign the version. sprint accepts a Backlogs sprint name/id (requires the Backlogs module and OpenProject 17.3+), or 'none' to unassign it. Pass 'none' to assignee, responsible, category or project_phase to unassign that field. Omitted fields stay unchanged. target_versions accepts a list of version names/ids to assign multiple target versions at once (OpenProject's multi-version feature); pass [] to clear all. target_versions and version write the same underlying data and cannot be used together in one call. If this work package already has more than one target version, version alone is rejected; use target_versions instead. A change of version, target_versions, sprint or a version custom field is refused while the work package holds such an assignment in a project OPENPROJECT_READ_PROJECTS does not cover, because the change would remove it. estimated_time, remaining_time, duration accept ISO8601 duration strings (e.g., 'PT8H' for 8 hours, 'PT1H30M' for 1.5 hours, 'P1D' for 1 day); omit to leave unchanged, or pass 'none' to clear the field. percentage_done is an integer 0-100. Setting status to a closed status auto-fills percentage_done=100 and remaining_time=PT0H when you don't supply them explicitly and OpenProject's schema reports those fields as writable (on instances using status-based progress calculation, OpenProject already derives them itself and this is skipped). On such an instance, explicitly passing percentage_done together with a closing status is rejected with a hard validation error (percentageDone is not writable there) rather than silently ignored; omit percentage_done and let OpenProject derive it instead. due_date's non-working-day shift and the confirm/preview contract work exactly as documented on create_work_package.

bulk_create_work_packagesA

Create multiple work packages in one call.

New items have no identifier field to set (unlike bulk_update_work_packages's work_package_id); each result item is matched back to its input purely by index.

Each item in items must contain project, type, and subject. Optional fields per item: description, version, target_versions (a list of version names/ids for OpenProject's multi-version feature; cannot be combined with version on the same item), project_phase, assignee, responsible, priority, category, custom_fields, parent_work_package_id (or parent, an alias for the same field, matching create_work_package's naming; do not specify both on the same item), start_date (YYYY-MM-DD), due_date (YYYY-MM-DD), estimated_time, remaining_time, duration (ISO8601 duration strings, e.g. 'PT8H' or 'P1D'). An item containing any other key is rejected with an indexed validation error rather than silently dropping the unrecognized field. due_date falling on a non-working day (e.g. a weekend) can be silently moved forward to the next working day by OpenProject; compare the request and the returned item's result.result.due_date if the exact calendar date matters. This server does not expose a way to opt out of that shift (OpenProject's own ignoreNonWorkingDays flag is not a write parameter here).

A rejected item's validation is not a tool error; inspect each item's success, error, and nested result rather than the MCP error envelope.

select restricts each item's nested result to the given fields (e.g. ["ready", "work_package_id"]); an invalid name returns the allowed set. These are write-result fields (work_package_id, ready, state, ...), not work package fields: display_id and the rest come with result, the full written work package. The index/success/error fields on each item are always included regardless of select, so you can still tell which items succeeded; listing them in select is accepted and changes nothing. For batches with many items or long descriptions, set select proactively; an unconfirmed preview echoes each item's full proposed payload, and an unbounded batch can exceed the tool-result size limit and get redirected to a file.

A per-item timeout is reported as that item's failure and does not stop the loop. If this call is cancelled outright (e.g. the host cancels the request), items already created beforehand remain on the server; items not yet attempted are not created. No result summary is returned in that case (the call ends via cancellation, not a normal return); use list_work_packages/get_work_package afterward to determine what was actually written. This operation is not atomic; OpenProject CE has no batch/transaction endpoint.

Items are processed strictly sequentially in list order, and each result's index reflects that order; use this tool instead of parallel create_work_package/create_subtask calls whenever the relative order of a batch matters.

bulk_update_work_packagesA

Update multiple work packages in one call.

Each item's identifier field is work_package_id, not id, e.g. {"work_package_id": 952, "status": "Closed"}.

Each item in items must contain work_package_id. At least one other field must be present per item. Optional fields per item: subject, description, type, version, target_versions (a list of version names/ids for OpenProject's multi-version feature; pass [] to clear all; cannot be combined with version on the same item; rejected if the item's work package already has more than one target version assigned), sprint (Backlogs sprint name/id, requires the Backlogs module and OpenProject 17.3+), project_phase, status, assignee, responsible, priority, category, custom_fields, parent_work_package_id (or parent, an alias for the same field, matching update_work_package's naming; do not specify both on the same item), start_date (YYYY-MM-DD), due_date (YYYY-MM-DD), estimated_time, remaining_time, duration (ISO8601 duration strings, e.g. 'PT8H' or 'P1D'; pass 'none' to clear one of these), percentage_done (integer 0-100). Pass 'none' to version, sprint, project_phase, assignee, responsible, category, parent_work_package_id, or parent to clear that field on the item, same as update_work_package. An item containing any other key is rejected with an indexed validation error rather than silently dropping the unrecognized field. Setting an item's status to a closed status auto-fills percentage_done=100 and remaining_time=PT0H when that item doesn't supply them explicitly and OpenProject's schema reports those fields as writable. On an instance using status-based progress calculation, explicitly passing percentage_done together with a closing status on the same item is rejected with a hard, indexed validation error (percentageDone is not writable there) rather than silently ignored; omit percentage_done on that item and let OpenProject derive it instead.

due_date's non-working-day shift, the confirm/preview contract, select's item-trimming behavior, and cancellation/atomicity semantics all work exactly as documented on bulk_create_work_packages; only the affected result field differs (result.result.due_date here too). As there, select names write-result fields (work_package_id, ready, state, ...), not work package fields: display_id and the rest come with result, the full written work package.

delete_work_packageA

Delete a work package with its time and cost entries, relations, attachments and history.

This cannot be undone. Up to 17.6 all its descendants are deleted too; 17.7+ only those you may delete, the others lose their parent.

add_work_package_commentB

Add a comment to a work package.

notify=false avoids change emails by default. The result never includes details/created_at, even on an ordinary, non-aggregated comment: OpenProject can aggregate a new comment into an existing, more recent journal entry (e.g. a prior status change) instead of always creating a fresh one, which would otherwise surface that unrelated change's details and timestamp here, and there is no reliable way to tell an aggregated response from a fresh one, so both fields are omitted unconditionally rather than only when aggregation is suspected; details_omitted is true. user is normally populated, but on rare cases where OpenProject's own write response omits it, a best-effort follow-up lookup fills it in; if that also comes back empty, user stays null even though the comment was saved successfully.

toggle_activity_emoji_reactionA

Toggle an emoji reaction on a work package comment activity.

Adds the reaction if absent, removes it if already present. reaction is one of: thumbs_up, thumbs_down, grinning_face_with_smiling_eyes, confused_face, heart, party_popper, rocket, eyes.

create_work_package_reminderA

Create a reminder on a work package.

remind_at is an ISO 8601 date-time (e.g. 2026-12-01T09:00:00Z). Only one active reminder per work package is allowed; creating a second one fails. Requires OpenProject 16.1+.

update_reminderA

Update a reminder's time or note; at least one is required, an omitted one stays unchanged.

Requires OpenProject 16.1+.

delete_reminderA

Delete a pending reminder so it never fires; this cannot be undone.

Its unread notifications are marked read. Only its creator can; requires OpenProject 16.1+.

create_work_package_relationA

Create a relation between work packages.

relation_type: relates, duplicates, duplicated, blocks, blocked, precedes, follows, includes, partof, requires, required. work_package_id becomes from_id and related_to_work_package_id becomes to_id, but OpenProject stores only one canonical type per pair and silently rewrites the other: creating with relation_type='precedes' (or 'blocked', 'duplicated', 'partof', 'required') is stored as the paired canonical type ('follows', 'blocks', 'duplicates', 'includes', 'requires' respectively) with from_id/to_id SWAPPED relative to work_package_id/related_to_work_package_id. The canonical types themselves ('follows', 'blocks', 'duplicates', 'includes', 'requires', and non-directional 'relates') are stored exactly as given, with from_id/to_id unswapped. This happens once at creation and does not depend on which work package's relations you later query; always read the actual type/from_id/to_id from the response rather than assuming they match what you requested.

delete_relationA

Delete a relation between work packages; this cannot be undone.

Removing a follows/precedes relation reschedules the former successor.

delete_attachmentB

Delete an attachment; this cannot be undone.

set_work_package_watcherA

Add/remove a watcher on a work package.

watching=true adds the watcher; watching=false removes it. The two previews are NOT symmetric: watching=true's preview looks up and returns the user's summary (result is populated; whether the user already watches is not checked); watching=false's preview makes no extra lookup and always returns result=null.

create_time_entryA

Create a time entry.

hours accepts an ISO8601 duration string (e.g., 'PT8H' for 8 hours, 'P1D' for 1 day). start_time is an ISO 8601 date-time and requires the instance setting "allow tracking of start and end times"; ignored otherwise. No end_time parameter -- OpenProject derives it read-only from start_time + hours. Use create_time_entry_until to specify an end time instead of hours directly.

update_time_entryA

Update a time entry.

hours accepts an ISO8601 duration string (e.g., 'PT8H' for 8 hours, 'P1D' for 1 day). start_time is an ISO 8601 date-time. No end_time parameter -- OpenProject derives it read-only from start_time + hours. Use update_time_entry_until to specify an end time instead of hours directly.

create_time_entry_untilA

Create a time entry by start/end time instead of a duration.

Computes hours = end_time - start_time locally; only hours and start_time are sent to OpenProject (end_time itself is never accepted by the server, see create_time_entry's docstring). end_time must be strictly after start_time. There is no ongoing parameter here -- a time entry with a known end time is complete, not still running; use create_time_entry for an ongoing entry.

update_time_entry_untilA

Update a time entry by start/end time instead of a duration.

Computes hours = end_time - start_time locally; only hours and start_time are sent to OpenProject (end_time itself is never accepted by the server, see update_time_entry's docstring). end_time must be strictly after start_time. Always sets ongoing=False, since a completed time span with a known end time cannot still be running.

delete_time_entryB

Delete a time entry; this cannot be undone.

update_relationA

Update the type or description of a relation.

relation_type is subject to the same write-time canonicalization as create_work_package_relation: setting it to a "reverse" pair member (precedes, blocked, duplicated, partof, required) rewrites the stored relation to the paired canonical type (follows, blocks, duplicates, includes, requires) with from_id/to_id swapped relative to the relation's existing from/to. Read the actual type/from_id/to_id back afterward rather than assuming they match what was requested.

delete_file_linkA

Delete a file link; this cannot be undone, but the file itself stays in the external storage.

create_work_package_wiki_linkA

Create a link from a work package to a wiki page.

Requires OpenProject 17.7+; on an older server this returns a [not_found] naming the required version.

identifier: the target wiki page's identifier at its provider; for the internal wiki, the page's numeric id (the wiki_page_id of get_wiki_page). OpenProject accepts any string, but list_work_package_wiki_links fails while none of a work package's links resolves to a page. provider: the wiki provider's universal identifier (e.g. "internal" for OpenProject's built-in wiki).

delete_work_package_wiki_linkA

Delete a work package's wiki page link; this cannot be undone.

Requires OpenProject 17.6+. On 17.4 or older this returns a [not_found] naming 17.6; on 17.5 the link check below usually fails first with a [server_error].

This tool lists the work package's links first to verify that link_id belongs to work_package_id (an authorization check), so it fails with a [server_error] whenever list_work_package_wiki_links does (see there).

Only relation links (link_type "relation") can be deleted. An inline link comes from a wiki link in the work package's description or from a wiki page that mentions the work package; this tool rejects it with a [validation_error], and the link goes away only when that text changes. Deleting needs the "Manage wiki page links" permission in the work package's project; without it OpenProject answers 404, which the tool reports as a [not_found] naming 17.6, not as [permission_denied].

work_package_id must be the work package the link belongs to; OpenProject has no single GET for a link, so the delete is authorized against that work package's project. link_id: the wiki page link's own id, from list_work_package_wiki_links.

create_membershipA

Create a project membership.

The preview is validated by OpenProject; problems come back in validation_errors.

update_membershipA

Update a project membership's roles.

The preview is validated by OpenProject; problems come back in validation_errors.

delete_membershipA

Delete a project membership; this cannot be undone.

Roles inherited from a group stay: if some roles come from a group, only the direct ones are removed; if all do, the call is rejected.

create_versionB

Create a version for a project.

A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope.

update_versionA

Update a version.

Omitted fields stay unchanged. A rejected validation preview is not a tool error; inspect ready and validation_errors in the result rather than the MCP error envelope.

delete_versionA

Delete a version; this cannot be undone.

Rejected while any work package, in any project, uses it (17.8+: also as its "found in" version).

create_boardA

Create a saved OpenProject board/query.

This saves a work package query (filters, columns, sorting, grouping); a board grid that shows queries as columns is created with create_grid.

update_boardB

Update a saved OpenProject board/query.

Omitted fields stay unchanged; a given list replaces the stored one.

delete_boardA

Delete one saved work package query; this cannot be undone.

A board grid that shows it as a column keeps a column pointing to the deleted query; board grids themselves are deleted with delete_grid.

create_meetingA

Create an OpenProject meeting.

Requires OpenProject 17.4+.

duration: an ISO 8601 duration like "PT1H30M" (hours/minutes only). state and notify cannot be set on create: OpenProject starts a new meeting as a draft with notifications off, so passing either is rejected; set them afterwards with update_meeting.

update_meetingA

Update one meeting's details; omitted fields stay unchanged.

Requires OpenProject 17.4+. The meeting's agenda is changed with the agenda item and section tools, and a recurring series' schedule with update_recurring_meeting; for an occurrence of a series this changes only that occurrence. With notifications on, a change of title, time, duration or location emails the invited participants (not for a closed or cancelled meeting). From 17.8, an update that takes a one-time meeting out of draft or turns notifications on also sends the invitations; 17.4 to 17.7 send no invitations on update. Changing participants is not supported: participant_user_refs is rejected.

delete_meetingA

Delete a meeting with its agenda, outcomes, attachments and the time entries logged on it.

Requires OpenProject 17.4+. Deleting a one-time meeting cannot be undone. An occurrence of a series is only cancelled; init_recurring_meeting_occurrence restores it, reset to the series template. Invited participants get a cancellation email when the meeting's notifications are on. The template meeting of a recurring series is rejected, because deleting it would break the series. To delete the series with all its occurrences use delete_recurring_meeting.

create_meeting_agenda_itemA

Add an item to a meeting's agenda.

Requires OpenProject 17.6+. Rejected while the meeting is closed. For a work package, its project must be inside OPENPROJECT_WRITE_PROJECTS. To record a result of a discussed item use create_meeting_outcome.

update_meeting_agenda_itemA

Update one agenda item of a meeting; omitted fields stay unchanged.

Requires OpenProject 17.6+. Rejected while the meeting is closed. Each confirmed update also updates the meeting's own timestamp. Outcomes of the item are changed with update_meeting_outcome.

delete_meeting_agenda_itemA

Delete a meeting agenda item with its outcomes.

Requires OpenProject 17.6+. Rejected while the meeting is closed. This cannot be undone.

create_meeting_outcomeA

Create a meeting outcome on an agenda item.

Requires OpenProject 17.6+.

kind must be one of "information", "decision", "work_package" (OpenProject's real enum values, not e.g. "info" or "action", which OpenProject rejects with an internal server error rather than a clean validation error). "information"-kind outcomes require notes; "work_package"-kind outcomes require work_package_id.

update_meeting_outcomeB

Update a meeting outcome.

Requires OpenProject 17.6+.

kind, if given, must be one of "information", "decision", "work_package" (OpenProject's real enum values, not e.g. "info" or "action", which OpenProject rejects with an internal server error rather than a clean validation error).

delete_meeting_outcomeB

Delete a meeting outcome; this cannot be undone.

Requires OpenProject 17.6+.

create_meeting_sectionA

Create a meeting section.

Requires OpenProject 17.6+.

backlog cannot be changed after creation via update_meeting_section; pass it only here, on create.

update_meeting_sectionA

Rename or move a meeting section; omitted fields stay unchanged.

Requires OpenProject 17.6+. Rejected while the meeting is closed, and for the backlog section. Its agenda items stay in it. backlog cannot be changed after creation; this tool does not accept it.

delete_meeting_sectionA

Delete a meeting section with its agenda items and their outcomes (they are not moved).

Requires OpenProject 17.6+. Rejected while the meeting is closed, and for the backlog section. This cannot be undone.

create_recurring_meetingA

Create a recurring meeting series; its template meeting starts as a draft.

Requires OpenProject 17.4+. For a single meeting use create_meeting. Occurrences are virtual until init_recurring_meeting_occurrence creates one; the series is changed with update_recurring_meeting.

update_recurring_meetingA

Update an OpenProject recurring meeting series.

Requires OpenProject 17.4+.

Already created future occurrences move and are renamed with the series. A schedule change other than the time of day can delete cancelled occurrences; on 17.4 to 17.8 a time-of-day change also rewrites the slot times of past occurrences. When the schedule changes, invited participants get an update email if notifications are on.

delete_recurring_meetingA

Delete a recurring meeting series with all its occurrences, past and future.

Requires OpenProject 17.4+. Each occurrence goes with its agenda, outcomes, attachments and time entries; this cannot be undone. Invited participants get a cancellation email when notifications are on.

init_recurring_meeting_occurrenceA

Materialize a virtual recurring-meeting occurrence into a real, standalone Meeting.

Requires OpenProject 17.4+.

start_time: the occurrence's exact start time (ISO 8601, matching a start_time value from list_recurring_meeting_occurrences); occurrences have no numeric id, they are addressed by this timestamp. On success, result is a full Meeting (use its id with get_meeting/ update_meeting/delete_meeting), not an occurrence. A cancelled occurrence is restored and its content reset to the template. If the series template is still a draft, this first sets it to open, which stays even if the init fails; on 17.6 and later OpenProject creates further occurrences automatically only once the template is open. On OpenProject 17.4 and 17.5, initializing an occurrence that already exists fails with a server error instead of returning it; 17.6 and later return the existing occurrence.

cancel_recurring_meeting_occurrenceA

Cancel a virtual (not-yet-materialized) recurring-meeting occurrence.

Requires OpenProject 17.4+.

start_time: the occurrence's exact start time (ISO 8601), matching init_recurring_meeting_occurrence's addressing scheme.

If the occurrence has already been materialized into a real Meeting and is not itself cancelled, this fails with an error (delete_meeting the materialized meeting directly instead). If NOT yet materialized, OpenProject records the cancellation as a cancelled Meeting (init_recurring_meeting_occurrence restores it, a later schedule change of the series other than the time of day can delete it); this call's result does not report that new meeting's id; list_meetings/ list_recurring_meeting_occurrences(filter="cancelled") can be used to find it afterward if needed.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.4/5.0

Scored across 147 tools

Disambiguation3/5

Descriptions are unusually detailed and explicitly cross-reference each other, which rescues many boundaries. However, there is genuine overlap in the work-package listing family (list_work_packages, search_work_packages, list_my_open_work_packages, execute_query, get_work_packages) and in paired tools like create_time_entry vs create_time_entry_until and list_work_package_relations vs list_relations. An agent can usually pick correctly after reading, but misselection is easy without the docstrings.

Naming Consistency4/5

Names overwhelmingly follow a snake_case verb_noun pattern (list_/get_/create_/update_/delete_ + resource), consistently across projects, work packages, meetings, memberships, etc. A few outliers exist (execute_query, toggle_activity_emoji_reaction, get_project_work_package_context, bulk_* prefixes), but they are readable and follow their own sub-convention.

Tool Count1/5

147 tools is far beyond any reasonable surface for an agent-facing MCP server; the rules mark 50+ as an extreme mismatch. While the domain (a full project-management suite) is broad, this count imposes heavy context cost and makes tool selection error-prone even with excellent descriptions.

Completeness4/5

Coverage is very broad: full CRUD for projects, work packages, relations, time entries, versions, memberships, meetings/series/agenda/outcomes, boards, grids, news, and many read-only integrations, with upstream limitations (wiki body text, posts, cost entries, statuses/types) clearly explained. Minor gaps remain—notably no create/delete for documents, and write operations for sprints/backlog buckets are absent—but they are narrow.

Maintenance

ActivityActive
ResponsivenessUsually handled