OpenProject CE MCP Server
This server provides 87 tools, prompts, and resource templates for comprehensive interaction with OpenProject, covering project management, collaboration, and reporting.
Work Packages: Search, list, create, update, delete; manage comments (with reactions), watchers, relations, reminders, custom actions; full detail including custom fields and children.
Attachments & File Links: List, download, upload, delete attachments; list external file links (Nextcloud, OneDrive).
Git & PR Activity: View commits, pull/merge requests, CI status; full PR details including diff and review comments.
Projects: List, create, update, archive, delete; copy projects with background job tracking; favorites; project phases.
Saved Queries: List, run, and save filter views; override filters at runtime.
Notifications: Inbox management; filter by reason/project; mark read/unread individually or bulk.
Time Tracking: List, log, update, delete time entries with detailed breakdowns and filtering.
Versions & Sprints: CRUD for versions/releases; sprint integration.
People & Memberships: Search users/groups/placeholders; manage memberships and roles (admin-gated); user profiles.
Instance Metadata: Check connectivity; get project schemas, types, statuses, permissions.
Meetings: CRUD for meetings, agenda items, outcomes; recurring meetings management.
Wiki: Retrieve page identity and project (content not exposed).
Documents & News: Full CRUD for documents and news with descriptions.
Budgets: List project budgets.
Reporting: Aggregated project data: work package counts by status, time totals, and roster.
Prompts:
/weekly_report,/daily_standup,/triage_inbox,/groom_backlogfor automated status and backlog grooming.Resources: Templates for work packages, projects, and attachments as JSON or binary.
Provides tools for interacting with OpenProject's API v3, enabling management of work packages, comments, relations, attachments, git/PR activity, projects, saved queries, notifications, time tracking, versions, people, memberships, meetings, news, documents, budgets, and reporting.
OpenProject MCP Server
An MCP (Model Context Protocol) server for the OpenProject API v3. It gives Claude and any other MCP client 87 tools covering work packages, comments and relations, attachments, git/PR activity, projects, saved queries, notifications, time tracking, versions, people and memberships, meetings, news, documents, budgets and reporting — plus 4 report/workflow prompts and 3 resource templates. Built on FastMCP 3.x and httpx (HTTP/2).
This project is aimed at the OpenProject Community edition. OpenProject's Enterprise edition now ships with its own built-in MCP integration; this server brings the same capability to self-hosted Community instances. It runs fine against any edition — it only needs the public API v3.
Design principles, all enforced in code:
Structured everything. Every tool returns a typed model, so clients get an
outputSchemaand machine-readablestructuredContent, not prose. Errors come back as a JSON envelope with a stabletype, the upstreamhttp_status, amessageand ahintdescribing how to correct the call.Honest degradation. OpenProject instances differ by version, installed modules and permissions. Tools report what they could not see as in-band notes — a missing module yields an empty page with an explanation, never a fake success or a bare traceback.
Safe by default. A read-only mode, admin-gated membership writes, per-group tool disabling, a
confirm=trueguard on every destructive tool, TLS always verified, and credentials that never appear in logs.Version-adaptive. Targets OpenProject 14 LTS through 17.x; API differences are detected by a lazy, cached feature probe instead of assumptions (see Supported OpenProject versions).
What it looks like
Two exchanges against a fictional "Website Relaunch" project — every id, name and date below is invented. You describe the outcome; the model picks the tools, and what comes back to it is a typed object rather than prose to re-parse.
You What is still open in Website Relaunch and due by Friday? The checkout
bug is urgent — raise it and leave a note for whoever owns it.
→ list_work_packages(project="website-relaunch", status_scope="open",
due_before="2026-03-13", sort_by=[["due_date", "asc"]])
{ "items": [
{ "id": 4821, "subject": "Checkout fails on expired coupon codes",
"type": {"id": 7, "name": "Bug"}, "status": {"id": 7, "name": "In progress"},
"assignee": {"id": 118, "name": "Dana Okafor"}, "due_date": "2026-03-11" },
{ "id": 4830, "subject": "Move press-kit assets to the new CDN",
"type": {"id": 1, "name": "Task"}, "status": {"id": 1, "name": "New"},
"assignee": null, "due_date": "2026-03-13" }],
"pagination": {"total": 2, "page": 1, "page_size": 20, "has_more": false} }
→ update_work_package(id=4821, priority="High")
→ add_work_package_comment(id=4821, comment="Raised to High — it blocks the
launch checklist. Dana, can you take a look today?")
Assistant Two open items are due by Friday. #4821 is High now and the note is on
its thread; #4830 is due Friday and still has nobody on it.The rows above are abridged: each one also carries priority, project, start_date,
percentage_done and updated_at, and the envelope keeps groups, sums and notes slots
for the calls that fill them.
The prompt templates cut out the tool round trips: the server reads OpenProject itself and hands back a finished document rather than instructions to go and fetch one. It arrives prefaced with a note that the numbers are already computed and must not be recomputed or embellished.
You /weekly_report project=website-relaunch
# WEEKLY REPORT - AGILE SCRUM
_Generated from live OpenProject data_
...
## B. EXECUTIVE SUMMARY
**Progress against the sprint goal:** At risk
**Highlighted deliverables (done):**
1. #4802 - Ship the new pricing page
2. #4795 - Halve the hero image payload
...
### Data notes
- open/closed bucketing uses each status's isClosed flag from
GET /statuses, not status names; a status this instance renamed
or translated is still bucketed correctlyThat last block is the house style: a report says which of its numbers are partial, and a tool that could not read something returns the gap as a note instead of guessing. The whole surface is in Tools and Prompts and resources.
Related MCP server: MCP Toolbox for Databases
Requirements
Python >= 3.12
An OpenProject instance, version 14 LTS through 17.x (any edition; aimed at Community — Enterprise ships its own MCP integration)
An OpenProject API key: in OpenProject, go to My account → Access tokens and generate an API token
Installation
The distribution name is openproject-mcp-server. It installs two identical console scripts,
openproject-mcp-server and openproject-mcp; the long form is canonical (an unrelated PyPI
package also installs a bin named openproject-mcp).
Run one-shot with uv, no install step:
uvx openproject-mcp-serverOr install persistently:
uv tool install openproject-mcp-server
# or
pip install openproject-mcp-serverThe minimal configuration is two environment variables:
export OPENPROJECT_URL=https://openproject.example.com
export OPENPROJECT_API_KEY=your-api-keyValidate the configuration without starting the server:
openproject-mcp-server --check--check verifies the configuration and exits; it does not contact your instance. Once
connected through a client, call the get_instance_info tool for a live end-to-end check.
When configuration is missing or invalid, the server prints the specific problems to stderr
and exits with code 2 — never a traceback.
Claude Code
claude mcp add openproject \
--env OPENPROJECT_URL=https://openproject.example.com \
--env OPENPROJECT_API_KEY=your-api-key \
-- uvx openproject-mcp-serverClaude Desktop and other MCP clients
Add to claude_desktop_config.json (or your client's equivalent mcpServers config):
{
"mcpServers": {
"openproject": {
"command": "uvx",
"args": ["openproject-mcp-server"],
"env": {
"OPENPROJECT_URL": "https://openproject.example.com",
"OPENPROJECT_API_KEY": "your-api-key"
}
}
}
}From source
git clone https://github.com/kar-thik/openproject-mcp
cd openproject-mcp
uv sync
uv run openproject-mcp-serverUpdating or rotating your API token
When a token is regenerated, revoked or invalidated (OpenProject major upgrades can do this —
the symptom is every tool suddenly failing with authentication_failed / HTTP 401), generate
a fresh one in OpenProject under My account → Access tokens and update it wherever your
key lives:
Shell environment (easiest to rotate). The server reads
OPENPROJECT_API_KEYstraight from the OS environment, so you can export it globally — e.g. in~/.zshenv— and register the server with no--envflags at all:claude mcp add openproject -- uvx openproject-mcp-serverRotation is then: edit the export, open a fresh terminal, reconnect. The client config never contains a secret. (This does not work for GUI apps like Claude Desktop, which don't read your shell profile.)
Claude Code with
--env. The registration stores the key, so replace it:claude mcp remove openproject claude mcp add openproject \ --env OPENPROJECT_URL=https://openproject.example.com \ --env OPENPROJECT_API_KEY=new-key \ -- uvx openproject-mcp-serverThen reconnect via
/mcp(a running session keeps the old environment until it does).Claude Desktop and other JSON-configured clients. Edit the
OPENPROJECT_API_KEYvalue in the client config and restart the client..envfile. Edit the file and restart the server.
Recent OpenProject versions allow several API tokens in parallel, so you can rotate with zero downtime: create the new token, switch your clients over, then revoke the old one.
Configuration
Configuration is entirely environment-driven. The table below is the authoritative reference:
the server binds exactly these 25 names and no others. Bare, unprefixed names such as
READ_TIMEOUT or API_KEY are deliberately ignored (a stray variable in your shell cannot
change or break the server), as is any other unknown variable. A .env file in the server's
working directory is read with the same names; real environment variables take precedence.
From-source users can start from
.env.example.
Variable | Default | Purpose |
| — (required) | Instance root URL, e.g. |
| — (required*) | API key from My account → Access tokens. Sent as HTTP Basic |
| unset | OAuth bearer token, as an alternative to the API key. *One of the two credentials is required. |
| unset | Sent as the |
| system trust store | Path to a CA bundle (PEM) for instances behind a private CA. TLS is always verified; there is deliberately no off switch. |
|
| Serve read tools only: every write, destructive and admin tool is removed at startup. |
|
| Expose the three admin-gated membership write tools (hidden by default). |
| empty | Comma-separated group tags to remove whole tool groups at startup (see below). |
|
| Allow |
|
| Directory where |
|
| Size cap for attachment downloads, in MiB. |
|
| TTL in seconds for the metadata cache (statuses, types, priorities, schemas). |
|
|
|
|
|
|
|
| Log request/response bodies — only at DEBUG level, with credentials redacted. Development use only. |
|
| Reserved for OpenTelemetry tracing. Accepted but not yet wired to anything in this release; setting it produces no traces. |
|
| Bind address for |
|
| Port for |
| unset | Comma-separated bearer tokens accepted by |
|
| Seconds to wait for a TCP/TLS connection to OpenProject. |
|
| Seconds to wait for response data. |
|
| Seconds to wait while sending request data (uploads). |
|
| Seconds to wait for a free connection from the pool. |
|
| Connection pool size toward OpenProject. |
|
| Retry budget for idempotent requests. |
Secrets (OPENPROJECT_API_KEY, OPENPROJECT_OAUTH_TOKEN, OPENPROJECT_MCP_AUTH_TOKENS) are
held in memory as Pydantic SecretStr values and are never written to logs; the
Authorization header is redacted in every log record.
Limiting what the model can do
Three settings shrink the tool surface at startup (the tool list is fixed for the lifetime of the process):
OPENPROJECT_MCP_READ_ONLY=1serves only the 37 read tools.OPENPROJECT_MCP_ADMIN_TOOLS=1reveals the three membership write tools (create_membership,update_membership,delete_membership); they are hidden by default.OPENPROJECT_MCP_DISABLEdrops whole groups to cut prompt cost, e.g.OPENPROJECT_MCP_DISABLE=meetings,news. The valid group tags are:work_packages,wp_collaboration,attachments,git_activity,projects,queries,notifications,time_entries,versions,people,metadata,meetings,wiki,documents,budgets,news,reporting— the same tags that head each section of the tool catalog below.
Independent of all three, every destructive tool (the eight permanent deletes) requires an
explicit confirm=true argument before it acts.
Transports
stdio (default) — what Claude Code, Claude Desktop and most local MCP clients use. Logs go to stderr; stdout carries only the protocol.
Streamable HTTP —
openproject-mcp-server --transport http. Binds127.0.0.1:8000by default; override with--host/--portorOPENPROJECT_MCP_HTTP_HOST/OPENPROJECT_MCP_HTTP_PORT. The HTTP transport refuses to start without authentication configured: setOPENPROJECT_MCP_AUTH_TOKENSto a comma-separated list of bearer tokens, or — for local development only — setOPENPROJECT_MCP_INSECURE=1.With tokens configured, every HTTP request to the MCP endpoint must carry an
Authorization: Bearer <token>header whose token matches one of the configured values (compared in constant time); requests with a missing, malformed or unknown token are rejected with a 401 and aWWW-Authenticateheader. Multiple tokens are supported — for example one per client, so each can be revoked independently. Tokens must be ASCII, and the endpoint applies no rate limiting, so use long random values — for examplepython -c "import secrets; print(secrets.token_urlsafe(32))".OPENPROJECT_MCP_INSECURE=1remains a development-only escape hatch that leaves the endpoint open without authentication.The server does not terminate TLS, so tokens would otherwise cross the network in plaintext: keep the default
127.0.0.1bind, and if you must expose the HTTP transport beyond localhost, put it behind a TLS-terminating reverse proxy (for example nginx or Caddy).
Tools
87 tools: 39 read, 45 write and 3 admin-gated writes. The admin tools stay hidden unless
OPENPROJECT_MCP_ADMIN_TOOLS=1; the 13 destructive tools additionally require confirm=true
on every call. Each section heading names the group tag accepted by
OPENPROJECT_MCP_DISABLE.
Work packages (work_packages)
Tool | Kind | What it does |
| Read | Find work packages by text when you do not know their ids. |
| Read | List work packages with structured filters — the workhorse read tool. |
| Read | Read one work package in full: description, dates, custom fields, parent and progress. |
| Write | Create a work package, validated through OpenProject's own form endpoint first. |
| Write | Change any writable field of a work package, with optimistic locking. |
| Write (destructive) | Permanently delete a work package and everything attached to it. |
Comments, relations, watchers, reminders (wp_collaboration)
Tool | Kind | What it does |
| Read | Read the comment thread and change history of a work package. |
| Write | Post a comment on a work package. |
| Write | Rewrite the text of an existing work-package comment. |
| Write | Subscribe a user to a work package's notifications. |
| Write | Unsubscribe a user from a work package's notifications. |
| Write | Link two work packages (blocks, follows, duplicates, relates, ...). |
| Write | Change an existing relation's type, lag or description. |
| Write (destructive) | Remove the link between two work packages. |
| Write | React to a work-package comment with an emoji, or take the reaction back. |
| Write | Set, change or clear your personal reminder on a work package. |
| Read | List your own upcoming work-package reminders. |
| Write | Run an instance-defined one-click action on a work package. |
Attachments and file links (attachments)
Tool | Kind | What it does |
| Read | List the files attached to one container. |
| Read | Download an attachment's bytes to a file on the machine running this server. |
| Write | Attach a local file to a work package, wiki page, meeting, document, budget or comment. |
| Write (destructive) | Permanently delete one attached file from OpenProject. |
| Read | List the external-storage files (Nextcloud, OneDrive/SharePoint) linked to a work package. |
Git and pull requests (git_activity)
Tool | Kind | What it does |
| Read | Show the code behind a work package: commits, pull/merge requests and CI status. |
| Read | Read one linked GitHub pull request in full, including its CI check runs. |
Projects (projects)
Tool | Kind | What it does |
| Read | List projects, filtered server-side, one page at a time. |
| Read | Read one project in full. |
| Write | Create a project, validated through OpenProject's own form endpoint first. |
| Write | Change a project's name, description, visibility, parent, status or archived state. |
| Write (destructive) | Schedule the permanent deletion of a project and everything inside it. |
| Write | Copy a project — its settings, and optionally its work packages — into a new one. |
| Read | Check whether a background job (a project copy, a scheduled deletion) has finished. |
| Write | Add or remove a project from the authenticated user's favorites (OpenProject 17+). |
| Read | List the instance-wide phase catalog (Initiating, Planning, ...) with start/finish gates (OpenProject 16.1+). |
| Read | Read one project's phase record; ids come from a work package's |
Saved queries (queries)
Tool | Kind | What it does |
| Read | List the saved work-package views (queries) this user can open. |
| Read | Run a saved view and get its work packages as they are right now. |
| Write | Save a filter set as a reusable OpenProject view the whole team can open. |
Notifications (notifications)
Tool | Kind | What it does |
| Read | Read the authenticated user's OpenProject inbox. |
| Write | Mark specific notifications read (or unread) in one bulk request. |
| Write | Mark everything matching the filters as read — the whole inbox by default. |
Time tracking (time_entries)
Tool | Kind | What it does |
| Read | List logged time, filtered server-side, with an optional accurate total. |
| Write | Book time against a work package or a project. |
| Write | Correct an existing time entry. |
| Write (destructive) | Permanently delete a logged time entry. |
Versions and sprints (versions)
Tool | Kind | What it does |
| Read | List versions (releases, milestones, sprints) you can assign work packages to. |
| Write | Create a version (release, milestone or sprint) inside a project. |
| Write | Change a version's name, dates, description, status or sharing. |
| Write (destructive) | Permanently delete a version. |
People and memberships (people)
Tool | Kind | What it does |
| Read | Find users, groups and placeholder users, and get their ids. |
| Read | Read one user's profile: name, login, email, admin flag and status. |
| Read | List who has access to which project, and with which roles. |
| Admin write | Grant a principal one or more roles in a project. |
| Admin write | Replace the roles of an existing membership. |
| Admin write (destructive) | Revoke a principal's access to a project. |
| Read | List the roles this instance defines, with their ids. |
The three membership write tools require OPENPROJECT_MCP_ADMIN_TOOLS=1 (and an OpenProject
account with the Manage members permission).
Instance metadata and schemas (metadata)
Tool | Kind | What it does |
| Read | Check the OpenProject connection and report what this instance supports. |
| Read | List the ids and names (types, statuses, priorities, ...) that are actually valid on this instance. |
| Read | Show which fields a work package of this type accepts in this project. |
| Read | List what the authenticated user is allowed to do, globally or in one project. |
Meetings (meetings)
Tool | Kind | What it does |
| Read | List meetings: what is coming up, what already ran. |
| Read | Read one meeting in full: participants, the agenda, and any recorded outcomes. |
| Write | Schedule a meeting in a project and optionally invite participants. |
| Write | Change a meeting's title, time, place or invite list — or move its lifecycle state. |
| Write (destructive) | Permanently delete a meeting, together with its agenda and recorded outcomes. |
| Write | Add one item to a meeting's agenda, optionally pinned to a work package. |
| Write | Edit one agenda item: retitle it, rewrite its notes, retime, reorder or re-link it. |
| Write (destructive) | Permanently delete one agenda item, with any outcomes recorded against it. |
| Write | Record an outcome — a decision, a note, a follow-up ticket — against an agenda item. |
| Write | Correct a recorded outcome's kind, text or linked work package. |
| Write (destructive) | Permanently delete a recorded outcome from a running meeting's minutes. |
| Read | List recurring meeting series — the repetition rules, not the individual meetings. |
| Read | Read one recurring series in full: the schedule plus its next occurrences. |
| Write | Create a recurring meeting series: a schedule plus a template the occurrences copy. |
| Write (destructive) | Permanently delete a recurring series: template, schedule, and every occurrence. |
| Write | Materialize one occurrence of a series as a real meeting, copied from the template. |
| Write (destructive) | Cancel one occurrence of a series — skip a slot without touching the schedule. |
Wiki (wiki)
Tool | Kind | What it does |
| Read | Read a wiki page's identity and project — not its content (API v3 does not expose page bodies). |
Documents (documents)
Tool | Kind | What it does |
| Read | List the documents visible to you, across every project. |
| Read | Read one document with its full description text. |
Budgets (budgets)
Tool | Kind | What it does |
| Read | List a project's budgets — their ids and names, which is all API v3 exposes. |
News (news)
Tool | Kind | What it does |
| Read | List project news — the announcements a team publishes on its project overview. |
| Read | Read one news entry in full, including the markdown body. |
| Write | Publish a news announcement in a project. |
| Write | Correct or rewrite a published news entry. |
| Write (destructive) | Permanently delete a news entry. |
Reporting (reporting)
Tool | Kind | What it does |
| Read | Aggregate everything a status report needs about one project and one date window. |
Prompts and resources
Four prompt templates render live OpenProject data into ready-to-use briefings:
weekly_report — a weekly status report for one project: done / in progress / planned, hours and impediments.
daily_standup — today's standup for one project: yesterday's movement, what is due today, and what is blocked.
triage_inbox — groups your unread notifications by reason and suggests actions.
groom_backlog — sweeps a project's open backlog for unassigned, stale and unestimated work.
Three resource templates expose OpenProject objects at stable URIs:
openproject://work_package/{id}— one work package as JSON.openproject://project/{identifier}— one project as JSON (identifier slug or numeric id).openproject://attachment/{id}— the attachment's bytes with the detected MIME type.
Supported OpenProject versions
The server targets OpenProject 14 LTS through 17.x. Instead of assuming one API dialect, it probes the instance lazily on first need and caches the result for an hour. The version-dependent surfaces:
Internal (private) comments need OpenProject >= 16. Older servers silently ignore the internal flag, so below 16 the server refuses with a clear error rather than posting a comment publicly that you asked to keep internal.
Emoji reactions (
toggle_comment_reaction) need OpenProject >= 16; detected from the version, tolerant of a 404 at call time.Project favorites (
set_project_favorite) need OpenProject >= 17; same tolerance.Time-entry filters: the work-package filter is probed (
entityId, falling back to the pre-15.xworkPackagespelling).Permission contexts (
list_permissions): the context prefix is probed (p{id}, falling back to thew{id}spelling introduced in 17.2).Meetings time filter: OpenProject 17.6 changed the wire dialect;
list_meetingsdiscovers which spelling the instance accepts and caches it.
Features that depend on optional instance modules (meetings, news, documents, budgets, wiki, backlogs, GitHub/GitLab integration, external storages) degrade honestly when the module is absent: list tools return an empty page with an in-band note naming the missing module, and detail tools return a structured error explaining both possible readings of the 404.
Security
The API key and OAuth token are held as
SecretStrand never logged;Authorizationand cookie headers are redacted from every log record. Request/response bodies are only logged at DEBUG level withOPENPROJECT_MCP_LOG_BODIES=1, for development.The
Authorizationheader is stripped whenever a redirect leaves the OpenProject origin — attachment downloads redirect to presigned object-storage URLs, and the credential must not travel there.TLS is always verified. Private CAs are supported via
OPENPROJECT_MCP_CA_BUNDLE; an insecure-TLS switch deliberately does not exist.Attachment downloads respect OpenProject's virus scanner: quarantined files are never fetched and produce a structured
attachment_quarantinederror. Downloads are capped byOPENPROJECT_MCP_MAX_DOWNLOAD_MB, stored under sanitized file names (directory separators and traversal sequences are neutralized), and reported with a SHA-256 of the bytes.Destructive tools require
confirm=trueand are annotated so clients can ask the user first.The HTTP transport refuses to start without
OPENPROJECT_MCP_AUTH_TOKENSconfigured and verifies theAuthorization: Bearerheader on every request — missing or invalid tokens are rejected with a 401, and token comparison is constant-time. The server does not terminate TLS: keep the default127.0.0.1bind, and put a TLS-terminating reverse proxy in front if the port must be reachable from anywhere else (see Transports).
Troubleshooting
Symptom | What it means and what to do |
Server exits with "cannot start, configuration is incomplete" | Run |
401 authentication_failed | The API key is wrong, revoked, or belongs to a blocked account. Generate a fresh token under My account → Access tokens. |
403 permission_denied | The account is authenticated but lacks a role permission for that project, or the relevant module is disabled for it. |
Empty list plus a note about a missing module | That OpenProject module (meetings, news, documents, ...) is not installed or not enabled in the project — the empty result is the honest answer, not an error. |
TLS certificate errors | Your instance uses a private CA: point |
| Set |
401 from the MCP HTTP endpoint itself | The request's |
Proxies | Standard |
When reporting an issue, please include your OpenProject version, the output of
openproject-mcp-server --version, and the output of openproject-mcp-server --check.
Development
git clone https://github.com/kar-thik/openproject-mcp
cd openproject-mcp
uv sync --group dev
uv run pytest # entire suite runs offline (respx-mocked HTTP), no instance needed
uv run ruff check .
uv run ruff format --check .
uv run pyright # strict modeThe suite currently contains no tests that need a live OpenProject instance; an opt-in
integration marker is registered and reserved for any that are added later. The
full technical specification lives in
SPEC.md.
Releasing is documented in CONTRIBUTING.md.
License and trademark
MIT — see LICENSE.
This is a community project. It is not affiliated with, endorsed by, or supported by OpenProject GmbH. "OpenProject" is a trademark of OpenProject GmbH and is used here only to indicate interoperability.
Available Tools
84 toolsadd_meeting_agenda_itemAdd meeting agenda itemA
Add one item to a meeting's agenda, optionally pinned to a work package.
Use it to build or extend an agenda: "add 'Release readiness' with 15 minutes", or "put #1234 on Thursday's agenda". Linking a work package is also how a ticket learns it was discussed — the link shows up on the work package's Meetings tab.
Returns the created item: {id, title, notes, duration_minutes, position, item_type, presenter, work_package, section, outcomes, meeting, created_at}. position is where
it landed in the agenda; items are appended to the meeting's last section.
Pitfalls. This needs the 'manage agendas' permission, so a 403 is about the account, not
the payload. A 422 usually means the work package is not visible to this account or the
meeting is already closed — violations names the attribute. The item's type is fixed
here: a simple item cannot become a work-package one later, because itemType is
create-only upstream.
Cross-references: get_meeting(meeting_id=...) to see the agenda you are appending to;
update_meeting_agenda_item to fix or reorder the item afterwards;
delete_meeting_agenda_item to remove it; add_meeting_outcome to record a decision
against it; list_meetings for the meeting id; search_work_packages for the work
package id.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | Markdown notes for the item. Omit for none. | |
| title | Yes | Agenda item title, e.g. 'Release readiness'. For a work-package item the UI shows the work package's subject instead, but a title is still accepted and stored. | |
| meeting_id | Yes | Numeric meeting id from list_meetings or get_meeting. The item is appended to that meeting's last agenda section. | |
| work_package_id | No | Work package to discuss under this item, from list_work_packages or search_work_packages. Passing it makes this a work-package item, which is what puts the meeting into that ticket's Meetings tab. It must be visible to this account. | |
| duration_minutes | No | Planned length of this item in minutes (0-1440). Omit to leave the item untimed; the meeting's own duration is unaffected either way. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Agenda item id (not the meeting id, not a work package id). |
| notes | No | Item notes as markdown (raw); html is dropped. |
| title | No | Item title. Empty for a work-package item, where the work package's subject is the title shown in the UI. |
| meeting | No | Meeting the item was added to. |
| section | No | Agenda section the item sits in, when the meeting uses sections. |
| outcomes | No | Outcomes recorded against this item; always a list, empty when none. |
| position | No | 1-based order within the agenda. |
| item_type | No | 'simple' for a free-text item, 'work_package' for a linked one. |
| presenter | No | User presenting this item. |
| created_at | No | ISO 8601 UTC timestamp. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting_agenda_item's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| work_package | No | Work package this item discusses. Null both when none is linked and when the linked one is invisible to this account — 'notes' says when the latter happened. |
| duration_minutes | No | Planned length in minutes; null when the organizer set none. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations flag a non-read-only, non-idempotent write, and the description adds substantial context: the 'manage agendas' permission requirement, meaning of 403 and 422, the fixture that itemType is create-only upstream, and append-to-last-section behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose, examples, return value, pitfalls, and cross-references. It is front-loaded with the core purpose and every sentence adds useful information without fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter mutation tool with an output schema, the description covers usage rationale, return shape, error semantics, and all relevant sibling tools. The output schema exists, so the return-value summary is appropriately supplementary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema coverage is 100% and each parameter already has a rich description (e.g., work_package_id effect, duration_minutes limits, meeting_id append behavior). The tool description offers examples and pitfall context but does not materially extend parameter-level semantics beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('Add') and resource ('one item to a meeting's agenda') plus an optional work-package pin. It clearly distinguishes itself from sibling tools by naming update, delete, and outcome-related alternatives in the cross-references.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says 'Use it to build or extend an agenda' and provides concrete usage examples. Cross-references specify when to use get_meeting, update_meeting_agenda_item, delete_meeting_agenda_item, and add_meeting_outcome, and it explains error conditions (403/422) for troubleshooting.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_meeting_outcomeAdd meeting outcomeA
Record an outcome — a decision, a note, a follow-up ticket — against an agenda item.
This is how minutes are written through the API: "decision: ship on Friday" becomes
kind='decision' with the text in notes; linking the follow-up work package makes
it kind='work_package'. Outcomes appear under their agenda item in get_meeting.
Returns the created outcome: {id, kind, notes, author, work_package, agenda_item}.
Pitfalls — the timing rule matters most. Outcomes can only be written while the
meeting state is exactly 'in_progress': before that, and again once it is closed,
every outcome write answers a validation error. Start the meeting with
update_meeting(meeting_id=..., state='in_progress') first. Items in a backlog
section refuse outcomes the same way. This needs the 'manage outcomes' permission, so
a 403 is about the account, not the payload. On OpenProject before 17.6 there is no
outcomes API at all — the 404 hint says so.
Cross-references: get_meeting for the agenda item id and to read the outcome back;
update_meeting to put the meeting into 'in_progress'; update_meeting_outcome /
delete_meeting_outcome to correct or remove it while the meeting still runs.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | 'information' (a note for the minutes, requires notes), 'decision' (what was decided), or 'work_package' (the outcome IS a follow-up ticket, requires work_package_id). | information |
| notes | No | The outcome text as markdown. Required for kind='information'; optional but usually worth writing for the other kinds. | |
| agenda_item_id | Yes | Numeric agenda item id the outcome is recorded against, from get_meeting's agenda_items. Not the meeting id. | |
| work_package_id | No | Work package the outcome points at, from search_work_packages. Required for kind='work_package'; it must be visible to this account. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Outcome id. |
| kind | No | Outcome kind as the instance defines it, e.g. 'decision'. |
| notes | No | Outcome text as markdown (raw); html is dropped. |
| author | No | User who recorded the outcome. |
| agenda_item | No | Agenda item the outcome is recorded against. |
| work_package | No | Work package the outcome points at, when one was linked. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses critical behavioral traits beyond annotations: the timing state machine with a validation error, the 'backlog section' refusal, the 'manage outcomes' permission causing 403, and the OpenProject <17.6 404 quirk. It also specifies the exact return payload shape. Annotations only set hints (readOnlyHint=false), but the description adds real operational context with no contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence carries weight. It is front-loaded with purpose, then flows into mechanics, return value, and a dedicated 'Pitfalls' section. For a tool with state constraints and cross-references, this structure is efficient and scannable, not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (meeting state dependency, multiple kinds, permissions, version quirks), the description covers all necessary context: what it does, how to use it, when it fails, what it returns, and how it relates to siblings. The output schema is also explained explicitly enough that a complete picture is provided with minimal reliance on the structured schema alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already covers all four parameters with meaningful descriptions, but the description enriches them with concrete usage examples (e.g., 'decision: ship on Friday' becomes kind='decision' with text in notes) and clarifies the relationship between kind, notes, and work_package_id. It also notes that agenda_item_id is not the meeting id, adding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Record an outcome against an agenda item') and immediately enumerates the exact kinds of outcomes (decision, note, follow-up ticket). It distinguishes itself from sibling tools by framing itself as the way to write minutes through the API, with explicit cross-references to get_meeting and update_meeting_outcome/delete_meeting_outcome.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use context: it is for writing minutes during an 'in_progress' meeting state, before which and after closure all writes fail. It names the required prerequisite (update_meeting to set state) and the permission ('manage outcomes'), plus points to update_meeting_outcome/delete_meeting_outcome for corrections. This goes far beyond vague guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_work_package_commentAdd 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
list_work_package_comments returns, so the id can be reused.
Pitfalls. Every call creates a new comment; it is not idempotent, so do
not retry blindly after a timeout — read the thread first.
internal=true is refused on OpenProject below 16.0 because those
versions accept the flag and publish the comment anyway; upgrade or post
publicly, deliberately. notify=false suppresses notifications only,
the comment is still visible to everyone who can see the work package.
Cross-references: read the thread with list_work_package_comments;
change fields (status, assignee, dates) with update_work_package
rather than describing the change in prose; attach a file with
upload_attachment.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Work package id to comment on. Get it from search_work_packages or list_work_packages. | |
| notify | No | Send the usual watcher/assignee notifications. Set false for bulk or bookkeeping comments so inboxes stay quiet. | |
| comment | Yes | Comment body in markdown. OpenProject renders it; @-mentions need the user's mention syntax, plain names do not notify anyone. | |
| internal | No | Post as an internal comment, visible only to project members with the internal-comments permission. Requires OpenProject >= 16; on older instances the call fails instead of posting publicly. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Activity id. Pass it back as list_work_package_comments(activity_id=...) to read a truncated comment in full. |
| kind | Yes | 'comment' when the entry carries comment text, 'field_change' otherwise. |
| author | No | User who wrote the entry. |
| comment | No | Comment body as markdown (raw); html is dropped. Cut to max_comment_chars when 'truncated' is true. |
| details | No | Field changes recorded with this entry; always a list, empty for a comment-only entry. |
| version | No | Journal version number of this entry within the work package. |
| internal | No | True for internal (project-member-only) comments; OpenProject >= 16. |
| truncated | No | True when the comment was cut to max_comment_chars. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp of the last edit. |
| work_package | No | Work package the entry belongs to. |
| comment_length | No | Character length of the untruncated comment; set only when truncated. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate non-idempotent behavior, but the description goes further by warning that every call creates a new comment and should not be blindly retried after a timeout. It also discloses the version-specific internal flag pitfall and clarifies that notify=false only suppresses notifications while the comment remains visible.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with sections for main usage, pitfalls, and cross-references. Every sentence carries useful information; no filler or redundancy exists.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description still notes the return shape and id reuse potential. It covers non-idempotence, version constraints, notification behavior, and alternative tools, making it complete for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds value beyond the schema. It explains the non-idempotent nature of the call (relevant to retries), the subtle behavior of internal=true on older OpenProject versions, and the exact meaning of notify=false. This enriches the parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Post a comment on a work package.' It clearly distinguishes this tool from siblings by cross-referencing list_work_package_comments, update_work_package, and upload_attachment, and by contrasting it with edit_work_package_comment.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use the tool: 'reply in a ticket's thread, record a decision, or leave a handover note.' It also provides direct alternatives: use update_work_package for field changes and upload_attachment for files, and read the thread with list_work_package_comments before retrying.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_work_package_watcherAdd work package watcherAIdempotent
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 user otherwise, and
adding someone other than yourself needs the add-work-package-watchers
permission (adding yourself only needs view access). Calling twice is
harmless: the second call reports changed: false. Watching is not
assignment — use update_work_package(assignee=...) for that.
Cross-references: get_work_package(include=['watchers']) lists who
already watches; remove_work_package_watcher is the reverse;
list_work_package_comments shows what watchers are being notified
about.
| Name | Required | Description | Default |
|---|---|---|---|
| user_id | Yes | Numeric id of the user to add. Get it from list_users, from get_instance_info for the current user, or from any Ref in a work-package result. The string 'me' is not accepted here — the API needs a real id. | |
| work_package_id | Yes | Work package to watch. Ids come from search_work_packages, list_work_packages or get_work_package. |
Output Schema
| Name | Required | Description |
|---|---|---|
| user | No | The watcher. 'id' is the user id; 'name' is filled in only when OpenProject returned the user resource (it does on add, not on remove). |
| changed | No | True when this call actually changed the watcher list, false when the user was already watching. Null when OpenProject does not report it — removals answer 204 whether or not the user was watching. |
| message | Yes | Human-readable confirmation. |
| watching | Yes | Watch state after the call: true after adding, false after removing. |
| work_package_id | Yes | Work package whose watcher list was changed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses idempotency ('Calling twice is harmless: the second call reports changed: false'), permission requirements, and error behavior (422 on user visibility violation). These go well beyond the annotations (idempotentHint, etc.) and add valuable behavioral context. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place. It uses clear sections (purpose, usage, return value, pitfalls, cross-references) and front-loads the main action. No fluff or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description still summarizes the return values. It covers prerequisites, error conditions, permissions, idempotency, and related tools. For a tool of moderate complexity with two parameters and a clear output schema, this is fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed parameter descriptions already. The description adds extra nuance for user_id (the 'me' string is not accepted, permission differences when adding oneself vs. others) and for work_package_id (source of valid IDs). This elevates it slightly above the high-coverage baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Subscribe a user to a work package's notifications.' It clearly distinguishes the tool from siblings by contrasting watching with assignment ('Watching is not assignment — use update_work_package(assignee=...) for that') and cross-referencing remove_work_package_watcher as the reverse operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'Use this when someone should be kept in the loop on a ticket without being assigned to it.' It also provides exclusions (assignment use case), alternatives (remove_work_package_watcher), and prerequisite conditions (user must have view access, permission needed for adding others). This is a model of usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cancel_recurring_meeting_occurrenceCancel recurring meeting occurrenceADestructiveIdempotent
Cancel one occurrence of a series — skip a slot without touching the schedule.
Use it for "no sync next Monday": the slot stays in the occurrences list as
'cancelled' (backed by a cancelled stub meeting) while the series keeps
running. A cancelled occurrence is recoverable —
init_recurring_meeting_occurrence at the same instant restores it to 'open' — but
the cancellation itself may email participants when the series has notify set,
which is why it is confirm-gated.
Returns a small confirmation object carrying the normalized instant.
Pitfalls. The instant is matched exactly and never validated against the schedule:
cancelling at a wrong time succeeds (204) by creating a cancelled phantom stub while
the real occurrence lives on — always copy start_time from
get_recurring_meeting's occurrences. A conflict error (409) means the occurrence
is already instantiated as a live meeting, which OpenProject refuses to cancel in
place: delete_meeting(meeting_id=...) (the id is in the occurrences row) is the
move then. Cancelling an already-cancelled slot is an idempotent success. This needs
the 'edit meetings' permission.
Cross-references: get_recurring_meeting for the exact start_time and the
occurrence's meeting_id; delete_meeting for an instantiated occurrence;
delete_recurring_meeting to end the whole series.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first. Calling with confirm=false returns a confirmation_required error rather than cancelling anything. | |
| start_time | Yes | The occurrence's scheduled instant, copied VERBATIM from a get_recurring_meeting occurrences row ('2026-08-12T10:00:00Z'). Matching is exact-instant — a wrong time cancels a phantom stub while the real occurrence lives on. | |
| recurring_meeting_id | Yes | Numeric series id from list_recurring_meetings. Never a meeting id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | Human-readable confirmation naming what was cancelled. |
| cancelled | Yes | True once OpenProject accepted the cancellation. |
| start_time | Yes | The cancelled instant, normalized to the canonical UTC 'Z' form. |
| recurring_meeting_id | Yes | Series the occurrence belongs to. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the annotations by detailing side effects (may email participants with `notify` set), recovery semantics, exact-instant matching pitfalls, conflict error meaning, idempotent success, and required permissions. This fully discloses the operational behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense yet well-organized: opening summary, use case, pitfalls, and cross-references. Every sentence adds meaningful detail; no filler words. It front-loads the core purpose and then layers nuance logically.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers permission requirements, error semantics, idempotency, side effects, recoverability, and exactly references related tools. With an output schema present, the return confirmation is briefly described without over-explaining. Nothing is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though schema coverage is 100%, the description enriches each parameter with critical context: `start_time` must be copied verbatim, exact matching is required, and wrong times create phantom stubs. `recurring_meeting_id` is explicitly distinguished from a meeting id. This adds real value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool cancels one occurrence of a series, distinguishing it from deleting the entire series. It explicitly contrasts with `delete_recurring_meeting` (end series) and `delete_meeting` (instantiated occurrence), making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete use cases ('no sync next Monday'), explicitly names alternatives (`init_recurring_meeting_occurrence`, `delete_meeting`, `delete_recurring_meeting`), and provides fallback behavior for conflict errors. This is exemplary guidance on when to use this tool vs. siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
copy_projectCopy 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 POST /projects/{id}/copy/form first, so a name that
derives a taken identifier comes back as violations naming the attribute instead of
a failed background job.
Copying is ASYNCHRONOUS: OpenProject queues a job and answers immediately. This tool
therefore returns {scheduled: true, job_id, status, message, notes} and NEVER
claims the copy exists — a large project takes minutes. Poll
get_job_status(job_id=...) until status is 'success' (it then reports the new
project) or 'failure'.
Pitfalls: only include_work_packages and notify are exposed; every other copy
flag (members, versions, wiki, boards, file links) keeps this instance's own default,
which the form fills in — so the copy can contain more than the two parameters
suggest. A 403 means the account may not copy this project (it needs the 'copy project'
permission on the template plus the right to create projects). Work packages come
across with their relations, but time entries and comment histories do not.
Cross-references: get_job_status follows the job to completion; list_projects
or get_project confirms the result; create_project makes an empty project
instead; update_project renames the copy afterwards.
| Name | Required | Description | Default |
|---|---|---|---|
| notify | No | Send OpenProject notification emails for everything the copy creates. false (default) keeps a large copy quiet; the copy itself is identical. | |
| new_name | Yes | Display name of the copy, e.g. 'Apollo migration (2027)'. OpenProject derives the copy's URL identifier from it; the derived value is reported by get_job_status once the job succeeds. | |
| id_or_identifier | Yes | Numeric id or URL identifier of the project to copy (the TEMPLATE, not the copy). Both are accepted and come from list_projects or get_project. | |
| include_work_packages | No | Copy the template's work packages too (default). false copies the project shell — members, versions, categories, wiki and the other settings the instance defaults to — without any tickets. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | What is still outstanding. Always says the copy has to be polled. |
| job_id | No | Background job id — pass it to get_job_status. Null when the instance reported none; 'notes' then says what to do instead. |
| source | Yes | The project id or identifier that was copied. |
| status | No | Job state at the moment the copy was accepted, normally 'in_queue'. Null when the instance reported none. |
| message | No | Message the job reported, when it carried one. |
| project | No | The copy itself ({id, name}) — only populated on the rare instance that finishes the job before answering. Normally null: read it from get_job_status. |
| new_name | Yes | Name requested for the copy. |
| scheduled | Yes | True once OpenProject accepted the request. The copy itself runs in the background and is not finished when this is true. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description extensively discloses behavioral traits beyond the annotations: copying is asynchronous, the tool returns a scheduled response and never claims the copy exists, the job may take minutes, permission requirements (403 meaning), and what is and isn't copied (time entries and comment histories are not). Annotations only indicate mutating and non-idempotent behavior, so the description carries the full burden and does so thoroughly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded: purpose, usage scenario, async behavior, pitfalls, and cross-references are each in logical paragraphs. Every sentence adds value; length is justified by the tool's complexity. No redundancy with schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (async job, permission requirements, default flags, cross-tool dependencies), the description is remarkably complete. It explains the return shape, how to poll for status, what the job reports, and even mentions the underlying POST /projects/{id}/copy/form call to explain validation behavior. The existence of an output schema means return-style details are not required, and the description still covers behavioral and procedural aspects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds crucial meaning beyond the schema: id_or_identifier is explicitly the template project, new_name's derived identifier is reported by get_job_status, include_work_packages explains the shell vs. full copy, and notify explains the impact on email volume. This extra semantic context is valuable and not merely schema repetition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear, specific verb and resource: 'Copy a project — its settings, and optionally its work packages — into a new one.' It also distinguishes this from related tools by noting it is the only way to reproduce members, versions, categories, and enabled modules in one call, differentiating it from create_project and update_project.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Use it to spin a new engagement or release off a template project.' It also gives exclusions and alternatives in the cross-references section, naming create_project for an empty project, get_job_status for following the async job, and list_projects/get_project for confirming the result. This is model guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_meetingCreate meetingA
Schedule a meeting in a project and optionally invite participants.
Use it for "book a review on Thursday" style requests. The call goes through
POST /meetings/form first, so a missing permission, an impossible time or a
participant who cannot see the project comes back as violations naming the attribute
instead of an opaque rejection.
Returns the created meeting in the same shape as get_meeting (its agenda_items are
empty — add them with add_meeting_agenda_item).
Pitfalls. Check state in the result: current OpenProject versions create meetings as
'draft', which means participants do not see it until it is opened —
update_meeting(meeting_id=..., state='open') publishes it, exactly as the UI does.
Invitation emails are not sent by an API create. start_time needs a timezone — the
server stores an instant, not a wall-clock time. Recurring meetings cannot be created
through this tool — use create_recurring_meeting.
Cross-references: add_meeting_agenda_item(meeting_id=...) to build the agenda;
get_meeting to read it back; list_projects for the project id; search_principals (or
list_project_memberships) for participant ids.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Meeting title, e.g. 'Sprint 12 planning'. | |
| project_id | Yes | Numeric id or identifier of the project the meeting belongs to. It must have the Meetings module enabled and this account needs the 'create meetings' permission in it. | |
| start_time | Yes | Start as ISO 8601 WITH a timezone: '2026-08-03T14:00:00Z' (UTC) or '2026-08-03T16:00:00+02:00'. A time without an offset is rejected locally rather than booked in the wrong hour. | |
| participants | No | User ids to invite, from search_principals or a project's memberships. Every one of them needs 'view meetings' in the project or the create is rejected. Omit to let OpenProject invite only the author. | |
| duration_minutes | Yes | Scheduled length in minutes (90 = one and a half hours). Sent as the API's ISO duration; the result reports it back as duration_hours. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Meeting id — what get_meeting and add_meeting_agenda_item take. |
| notes | No | Degradation notes: agenda items that could not be read, work packages this account may not see. |
| state | No | Lifecycle state: 'draft' (not yet opened to participants), 'open', 'in_progress', 'closed' or 'cancelled'. Cancelled meetings are excluded from listings. |
| title | No | Meeting title. |
| author | No | User who created the meeting. |
| project | No | Project the meeting belongs to. |
| end_time | No | ISO 8601 UTC end timestamp, derived from start plus duration. |
| location | No | Room name or meeting URL as typed by the organizer. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_time | No | ISO 8601 UTC start timestamp; null for an undated meeting. |
| updated_at | No | ISO 8601 UTC timestamp. |
| agenda_items | No | The agenda in order; always a list. Empty means either no agenda or an unreadable one — check 'notes' before concluding the meeting had none. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| participants | No | Invited users; always a list. Attendance is not exposed by API v3. |
| duration_hours | No | Scheduled length in hours (1.5 = 90 minutes); the wire sends an ISO duration, which is converted here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavioral context beyond annotations: it reveals the POST /meetings/form endpoint behavior (violations naming the attribute), draft state pitfall, lack of invitation emails, timezone storage semantics, and non-support for recurring meetings. This complements the annotations (readOnlyHint=false, idempotentHint=false) without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a purpose statement, usage context, pitfalls, and cross-references. While moderately long, every sentence conveys essential operational information—no filler or repetition of schema fields. Front-loaded with the action and key use case.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a rich output schema and sibling context, the description fully covers the tool's behavior: return shape (same as get_meeting with empty agenda_items), how to add agenda items, and all necessary cross-references (list_projects, search_principals, update_meeting for state). It equips the agent to use the tool correctly end-to-end.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already has 100% coverage with thorough descriptions for each parameter. The description adds extra value by explaining the timezone requirement for start_time, the view-meetings permission for participants, and the ISO duration conversion for duration_minutes, enriching the schema's meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Schedule a meeting in a project and optionally invite participants.' It clearly distinguishes from sibling tools by explicitly stating recurring meetings are not supported (use create_recurring_meeting) and referencing add_meeting_agenda_item for agenda building.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage context ('Use it for "book a review on Thursday" style requests'), prerequisites (project must have Meetings module, permissions), and exclusions (recurring meetings). Cross-references to list_projects and search_principals give concrete guidance for finding required parameter values.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_newsCreate newsA
Publish a news announcement in a project.
Use it for release notes, a weekly report, a maintenance window — anything the whole project should see on its overview page. The author is the authenticated account and is set by the server; project members watching the project are notified.
Returns the created entry {id, title, summary, description, project, author, created_at, updated_at, can_manage}; the id is what update_news and
delete_news consume.
Pitfalls: this needs the 'manage news' permission in that project, which exists only
while the project has the news module enabled — a 403 is about the account or the
module, never about the text. title is required and rejected when blank (checked
here, before the request). There is no draft state: the entry is public to everyone
who can view the project the moment it is created. News is not a work package — for
something that needs assigning and tracking use create_work_package instead.
Cross-references: list_news to see what is already published (and to avoid
duplicates); update_news to correct an entry afterwards; list_projects for
the project id.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Headline, required, up to 256 characters. This is what readers see in the project overview and in notification digests. | |
| summary | No | Optional teaser shown under the headline, up to 255 characters. Plain text, not markdown. Omit for none. | |
| project_id | Yes | Numeric id (or URL identifier, resolved for you) of the project to publish in. The project cannot be changed afterwards, and it must have the news module enabled. | |
| description | No | The announcement body as markdown — headings, lists and links all render. Omit for a headline-only entry. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | News id — what get_news, update_news and delete_news consume. |
| title | No | Headline of the announcement. |
| author | No | User who published it ({id, name}); set by the server. |
| project | No | Project the announcement belongs to ({id, name}). |
| summary | No | Short teaser OpenProject shows under the headline; may be empty. The full body is NOT here — read it with get_news(news_id=...). |
| can_manage | No | True when this account may change or delete this entry (OpenProject renders the update/delete links only with the 'manage news' permission). False means update_news and delete_news would fail with 403. |
| created_at | No | ISO 8601 UTC publication timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | The announcement body as markdown (raw); html is dropped. Empty string when the entry has only a headline and summary. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the basic annotations by disclosing server-set author, member notifications, permission requirements ('manage news'), 403 semantics, no draft state, immediate public visibility, and that the returned id feeds update/delete tools. This rich behavioral context is not present in the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured: one-sentence purpose, usage, return shape, pitfalls, and cross-references. Every section earns its place; the pitfalls are essential. While not ultra-short, the length is justified by the tool's complexity and failure modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers all bases: purpose, usage, expected output, permissions, edge cases (403, blank title), API behavior (no draft, immediate public), and relationships to sibling tools. The output schema is explained with the returned fields and how the id is consumed. Very complete for a create operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description adds meaningful extra context: project_id cannot be changed afterwards, title blank-check happens before the request, summary is plain text not markdown, and description supports markdown. This goes beyond the schema's own field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Publish a news announcement in a project.' It clearly defines the scope (project overview page) and explicitly distinguishes this from work packages ('News is not a work package'), making it easy to identify against siblings like create_work_package.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Use it for release notes, a weekly report, a maintenance window — anything the whole project should see on its overview page.' It also names an alternative: 'use create_work_package instead' for assignable/trackable items, and cross-references list_news/update_news/list_projects.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_projectCreate projectA
Create a project, validated through OpenProject's own form endpoint first.
Use it for a new workspace or, with parent_id, for a subproject of an existing
one. The call runs POST /projects/form before committing, so a taken identifier,
an unusable parent or a bad status comes back as violations naming the attribute
instead of an opaque failure — and the identifier OpenProject derives from the name
is used verbatim on the commit.
Returns the created project: {id, identifier, name, active, public, parent, status_code, description, status_explanation, created_at, updated_at}. Keep the
id — every other tool's project_id accepts it, as does the identifier.
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
get_project_metadata(project_id=...) before creating work packages in it.
Members are not copied from the parent; add them with create_membership.
Cross-references: list_projects finds the parent id; update_project changes
any of these fields afterwards; get_project_metadata lists the types, versions
and categories valid inside the result.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Display name of the new project, e.g. 'Apollo migration'. Names need not be unique on the instance; the identifier is what must be. | |
| public | No | True makes the project visible to every logged-in user without a membership. Omit to take the instance default (normally private). | |
| parent_id | No | Numeric id or identifier of the parent project, to create a subproject. Ids come from list_projects. Omit for a top-level project. Creating a subproject needs the 'add subprojects' permission on the parent. | |
| identifier | No | URL slug for /projects/<identifier>: lowercase letters, digits, '-' and '_' only, unique across the whole instance. Omit it and OpenProject derives one from the name — the derived value is in the result. A slug that is already taken comes back as a validation violation, not a surprise rename. | |
| description | No | Project description in markdown. Omit to leave it empty. | |
| status_code | No | Initial project status: on_track, at_risk, off_track, not_started, finished or discontinued. These codes are the only accepted values — a free-text status is rejected rather than silently dropped. Omit for no status. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Numeric project id; accepted by every project_id parameter. |
| name | No | Display name. |
| active | No | False for archived projects (read-only in the UI). |
| parent | No | Parent project, when this is a subproject. |
| public | No | True when visible to users without a membership. |
| created_at | No | ISO 8601 UTC timestamp. |
| identifier | No | URL slug from /projects/<identifier>; also accepted wherever an id is. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
| status_code | No | Project status code, one of: on_track, at_risk, off_track, not_started, finished, discontinued. A code, never a translated label; null means no status has been set. |
| workspace_type | No | Workspace kind: 'project', 'program' or 'portfolio'. Pre-17 instances only have 'project'; on 17.x project listings mix all three kinds, so check this before treating a row as a plain project. |
| status_explanation | No | Free-text explanation of status_code, markdown (raw). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description reveals critical behavior: it runs POST /projects/form first, returns violations instead of opaque failures, uses the derived identifier verbatim, requires specific permissions (403 meaning), and notes that members are not copied. This significantly exceeds what annotations alone provide, with no contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear paragraphs for usage, return value, pitfalls, and cross-references. Every sentence carries useful information such as permission caveats, derived identifier behavior, and sibling tool links. It is appropriately sized for a complex creation tool with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (6 parameters, output schema, cross-tool dependencies), the description is comprehensive. It covers the return format, notes to keep the id, explains permission pitfalls, warns about default modules/types, and directs to get_project_metadata for valid values. The output schema exists, so detailed return documentation isn't needed, but it still provides a useful summary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% description coverage, so the baseline is 3. The description adds extra value by explaining the form-endpoint validation for parameters, explicitly linking parent_id to subprojects, and noting that the identifier is derived from name. However, most parameter meaning already lives in the schema, so the added semantic layer is modest.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a project' and specifies the resource and action clearly, then differentiates from siblings by mentioning subprojects via parent_id and cross-referencing list_projects, update_project, and get_project_metadata. This makes the tool's unique purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: 'Use it for a new workspace or, with parent_id, for a subproject of an existing one.' It also names alternatives and related tools in the cross-references section, such as list_projects for finding parent IDs and update_project for later changes, giving clear when-to-use and when-not-to-use context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_recurring_meetingCreate recurring meetingA
Create a recurring meeting series: a schedule plus a template the occurrences copy.
Use it for "set up a weekly sync Mondays at 9" style requests. The frequency and end_after combinations are validated locally BEFORE anything is sent — OpenProject's own "infer the monthly fields" defaults never apply to API creates, so a bad combination is rejected here with the allowed matrix spelled out.
Returns the created series in the same shape as get_recurring_meeting, including
the computed next occurrences (their start_time strings are what the occurrence
tools take) and template_meeting_id.
Pitfalls — two upstream quirks are handled but must be understood. First, the
template meeting is created as a DRAFT: notes says so, and occurrences cannot be
initialized until update_meeting(meeting_id=<template_meeting_id>, state='open')
publishes it. Second, OpenProject overwrites time_zone on create with the API
account's own zone; this tool detects that and corrects it with a follow-up PATCH —
if that correction is refused (it needs 'edit meetings'), the series is still created
and notes names the zone it actually runs in. start_time must be now or in the
future, or the create is rejected with a validation error.
Cross-references: get_recurring_meeting to read it back;
update_meeting(meeting_id=<template_meeting_id>, ...) to build the shared agenda
and publish the template; init_recurring_meeting_occurrence to materialize a slot;
list_projects for the project id.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Series title, e.g. 'Weekly team sync'. | |
| notify | No | True emails participants about schedule changes and cancellations. Defaults to false — an API-created series stays quiet. | |
| end_date | No | Last possible date as 'YYYY-MM-DD'; required for, and only valid with, end_after='specific_date'. | |
| interval | No | Every N days/weeks/months (default 1 = every occurrence of the rule). Not applicable to 'working_days'. | |
| location | No | Room name or meeting URL every occurrence inherits. Omit for none. | |
| end_after | No | How the series ends: 'never' (the default), 'specific_date' (needs end_date) or 'iterations' (needs iterations). | never |
| frequency | No | Repetition rule: 'daily', 'working_days' (every working day), 'weekly' (the default), 'monthly_day_of_month' (needs monthly_day) or 'monthly_nth_weekday' (needs monthly_ordinal + monthly_weekday). | weekly |
| time_zone | Yes | IANA time zone the schedule computes in, e.g. 'Europe/Berlin' or 'Etc/UTC' — required, because it decides what 'every Monday 09:00' means across DST changes. Validated locally: OpenProject would store a typo silently and fall back to the account's zone. | |
| iterations | No | Total number of occurrences (1-1000); required for, and only valid with, end_after='iterations'. | |
| project_id | Yes | Numeric id or identifier of the project the series belongs to. It must have the Meetings module enabled and this account needs the 'create meetings' permission in it. | |
| start_time | Yes | First occurrence as ISO 8601 WITH a timezone: '2026-09-01T09:00:00Z' or '2026-09-01T11:00:00+02:00'. Must be now or in the future; a time without an offset is rejected locally. | |
| monthly_day | No | Day of the month (1-31); required for, and only valid with, frequency='monthly_day_of_month'. | |
| monthly_ordinal | No | Which weekday of the month: 1-4, or -1 for the last one; required for, and only valid with, frequency='monthly_nth_weekday'. | |
| monthly_weekday | No | Weekday name ('monday'…'sunday'); required for, and only valid with, frequency='monthly_nth_weekday'. | |
| duration_minutes | Yes | Length of each occurrence in minutes (90 = one and a half hours). The result reports it back as duration_hours. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Series id — what get_recurring_meeting and the occurrence tools take. Not a meeting id. |
| notes | No | Degradation markers: an unreadable schedule, the occurrence cap, a time zone that could not be applied, a draft template. |
| title | No | Series title. |
| author | No | User who created the series. |
| project | No | Project the series belongs to. |
| end_date | No | Last possible date (ISO); only when end_after='specific_date'. |
| interval | No | Every N days/weeks/months; always 1 for 'working_days'. |
| location | No | Room name or meeting URL each occurrence inherits. |
| end_after | No | 'never', 'specific_date' or 'iterations'. |
| frequency | No | Repetition rule: 'daily', 'working_days', 'weekly', 'monthly_day_of_month' or 'monthly_nth_weekday'. |
| time_zone | No | Zone the schedule computes in, as OpenProject stores it (an IANA identifier or a Rails zone name). |
| iterations | No | Total occurrences; only when end_after='iterations'. |
| start_time | No | First-occurrence start as ISO 8601 UTC. |
| monthly_day | No | Day of month (1-31); only for 'monthly_day_of_month'. |
| occurrences | No | The next upcoming slots in order (capped; see 'notes'). meeting_id is null until a slot is instantiated, and state 'planned' marks exactly those. |
| duration_hours | No | Length of each occurrence in hours (1.5 = 90 minutes). |
| monthly_ordinal | No | Which weekday occurrence (1-4, -1 = last); only for 'monthly_nth_weekday'. |
| monthly_weekday | No | Weekday name; only for 'monthly_nth_weekday'. |
| template_meeting_id | No | Id of the template meeting the occurrences are copied from. Its agenda is edited with the regular meeting tools, and a freshly created template is a DRAFT — publish it with update_meeting(meeting_id=<this>, state='open') before initialising occurrences. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses extensive behavioral traits beyond annotations: local validation of frequency/end_after combinations, template created as DRAFT, time_zone overwrite and follow-up PATCH, permission requirements, and start_time validation. Annotations only provide basic hints (readOnlyHint false, idempotentHint false), so the description carries the full burden and excels.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but appropriately structured: purpose first, then usage, return shape, pitfalls, and cross-references. Every sentence provides necessary context for a tool with 15 parameters and multiple upstream quirks. No fluff or redundancy; it earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite an output schema existing, the description still explains the return shape (same as get_recurring_meeting, with occurrences and template_meeting_id). It covers permissions, project module requirements, validation behavior, the draft-template workflow, timezone correction, and related tools. This is comprehensive for the complexity of the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds cross-parameter semantics by explaining that frequency and end_after combinations are validated locally, and that OpenProject's infer defaults never apply. It also clarifies that start_time must be now or future and time_zone is validated, adding value beyond the already-rich schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Create a recurring meeting series: a schedule plus a template the occurrences copy.' This goes beyond a generic 'create' verb, specifying the resource (recurring meeting series) and distinguishing it from single-meeting tools like create_meeting in the sibling list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use it for "set up a weekly sync Mondays at 9" style requests,' and provides cross-references to related tools for different steps (e.g., get_recurring_meeting, update_meeting, init_recurring_meeting_occurrence, list_projects). This gives clear when-to-use and alternative context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_versionCreate 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 POST /versions/form first, so a duplicate name or an
impossible date range comes back as violations naming the attribute instead of an
opaque rejection.
Returns the created version {id, name, project, status, start_date, end_date, description, sharing, source, created_at, updated_at}. The id is what
update_work_package(version=...) and update_version consume.
Pitfalls: end_date is the version's finish date and is written to the API's
endDate field — passing a date here always lands (an older client dropped it
silently). Creating versions needs the 'manage versions' permission in the project,
so a 403 is about the account, not the payload. Versions are per project: sharing is
the only way another project sees this one.
Cross-references: list_versions for what already exists (and for the ids);
update_version to change dates or close it later; list_projects for the
project id.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Version name, e.g. 'Sprint 12' or 'Release 2.1'. Must be unique inside the defining project. | |
| status | No | open (default upstream), locked or closed. Locked and closed versions cannot receive new work packages, so create with 'open' unless you are recording history. | |
| sharing | No | Which other projects may use this version: none (default), descendants, hierarchy, tree or system (the whole instance). Use 'descendants' for a release shared with subprojects. | |
| end_date | No | ISO date (YYYY-MM-DD) the version finishes — this is the version's due date, sent as the API's 'endDate'. Omit for none. | |
| project_id | Yes | Numeric id or identifier of the project that will DEFINE the version. Sharing decides which other projects can use it; the defining project cannot be changed afterwards. | |
| start_date | No | ISO date (YYYY-MM-DD) the version starts. Omit for none. | |
| description | No | Markdown description of the version's scope. Omit for none. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Version id — what update_version, delete_version and the work-package 'version' field consume. |
| name | No | Version name, e.g. 'Sprint 12' or '2.1'. |
| source | No | 'sprint' when the row came from the backlogs sprints endpoint, 'version' otherwise. Sprints ARE versions upstream, so a row can be both and is then reported as 'sprint'. |
| status | No | open, locked or closed. Locked and closed versions reject new work package assignments. |
| project | No | The project that DEFINES the version. A version shared from a parent project shows that parent here, not the project you asked about. |
| sharing | No | How far the version is shared: none, descendants, hierarchy, tree, system. |
| end_date | No | ISO date (YYYY-MM-DD); the version's finish date. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses significant behavioral details beyond the annotations: POST /versions/form validation with 'violations' naming attributes, the returned object and id usage, the end_date-to-endDate mapping (and the older client dropping it silently), the 'manage versions' permission requirement, and the per-project sharing semantics. This is rich, non-obvious context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with clear paragraphs: primary use, when-to-use, validation behavior, return value, pitfalls, and cross-references. Every sentence earns its place, and no information is wasted. It is appropriately sized for a tool with 7 parameters and subtle behavioral caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description is complete: it covers creation purpose, validation behavior, return value, permission requirements, pitfalls, and relationships to sibling tools. The output schema is also present, further reducing ambiguity. This is a comprehensive and self-sufficient description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameters with detailed descriptions (100% coverage), so the baseline is 3. The description adds extra meaning around key parameters: end_date 'always lands' and maps to the API's endDate, project_id is the defining project that cannot be changed afterwards, and the returned id is consumed by update_work_package(version=...) and update_version. These nuances go beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a version (release, milestone or sprint) inside a project,' which uses a specific verb and resource, and clearly distinguishes this tool from sibling tools like update_version, list_versions, and delete_version.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool ('Use it to open a new sprint or plan a release before assigning work packages to it') and provides cross-references to alternatives: list_versions for existing versions, update_version for changes, and list_projects for project id. This gives clear guidance on when to use this tool vs alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_work_packageCreate work packageA
Create a work package, validated through OpenProject's own form endpoint first.
Use it for new tasks, bugs, subtasks (parent_id) and milestones (date). The form
pre-flight is what makes failures useful: an invalid status, a missing required custom
field or a type the project does not enable comes back as structured violations with the
allowed values, before anything is written.
Returns the created work package in full detail, including its new id, lock_version
and resolved custom fields.
Pitfalls: type, status and priority take names or ids, but versions, assignees and
parents need numeric ids. Milestone types reject start_date/due_date — use date.
Custom fields must exist on the project/type schema; check get_work_package_schema when
unsure.
To change it afterwards use update_work_package; to attach a file to an existing work
package use upload_attachment.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | The single ISO date of a **milestone**. Milestones carry `date` instead of start_date/due_date; passing both shapes is rejected locally. | |
| type | Yes | Work package type as a **name or numeric id** ('Task', 'Bug', 'Milestone', or 7). Names resolve against this instance's types; an unknown or ambiguous name fails with the valid values listed. | |
| notify | No | Send OpenProject notification emails for this creation. | |
| status | No | Status name or numeric id. Omit to take the type's default status — do not guess an id. | |
| project | Yes | Numeric project id or project identifier (URL slug). Both come from list_projects. | |
| subject | Yes | The title. Required and must not be blank. | |
| version | No | Numeric version / sprint id; from get_project_metadata. | |
| assignee | No | Numeric user id to assign. 'me' is not accepted in writes — call get_instance_info for the current user's id. | |
| due_date | No | ISO date (YYYY-MM-DD). Not valid on milestone types. | |
| priority | No | Priority name or numeric id ('High', 'Normal', or 8). Omit for the instance default; priority ids differ per instance. | |
| parent_id | No | Create this as a child of an existing work package id. | |
| start_date | No | ISO date (YYYY-MM-DD). Not valid on milestone types. | |
| description | No | Body text in markdown. Omit for an empty description. | |
| responsible | No | Numeric user id of the accountable person. | |
| custom_fields | No | Custom field writes keyed by wire key or display name: {'customField12': 'High'} or {'Severity': 'High'}. List/user/version fields accept option ids or option names. Unknown or ambiguous keys fail with the valid keys listed — nothing is ever silently dropped. get_work_package_schema shows what this project and type accept. | |
| estimated_hours | No | Estimate in hours as a decimal (7.5 = seven and a half). | |
| attachment_paths | No | Local file paths to attach. Files upload uncontainered first and are claimed by the new work package, which is the flow that works even when the author lacks edit permission. Only usable when the server shares a filesystem with you (stdio transport). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Work package id. |
| date | No | Milestone date (ISO YYYY-MM-DD); null for non-milestones. |
| type | No | Work package type. |
| notes | No | Degradation notes for this result. |
| author | No | Creating user. |
| parent | No | Parent work package. |
| status | No | Status. |
| project | No | Owning project. |
| subject | No | Subject line. |
| version | No | Version / sprint. |
| assignee | No | Assigned user or group. |
| category | No | Category. |
| due_date | No | ISO date (YYYY-MM-DD). |
| priority | No | Priority. |
| available | No | Feature availability for this WP: dev links, meetings, files. |
| created_at | No | ISO 8601 UTC timestamp. |
| display_id | No | Human-facing id as the instance renders it. Matches the numeric id unless the instance uses semantic identifiers (17.x, e.g. 'PROJ-42'); null when the instance predates it. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
| responsible | No | Accountable user. |
| spent_hours | No | Logged time in hours. |
| lock_version | No | Optimistic-locking version; pass to update_work_package. |
| custom_fields | No | Always a list; empty when none are set. |
| project_phase | No | Project phase this work package sits in (16.1+, only when phases are active in the project and visible to this user); details via get_project_phase. |
| estimated_hours | No | Estimate in hours. |
| percentage_done | No | Progress, 0-100. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint=false, destructiveHint=false, etc.), the description discloses the form-validation pre-flight, structured error responses with allowed values, return details (id, lock_version, resolved custom fields), and pitfalls around parameter types (names vs numeric ids) and milestone date restrictions. This is rich, non-obvious behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly organized: purpose, use cases, validation behavior, return value, pitfalls, and alternatives. Every sentence provides actionable information. It is longer than average, but the density of useful detail justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (17 parameters, output schema present, rich annotations), the description covers everything an agent needs: what it does, how validation works, what is returned, key parameter restrictions, and where to get more schema details. It also names sibling tools for follow-up actions, making the description contextually complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value through synthesized pitfalls: 'type, status and priority take names or ids, but versions, assignees and parents need numeric ids' and 'Milestone types reject start_date/due_date — use date.' These cross-cutting rules go beyond individual schema descriptions but are not huge; hence 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a work package' — a specific verb and resource. It further clarifies scope by listing use cases: new tasks, bugs, subtasks (parent_id), and milestones (date). It distinguishes from siblings by explicitly naming update_work_package and upload_attachment for post-creation actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use it for new tasks, bugs, subtasks...' and gives alternatives: 'To change it afterwards use update_work_package; to attach a file to an existing work package use upload_attachment.' It also directs users to get_work_package_schema when unsure about custom fields, providing clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_work_package_relationCreate 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 precedes from A to
B comes back as B follows A, with from_work_package and
to_work_package swapped. That is the same fact, not an error — read
type and reverse_type from the result rather than assuming what
you sent. Only one relation may
exist between two work packages: a second one answers 409 conflict, and
changing it means update_work_package_relation on the existing id. A
relation that would close a scheduling cycle is rejected with a
validation error. Creating a follows relation can move dates, since
OpenProject reschedules the successor.
Cross-references: get_work_package(include=['relations']) lists what
a work package is already linked to and produces relation ids;
update_work_package_relation edits one;
delete_work_package_relation removes it; parent/child hierarchy goes
through update_work_package(parent_id=...).
| Name | Required | Description | Default |
|---|---|---|---|
| lag | No | Working days to keep between the two work packages. Valid only on 'follows' and 'precedes'; passing it with any other type is refused here before the request is sent. | |
| type | Yes | How from_id relates to to_id. 'follows' schedules from_id after to_id, 'precedes' before it; 'blocks'/'blocked' express dependency without scheduling; 'duplicates'/'duplicated', 'includes'/'partof', 'requires'/'required' come in mirrored pairs; 'relates' is the neutral link. Parent/child hierarchy is NOT a relation — set it with update_work_package(parent_id=...). | |
| to_id | Yes | The other work package. It must be visible to the account and different from from_id. | |
| from_id | Yes | Work package the relation is read from — 'type' describes what this one does to the other. Ids come from search_work_packages or list_work_packages. | |
| description | No | Optional note explaining why the two work packages are linked. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Relation id. Pass it to update_work_package_relation or delete_work_package_relation — it is not a work package id. |
| lag | No | Working days kept between the predecessor and the successor. Only follows/precedes relations carry one; null everywhere else. |
| type | No | Relation type as OpenProject stored it, read from the 'from' work package (e.g. 'follows' means 'from' is scheduled after 'to'). |
| description | No | Free-text note stored on the relation; null when unset. |
| reverse_type | No | The same relation read from the 'to' work package: 'follows' <-> 'precedes', 'blocks' <-> 'blocked', 'relates' <-> 'relates'. |
| to_work_package | No | Work package the relation points to. |
| from_work_package | No | Work package the relation starts at. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Reveals critical server-side behaviors: canonical direction rewriting, 409 on duplicate relations, rescheduling on 'follows', and cycle rejection. This goes far beyond the annotations' basic readOnly/destructive hints and gives the agent essential insight into side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though relatively long, the description is tightly organized into purpose, return value, pitfalls, and cross-references. Key information is front-loaded in the first sentence, and every paragraph adds necessary operational context without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers return shape, error conditions (409, validation errors), side effects (date rescheduling), and alternatives. With an output schema present and thorough parameter descriptions, nothing essential is missing for correct tool selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema descriptions already cover all 5 parameters thoroughly, including enum meanings and lag constraints. The description adds important nuances like direction canonicalization and the single-relation rule, which affect how parameters are interpreted and what to expect in the response.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Link two work packages' and lists the relation types, making the tool's specific verb and resource unmistakable. It clearly distinguishes itself from sibling tools by focusing on creating a relation, versus updating or deleting one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use this to record a dependency' and provides real examples. Pitfalls state when not to use it (409 conflict, use update on existing id) and cross-references list alternatives for listing, editing, deleting relations, and for parent-child hierarchy.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_attachmentDelete attachmentADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first: OpenProject offers no undo and no trash. Calling with confirm=false returns a confirmation_required error and nothing is read or deleted. | |
| attachment_id | Yes | Numeric attachment id from list_attachments or get_work_package(include=['attachments']). Never a work package or container id — deleting the wrong id cannot be undone. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the attachment that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
| container | No | Object the file was attached to (work package, wiki page, meeting, ...); null for an uploaded file that was never claimed by one. |
| file_name | No | Name of the file that was removed, read before deleting it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructive=true, but the description goes far beyond by explaining the read-before-delete flow, 403/404 semantics, non-idempotency (second call returns 404), cascading effects on embedded content, and permission requirements. No contradiction with annotations; instead it enriches them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structured with clear sections (main action, usage, pitfalls, related). Every sentence provides necessary information for a destructive tool; nothing is redundant or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Comprehensive for a destructive attachment operation: purpose, prerequisites, error behavior, side effects, and alternatives are all covered. The presence of an output schema and 100% schema parameter coverage complements the rich description, making this a complete tool definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers both parameters well (100% coverage). The description adds valuable extra context by explaining that attachment_id must come from list_attachments or get_work_package and that an unknown id fails before deletion, supplementing the schema's warning about wrong id types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Permanently delete one attached file from OpenProject' — a specific verb, resource, and scope. It clearly distinguishes this from sibling attachment operations like list_attachments, upload_attachment, and download_attachment.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidance: 'Use it only on explicit user instruction' with concrete use cases (remove a mis-uploaded or superseded file). The Related section names list_attachments, upload_attachment, and download_attachment as alternatives, making when-to-use crystal clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_meetingDelete meetingADestructive
Permanently delete a meeting, together with its agenda and recorded outcomes.
Use only on explicit user instruction. The meeting, its agenda items, their outcomes
and its attachments all go with it, and OpenProject offers no API-side undo. If the
meeting merely did not happen, update_meeting(meeting_id=..., state='cancelled') is
the reversible alternative that keeps the record.
Returns a small confirmation object once OpenProject accepts the deletion.
Pitfalls. This needs the 'delete meetings' permission, so a 403 is about the account, not the id. A 404 means the id is wrong, the meeting was already deleted, or — on OpenProject before 17.4 — the meetings write API does not exist at all; the hint names all readings.
Cross-references: get_meeting to check what you are about to destroy;
update_meeting(state='cancelled') for the reversible alternative; list_meetings
for the id.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. | |
| meeting_id | Yes | Numeric meeting id to delete permanently. Read it back with get_meeting first and show the user the title — meeting ids are not human-readable. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the meeting, agenda item, outcome or recurring series that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation naming what was removed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=true, etc.), the description discloses critical behavioral details: irreversibility ('no API-side undo'), cascading deletion of agenda/outcomes/attachments, permission requirements, and specific error semantics (403 vs 404, plus OpenProject version caveat). This adds substantial context beyond what annotations alone convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The text is well-organized into paragraphs and bullet-like pitfalls, each sentence carrying meaningful information. It front-loads the primary purpose and irreversibility, then provides alternatives, return behavior, and error guidance without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is fully complete for a destructive, annotation-rich tool. It covers safety prerequisites, alternatives, error interpretation, and cross-references to check before deletion. The presence of an output schema means return value details are not needed, and the description fills all other essential context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema already covers 100% of parameters, the description enhances meaning for both: confirm is explained as mandatory to avoid accidental deletion with a clear consequence for false, and meeting_id is advised to be read back and shown to the user because ids are not human-readable. This goes beyond the schema's field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ('Permanently delete a meeting, together with its agenda and recorded outcomes'), clarifying both the action and its scope. It also distinguishes itself from sibling tools like update_meeting and get_meeting by explaining what makes this tool unique.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use the tool ('Use only on explicit user instruction') and provides a concrete reversible alternative ('update_meeting(meeting_id=..., state='cancelled')') for situations where deletion is not appropriate. Cross-references further guide selection among related meeting tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_meeting_agenda_itemDelete meeting agenda itemADestructive
Permanently delete one agenda item, with any outcomes recorded against it.
Use only on explicit user instruction. The item and its recorded outcomes disappear from the agenda for good; the items after it move up. The meeting itself is untouched.
Returns a small confirmation object once OpenProject accepts the deletion.
Pitfalls. A CLOSED meeting's agenda is frozen: the delete answers a validation error,
not a 403, until the meeting is reopened with update_meeting(state='open'). This
needs the 'manage agendas' permission. A 404 means the id is wrong, the item is
already gone — or, on OpenProject before 17.6, that the flat agenda-item write routes
do not exist; the hint names all readings.
Cross-references: get_meeting to check which item the id points at;
update_meeting_agenda_item when the item only needs fixing, not removing.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. | |
| agenda_item_id | Yes | Numeric agenda item id to delete, from get_meeting's agenda_items. Check the item's title there first — it is not the meeting id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the meeting, agenda item, outcome or recurring series that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation naming what was removed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the destructiveHint annotation, the description discloses that outcomes are permanently removed, items shift up, the meeting is untouched, a confirmation object is returned, closed meetings reject deletion, and a 404 can have multiple causes (including OpenProject version). This is rich behavioral context that annotations alone don't provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the main action, then efficiently covers implications, pitfalls, and alternatives. Every sentence adds value, and the structured pitfalls section improves scannability without fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the destructive nature and the existing output schema, the description covers return value, permission requirements, edge cases (closed meeting, version differences), and how to verify the target. It is fully complete for an agent to invoke correctly with minimal ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds critical semantics: `agenda_item_id` is clarified as coming from `get_meeting` and being distinguished from meeting id, and `confirm` is explained with the exact failure mode (`confirmation_required` error) when false. This goes well beyond the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with 'Permanently delete one agenda item, with any outcomes recorded against it' — a specific verb and resource. It distinguishes itself from the sibling `update_meeting_agenda_item` by stating that it removes rather than fixes, so the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says 'Use only on explicit user instruction' and provides cross-references: `get_meeting` to check the id, `update_meeting_agenda_item` for fixing instead of removing. This gives clear when-to-use and alternative guidance beyond the schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_meeting_outcomeDelete meeting outcomeADestructive
Permanently delete a recorded outcome from a running meeting's minutes.
Use only on explicit user instruction, for an outcome recorded by mistake or against
the wrong item. If the text is merely wrong, update_meeting_outcome corrects it and
keeps the record.
Returns a small confirmation object once OpenProject accepts the deletion.
Pitfalls. The outcome timing rule applies to deletion too: it only works while the meeting state is exactly 'in_progress' — a closed meeting's minutes are frozen, and the delete answers a validation error, not a 403. This needs the 'manage outcomes' permission. A 404 means the id is wrong, the outcome is already gone — or, on OpenProject before 17.6, that there is no outcomes API at all.
Cross-references: get_meeting to check which outcome the id points at;
update_meeting_outcome for the reversible fix.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. | |
| outcome_id | Yes | Numeric outcome id to delete, from get_meeting's agenda_items[].outcomes. Check its text there first — ids are not human-readable. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the meeting, agenda item, outcome or recurring series that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation naming what was removed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description reveals many behavioral details beyond annotations: permission requirement, meeting state constraint (in_progress only), validation error vs 403, 404 meanings including OpenProject version nuance, confirmation behavior with confirm=false, and permanent no-undo nature. These enrich the safety profile indicated by destructiveHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and concise, front-loading the core action, then usage, return value, pitfalls, and cross-references. Every sentence provides essential information without fluff, and formatting (paragraphs and bullets) aids readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive tool, the description covers all necessary context: prerequisites (permission, meeting state), exact error semantics, confirmation requirement, and retrieval of the id. With an output schema present, the description needn't detail the return object, making it fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds crucial contextual meaning: confirm must be true because the API offers no undo, and outcome_id should come from get_meeting after checking the text. It explains error responses tied to parameters, surpassing baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Permanently delete a recorded outcome from a running meeting's minutes.' It clearly distinguishes the tool from siblings like update_meeting_outcome by emphasizing irreversible deletion versus correction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage guidance is provided: 'Use only on explicit user instruction, for an outcome recorded by mistake or against the wrong item.' It also names the alternative for wrong text and cross-references get_meeting for verification, leaving no ambiguity about when to use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_newsDelete newsADestructive
Permanently delete a news entry.
Use it for an announcement published by mistake or in the wrong project. For an
outdated but real announcement, update_news is usually the better answer: the
entry stays part of the project's record.
Returns a small confirmation once OpenProject accepts the deletion.
Pitfalls: the entry and every comment left on it are removed for good — API v3 offers no undo and no trash. The 'manage news' permission is required, so a 403 is about the account or a disabled news module; a 404 means the id is wrong or its news is not visible to you.
Cross-references: list_news/get_news for the id and for can_manage;
update_news for the reversible alternative.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user first: there is no undo. Calling with confirm=false returns a confirmation_required error and deletes nothing. | |
| news_id | Yes | Numeric news id to delete. Read it back with get_news first — headlines repeat across projects, ids do not. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the news entry that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite the destructiveHint annotation already flagging danger, the description adds critical behavioral details: the entry and all comments are removed permanently, API v3 offers no undo or trash, the 'manage news' permission is required, and 403 vs 404 error meanings are explained. This goes well beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized with a clear lead sentence, usage context, return value, pitfalls, and cross-references. Every sentence adds value and there is no redundant fluff—the length is justified by the density of useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the destructive nature and availability of an output schema, the description covers all essential context: when to use, what happens (permanent deletion, comments removed), permission requirements, error interpretation, and related sibling tools. It is complete for a responsible AI agent to invoke safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides thorough descriptions for both parameters (confirm must be true, news_id should be read back from get_news), achieving 100% coverage. The description itself adds no additional param-specific meaning beyond the schema, so it matches the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a clear, specific verb and resource: 'Permanently delete a news entry.' It also distinguishes the tool from its sibling update_news, explicitly positioning delete_news as the irreversible action for mistaken or wrong-project announcements.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: use for an announcement 'published by mistake or in the wrong project.' It names the alternative, update_news, for outdated but real announcements, and cross-references list_news/get_news for retrieving the id and can_manage permissions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_projectDelete projectADestructive
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, update_project(active=false) archives it
instead — reversible, and it preserves the data.
Deletion is ASYNCHRONOUS upstream: OpenProject accepts the request and runs it as a
background job. This tool therefore returns {scheduled: true, job_id, message},
never a claim that the project is already gone — for a large project the data
disappears over minutes and get_project may still answer during that window.
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
get_project (it should eventually 404) rather than assuming success.
Cross-references: get_project to check what you are about to destroy;
list_projects(parent_id=...) to see the subprojects that would go with it;
update_project(active=false) for the reversible alternative.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first: this is irreversible and cascades. Calling with confirm=false returns a confirmation_required error and deletes nothing. | |
| id_or_identifier | Yes | Numeric project id or URL identifier of the project to destroy. Read it back with get_project first and show the user the name — an identifier typo can point at a different project. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | The project id or identifier the deletion was asked for. |
| job_id | No | Background job id, when the instance reported one; null otherwise. OpenProject exposes it at /api/v3/job_statuses/{id}. |
| message | Yes | What was scheduled, in plain language. |
| scheduled | Yes | True once OpenProject accepted the request. Deletion runs as a background job, so the project can still be readable for a while afterwards. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, but the description adds substantial context: cascading deletion of all sub-objects, asynchronous background execution, the scheduled-return contract, admin rights requirement, 404 semantics, and the possibility of invisible background failures. This goes far beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though long, the description is tightly structured with clear sections: primary action, usage warning, cascade effect, alternative, async behavior, pitfalls, and cross-references. Every sentence provides actionable information, and the front-loaded summary ensures the core purpose is immediately visible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, asynchronous, cascading deletion tool, the description covers all essential aspects: what gets deleted, how to verify, error conditions, failure visibility, and cross-references to related tools. The presence of an output schema reduces the need to explain return values, but the description still covers the scheduling aspect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with descriptions for both parameters. The description reinforces the schema by emphasizing the confirm requirement and advising to read back the project before passing id_or_identifier, adding practical guidance that helps avoid typos. This exceeds the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Schedule the permanent deletion of a project and everything inside it.' It clearly distinguishes this from sibling tools like delete_work_package and update_project by emphasizing the cascading, destructive scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('Use only on an explicit, specific instruction') and provides a concrete reversible alternative: update_project(active=false) for archiving. Also mentions prerequisites (admin rights) and what to do in error cases, giving clear situational guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_recurring_meetingDelete recurring meetingADestructive
Permanently delete a recurring series: template, schedule, and EVERY occurrence.
Use only on explicit user instruction, and make sure the user means the whole series:
every instantiated meeting of the series — past minutes included — is destroyed with
the template, and when the series has notify set, participants are emailed
cancellations. To drop a single slot instead, cancel_recurring_meeting_occurrence
is the right tool, and delete_meeting removes one instantiated meeting.
Returns a small confirmation object once OpenProject accepts the deletion.
Pitfalls. This needs the 'delete meetings' permission, so a 403 is about the account, not the id. A 404 means the id is wrong, the series was already deleted — or OpenProject before 17.4, which has no recurring-meetings API at all; the hint names all readings.
Cross-references: get_recurring_meeting to check what you are about to destroy;
cancel_recurring_meeting_occurrence for one slot; delete_meeting for one
instantiated meeting.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. | |
| recurring_meeting_id | Yes | Numeric series id to delete permanently, from list_recurring_meetings. Read it back with get_recurring_meeting first and show the user the title — this removes every occurrence, not one meeting. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the meeting, agenda item, outcome or recurring series that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation naming what was removed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even though annotations already declare destructiveHint=true, the description adds critical behavioral context: past minutes are destroyed, participants are emailed cancellations when notify is set, permission requirements (403 indicates account problem), 404 has three possible meanings including OpenProject version limits, and the return is a confirmation object. It also explains the confirm parameter's role in preventing accidental deletion.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence adds value: primary purpose, usage guardrail, return value, pitfalls, cross-references. It is well-structured with clear paragraph breaks (Pitfalls, Cross-references) and front-loads the most critical information. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given this is a destructive, complex operation with an output schema, the description provides comprehensive context: permission errors, version compatibility, how to confirm scope, alternative tools, and what the return object is. It leaves no obvious gap for an agent to misuse the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed descriptions for both parameters (confirm must be true, recurring_meeting_id from list_recurring_meetings). The description adds complementary context by reinforcing that the ID refers to the whole series and advising to read it back with get_recurring_meeting and show the user the title. It doesn't repeat schema details but enriches them with operational guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it permanently deletes a recurring series (template, schedule, and EVERY occurrence), which is a specific action on a specific resource. It also distinguishes itself from sibling tools like cancel_recurring_meeting_occurrence and delete_meeting, eliminating ambiguity about scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: 'Use only on explicit user instruction, and make sure the user means the whole series'. It names exact alternatives for dropping a single slot or a single instantiated meeting, and cross-references get_recurring_meeting for pre-checking. This is textbook when-to-use/when-not-to-use clarity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_time_entryDelete time entryADestructive
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, update_time_entry is the better
answer — it keeps the audit trail. Deleting someone else's entry needs
an administrative permission and otherwise fails with
permission_denied. Entries inside a closed cost-reporting period
cannot be deleted.
Cross-references: find the id with list_time_entries; correct
instead of deleting with update_time_entry.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. | |
| time_entry_id | Yes | Id of the time entry to delete permanently. It comes from list_time_entries — confirm the id belongs to the entry you mean before calling, since ids are not human-readable. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the time entry that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds significant context beyond annotations: deletion 'removes the booked hours from every cost report and from the work package's aggregated spent time, with no API-side undo.' Also discloses permission requirements, failure mode (permission_denied), and closed-period limitations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the main purpose, then organized into usage, behavior, pitfalls, and cross-references. Every sentence carries necessary safety or usage information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive tool with a confirmation flag, this description covers when to use, how to confirm, side effects, failure modes, and alternatives. Output schema exists, so return format doesn't need explanation. Fully equips the agent for safe invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already covers both parameters at 100%, but description enriches their meaning: explains confirm must be true, the confirmation_required error when false, and that time_entry_id comes from list_time_entries and must be checked before calling. This adds practical workflow context beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description opens with 'Permanently delete a logged time entry' — a specific verb and resource. It explicitly distinguishes itself from update_time_entry and list_time_entries through cross-references, making it unambiguous which tool to use.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Use only on explicit user instruction, and only for genuinely wrong entries.' Names update_time_entry as the better alternative for wrong day/duration/activity and notes permission and closed-period restrictions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_versionDelete versionADestructive
Permanently delete a version.
Use it only for a version created by mistake. For a finished release or sprint,
update_version(status='closed') is almost always the right answer: it keeps the
history and stops new assignments.
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_versions for the id; update_version(status='closed')
for the reversible alternative; list_work_packages to find what still points at
the version before removing it.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user first: there is no undo. Calling with confirm=false returns a confirmation_required error and deletes nothing. | |
| version_id | Yes | Numeric version id to delete. Read it back with list_versions first — version names repeat across projects, ids do not. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the version that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses key behaviors beyond annotations: work packages are NOT deleted, 422 refusal on referenced versions, 403 permission requirement, and shared version impact. This goes well beyond the destructiveHint annotation and adds context that affects the agent's decision-making.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured with separate paragraphs for usage guidance, side effects, and pitfalls. Each sentence carries useful information, though a few could be merged. It earns a 4 for density without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers prerequisites (list_versions), alternatives (update_version), side effects (shared versions, work packages), error scenarios (422, 403), and confirmation flow. With an output schema present, no need to describe return values. Complete for a destructive mutation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed descriptions for both params (confirm must be true, version_id numeric and from list_versions). The description reinforces these points (e.g., 'Ask the user first') but adds little beyond schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states 'Permanently delete a version' with a specific verb and resource, clearly distinguishing it from the reversible alternative update_version(status='closed'). It also clarifies the scope (work packages are not deleted, only lose their version).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('only for a version created by mistake') and when not to (for a finished release or sprint, use update_version(status='closed')). Cross-references list_versions and list_work_packages for prerequisites and pre-removal checks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_work_packageDelete work packageADestructive
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
get_work_package(id, include=['children']) first and decide what happens to them. If you
only want the work package out of the way, update_work_package(id, status=<a closed status>) is almost always the better answer — get_project_metadata lists which statuses
this instance treats as closed.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Work package id to delete permanently. | |
| confirm | No | Must be true. Ask the user to confirm first — the API offers no undo. Calling with confirm=false returns a confirmation_required error rather than deleting anything. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the work package that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description adds substantial context: deletion is permanent, removes comments/attachments/time entries/relations, has no API-side undo, and the key pitfall that children are not deleted. It also clarifies the return behavior with a confirmation object.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear purpose, warning, return note, and pitfalls section. Every sentence carries important operational information, with no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's destructive nature and the presence of an output schema, the description covers all critical aspects: prerequisites (check children), cascading deletion effects, alternatives, the confirm flag behavior, and the permanent consequences. It is fully sufficient for an agent to invoke this tool safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the input schema already provides detailed descriptions for both parameters, including the confirm flag's requirement and behavior. The description reinforces the confirmation requirement but does not add new parameter-level details beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific statement: 'Permanently delete a work package and everything attached to it.' This names the exact action and resource, and distinguishes the tool from related siblings like delete_work_package_relation and update_work_package.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use instructions: 'Use only on explicit user instruction.' It also advises checking children via get_work_package before deletion and recommends update_work_package with a closed status as a safer alternative, referencing get_project_metadata for closed statuses.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_work_package_relationDelete work package relationADestructive
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 follows relation drops the scheduling
constraint, so OpenProject may reschedule the work package that was
waiting. There is no undo; recreating the relation with
create_work_package_relation is the only way back. A second delete
of the same id answers 404. Parent/child hierarchy is not a relation —
clear it with update_work_package(parent_id=null).
Cross-references: get_work_package(include=['relations']) shows what
would be removed; update_work_package_relation changes a relation
instead of removing it.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be true. Ask the user first — the API offers no undo. Calling with confirm=false returns a confirmation_required error and deletes nothing. | |
| relation_id | Yes | Id of the relation to remove permanently. It comes from create_work_package_relation or get_work_package(include=['relations']). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the relation that was deleted. |
| deleted | Yes | True once OpenProject accepted the deletion. |
| message | Yes | Human-readable confirmation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the destructiveHint and non-idempotent annotations, the description adds crucial context: deleting a 'follows' relation may trigger rescheduling, there is no undo, and a second delete returns 404. It also explains the confirm parameter's role in avoiding accidental deletion. This goes well beyond what annotations alone convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear lead, a 'Pitfalls' section, and cross-references. Each sentence earns its place, and it is succinct despite covering multiple nuances. It is neither under-specified nor bloated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the destructive nature of the tool, the description covers all critical aspects: side effects, non-reversibility, idempotency behavior, and alternative tools. With an output schema present, return values need not be described. The description is complete for an agent to invoke this tool safely and correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% parameter coverage, including the confirm requirement and the source of relation_id. The description itself adds almost no new parameter-level detail, so the baseline of 3 is appropriate. The mention of confirm=false behavior in the description is redundant with the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific action: 'Remove the link between two work packages.' It goes on to distinguish this from sibling tools like update_work_package_relation and delete_work_package, and clarifies that parent/child hierarchy is not a relation. This makes the tool's exact scope immediately obvious.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use it ('Use it when a dependency no longer holds') and provides concrete alternatives: 'update_work_package_relation changes a relation instead of removing it' and 'clear it with update_work_package(parent_id=null)' for hierarchy. This gives the agent clear decision criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_attachmentDownload attachmentARead-only
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.
| Name | Required | Description | Default |
|---|---|---|---|
| save_dir | No | Absolute directory to save into; it is created when missing. Defaults to OPENPROJECT_MCP_DOWNLOAD_DIR, and otherwise to an 'openproject-downloads' folder beside the server's working directory. Relative paths are rejected because the server's working directory is not the user's. | |
| return_image | No | Also return the file as an inline image so the model can look at it. Honored only for image/* content of at most 1 MB; otherwise the file is still saved and a note explains why nothing was shown. | |
| attachment_id | Yes | Numeric attachment id from list_attachments or get_work_package(include=['attachments']). Not a work package id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | Absolute path of the saved file on the MCP server's machine. |
| notes | No | Degradation markers: renamed target, image not shown inline, … |
| sha256 | Yes | SHA-256 of the downloaded bytes, hex encoded. |
| file_name | Yes | Name the file was saved under; may differ from the attachment's own name when it collided with an existing file. |
| size_bytes | Yes | Bytes actually written to disk. |
| content_type | No | MIME type reported by OpenProject. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only hint at read-only and non-destructive intent, but the description explains actual behavior: streaming to disk with progress, return fields including SHA-256, local-vs-server path caveat, quarantine and virus-scan failures, size limits with mid-stream aborts, and name-collision handling. This far exceeds annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place. It is structured with a clear lead, workflow, return-value summary, pitfalls, and related tools, making it scannable and information-dense without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the full lifecycle: prerequisites, execution behavior, return values, error conditions, size limits, filename conflicts, and deployment context. For a tool with this complexity and an output schema, the description is complete and self-contained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though schema coverage is 100%, the description adds critical nuances: attachment_id is explicitly 'Not a work package id', save_dir defaults and relative-path rejection rationale, and return_image's 1 MB/type limitations. This adds real semantic value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Download an attachment's bytes to a file on the machine running this server,' which uses a specific verb and resource, clearly distinguishing it from sibling tools like upload_attachment, list_attachments, and delete_attachment. It also references related tools, reinforcing its unique role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('Use it once list_attachments (or get_work_package(include=["attachments"])) has given you an attachment_id') and provides prerequisites. It also covers when not to use (quarantined, unfinished virus scan, oversized files) and names the reverse operation (upload_attachment).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_work_package_commentEdit work package commentAIdempotent
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
list_work_package_comments returns.
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
list_work_package_comments; post a new comment with
add_work_package_comment; change fields (status, assignee, dates)
with update_work_package instead of describing them in prose.
| Name | Required | Description | Default |
|---|---|---|---|
| comment | Yes | The replacement body in markdown. It replaces the whole comment — there is no append mode, so read the current text first if you mean to add to it. @-mentions need OpenProject's mention syntax; plain names notify nobody. | |
| activity_id | Yes | Id of the journal entry to rewrite. It comes from list_work_package_comments (the 'id' of an entry with kind='comment') or from add_work_package_comment's result. It is an activity id, not a work package id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Activity id. Pass it back as list_work_package_comments(activity_id=...) to read a truncated comment in full. |
| kind | Yes | 'comment' when the entry carries comment text, 'field_change' otherwise. |
| author | No | User who wrote the entry. |
| comment | No | Comment body as markdown (raw); html is dropped. Cut to max_comment_chars when 'truncated' is true. |
| details | No | Field changes recorded with this entry; always a list, empty for a comment-only entry. |
| version | No | Journal version number of this entry within the work package. |
| internal | No | True for internal (project-member-only) comments; OpenProject >= 16. |
| truncated | No | True when the comment was cut to max_comment_chars. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp of the last edit. |
| work_package | No | Work package the entry belongs to. |
| comment_length | No | Character length of the untruncated comment; set only when truncated. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description discloses that only comment entries are editable, field-change entries are rejected locally before any write, editing requires specific permissions, the text is replaced entirely with no API-visible history, editing does not notify anyone, and the return shape matches list_work_package_comments. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though lengthy, every sentence earns its place: purpose, use cases, return shape, pitfalls, permissions, irreversibility, and cross-references. The description is front-loaded with the primary action and well-structured with paragraph breaks for readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is exhaustive given the tool's complexity: covers prerequisites, error handling (403), non-comment journal entries, side effects (no notifications, no history), and explicitly names sibling tools. An output schema exists, so return values require no further explanation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed descriptions for both parameters (comment, activity_id). The tool description adds little beyond the schema: 'replaces text entirely' and 'activity id' already appear in the schema. The baseline of 3 applies because structured data carries the semantic load.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Rewrite the text of an existing work-package comment,' a specific verb+resource statement. It further clarifies use cases (fix a typo, correct a wrong statement, extend a note) and differentiates from add_work_package_comment and update_work_package via cross-references.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('Use this to fix a typo...'), when not to use ('do not use it to "undo"'), and names specific alternatives (list_work_package_comments, add_work_package_comment, update_work_package). It also explains the permission requirements and 403 implication.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
execute_custom_actionExecute 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 get_work_package(include=['custom_actions']) offers it,
instead of reproducing its effects field by field. Returns the updated
work package row (subject, type, status, priority, assignee, project,
dates, percentage_done, updated_at).
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 get_work_package
again for the full detail (including the new lock_version for your
next update). Availability is per work package: an action listed on one
ticket may 403 on another because its conditions no longer hold, and a
422 usually means the resulting work package would be invalid (a
required field the action leaves empty). Writes are never retried
automatically — a conflict comes back with the fresh lock_version so
you can re-read and decide.
Cross-references: get_work_package(include=['custom_actions'])
produces the ids and says which are available right now;
update_work_package is the explicit alternative when you know exactly
which fields to set; list_work_package_comments shows what the action
recorded in the journal.
| Name | Required | Description | Default |
|---|---|---|---|
| lock_version | No | The work package's current lockVersion, from get_work_package. Omit it and the current value is read first — safe, one extra request. A stale value returns a conflict carrying the fresh one. | |
| work_package_id | Yes | Work package to run the action on. It must be the one the action was listed for; conditions are re-checked server-side. | |
| custom_action_id | Yes | Id of the custom action to run. Get it from get_work_package(id=..., include=['custom_actions']), which lists the actions this instance defines AND this work package currently qualifies for — the names are instance-specific, so never guess an id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Work package id. |
| type | No | Work package type. |
| status | No | Status. |
| project | No | Owning project. |
| subject | No | Subject line. |
| assignee | No | Assigned user or group. |
| due_date | No | ISO date (YYYY-MM-DD). |
| priority | No | Priority. |
| display_id | No | Human-facing id as the instance renders it. Matches the numeric id unless the instance uses semantic identifiers (17.x, e.g. 'PROJ-42'); null when the instance predates it. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| percentage_done | No | Progress, 0-100. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint=false, destructiveHint=false), the description reveals critical behavioral traits: the action determines changes and does not report which fields it touched, availability varies per work package (403/422 errors), writes are never auto-retried, and conflicts return a fresh lock_version. This adds substantial context beyond the structured metadata and matches the annotations (no contradiction).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is moderately long but every sentence carries weight: it defines the tool, gives examples, explains pitfalls, and offers cross-references. It is front-loaded with the core behavior and then organized into a clear pitfalls section, making it scannable and dense without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description still adds essential context: it summarizes what the return row contains, explains error conditions (403, 422), concurrency behavior, and how to recover from conflicts. It also references related tools for obtaining IDs and reading journal entries, making it complete for an agent to safely execute the action in real scenarios.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds extra meaning for custom_action_id ('never guess an id', tells where to get it) and reinforces lock_version behavior, going beyond the schema's descriptions. It doesn't add much for work_package_id but the schema already covers it adequately. Thus a 4 is justified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Run') and names the resource ('instance-defined one-click action on a work package'), immediately clarifying what the tool executes. It also distinguishes itself from update_work_package and get_work_package by referencing them as alternatives, so the agent can tell them apart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says when to use the tool: 'Use one when get_work_package(include=['custom_actions']) offers it, instead of reproducing its effects field by field.' It also names the alternative tools (update_work_package, get_work_package) and explains when those are preferred, giving clear usage boundaries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_documentGet documentARead-onlyIdempotent
Read one document with its full description text.
Use it after list_documents when the title is not enough: this adds description as
markdown, alongside {id, title, project, created_at, updated_at}.
Pitfalls. The attached files are not part of this result — list them with
list_attachments(container_type='document', container_id=<this id>). A document created
with OpenProject's block editor keeps its rich content in a field API v3 does not render,
so description can be empty for a document that clearly has text in the UI; say so
rather than reporting the document as blank. Editing documents is not supported by
this server.
Cross-references: list_documents for the id; download_attachment for the files.
| Name | Required | Description | Default |
|---|---|---|---|
| document_id | Yes | Numeric document id from list_documents (it is also the number in a /documents/<id> UI URL). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Document id — pass it to get_document for the description. |
| title | No | Document title. |
| project | No | Project the document belongs to. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint, idempotentHint, destructiveHint false), the description discloses that attachments are not part of the result, that description can be empty for block editor content, and that editing is unsupported. These are significant behavioral traits that the agent would not know from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense, with clear paragraphs for purpose, usage, pitfalls, and cross-references. Every sentence earns its place, and the use of 'Pitfalls' makes unusual behaviors easy to spot. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read operation with one parameter and an output schema, the description covers all critical aspects: what is returned, what is not returned (attachments), potential empty description, and related tools. The existence of an output schema means return format details are already external, so the description is fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter document_id is fully described in the input schema, including its origin ('from list_documents') and format. The description reinforces this by cross-referencing list_documents for the id, but does not add meaning beyond what the schema already provides. Since schema coverage is 100%, the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Read one document with its full description text', which is a specific verb and resource. It clearly differentiates from siblings like list_documents (listing all) and download_attachment (files) by focusing on a single document's description text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states 'Use it after `list_documents` when the title is not enough', providing a clear when-to-use condition. It also names alternatives for attachments (list_attachments) and files (download_attachment), and notes that editing is unsupported, giving complete guidance on when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_github_pull_requestGet GitHub pull requestARead-onlyIdempotent
Read one linked GitHub pull request in full, including its CI check runs.
Use it after get_work_package_git_activity when the summary is not enough: this
adds the pull-request body (markdown), the diff size (additions, deletions,
changed_files), comment counts, who merged it, and every work package the PR is
linked to — plus the same check_runs with status and conclusion.
Pitfalls. github_pull_request_id is OpenProject's id, never the GitHub number; the
two are unrelated and there is no lookup by GitHub number. The record is a mirror
that OpenProject refreshes from GitHub webhooks, so updated_at is when OpenProject
last synced, not when GitHub changed. A 404 usually means the id came from the wrong
field or the GitHub module is not installed on this instance.
A pull request appears in OpenProject only when its description or a comment mentions 'OP#123' or the full work-package URL; commits link separately via 'refs #123' in the commit message. Neither link can be created through the API.
Cross-references: find the id with get_work_package_git_activity(work_package_id=…);
GitLab merge requests have no per-id tool — they come back in full from that same
call.
| Name | Required | Description | Default |
|---|---|---|---|
| github_pull_request_id | Yes | The **OpenProject-internal** pull-request id — the 'id' field of an entry in get_work_package_git_activity's github_pull_requests. It is NOT the GitHub PR number ('number' in that same entry, the '#481' on github.com); passing the GitHub number fetches the wrong record or 404s. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | OpenProject-internal id. This is what get_github_pull_request takes — NOT the GitHub/GitLab number. |
| body | No | Pull-request description as markdown (raw); html is dropped. |
| draft | No | True while marked draft / work in progress. |
| state | No | Provider state verbatim: GitHub 'open'/'closed', GitLab 'opened'/'closed'/'merged'/'locked'. Check 'merged' for the merge fact. |
| title | No | Pull/merge request title. |
| author | No | Provider account that opened it ({id, name}); a GitHub/GitLab user, not an OpenProject user. |
| labels | No | Label names; always a list, empty when unlabelled. |
| merged | No | True once merged; a closed request may never have merged. |
| number | No | The number humans use on GitHub/GitLab (the '#481' in the PR title). Never pass it to get_github_pull_request. |
| html_url | No | Provider URL of the request. |
| additions | No | Lines added across the diff. |
| deletions | No | Lines removed across the diff. |
| merged_at | No | ISO 8601 UTC merge time, if merged. |
| merged_by | No | Provider account that merged it. |
| check_runs | No | CI check runs GitHub reported for this pull request; always a list. |
| created_at | No | ISO 8601 UTC creation time. |
| repository | No | Repository slug, e.g. 'acme/web'. |
| updated_at | No | ISO 8601 UTC time OpenProject last synced this record. |
| changed_files | No | Number of files touched. |
| work_packages | No | Work packages this pull request is linked to; a PR may reference several. |
| comments_count | No | Issue-style comments. |
| review_comments_count | No | Inline review comments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly, idempotent, non-destructive), the description explains critical behavioral nuances: the record is a mirror refreshed via webhooks so `updated_at` is sync time, not GitHub change time; a 404 commonly indicates a wrong id or missing GitHub module; and PRs appear only when they mention 'OP#123' or the full work-package URL, with no API-based link creation. This adds substantial context to annotation-provided hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although lengthy, the description is well-structured with clear paragraphs: main purpose, usage context, pitfalls, data-appearance conditions, and cross-references. Each sentence contributes meaningful information; there is no filler or repetition beyond acceptable reinforcement. The use of 'Pitfalls.' and 'Cross-references:' headers improves scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (external data mirror, non-obvious id semantics, linkage constraints), the description is complete. It covers usage flow, error interpretation, data freshness, privacy of linkage, and alternatives. The output schema already documents return values, so the description focuses on behavioral and contextual aspects, making it fully adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already has 100% coverage with a rich description clearly distinguishing `github_pull_request_id` from the GitHub number. The description reinforces this with the pitfall statement ('never the GitHub number… no lookup by GitHub number') and adds a diagnostic hint ('404 usually means the id came from the wrong field or the GitHub module is not installed'). While mostly redundant with the schema, the added 404 context nudges it above the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with a specific verb and resource: 'Read one linked GitHub pull request in full, including its CI check runs.' It clearly distinguishes this tool from the sibling `get_work_package_git_activity` by specifying that this provides full PR details while the sibling returns a summary. The scope is unambiguous and the resource is precisely identified.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is given: 'Use it after get_work_package_git_activity when the summary is not enough.' It also documents when not to use it for GitLab merge requests, stating they 'have no per-id tool — they come back in full from that same call.' Cross-references show exactly how to find the id, satisfying the guideline requirement thoroughly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_instance_infoGet instance infoARead-onlyIdempotent
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
(maximum_attachment_file_size_bytes), the page sizes the instance offers, the
authenticated user {id, name, login, admin}, and features — the probe result
telling you whether internal comments, emoji reactions and project favorites exist
here, and which time-entry filter spelling this version uses.
Pitfalls: features describes the server version, not this user's permissions —
a supported feature can still 403. A failure here is the actionable one: 401 means
the API key is wrong or revoked, a network error means the URL, DNS, proxy or TLS
trust is wrong; both come back with a hint naming the environment variable to fix.
For per-project ids (types, statuses, priorities, versions, categories, activities)
use get_project_metadata; for what the current user may do use list_permissions.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| api_url | No | API base URL this server talks to. |
| features | Yes | Probed feature availability; read it before using gated params. |
| core_version | No | OpenProject core version, e.g. '17.7.1'. |
| current_user | Yes | Who this server is authenticated as. |
| instance_name | No | Configured instance name. |
| per_page_options | No | Page sizes the instance offers; the largest is the effective page_size cap. |
| maximum_attachment_file_size_bytes | No | Upload ceiling; upload_attachment pre-flights against it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite annotations already indicating read-only, idempotent, and non-destructive behavior, the description adds significant behavioral nuance: it authenticates on every call rather than caching, describes what 'features' represents (server version vs user permissions), and explains error semantics (401 vs network error) with actionable hints. This goes beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: it opens with a clear purpose, then provides usage triggers, a concise list of return values, pitfalls, and alternatives. Each sentence adds unique value. Despite its length, it is tightly written and front-loaded with the most critical information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although the tool has no parameters and a rich output schema, the description fully explains when to use it, what it returns, why it should be called first, how to interpret failures, and how it differs from related tools. There is no gap in context for an agent to infer from the schema alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is nothing for the description to explain about parameters. The schema coverage is trivially 100%. The description does add substantial value by detailing the return fields (version, name, attachment size, user info, features), which compensates for the lack of parameter-level semantics. The score is a baseline 4 because with no parameters, the description cannot earn full marks in this dimension.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: "Check the OpenProject connection and report what this instance supports." It uses specific verbs (check, report) and a specific resource (instance info). It also distinguishes itself from sibling tools by explicitly pointing to get_project_metadata and list_permissions for related but different information.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: "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 also names alternatives for per-project metadata and user permissions, making the usage context crystal clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_job_statusGet background job statusARead-onlyIdempotent
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 copy_project or
delete_project and wait for a terminal state before reporting an outcome to the
user.
Returns {id, status, finished, successful, message, project, result_url, notes}.
status is 'in_queue' or 'in_process' while the job runs and 'success', 'failure',
'error' or 'cancelled' once it is over; finished and successful are derived
from it, and successful stays null while the job runs rather than defaulting to
false. A finished copy reports the new project in project and the URL it lives at
in result_url.
Pitfalls: a 200 does not mean the job worked — read status. Polling is on you:
wait a few seconds between calls rather than looping tightly. OpenProject drops job
statuses after a while, so a 404 can mean 'long finished' as easily as 'wrong id';
confirm with get_project or list_projects. When a job fails, message is
what OpenProject recorded — there is no API to retry it, so the underlying tool has to
be called again deliberately.
Cross-references: copy_project and delete_project produce the job_id;
get_project / list_projects verify what the job actually did.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | Background job id — a uuid such as '9f4c1d5e-0e2a-4f2b-9a11-2f1b3c4d5e6f'. It comes from copy_project or delete_project ('job_id' in their results), never from a project id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Job id (a uuid) this status belongs to. |
| notes | No | Degradation markers: still running, or a reference that had to be derived. |
| status | No | Job state: 'in_queue' or 'in_process' while it runs, 'success', 'failure', 'error' or 'cancelled' once it is over. |
| message | No | What the job reported, e.g. why it failed. |
| project | No | Project the job produced or acted on ({id, name}), when it names one — this is how a finished copy_project job hands back the new project. |
| finished | Yes | True once status is terminal. False means the job is still running — poll again rather than reporting a result. |
| result_url | No | Web URL the job stored for its result (the new project, an export download). A UI URL, not an API endpoint. |
| successful | No | True when the job finished successfully, false when it failed, null while it is still running. Never guess from 'finished' alone. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly, idempotent, etc.), the description adds rich behavioral detail: status value lifecycle, derived fields, the meaning of a 200 response, polling requirements, job status expiration, 404 interpretation, failure message behavior, and lack of retry API. This goes far beyond what annotations alone convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: purpose, background, return format, pitfalls, and cross-references are clearly segmented. It is front-loaded with the core purpose and structured for quick scanning despite its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter polling tool, the description is exceptionally complete: it covers return fields, all possible statuses, edge cases (404, successful null), polling best practices, and verification alternatives. The presence of an output schema means return details need not be fully restated, but the description still provides them, ensuring no gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema's description for job_id already covers its provenance (uuid from copy_project/delete_project) and distinction from project id. The tool description reinforces this but adds no new parameter-specific semantics beyond usage context, so the baseline 3 for high schema coverage (100%) applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Check whether a background job (a project copy, a scheduled deletion) has finished.' It clearly distinguishes itself from siblings by stating it is the 'only way' to learn what happened to a job, and cross-references copy_project and delete_project as producers of the job_id.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is provided: 'call it after copy_project or delete_project and wait for a terminal state before reporting an outcome.' It also names alternatives for verification (get_project / list_projects) and warns about the 404 ambiguity, giving clear context on how to interpret results.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_meetingGet meetingARead-onlyIdempotent
Read one meeting in full: participants, the agenda, and any recorded outcomes.
This is the "what was discussed / what was decided" call. It returns the meeting fields
(title, project, start_time, end_time, duration_hours, location, state,
author, timestamps), the invited participants as {id, name} refs, and
agenda_items in agenda order — each with its title, notes (markdown),
duration_minutes, presenter, the work_package it discusses, its section, and the
outcomes recorded against it (kind, notes, author, linked work package).
Pitfalls. A work-package agenda item carries an empty title — the work package's
subject is what the UI shows, so read work_package.name. When the linked work package
is invisible to this account, work_package is null and notes says so; do not report
the item as unlinked. If the agenda itself cannot be read (403/404 on the sub-resource),
agenda_items is empty and notes explains why — an empty agenda and an unreadable one
are different answers. Attendance, minutes as a document, and meeting sections' own
titles beyond the item link are not exposed by API v3.
Cross-references: add_meeting_agenda_item to extend the agenda; list_meetings for the
id; list_attachments(container_type='meeting', container_id=<meeting id>) for files;
get_work_package for a linked ticket. update_meeting / delete_meeting change or
remove the meeting itself — the result's lock_version is what update_meeting echoes.
| Name | Required | Description | Default |
|---|---|---|---|
| meeting_id | Yes | Numeric meeting id from list_meetings (it is also the number in a /meetings/<id> UI URL). Never a project id or an agenda item id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Meeting id — what get_meeting and add_meeting_agenda_item take. |
| notes | No | Degradation notes: agenda items that could not be read, work packages this account may not see. |
| state | No | Lifecycle state: 'draft' (not yet opened to participants), 'open', 'in_progress', 'closed' or 'cancelled'. Cancelled meetings are excluded from listings. |
| title | No | Meeting title. |
| author | No | User who created the meeting. |
| project | No | Project the meeting belongs to. |
| end_time | No | ISO 8601 UTC end timestamp, derived from start plus duration. |
| location | No | Room name or meeting URL as typed by the organizer. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_time | No | ISO 8601 UTC start timestamp; null for an undated meeting. |
| updated_at | No | ISO 8601 UTC timestamp. |
| agenda_items | No | The agenda in order; always a list. Empty means either no agenda or an unreadable one — check 'notes' before concluding the meeting had none. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| participants | No | Invited users; always a list. Attendance is not exposed by API v3. |
| duration_hours | No | Scheduled length in hours (1.5 = 90 minutes); the wire sends an ISO duration, which is converted here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint, idempotentHint, destructiveHint), the description discloses important edge-case behaviors: empty title for work-package agenda items, null work_package when invisible, and the distinction between an empty agenda vs. an unreadable one (agenda_items empty with explanation). It also states API v3 limitations, adding significant transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than typical but well-structured: it starts with the core purpose, then details return fields, then pitfalls, then cross-references. Every section provides value, especially the subtle edge cases. However, the enumeration of return fields may be redundant given the output schema exists, and the prose is dense. Still, it earns its length, so 4.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (meeting data with agenda items, outcomes, visibility nuances) and the presence of an output schema, the description is remarkably complete. It covers return values, edge cases (empty title, invisible work packages, unreadable agendas), API limitations, and related tools, leaving no obvious gaps for an agent to misuse the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already fully describes meeting_id (numeric id from list_meetings, not a project or agenda item id), so schema coverage is 100%. The description adds the cross-reference 'list_meetings for the id' and the note about lock_version for update_meeting, but it does not add new semantics for the parameter beyond what the schema provides. At baseline, a 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Read one meeting in full: participants, the agenda, and any recorded outcomes.' It clearly distinguishes the tool from siblings by naming alternatives in cross-references (add_meeting_agenda_item, list_meetings, update_meeting, delete_meeting), making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage context ('This is the "what was discussed / what was decided" call') and gives alternatives with clear purposes: 'list_meetings for the id', 'update_meeting / delete_meeting change or remove the meeting itself.' It also notes what is NOT exposed (attendance, minutes, section titles), helping the agent decide when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_newsGet news entryARead-onlyIdempotent
Read one news entry in full, including the markdown body.
Use it after list_news when the summary is not enough — this adds description,
the announcement's complete text as markdown (html is dropped), plus updated_at.
Returns {id, title, summary, description, project, author, created_at, updated_at, can_manage}. author is the account that published the entry and cannot be
changed; can_manage says whether editing or deleting it would be permitted.
Pitfalls: a 404 here means "no such entry, or you may not read news in its project" — the news module is enabled per project, so a missing entry is not always a wrong id. Comments people left on the announcement are not exposed by API v3 and are not included.
Cross-references: list_news produces the id; update_news changes the text;
delete_news removes the entry for good.
| Name | Required | Description | Default |
|---|---|---|---|
| news_id | Yes | Numeric news id from list_news. It is the same id as in the UI's /news/{id} URL; the headline is not an id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | News id — what get_news, update_news and delete_news consume. |
| title | No | Headline of the announcement. |
| author | No | User who published it ({id, name}); set by the server. |
| project | No | Project the announcement belongs to ({id, name}). |
| summary | No | Short teaser OpenProject shows under the headline; may be empty. The full body is NOT here — read it with get_news(news_id=...). |
| can_manage | No | True when this account may change or delete this entry (OpenProject renders the update/delete links only with the 'manage news' permission). False means update_news and delete_news would fail with 403. |
| created_at | No | ISO 8601 UTC publication timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | The announcement body as markdown (raw); html is dropped. Empty string when the entry has only a headline and summary. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses several behavioral traits beyond the readOnlyHint annotation: it returns specific fields, 'author' cannot be changed, 'can_manage' indicates edit/delete permission, HTML is dropped, and comments are not exposed. This provides rich context not available from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but well-structured: it opens with the core purpose, then covers return value, pitfalls, and cross-references. Every sentence adds valuable information, and the front-loading makes it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read operation with one parameter, the description is complete. It covers return fields, permission hints, error semantics, and exclusions. Since an output schema exists, the description need not explain return values in detail, but it does so anyway, making it self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already fully describes the only parameter news_id, including its numeric type and source from list_news. The description reinforces this by saying 'list_news produces the id' but adds no new semantic meaning beyond the schema, so a baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with 'Read one news entry in full, including the markdown body,' which clearly states the action and resource. It also distinguishes from siblings by noting it is used after list_news and that update_news/delete_news are cross-references.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'Use it after list_news when the summary is not enough.' It also provides alternatives (update_news, delete_news) and includes a pitfall about 404 semantics, which helps avoid misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_projectGet projectARead-onlyIdempotent
Read one project in full.
Use it after list_projects when you need the description, the status explanation
or the parent of a specific project — or to verify that an id or identifier a user
gave you actually resolves.
Returns {id, identifier, name, active, public, parent, status_code, workspace_type, description, status_explanation, created_at, updated_at}. Rich
text comes back as markdown raw; html is dropped.
Pitfalls: status_code is one of on_track, at_risk, off_track, not_started,
finished, discontinued — an empty status_code means the project has no status
set, not "on track". A 404 here means the id/identifier is wrong or the project is
archived and invisible to this user; the error hint says which spelling to try next.
For the ids valid inside this project (types, versions, categories, time-entry
activities) call get_project_metadata(project_id=...); for its work packages call
list_work_packages(project=...).
| Name | Required | Description | Default |
|---|---|---|---|
| id_or_identifier | Yes | Numeric project id (e.g. 7) or the URL identifier (e.g. 'demo-project'). Both are accepted; identifiers come from list_projects, and are the slug in /projects/<identifier>, not the display name. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Numeric project id; accepted by every project_id parameter. |
| name | No | Display name. |
| active | No | False for archived projects (read-only in the UI). |
| parent | No | Parent project, when this is a subproject. |
| public | No | True when visible to users without a membership. |
| created_at | No | ISO 8601 UTC timestamp. |
| identifier | No | URL slug from /projects/<identifier>; also accepted wherever an id is. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
| status_code | No | Project status code, one of: on_track, at_risk, off_track, not_started, finished, discontinued. A code, never a translated label; null means no status has been set. |
| workspace_type | No | Workspace kind: 'project', 'program' or 'portfolio'. Pre-17 instances only have 'project'; on 17.x project listings mix all three kinds, so check this before treating a row as a plain project. |
| status_explanation | No | Free-text explanation of status_code, markdown (raw). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true, so the safety profile is known. The description adds valuable behavioral details: the exact return fields, rich text returned as markdown raw with HTML dropped, the pitfall that an empty status_code means no status set rather than 'on track', and that a 404 indicates a wrong id/identifier or an archived/invisible project with an error hint. These go well beyond the structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is organized with a clear opening purpose, usage guidance, return format, pitfalls, and pointers to related tools. Each sentence provides essential information, especially the pitfalls section that prevents misinterpretation of status_code and 404 responses. It is detailed but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers what the tool returns, how to use it, important pitfalls, and how it relates to sibling tools. With an output schema present, the description's list of return fields is redundant but adds clarity. It fully equips an agent to decide when to call this tool and what to expect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%; the schema already documents that id_or_identifier accepts either a numeric id or URL identifier, with examples and a note about slugs. The description reinforces that identifiers come from list_projects but does not add new semantic detail beyond the schema. Baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Read one project in full,' which clearly states the action (read) and resource (project). It distinguishes from siblings by specifying that it retrieves a single project by id or identifier, unlike list_projects which lists all projects.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance: 'Use it after list_projects when you need the description, the status explanation or the parent of a specific project — or to verify that an id or identifier a user gave you actually resolves.' It also names alternative tools for related needs: get_project_metadata for internal ids and list_work_packages for work packages.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_metadataGet project metadataARead-onlyIdempotent
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 project_id returns the global types, statuses, priorities and
roles. With project_id the types list narrows to the ones enabled in that
project and versions, categories and time_entry_activities are filled in.
Every row is {id, name} plus its flags: statuses[].is_closed is the
authoritative done marker (never classify by status name — it is localized),
types[].is_milestone tells you the type takes a single date, and
priorities[].is_default / time_entry_activities[].is_default say what you get
by omitting the field.
Pitfalls: results are cached (default 300 s) — pass refresh=true after an admin
change. Time-entry activities are read from the time-entry form, so if the time
tracking module is off or you lack permission the list comes back empty with a note
in notes rather than an error (check notes).
For the writable fields and custom fields of one project+type combination use
get_work_package_schema; for project ids themselves use list_projects.
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | Bypass the metadata cache. Use it right after an administrator added a type, status, version or category; otherwise leave it false. | |
| project_id | No | Numeric project id or URL identifier. Omit for the instance-global sets (types, statuses, priorities, roles) — cross-project filtering never needs an arbitrary project. Supply it to additionally get this project's types, versions, categories and time-entry activities. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | Degradation notes: modules off, permissions missing. |
| roles | No | All membership roles. |
| types | No | Work-package types; scoped to the project when project_id was given. |
| statuses | No | All statuses, each with is_closed. |
| versions | No | Project versions/sprints; null unless project_id was given. |
| categories | No | Project categories; null unless project_id was given. |
| priorities | No | All priorities. |
| project_id | No | The project this was scoped to; null for the global sets. |
| time_entry_activities | No | Activities log_time accepts here; null unless project_id was given. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, and non-destructive hints, but the description goes further. It discloses cache behavior (300s TTL, refresh=true), the empty-list-with-notes behavior when time tracking is disabled, per-row flag semantics, and the localization caveat for status names—all beyond what annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is somewhat long but every sentence earns its place—each covers a distinct aspect: purpose, timing, row structure, pitfalls, and alternatives. It is front-loaded with the most important use case, well-paragraphed, and free of filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the existence of an output schema, the description is exceptionally complete. It covers invocation timing, parameter behavior, return value shape, caching, edge cases, and sibling tool pointers, fully equipping an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds meaningful semantics: project_id narrows types and fills versions/categories/activities, while refresh bypasses the cache after admin changes. This enriches understanding far beyond the schema's generic parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists valid ids and names for the instance, with the specific verb 'List' and a clear resource scope. It also differentiates itself from siblings by explicitly naming get_work_package_schema and list_projects as alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Call it before any create or update, before filtering by ids, and whenever a write fails with an allowed-values error.' It also names alternatives for other lookup needs, leaving no ambiguity about when to choose this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_phaseGet project phaseARead-onlyIdempotent
Read one project's phase record: name, active flag and its definition.
Use it after get_work_package surfaced a project_phase reference and you
need to know which project and definition that phase belongs to, or whether it is
still active.
Returns {id, name, active, definition, project, created_at, updated_at}.
Pitfalls: the API has no phases index — ids only come from work packages'
project_phase references. Phase dates are not exposed by the API (the notes
say so), so "which projects are in this phase now" goes through
list_projects(in_phase=...) instead. A 404 covers a wrong id, a phase invisible
to this user (view_project_phases), and instances that predate project phases
(16.1).
Cross-references: list_project_phase_definitions for the instance-wide catalog
and gates; list_projects(in_phase=...) for date-based phase queries.
| Name | Required | Description | Default |
|---|---|---|---|
| phase_id | Yes | Per-project phase record id, from a work package's project_phase reference. NOT a definition id from list_project_phase_definitions. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Phase id — a per-project record id, not the definition id. |
| name | No | Phase name. |
| notes | No | Degradation notes for this result. |
| active | No | False when the project switched this phase off. |
| project | No | Project this phase belongs to. |
| created_at | No | ISO 8601 UTC timestamp. |
| definition | No | The instance-global phase definition this instantiates. |
| updated_at | No | ISO 8601 UTC timestamp. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint/idempotentHint annotations, the description discloses important behavioral traits: there is no phases index, ids only come from work package references, phase dates are not exposed, and a 404 covers three distinct error cases. These details meaningfully help an agent anticipate tool behavior and failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with an initial summary, a usage trigger, a pitfalls section, and cross-references. Every sentence carries unique information; there is no filler or repetition of schema/annotation content. The length is justified by the tool's edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the return shape, the prerequisite data source, alternative tools, and the full meaning of a 404 error. Combined with a rich output schema and strong annotations, this description leaves no significant gap for an agent to misuse the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage and explicitly warns that phase_id is "NOT a definition id from list_project_phase_definitions." The description reinforces this by stating ids only come from work packages' project_phase references. This adds contextual value over the schema alone, though most semantic weight is carried by the schema's own description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: "Read one project's phase record: name, active flag and its definition." It clearly scopes the tool to a single phase record and lists the exact fields returned, making its purpose unambiguous and distinguishable from broader list/search siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool: "Use it after get_work_package surfaced a project_phase reference..." It also gives a when-not-to-use by pointing to list_projects(in_phase=...) for date-based phase queries and cross-references list_project_phase_definitions for the catalog. This is textbook usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_report_dataGet project report dataARead-onlyIdempotent
Aggregate everything a status report needs about one project and one date window.
Use it for weekly reports, sprint reviews, standups and "what happened in June" —
one call replaces a dozen filtered listings. It returns, for the window: created,
updated and closed work-package buckets (each {items, total, truncated, more_via} with compact rows), open_total plus open_by_status counts computed
server-side over the whole open set, a time summary (total hours with per-activity
and per-user breakdowns) and the project's membership roster.
Done/in-progress classification is safe here: every row carries is_closed, read
from the status's own isClosed flag on this instance, so it works on translated
and renamed workflows where matching status names would not. closed is exactly
"in a closed status and touched inside the window" — the done-this-week set.
Pitfalls. Counts and row lists are different things: total is always the server's
number, while items stops at an internal cap and then sets truncated and adds a
notes entry — quote the count, not the row count. open_by_status covers the open
set as it is now, not as it was during the window. updated includes the rows in
closed. Time visibility is permission-bound, so a total_hours of 0 can mean "not
allowed to see" rather than "nobody logged time" — an unreadable time ledger and an
unreadable roster each degrade into a notes entry instead of failing the call.
Read notes before calling any number complete.
Cross-references: rendered reports are the weekly_report and daily_standup
prompts, which run this same aggregation server-side; drill into a bucket with
list_work_packages, into hours with list_time_entries, and into one row with
get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| to_date | Yes | Last day of the report window, ISO YYYY-MM-DD, inclusive. | |
| from_date | Yes | First day of the report window, ISO YYYY-MM-DD, inclusive. Required and never inferred — 'this week' means different days to different people. | |
| project_id | Yes | Numeric project id or project identifier (the URL slug). Both come from list_projects; the identifier is what appears in /projects/<identifier>. |
Output Schema
| Name | Required | Description |
|---|---|---|
| time | Yes | Time logged against the project inside the window. |
| notes | No | In-band markers: which lists were capped, which sources degraded. Read them before quoting a number as complete. |
| closed | Yes | Work packages in a closed status that changed inside the window — the 'done this week' set. |
| roster | No | Project membership roster: who may act in the project and with which roles. |
| created | Yes | Work packages created inside the window (createdAt range filter). |
| project | No | The project the report covers. |
| to_date | Yes | Window end, ISO YYYY-MM-DD, inclusive. |
| updated | Yes | Work packages changed inside the window (updatedAt range filter). Includes the ones that were closed. |
| from_date | Yes | Window start, ISO YYYY-MM-DD, inclusive. |
| open_total | No | Open work packages in the project right now, server-reported. |
| open_by_status | No | Server-side groupBy=status counts over the whole open set, independent of paging. Never re-add these from rows. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint/idempotentHint, but the description adds extensive behavioral context: truncation and `truncated`/`notes` behavior, `open_by_status` being 'as of *now*', `updated` including closed rows, permission-bound time visibility degrading into `notes` entries, and the `is_closed` flag robustness on translated workflows. This far exceeds the annotation baseline.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose, return contents, safe classification, pitfalls, and cross-references. Though long, every sentence conveys critical edge-case or usage information – it is dense but not bloated, and the key purpose is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (multiple return buckets, truncation, permission-dependent fields, server-side computed counts), the description covers all major aspects: return structure, pitfalls, degradation behavior, and drill-down alternatives. The presence of an output schema further completes return-value understanding, so the description is fully adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with detailed parameter descriptions (e.g., from_date explains inclusive ISO and that it's never inferred). The description does not add per-parameter semantics beyond the schema; it focuses on usage and return behavior. Per rubric, baseline 3 applies when schema fully documents parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description opens with 'Aggregate everything a status report needs about one project and one date window' – a specific verb, resource, and scope. It distinguishes from sibling listing tools by saying 'one call replaces a dozen filtered listings' and enumerates exactly what it returns (buckets, counts, time summary, roster).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states 'Use it for weekly reports, sprint reviews, standups and "what happened in June"' and provides clear alternatives for drill-down: 'drill into a bucket with list_work_packages, into hours with list_time_entries, and into one row with get_work_package.' This gives both when-to-use and when-to-use-alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recurring_meetingGet recurring meetingARead-onlyIdempotent
Read one recurring series in full: the schedule plus its next occurrences.
This is the step before touching any occurrence: the occurrences rows carry the
exact start_time strings that init_recurring_meeting_occurrence and
cancel_recurring_meeting_occurrence key on, and the meeting_id that get_meeting
/ delete_meeting take once a slot is instantiated.
Returns the series fields (schedule, duration_hours, location, author,
template_meeting_id) plus occurrences in date order: {start_time, state, meeting_id}. state is 'planned' for a slot that exists only in the schedule;
once instantiated it is the backing meeting's own state, and only then is
meeting_id non-null.
Pitfalls. occurrences is capped at the next few slots — notes says when the cap
was hit, and an unbounded series always computes more. The template meeting (its
agenda seeds every occurrence) is edited through the regular meeting tools via
template_meeting_id; while it is still a draft, occurrences cannot be initialized.
A 404 means a wrong id, no 'view meetings' permission, the module is off — or
OpenProject before 17.4, which has no recurring-meetings API; the hint names all
readings.
Cross-references: list_recurring_meetings for the series id;
init_recurring_meeting_occurrence / cancel_recurring_meeting_occurrence for one
slot; update_meeting on the template to build the shared agenda or publish a draft
template; delete_recurring_meeting to remove the whole series.
| Name | Required | Description | Default |
|---|---|---|---|
| recurring_meeting_id | Yes | Numeric series id from list_recurring_meetings. Never a meeting id — a series and its occurrences are different resources. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Series id — what get_recurring_meeting and the occurrence tools take. Not a meeting id. |
| notes | No | Degradation markers: an unreadable schedule, the occurrence cap, a time zone that could not be applied, a draft template. |
| title | No | Series title. |
| author | No | User who created the series. |
| project | No | Project the series belongs to. |
| end_date | No | Last possible date (ISO); only when end_after='specific_date'. |
| interval | No | Every N days/weeks/months; always 1 for 'working_days'. |
| location | No | Room name or meeting URL each occurrence inherits. |
| end_after | No | 'never', 'specific_date' or 'iterations'. |
| frequency | No | Repetition rule: 'daily', 'working_days', 'weekly', 'monthly_day_of_month' or 'monthly_nth_weekday'. |
| time_zone | No | Zone the schedule computes in, as OpenProject stores it (an IANA identifier or a Rails zone name). |
| iterations | No | Total occurrences; only when end_after='iterations'. |
| start_time | No | First-occurrence start as ISO 8601 UTC. |
| monthly_day | No | Day of month (1-31); only for 'monthly_day_of_month'. |
| occurrences | No | The next upcoming slots in order (capped; see 'notes'). meeting_id is null until a slot is instantiated, and state 'planned' marks exactly those. |
| duration_hours | No | Length of each occurrence in hours (1.5 = 90 minutes). |
| monthly_ordinal | No | Which weekday occurrence (1-4, -1 = last); only for 'monthly_nth_weekday'. |
| monthly_weekday | No | Weekday name; only for 'monthly_nth_weekday'. |
| template_meeting_id | No | Id of the template meeting the occurrences are copied from. Its agenda is edited with the regular meeting tools, and a freshly created template is a DRAFT — publish it with update_meeting(meeting_id=<this>, state='open') before initialising occurrences. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark it read-only and non-destructive, but the description adds significant behavioral detail: occurrences are capped with a `notes` field indicating when the cap was hit, template edits go through regular meeting tools, draft templates block initialization, and a 404 can mean four different things including API version compatibility. This goes far beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence has purpose. It is front-loaded with a one-line summary, then structured into use-case, return format, pitfalls, and cross-references. Formatting with code spans and paragraph breaks improves scannability without wasting words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read tool with one parameter, an output schema, and good annotations, the description fully covers return fields, occurrence state semantics, the cap pitfall, template behavior, and error interpretation. It also positions the tool within the broader toolset, making it complete for an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% — the parameter `recurring_meeting_id` is already described as 'Numeric series id from list_recurring_meetings. Never a meeting id'. The description reinforces this by saying to use `list_recurring_meetings` for the series id, but doesn't add substantially new parameter-specific meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Read one recurring series in full: the schedule plus its next occurrences,' which clearly states a specific verb (read) and resource (recurring series). It distinguishes from siblings like list_recurring_meetings (listing all series), init/cancel occurrence (modifying a single slot), and delete_recurring_meeting (removing the whole series).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly frames this as 'the step before touching any occurrence' and explains how the returned `start_time` and `meeting_id` feed into other tools. Cross-references name exact alternatives: `list_recurring_meetings` for the series id, `init_recurring_meeting_occurrence` / `cancel_recurring_meeting_occurrence` for a slot, `update_meeting` on the template, and `delete_recurring_meeting` for the whole series. This is clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_userGet user detailARead-onlyIdempotent
Read one user's profile: name, login, email, admin flag and status.
Use it after search_principals when you need more than a name — to
confirm an account is active before assigning work, to check whether
somebody is an instance administrator, or to learn which account the
server itself is acting as (id_or_me='me').
Returns {id, name, login, email, admin, status, language, created_at, updated_at}. The avatar URL is deliberately dropped: it costs tokens
and cannot be rendered here.
Pitfalls. email, login and admin are visibility-dependent — a null
means the authenticated account may not see that field, never that the
value is empty. admin=true says nothing about project permissions; use
list_permissions for what the current user may actually do. A group or
placeholder-user id returns 404 from this endpoint; those principals only
appear in search_principals.
Cross-references: search_principals to find the id;
list_memberships(principal_id=...) for the projects and roles this
person holds; get_instance_info also reports the current user.
| Name | Required | Description | Default |
|---|---|---|---|
| id_or_me | Yes | Numeric user id from search_principals, or the literal string 'me' for the account this server authenticates as. Group and placeholder ids are not accepted here — this endpoint serves users only. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Numeric user id. |
| name | No | Display name, e.g. 'Grace Hopper'. |
| admin | No | True for instance administrators. Null when the field is not visible to the caller — treat null as 'unknown', never as 'not an admin'. |
| No | Email address; null when the authenticated account may not see it. | |
| login | No | Login name used to sign in. |
| status | No | Account status, one of: active, invited, registered, locked. |
| language | No | Interface language code, e.g. 'en'. Absent on some instances. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant context beyond annotations: visibility-dependent nulls, the deliberate dropping of avatar URL, 404 behavior for group/placeholder ids, and the meaning of 'me'. No contradiction with readOnlyHint=true or other annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is thorough and organized with a clear opening, a usage paragraph, a return-value line, a pitfalls section, and cross-references. It is longer than necessary but every sentence adds value; front-loaded with the core purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for a simple read tool: it covers what fields are returned, visibility caveats, error behavior (404), idempotency context, and direct alternatives. The presence of an output schema and supportive annotations further reduces ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description reinforces the parameter's dual type (integer or 'me') and mentions the restriction on group ids, but the schema already captures these details. No substantial additional parameter semantics are provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Read one user's profile') and lists concrete fields (name, login, email, admin flag, status). It clearly distinguishes itself from siblings by referencing search_principals (which returns only names) and list_permissions (for project permissions).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage guidance is provided: 'Use it after search_principals when you need more than a name' with concrete scenarios (confirm active account, check admin, use 'me'). It also gives an exclusion ('admin=true says nothing about project permissions; use list_permissions') and cross-references other tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_wiki_pageGet wiki pageARead-onlyIdempotent
Read a wiki page's identity and project — NOT its content.
Two limits define this tool, and both must be passed on to the user rather than worked
around. First, wiki_page_id comes from a wiki page URL the user supplies: API v3 has no
wiki index and no wiki search, so there is no way to look a page up by title or to list a
project's pages. Second, the page's CONTENT is not exposed by the API at all — the
response carries only {id, title, project} plus the page's attachments, and notes
repeats that in-band.
So: use it to confirm which page a URL points at, to get the project a page belongs to, and as the step before fetching its files. To read the text, ask the user to paste it or open the page in the browser.
Pitfalls. A 404 means the id is wrong, the page was deleted, or the wiki is disabled in that project — it does not mean the wiki is empty. Sub-pages, revisions, page history and wiki-page↔work-package links are not exposed either. Creating or editing wiki pages is not supported by this server.
Cross-references: list_attachments(container_type='wiki_page', container_id=<id>) lists
the files on the page and download_attachment fetches one; get_project_metadata for
what the project does expose.
| Name | Required | Description | Default |
|---|---|---|---|
| wiki_page_id | Yes | Numeric wiki page id. There is no wiki index or search in API v3, so this id can only come from a page URL the user gives you — the number in /projects/<project>/wiki/<id> or in the page's 'Info' view. Never guess it. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Wiki page id. |
| notes | No | Always carries the marker that page content is not part of API v3. |
| title | No | Page title as shown in the wiki menu. |
| project | No | Project that owns the wiki. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, but the description adds substantial context beyond these: no wiki index or search, content not exposed by the API, 404 meaning an invalid id or disabled wiki, and no sub-pages or revisions. These details are critical and not derivable from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear opening statement, logical sections for limits, use cases, and pitfalls, and no redundant sentences. Every detail earns its place, and the length is justified by the tool's unusual constraints.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers the tool's context: what it returns, what it cannot do, how to handle errors, and related tools. Given that an output schema exists (indicated by context), the lack of explicit return-type explanation is not a gap. This is complete for the tool's complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides 100% coverage with a detailed description of wiki_page_id, including its source (page URL) and a warning to never guess it. The tool description reiterates this information without adding new parameter-level semantics, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Read a wiki page's identity and project — NOT its content.' It explicitly distinguishes the tool's scope from content retrieval and clarifies the response shape. This clearly differentiates it from sibling tools that handle attachments or work packages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'use it to confirm which page a URL points at, to get the project a page belongs to, and as the step before fetching its files.' It also provides an explicit alternative for reading content: 'ask the user to paste it or open the page in the browser.' Cross-references to list_attachments and get_project_metadata further guide tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_work_packageGet work package detailARead-onlyIdempotent
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 lock_version in the result is what
update_work_package needs for a safe concurrent edit.
Returns every core field, custom_fields in the canonical
[{key, name, type, value, value_ids}] shape (only fields that have a value), an
available map saying whether this work package exposes dev links, meetings or file
links, and any requested includes.
Pitfalls: includes are capped at 20 — a truncated children list means you should call
list_work_packages(parent_id=…) for the rest, which more_via spells out verbatim. A
sub-resource that 403s or 404s (module off, no permission) degrades into a notes entry
instead of failing the whole read.
For the comment thread use list_work_package_comments; for attachment bytes use
download_attachment; for linked PRs and commits use get_work_package_git_activity.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Work package id — the number shown as #1234 in OpenProject. Comes from search_work_packages or list_work_packages. Instances on 17.x with semantic identifiers enabled also accept the semantic form ('PROJ-42', the row's display_id). | |
| include | No | Extra sub-resources, fetched concurrently: 'relations', 'watchers', 'attachments', 'children', 'custom_actions'. Each is capped at 20 items and reports {truncated, total, more_via} when there are more. Ask only for what you need — every include is one more upstream request. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Work package id. |
| date | No | Milestone date (ISO YYYY-MM-DD); null for non-milestones. |
| type | No | Work package type. |
| notes | No | Degradation notes for this result. |
| author | No | Creating user. |
| parent | No | Parent work package. |
| status | No | Status. |
| project | No | Owning project. |
| subject | No | Subject line. |
| version | No | Version / sprint. |
| assignee | No | Assigned user or group. |
| category | No | Category. |
| children | No | Present only when 'children' was requested. |
| due_date | No | ISO date (YYYY-MM-DD). |
| priority | No | Priority. |
| watchers | No | Present only when 'watchers' was requested. |
| available | No | Feature availability for this WP: dev links, meetings, files. |
| relations | No | Present only when 'relations' was requested. |
| created_at | No | ISO 8601 UTC timestamp. |
| display_id | No | Human-facing id as the instance renders it. Matches the numeric id unless the instance uses semantic identifiers (17.x, e.g. 'PROJ-42'); null when the instance predates it. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| attachments | No | Present only when 'attachments' was requested. |
| description | No | Description as markdown (raw); html is dropped. |
| responsible | No | Accountable user. |
| spent_hours | No | Logged time in hours. |
| lock_version | No | Optimistic-locking version; pass to update_work_package. |
| custom_fields | No | Always a list; empty when none are set. |
| project_phase | No | Project phase this work package sits in (16.1+, only when phases are active in the project and visible to this user); details via get_project_phase. |
| custom_actions | No | Present only when 'custom_actions' was requested. |
| estimated_hours | No | Estimate in hours. |
| percentage_done | No | Progress, 0-100. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses rich behavioral details beyond the readOnlyHint/idempotentHint annotations: the return shape, includes cap at 20, truncation via more_via, sub-resource degradation into notes on 403/404, and lock_version semantics for concurrent edits. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than typical but every paragraph serves a distinct purpose: usage context, return format, pitfalls, and alternative tools. It is front-loaded with the core purpose and contains no redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers usage triggers, return values, edge cases, and related tools. Combined with the fully described input schema and annotations, it gives an agent everything needed to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with both id and include already fully documented in the input schema. The tool description adds little parameter-specific meaning beyond repeating the includes cap, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Read one work package in full' with specific content areas, and it distinguishes itself from siblings by noting it is 'the only way to read a work package's description text'. It also explicitly references related tools for other concerns.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says when to use the tool: 'This is the tool to call once a search or list has given you an id'. It also provides direct alternatives: 'For the comment thread use list_work_package_comments; for attachment bytes use download_attachment; for linked PRs and commits use get_work_package_git_activity'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_work_package_git_activityGet work package git activityARead-onlyIdempotent
Show the code behind a work package: commits, pull/merge requests and CI status.
Use this for "is this ticket implemented", "what shipped for it", "did CI pass",
"which branch/PR is this in". It returns, in one call: revisions (commits whose
message references the work package, with full SHA, short SHA, author, message and
commit time), github_pull_requests (title, state, draft, merged/merged_at, labels,
author, URL and the CI check_runs with status and conclusion),
gitlab_merge_requests (the same, with pipelines instead of check runs) and
gitlab_issues.
available says, per source, whether this instance and this account can answer at
all, and notes explains every false — "module absent" and "no permission" are
different answers and neither means "no code was written". Report the notes rather
than concluding a ticket has no development activity.
Pitfalls. Every pull/merge request carries two numbers: id is the
OpenProject-internal id (the only thing get_github_pull_request accepts) and
number is the '#481' humans quote on GitHub/GitLab. A state of 'closed' does not
mean merged — check merged. A source that 403s or 404s is reported in notes, not
raised, so a missing GitLab module never hides GitHub results.
Nothing appears here by magic. Links are created by text, not by the API: a commit message must mention the work package ('refs #123', or 'fixes #123' / 'closes #123' to also close it), and a pull or merge request must mention 'OP#123' or the full work-package URL in its description or a comment. OpenProject cannot browse repositories, list branches or diffs, or create these links through the API.
Cross-references: full pull-request detail (body, diff counts, all check runs) via
get_github_pull_request(github_pull_request_id=<the id field>); the ticket itself
via get_work_package; the discussion via list_work_package_comments.
| Name | Required | Description | Default |
|---|---|---|---|
| include | No | Which sources to fetch: any of 'revisions' (SCM commits), 'github' (pull requests + CI check runs), 'gitlab' (merge requests, issues + pipelines). Omit for all three — they are fetched concurrently, so narrowing this saves little. Availability is reported for all three regardless of what was fetched. | |
| work_package_id | Yes | Work package id. Comes from search_work_packages, list_work_packages or get_work_package — never guess it. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | Degradation notes: which sources were skipped and why. Read them before reporting 'there are no pull requests'. |
| available | Yes | Per-source availability, derived from the work package's own links. |
| revisions | No | Commits referencing this work package; always a list. |
| work_package | No | The work package these results belong to ({id, name}). |
| gitlab_issues | No | Linked GitLab issues; always a list. |
| github_pull_requests | No | Linked GitHub pull requests; always a list. |
| gitlab_merge_requests | No | Linked GitLab merge requests; always a list. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even with readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false already present, the description adds crucial behavioral context: the distinction between internal `id` and human-facing `number`, the fact that 'closed' does not imply 'merged', and the error-handling behavior where 403/404s are reported in `notes` instead of being raised. It also discloses that links are only created via text mentions in commit messages or MR/PR descriptions, not by the API.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but deliberately structured: purpose, use cases, return shape, availability semantics, pitfalls, and cross-references. Every sentence carries weight—none are filler. The critical warnings (id vs number, closed vs merged, notes for errors) are essential for correct invocation and interpretation. It front-loads the purpose and then layers details logically.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (multiple source types, availability flags, CI status, pitfalls), the description covers every operational aspect: what data comes back, what each field means, how errors surface, and how links are created. It also points to companion tools for deeper detail, and the output schema covers the exact return shape. There is no obvious gap that would leave an agent guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already describes both parameters with 100% coverage. The description goes further by explaining the concurrency behavior of `include` ('they are fetched concurrently, so narrowing this saves little') and the fact that availability is always reported for all three sources regardless. It also strongly advises against guessing `work_package_id` and tells the agent where it comes from, adding trust and safety guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific and vivid statement: 'Show the code behind a work package: commits, pull/merge requests and CI status.' It clearly names the resource (work package) and the action (show git activity), and enumerates the three concrete result categories. This unambiguously distinguishes it from siblings like get_work_package or list_work_package_comments.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly prescribes when to use the tool: 'Use this for "is this ticket implemented", "what shipped for it", "did CI pass", "which branch/PR is this in".' It also names alternatives and cross-references: full PR detail via get_github_pull_request, the ticket via get_work_package, and the discussion via list_work_package_comments. This gives the agent clear decision criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_work_package_schemaGet work package schemaARead-onlyIdempotent
Show which fields a work package of this type accepts in this project.
Call it before create_work_package/update_work_package when you need the
required fields, when you want a custom field's key or its allowed options, or after
a 422 that named a field you do not recognise.
Returns required_fields (writable keys you must supply), fields — every core
attribute with {key, name, type, required, writable, has_default, allowed_values}
— and custom_fields with {key, name, type, required, writable, options}.
allowed_values/options are {id, name} lists for status, category, version
and list/user custom fields.
Pitfalls: key is the wire spelling (startDate, customField12) — that is
what raw_filters and custom_fields writes use, though writes also accept the
display name. A field with writable: false is computed by OpenProject; sending it
is an error, not a no-op. allowed_values is null when the API only offers a lookup
URL (assignee, project) — resolve those with search_principals or list_projects
instead. Long option lists are capped at 50 with a marker in notes.
Ids for both parameters come from get_project_metadata; to read the values
actually set on one work package use get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | Bypass the cache after an administrator changed the type or its custom fields. | |
| type_id | Yes | Numeric work-package type id from get_project_metadata(project_id=...).types — not the type name. | |
| project_id | Yes | Numeric project id — this endpoint does not accept the string identifier. The schema is per project AND type: the same type carries different custom fields in another project. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | Degradation notes, e.g. capped allowed-value lists. |
| fields | No | Core attributes with type/required/writable. |
| type_id | Yes | Work-package type the schema was requested for. |
| project_id | Yes | Project the schema was requested for. |
| custom_fields | No | Always a list; empty when the type has none. |
| required_fields | No | Writable keys that must be supplied on create. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds rich behavioral context: wire spelling pitfalls, writable:false being an error, allowed_values null for lookup URLs, a 50-item cap, and cache-bypass behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a purpose sentence, explicit usage directions, a clear return-format listing, and a dedicated pitfalls section. Every sentence adds value, and it is front-loaded with the main purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description thoroughly covers when to use, what to expect, pitfalls, and parameter provenance. The output schema exists, so the return-format summary is a bonus, and the description is complete for an agent to invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds meaning beyond the schema: project_id must be numeric (not string), type_id is not the name, both ids come from get_project_metadata, and the schema is per project AND type. This enriches the parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb+resource construction: 'Show which fields a work package of this type accepts in this project.' This clearly distinguishes it from siblings like get_work_package (which reads actual values) and create/update_work_package (which write).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to call: 'Call it before create_work_package/update_work_package when you need the required fields, when you want a custom field's key or its allowed options, or after a 422 that named a field you do not recognise.' Also names alternatives (search_principals/list_projects) for resolving lookup URLs, providing clear when/when-not context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
init_recurring_meeting_occurrenceInit recurring meeting occurrenceAIdempotent
Materialize one occurrence of a series as a real meeting, copied from the template.
Use it when a specific slot needs its own agenda, minutes or attachments before the day: the occurrence becomes a normal meeting (template agenda and attachments copied) that every meeting tool can work on. Called on a cancelled occurrence it RESTORES it to 'open'; called where an open meeting already exists it idempotently returns that meeting.
Returns the instantiated meeting in the same shape as get_meeting — its id is
the meeting id for follow-up calls, distinct from the series id.
Pitfalls — the instant is trusted, not validated. OpenProject matches start_time
by timestamp equality and does NOT check it against the schedule, so a wrong instant
creates a real off-schedule meeting: always copy the string from
get_recurring_meeting's occurrences (offsets are normalized to UTC 'Z' form on the
wire). An HTTP 500 here almost always means the series' template is still a DRAFT —
OpenProject fails uncleanly on that instead of answering 422; publish the template
with update_meeting(meeting_id=<template_meeting_id>, state='open') and retry.
This needs the 'create meetings' permission (403 otherwise; OpenProject 17.4 itself
briefly wanted 'edit meetings').
Cross-references: get_recurring_meeting for the exact start_time strings and the
template id; update_meeting / add_meeting_agenda_item on the result;
cancel_recurring_meeting_occurrence for the opposite move.
| Name | Required | Description | Default |
|---|---|---|---|
| start_time | Yes | The occurrence's scheduled instant, copied VERBATIM from a get_recurring_meeting occurrences row ('2026-08-12T10:00:00Z'). Matching is exact-instant and OpenProject does not check the value against the schedule — a retyped or rounded time silently creates an off-schedule meeting. | |
| recurring_meeting_id | Yes | Numeric series id from list_recurring_meetings. Never a meeting id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Meeting id — what get_meeting and add_meeting_agenda_item take. |
| notes | No | Degradation notes: agenda items that could not be read, work packages this account may not see. |
| state | No | Lifecycle state: 'draft' (not yet opened to participants), 'open', 'in_progress', 'closed' or 'cancelled'. Cancelled meetings are excluded from listings. |
| title | No | Meeting title. |
| author | No | User who created the meeting. |
| project | No | Project the meeting belongs to. |
| end_time | No | ISO 8601 UTC end timestamp, derived from start plus duration. |
| location | No | Room name or meeting URL as typed by the organizer. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_time | No | ISO 8601 UTC start timestamp; null for an undated meeting. |
| updated_at | No | ISO 8601 UTC timestamp. |
| agenda_items | No | The agenda in order; always a list. Empty means either no agenda or an unreadable one — check 'notes' before concluding the meeting had none. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| participants | No | Invited users; always a list. Attendance is not exposed by API v3. |
| duration_hours | No | Scheduled length in hours (1.5 = 90 minutes); the wire sends an ISO duration, which is converted here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (idempotentHint, openWorldHint), the description discloses critical behaviors: idempotent return of an existing open meeting, restoration of cancelled occurrences, permission requirements, HTTP 500 for draft templates, and the non-validation of the instant leading to off-schedule meetings. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but every section earns its place: purpose, usage, return shape, pitfalls, permissions, and cross-references. It is well-structured with clear paragraphs and bullet-like pitfall sections, though slightly verbose; could be tightened without losing critical content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the existence of an output schema, the description provides comprehensive context: it explains the returned meeting shape references get_meeting, covers error modes (wrong instant, draft template), permission issues, and cross-references sibling tools. The agent has everything needed to invoke correctly and avoid known pitfalls.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already has 100% coverage with detailed descriptions for both parameters, including the verbatim copy warning for start_time and the distinction between series id and meeting id. The main description repeats these points but adds no new parameter semantics beyond what the schema provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource+outcome: 'Materialize one occurrence of a series as a real meeting, copied from the template.' It clearly differentiates this tool from its siblings (e.g., cancel_recurring_meeting_occurrence) by explaining the occurrence becomes a normal meeting that other meeting tools can operate on.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use it ('when a specific slot needs its own agenda, minutes or attachments before the day'), describes behavior on cancelled vs. existing occurrences, and points to alternatives like get_recurring_meeting for exact start_time strings and update_meeting for follow-up changes. It also warns against misuse with specific pitfall examples.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_attachmentsList attachmentsARead-onlyIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| container_id | Yes | Numeric id of the container itself: the work package id, wiki page id, meeting id, document id, budget id, or activity id. Never an attachment id. | |
| container_type | Yes | Kind of object that owns the files. Use 'comment' for files attached to a work-package comment — those live on the activity, so pass the activity id from list_work_package_comments as container_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses significant behavioral traits beyond the annotations: the upstream collection is not paginated and has_more is always false; the status field represents virus-scan state with downloadable vs. quarantined values; and a 404 can indicate an invalid container or a disabled module. This adds context that the readOnly and idempotent hints do not cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured logically with a clear first sentence, then a definition of containers, usage guidance, return format, pitfalls, and related tools. Every sentence earns its place; there is no unnecessary verbosity. It is long but information-dense and well-organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (multiple container types, virus scan status, pagination behavior), the description covers all necessary aspects: what it does, when to use it, return row fields, pitfalls, and related tools. The presence of an output schema is acknowledged but the description still explains the envelope and status semantics, making it complete for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the schema already provides descriptions for both parameters, the description adds crucial semantics: 'container_id identifies the container, not the file,' and explains that container_type='comment' requires passing an activity id from list_work_package_comments. This extra detail goes beyond the schema, clarifying ambiguous cases.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'List the files attached to one container,' which is a specific verb+resource statement. It defines containers as work packages, wiki pages, meetings, documents, budgets, and comments, and explicitly differentiates itself from related tools like download_attachment, upload_attachment, and get_work_package(include=['attachments']).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'Use this to discover attachment ids before calling download_attachment, or to check what a work package already carries.' It also names alternatives in the 'Related' section, clarifying when not to use this tool. Pitfalls, such as the 404 meaning, further guide usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_budgetsList budgetsARead-onlyIdempotent
List a project's budgets — their ids and names, which is all API v3 exposes.
Use it to see whether a project tracks budgets at all and to get a budget id, which is
what list_attachments(container_type='budget', ...) consumes.
Returns the standard list envelope with rows of {id, subject}. The collection is
fetched in full, so has_more is false.
Pitfalls — read before answering a money question. API v3's budget representer carries
no amounts: planned costs, spent costs, labor/material breakdowns and the assigned
work packages are simply not there. Do not infer them and do not present a budget row as
financial data; point the user at the budget in the UI, or use
get_project_report_data / list_time_entries for the effort side. Budgets are also a
module: a 404 (not installed, or not enabled in this project) and a 403 (this account
lacks 'view budgets') both come back as a SUCCESSFUL call with an empty items and the
reason in notes. Neither means the project has no budgets, so check notes before
answering — only an empty list with no notes means there are none.
Cross-references: list_projects for the project id; list_time_entries for logged
effort; list_attachments(container_type='budget', container_id=...) for budget files.
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | Numeric project id or URL identifier, from list_projects. Budgets are always read per project; there is no instance-wide budget listing. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description discloses critical behavior: the API returns no amounts, budgets are a module with silent failures (404/403 appear as successful empty lists), and the collection is fetched in full. This goes well beyond the readOnly/idempotent hints and prevents misleading financial inferences.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is organized into purposeful paragraphs: what it does, how to use it, return format, pitfalls, and cross-references. No redundancy; every sentence contributes unique value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (API limitations, module errors) and the presence of an output schema, the description fully covers return envelope, error semantics, and related tools. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description does not add much beyond the schema's project_id explanation; it references list_projects but the schema already says to use that. No additional parameter semantics are provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List a project's budgets — their ids and names', which is a specific verb, resource, and scope. It clearly distinguishes this tool from siblings like list_projects or list_time_entries by focusing solely on budgets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool ('to see whether a project tracks budgets at all and to get a budget id') and names alternatives ('get_project_report_data' / 'list_time_entries' for effort side). It also explains how 404/403 errors are represented, so the agent knows when not to treat an empty result as definitive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_documentsList documentsARead-onlyIdempotent
List the documents visible to you, across every project.
Documents are OpenProject's filing cabinet: a title, a description, and attached files.
Use this to find a document id for get_document or for
list_attachments(container_type='document', ...).
Returns the standard list envelope: rows of {id, title, project, created_at, updated_at} plus pagination. The description is deliberately left out of the rows —
get_document returns it in full.
Pitfalls. This is instance-wide: the endpoint takes no project parameter here, so filter
by reading project on the rows, and page through rather than assuming page one is
everything (pagination.has_more says). Documents are a module: where it is not
installed, or not enabled in any project you can see (404), or where this account may not
read documents (403), the call still SUCCEEDS with an empty items and the reason in
notes — an empty list with a note does not mean no documents exist, so read notes
first. The files themselves are attachments, not part of these rows.
Cross-references: get_document(document_id=...) for the description;
list_attachments(container_type='document', container_id=...) then
download_attachment for the files.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Documents per page (max 100). |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and idempotentHint=true, but the description adds substantial behavioral detail: the call is instance-wide with no project parameter, pagination must be checked via pagination.has_more, and the call can succeed with empty items plus a note when the module is unavailable or permissions are insufficient. It also clarifies that files are not part of the rows. This goes far beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with a clear lead sentence, a use-case paragraph, a return-format note, and a pitfalls section. Every sentence adds value: no fluff. The length is justified by the complex pitfalls and cross-references, and the front-loaded purpose ensures quick comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, output format (standard list envelope), navigation, pitfalls (module availability, permissions, pagination), and relationships to other tools. With an output schema present, it appropriately omits full return field details but gives enough context for the agent to invoke it correctly. It is fully self-sufficient for the agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides descriptions for page and page_size with 100% coverage, so the baseline is 3. The description adds practical guidance on how to use pagination ('page through rather than assuming page one is everything') and references pagination.has_more, which enriches the semantic understanding of both parameters beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List the documents visible to you, across every project,' which gives a specific verb, resource, and scope. It distinguishes itself from sibling tools like get_document and list_attachments by explicitly positioning itself as the way to find document IDs, and from search tools by noting it returns all documents rather than filtered results.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states usage: 'Use this to find a document id for get_document or for list_attachments(...)'. It also provides cross-references for related operations (get_document for descriptions, list_attachments for files) and warns when not to rely on a single page, directing the agent to paginate. This gives clear when-to-use and alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_file_linksList file linksARead-onlyIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| work_package_id | Yes | Numeric work package id whose linked storage files to list. It comes from search_work_packages, list_work_packages or get_work_package — never an attachment id and never a project id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal readOnly, idempotent, and non-destructive, but the description adds considerable detail: the list is fetched in full (has_more always false), 404/403 results return an empty list with a note, and the permission field semantics are explained ('View allowed' vs other values). It also discloses that URLs require the user's own OpenProject login and that the server cannot fetch bytes. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-structured, moving from a clear definition to return details, pitfalls, and related tools. Every sentence contributes important nuance, especially around edge-case behaviors. It could be trimmed slightly, but the density warrants a 4 rather than 3.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the rich output schema, the description is remarkably complete: it explains the return payload fields, error patterns, permission implications, and relationships to sibling tools. It addresses all foreseeable agent questions without redundancy.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides 100% coverage of the single parameter, so baseline is 3. The description adds value beyond the schema by clarifying that work_package_id is never an attachment id or project id and that it comes from specific list/search tools. This extra context justifies a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List the external-storage files (Nextcloud, OneDrive/SharePoint) linked to a work package.' This is a specific verb+resource+scope statement that clearly identifies the tool's purpose. It also explicitly distinguishes file links from attachments, setting it apart from the sibling list_attachments.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It says 'Use this to answer "which documents belong to this ticket"' and instructs pairing with list_attachments because the two lists are disjoint. It further states download_attachment does not work on these links and points to get_work_package as the provenance. This gives explicit when-to-use, when-not-to-use, and alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_meetingsList meetingsARead-onlyIdempotent
List meetings — the schedule side of a project: what is coming up, what already ran.
Use it to answer "when do we next meet", "what meetings does this project have", or to
find the meeting id that get_meeting and add_meeting_agenda_item need. Meeting ids
are instance-wide and never guessable, so this is the way to get one.
Returns the standard list envelope: rows of {id, title, project, start_time, end_time, duration_hours, location, state} plus pagination and notes. Times are ISO 8601 UTC
and duration_hours is a float (1.5 = 90 minutes). Rows are ordered by start time —
ascending when upcoming_only, descending otherwise.
Pitfalls. Cancelled meetings and recurring-series templates are excluded upstream, so an
absent meeting may exist in another state. state of 'draft' means the meeting has not
been opened to its participants yet. Agenda items and participants are NOT in these rows;
get_meeting fetches them. Meetings are a module: when it is not installed here or not
enabled in the project (404), or this account may not read meetings (403), the call still
SUCCEEDS with an empty items and the reason in notes — read notes before saying a
project has no meetings, because an empty list with a note is not an empty schedule.
Cross-references: get_meeting(meeting_id=...) for participants, agenda and outcomes;
create_meeting to schedule one; list_projects for the project id.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Meetings per page (max 100). | |
| project_id | No | Numeric project id to list only that project's meetings, from list_projects. Unlike most project parameters this one takes the id only — the meetings filter rejects a URL identifier, and passing one fails immediately with that explanation. Omit for every meeting visible to you. | |
| upcoming_only | No | True (the default) lists meetings that have not finished yet, soonest first. False lists past meetings too, most recent first — use it for 'what did we discuss last week'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations (readOnlyHint, idempotentHint) by disclosing several behavioral traits: return envelope fields, ISO 8601 UTC format, ordering rules, exclusion of cancelled/recurring templates, the meaning of 'draft' state, and the critical silent-success behavior when meetings module is unavailable (empty items with reason in notes). It also warns that absence may not mean no meetings, which is essential for correct interpretation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded: first sentence states purpose, followed by usage, return format, pitfalls, and cross-references. Though lengthy, every sentence carries relevant information—no filler. The 'Pitfalls' section is clearly demarcated, improving readability and scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (module behavior, silent failures, ordering, exclusions) and the moderate schema/annotations, the description covers all essential aspects: output format, time zone, ordering, edge cases, and error-like conditions. The output schema covers return structure, so the description compensates for any remaining ambiguity about behavioral nuances.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with each parameter described, so the baseline is 3. The description adds extra semantics: it clarifies that project_id takes only the numeric ID (rejecting URL identifiers) and explains the ordering behavior tied to upcoming_only. While these are refinements, they add useful meaning beyond the schema's own descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists meetings, the schedule side of a project, and explicitly distinguishes it from related tools like get_meeting and create_meeting. It also gives concrete use cases ('when do we next meet', 'what meetings does this project have'), making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: it says to use it to find meeting IDs needed by get_meeting and add_meeting_agenda_item, and to answer schedule questions. It also names alternatives (get_meeting for participants/agenda, create_meeting to schedule, list_projects for project IDs), fulfilling the when-not and alternative criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_membershipsList project membershipsARead-onlyIdempotent
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: items of {id, project, principal {id,name,type}, roles[], created_at, updated_at} plus
pagination{total,page,page_size,has_more}.
Pitfalls. The id in each row is the membership id — the handle for
update_membership and delete_membership — not the principal id and
not the project id; mixing them up revokes the wrong access. A person
can also reach a project through a group membership, so an empty
result for principal_id does not prove they have no access. Memberships
say who may act, not what they may do: roles carry the permissions.
Cross-references: list_roles for role ids and their permissions;
create_membership / update_membership / delete_membership to change
access (admin-gated); search_principals for principal ids;
list_permissions for what the current user may do.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Memberships per page, 1-100. | |
| project_id | No | Only memberships in this project. Numeric id or the URL identifier (an identifier costs one extra lookup). Combine with principal_id to check one person's roles in one project. | |
| principal_id | No | Only memberships held by this principal — a user, group or placeholder-user id from search_principals. Use it to answer 'which projects can this person see'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false. The description adds meaningful behavioral context: the membership id is not the principal/project id, group memberships can cause false negatives, and memberships indicate permission potential, not actual actions. It also describes pagination behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though longer than typical, it is tightly structured: purpose sentence, use cases, list envelope, Pitfalls paragraph, and Cross-references. Every sentence adds value, and the most important information is front-loaded. The length is justified by the tool's RBAC complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 4 optional filters and complex access semantics, the description covers use cases, large-result warnings, output envelope, pitfalls, and cross-references. Existing output schema reduces the need to document return values, but the description still provides helpful context. Nothing significant is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description augments this by explaining practical semantics: project_id supports URL identifiers with an extra lookup cost, combining project_id and principal_id to check one person's roles, and principal_id accepts user/group/placeholder ids. It also notes the no-argument behavior and 'which projects can this person see' use case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'List who has access to which project, and with which roles.' This clearly distinguishes it from sibling tools like list_roles and list_projects by focusing on membership objects. The title reinforces this without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit use cases are stated: 'Use it before granting or revoking access...', 'to audit a project's member list', and 'to see which projects a principal can reach.' It also names cross-reference tools (list_roles, create_membership, search_principals, list_permissions) and warns about the no-arguments behavior, providing clear alternatives and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_newsList newsARead-onlyIdempotent
List project news — the announcements a team publishes on its project overview.
Use it to answer "what was announced recently", to find the id of an entry before reading, editing or deleting it, or to check whether a report was already published. Results come back newest first (sorted by creation date descending).
Returns the standard list envelope: items of {id, title, summary, project, author, created_at, can_manage} plus pagination and notes. Rows carry the
short summary only — the full markdown body is fetched per entry with
get_news(news_id=...), which keeps a long announcement out of a listing.
can_manage tells you in advance whether update_news/delete_news would be
allowed for that row.
Pitfalls: news is only visible where the project has the news module enabled and this
account holds the 'view news' permission, and neither absence is an error — an empty
page carries a notes entry saying so, and reporting "this project has no
announcements" without reading it would be wrong. The project scope is matched by
numeric id; an identifier is resolved with one extra lookup, so an unknown identifier
fails as not_found rather than silently listing the whole instance.
Cross-references: get_news for the full body of one entry; create_news to
publish one; list_projects for project ids; work-package discussion lives in
list_work_package_comments, not here.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | News entries per page (max 100). | |
| project_id | No | Numeric project id (or URL identifier, resolved for you) to list only that project's announcements. Comes from list_projects. Omit it for every announcement visible to you across the instance. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare it read-only, idempotent, and non-destructive. The description adds substantial context: newest-first sorting, the exact envelope fields (items/pagination/notes), the distinction between summary and full body (fetched via get_news), the meaning of can_manage for permission checking, and edge cases like missing module/permission yielding an empty page with notes rather than an error. It also explains how project_id resolution works and that unknown identifiers fail as not_found. This significantly extends the structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with labeled sections (Use it, Returns, Pitfalls, Cross-references). Every sentence adds meaningful context—purpose, ordering, return format, safety hints via can_manage, and important edge cases—without fluff. It is long but necessary given the tool's nuances.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 3 optional parameters and an output schema, yet the description still explains return values in detail (items fields, pagination, notes) and covers pitfalls like empty pages due to missing permissions and unknown identifiers. It also clarifies the relationship with get_news for full bodies, making it complete for an agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining that project_id comes from list_projects, that omitting it lists across the instance, and that identifier resolution may involve an extra lookup with a not_found failure. It doesn't add extra semantics for page/page_size, but schema already documents them clearly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource+scope: "List project news — the announcements a team publishes on its project overview." It distinguishes itself from siblings by explicitly noting that work-package discussion lives in list_work_package_comments, and cross-references get_news/create_news for other news operations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage examples are given: "Use it to answer 'what was announced recently', to find the id of an entry before reading, editing or deleting it, or to check whether a report was already published." The Pitfalls section also clarifies when not to use it (e.g., don't report 'no announcements' without reading the notes) and points to alternative tools for work-package comments.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_notificationsList notificationsARead-onlyIdempotent
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: items of {id, reason, read, created_at, actor, project, resource} plus pagination with
total/page/page_size/has_more. resource is the thing
the notification is about — {id, type, title}, usually
type='WorkPackage', so resource.id feeds straight into
get_work_package or list_work_package_comments.
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
mark_notifications. unread_only=false can return a very long
history; keep a page size that fits your reply. A notification whose
project the token owner has lost access to disappears from the inbox
entirely.
Cross-references: mark specific rows read with mark_notifications;
clear the whole (optionally filtered) inbox with
mark_all_notifications_read; open the underlying ticket with
get_work_package(resource.id) and its discussion with
list_work_package_comments(resource.id).
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| reason | No | Restrict to one trigger: mentioned, assigned, responsible, watched, subscribed, commented, created, processed, prioritized, scheduled, shared, reminder, dateAlert. 'mentioned' is what answers "who needs me?". dateAlert filtering is an OpenProject Enterprise feature and is rejected with an explanatory hint on Community instances. | |
| page_size | No | Notifications per page (max 100); the instance may clamp it lower and the returned pagination reports what actually came back. | |
| project_id | No | Numeric project id or identifier to scope the inbox to one project. Ids come from list_projects. | |
| unread_only | No | true (default) returns only notifications still unread in the in-app inbox; false returns read and unread together. There is no 'read only' mode — filter the rows on read=false/true yourself if you need one. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint/idempotentHint annotations, the description reveals important behavioral traits: the inbox is always the token owner's, notifications aggregate multiple changes, reading here does not mark as read, unread_only=false may return a long history, and access loss causes notifications to disappear. It also notes the Enterprise-only dateAlert limitation. This significantly exceeds the annotation baseline.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with clear sections: purpose, example questions, envelope format, pitfalls, and cross-references. Every sentence adds practical information for a complex tool. It is front-loaded with the core purpose and stays efficient despite its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite the output schema existing, the description provides a complete picture: return envelope structure, resource semantics, aggregation behavior, read-state implications, access edge cases, and explicit sibling tool handoffs. No major gaps remain for an agent to correctly invoke and interpret this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by warning about 'unread_only=false can return a very long history' and advising page size tuning. It also clarifies that 'mentioned' is what answers 'who needs me?', but this is also in the schema. The extra context on unread_only and pagination justifies a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads the authenticated user's OpenProject inbox, using the specific verb 'Read' and resource 'inbox'. It explicitly distinguishes itself from siblings by noting 'it is the only tool that sees notifications', making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit 'Use this to answer' framing for common questions ('what needs my attention?') and provides alternatives via cross-references (mark_notifications, mark_all_notifications_read, get_work_package). This clearly tells the agent when to use this tool and what other tools handle related actions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_permissionsList permissionsARead-onlyIdempotent
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 items are one row per context —
{id, context, project, actions} with actions such as
work_packages/create — plus principal (the user asked about),
capability_count, and check when permission was given.
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 "me" value, so the numeric id of the
authenticated user is resolved first (from the cached users/me) — you cannot ask
about another user with this tool. Results are capped at 500 capabilities with a note
in notes when the cap is hit; scope with project_id to stay well under it.
Capabilities are about permission only: a permitted action can still fail validation.
Cross-references: get_instance_info reports who this server is authenticated as
and what the instance version supports; list_memberships and list_roles show
where the permissions come from; get_project_metadata lists the ids a permitted
action needs.
| Name | Required | Description | Default |
|---|---|---|---|
| permission | No | Optional single action to check, spelled the way OpenProject does: '<resource>/<action>', e.g. 'work_packages/create', 'memberships/create', 'projects/update'. Adds a {checked, allowed, granted_in} predicate to the result; the full grouped listing is returned either way, so you can see the exact spellings this instance uses. | |
| project_id | No | Numeric project id or URL identifier to scope the question to that project ('may I create work packages HERE'). Omit it for the instance-wide (global) actions such as creating projects or administering users. An identifier is resolved to its numeric id first, because the capabilities API only accepts numeric project ids. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| check | No | Present only when the 'permission' parameter was given. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| principal | Yes | The user the capabilities were resolved for — always the authenticated account, by numeric id. |
| pagination | Yes | Total/page/page_size/has_more. |
| capability_count | No | Individual capabilities read from the API before grouping. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the readOnly/openWorld/idempotent hints by disclosing how it resolves the current user ID (from cached users/me), the API's lack of a 'me' value, the 500-capability cap with notes, the subset-of-permissions caveat, and that permission is only a necessary condition (validations can still fail). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than typical, but each section (purpose, caveat, pitfalls, cross-references) earns its place for a tool with non-obvious edge cases. It is well-structured with bolded lead-ins; while dense, it remains scannable and free of fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description covers the operational context thoroughly: when to use it, what it returns (items, principal, capability_count, check), its limitations (subset, cap, no 'me'), and how it relates to sibling tools. The output schema exists, so return-value details are already structured, and the description adds the critical 'miss ≠ denied' interpretation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers both parameters with full descriptions, so the baseline is 3. The description adds extra value by reinforcing the purpose of each parameter with practical examples ('may I create work packages HERE') and explaining the 'check' predicate behavior, which goes beyond the schema's syntax-level detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List what the authenticated user is allowed to do, globally or in one project' — a specific verb, clear resource, and scope. It immediately distinguishes itself from the many work-package/project tools by focusing on capabilities and even contrasts itself with tools like list_memberships and list_roles.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: before a write that might 403, to explain failures, or to choose between tools. It also gives exclusions ('does not return a user profile', cannot ask about another user) and cross-references sibling tools (list_memberships, list_roles, get_instance_info) for related needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_project_phase_definitionsList project phase definitionsARead-onlyIdempotent
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 list_projects(in_phase=...)
accepts.
Returns the standard list envelope of {id, name, start_gate, start_gate_name, finish_gate, finish_gate_name} rows. Gates are the checkpoints a phase can begin
or end with; a definition without gates has both flags false.
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
view_project_phases permission in at least one project; older instances 404
(the error hint says so).
Cross-references: list_projects(in_phase=...) to find the projects a phase
covers today; get_project_phase for one project's phase record (its id comes
from a work package's project_phase).
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Records per page (max 100); most instances define fewer than a page of phases. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark readOnlyHint/idempotentHint/destructiveHint, and the description adds substantial context: the standard envelope shape, gate semantics, permission/version requirements, and 404 behavior on older instances. This goes well beyond the structured annotations without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is organized into clear, purposeful sections: purpose, usage, return format/gate semantics, pitfalls, and cross-references. Every sentence adds information; no filler or repetition of annotations. Although multiple paragraphs, the complexity of the tool justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, full annotation coverage, and only two well-described optional parameters, the description fully compensates for any gaps. It even covers edge cases (missing gates, 404 on older instances, permission requirements) and cross-tool integration, making it complete for an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters have their own descriptions, including defaults and bounds. The tool description adds no additional parameter-level semantics, so the baseline of 3 applies per the rubric.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'List the instance-global phase definitions — the vocabulary of the project life cycle.' It clearly distinguishes this tool from siblings like get_project_phase by emphasizing instance-wide catalog vs. per-project phases.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use the tool ('Use it to learn which phases...') and references alternatives: list_projects(in_phase=...) and get_project_phase. It also identifies when not to rely on it (phase dates not exposed, catalog vs. actual project phases), providing clear selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_projectsList projectsARead-onlyIdempotent
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 project_id parameter in this
server.
Returns the standard list envelope: items of
{id, identifier, name, active, public, parent, status_code, workspace_type} plus
pagination with total/page/page_size/has_more. Nothing is
truncated silently — page explicitly until has_more is false.
Pitfalls: search matches name and identifier only (not descriptions);
parent_id returns direct children, so a deep hierarchy needs one call per level;
status_code is a code such as on_track, never a translated label. On
OpenProject 17.x this listing deliberately mixes plain projects with programs and
portfolios — workspace_type says which each row is. in_phase tests phase
dates ("which projects are in Executing today"), so projects whose phases carry
no dates never match it.
For a single project's description and status explanation use get_project. For
the types, versions, categories and time-entry activities valid inside a project use
get_project_metadata. To list a project's work packages use
list_work_packages(project=...).
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| active | No | true (default) lists active projects, false lists only archived ones, null lists both. Archived projects are read-only in OpenProject. | |
| search | No | Case-insensitive substring matched against the project name AND its identifier. Descriptions are not searched. Omit to list everything the filters allow. | |
| sort_by | No | Server-side sort, e.g. [["name", "asc"], ["created_at", "desc"]]. Allowed keys: active, created_at, id, identifier, name, public, updated_at. Unknown keys are rejected with the allowed set listed. | |
| in_phase | No | Restrict to projects whose named phase covers a date (today unless phase_on_date says otherwise). Accepts a definition id or name from list_project_phase_definitions. Requires OpenProject 16.1+. | |
| fetch_all | No | Aggregate every page into one result instead of returning page 1. Capped at 500 items with a note when the cap bites; mutually exclusive with page. | |
| page_size | No | Records per page (max 100); the instance may clamp it lower and the returned pagination reports what actually came back. | |
| parent_id | No | Numeric id or identifier of a parent project; returns its DIRECT children only, not the whole subtree. Ids come from a previous list_projects call. | |
| phase_on_date | No | ISO date (YYYY-MM-DD) the in_phase filter should test instead of today. Only valid together with in_phase. | |
| favorites_only | No | Restrict to projects the authenticated user has favorited. Instances that predate project favorites reject this filter with a 400. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is covered. The description adds substantial behavioral context beyond that: the standard list envelope, explicit pagination ('Nothing is truncated silently — page explicitly until has_more is false'), and pitfalls (search scope, direct-children semantics, status_code codes, workspace_type mixing, in_phase date semantics).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every section earns its place: overview, use cases, return envelope, pitfalls, and explicit alternatives. It is front-loaded with the core purpose and structured logically, avoiding fluff. For a tool with 10 parameters and significant edge cases, this length is justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (10 parameters, many edge cases, and many sibling tools), the description is remarkably complete. It covers the result envelope, pagination behavior, parameter interplay, version-specific behavior (OpenProject 17.x), and redirects to relevant alternatives. An output schema exists, so return-value detail is appropriately left to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value by explaining behavioral implications of specific parameters: 'parent_id returns direct children, so a deep hierarchy needs one call per level', 'in_phase tests phase dates', and that search ignores descriptions. These go beyond the raw schema text, earning a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource ('List projects') and clearly differentiates from siblings by positioning this tool as the id-producing path for every project_id, and by naming alternatives (get_project, get_project_metadata, list_work_packages). It also enumerates concrete use cases (name→id, enumerate sub-projects, review off track).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is provided ('Use this to turn a project name into the id...'), and exclusions are named with alternative tools ('For a single project's description... use get_project; For the types... use get_project_metadata; To list a project's work packages use list_work_packages'). This gives clear steering versus alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_queriesList saved queriesARead-onlyIdempotent
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 run_query reproduces exactly what they see in the UI, including their
grouping and sums.
Returns the standard list envelope: rows of {id, name, project, public, starred, updated_at} plus pagination. project is null for a global query (saved outside
any project); public false means the query is private to its owner, and only the
owner's queries are visible to this account.
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_query(query_id=…); build an ad-hoc query
instead with list_work_packages; project ids come from list_projects.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Queries per page (max 100). | |
| project_id | No | Numeric project id to list only that project's saved views. Comes from list_projects. Omit to list everything visible to the current user, global queries included. A project identifier (URL slug) is not accepted here by OpenProject — use the numeric id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint, openWorldHint, idempotentHint), the description discloses the return envelope shape, null semantics for project, public/private visibility, instance-wide query IDs, and the fact that it never runs queries—so it cannot reveal result counts. This is rich behavioral context that helps the agent understand side effects and limitations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well organized into purpose, usage guidance, return details, pitfalls, and cross-references. Every sentence contributes valuable information without redundancy, and the main verb and object lead the first sentence, making it front-loaded and easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, the description still explains the envelope and important edge cases such as global queries, visibility rules, and ID scope. It also covers cross-tool relationships with run_query, list_work_packages, and list_projects, leaving no obvious gaps for an agent to misuse the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides thorough descriptions for all three parameters, including project_id's numeric requirement, default behavior, and relationship to list_projects. The description adds little parameter-level meaning beyond what the schema offers, so baseline 3 applies with 100% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List the saved work-package views (queries) this user can open,' which clearly identifies the action and resource. It distinguishes itself from siblings like run_query and list_work_packages by explicitly focusing on saved definitions rather than executing or building queries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly advises using this tool to discover what a team already tracks before hand-building filters, and it names alternatives: 'run one with run_query(query_id=…)', 'build an ad-hoc query instead with list_work_packages', and 'project ids come from list_projects'. This provides clear when-to-use and alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_recurring_meetingsList recurring meetingsARead-onlyIdempotent
List recurring meeting series — the repetition rules, not the individual meetings.
Use it to answer "what regular meetings do we have" and to find the series id that
get_recurring_meeting, the occurrence tools and delete_recurring_meeting need.
Series ids are their own id space: a series id is never a meeting id, and the weekly
occurrences themselves show up in list_meetings, not here.
Returns the standard list envelope: rows of {id, title, project, start_time, time_zone, frequency, interval, monthly_day, monthly_ordinal, monthly_weekday, end_after, end_date, iterations, duration_hours, location} plus pagination and
notes.
Pitfalls. The listing is instance-wide — the endpoint takes no project filter, so
scope by reading project on the rows. duration_hours is per occurrence. Recurring
meetings are 17.4+ AND a module: where the API predates them, the module is off, or
this account may not read meetings, the call still SUCCEEDS with an empty items and
the reason in notes — read notes before saying there are no recurring meetings.
Cross-references: get_recurring_meeting(recurring_meeting_id=...) for the schedule
with its next occurrences; create_recurring_meeting to start a series;
list_meetings for the instantiated occurrences.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| page_size | No | Series per page (max 100). |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses several behavioral traits beyond annotations: instance-wide listing with no project filter, `duration_hours` per occurrence, and the critical pitfall that the call succeeds with empty items when the API version/module/account cannot read meetings, with the reason in `notes`. This adds substantial value beyond the readOnlyHint and destructiveHint annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose, usage, return format, pitfalls, and cross-references. Every sentence adds information; there is no fluff. Despite its length, it remains focused and scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema and good annotations, the description covers all necessary context: what it lists, how to use it, return field details, important limitations (instance-wide, version/module caveats), and related tools. Nothing important is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides complete descriptions for both `page` and `page_size` (100% coverage), so the baseline is 3. The description adds only a general mention of the 'standard list envelope' but does not elaborate on parameter behavior beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List recurring meeting series — the repetition rules, not the individual meetings,' which states the verb, resource, and exact scope. It explicitly distinguishes from individual meetings and references sibling tools like `list_meetings`, making the purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It directly tells when to use the tool: to answer 'what regular meetings do we have' and to find series IDs needed by other tools. It also explains what it does NOT include (occurrences are in `list_meetings`) and provides cross-references to related tools, giving clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_remindersList remindersARead-onlyIdempotent
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:
items of {id, remind_at, note, work_package, creator} plus
pagination and notes.
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
work_package; a reminder is not a work package and its id is not one.
Cross-references: set_work_package_reminder creates, moves or deletes
one; list_notifications shows what OpenProject has actually notified
you about, including fired reminders; get_work_package opens the
ticket a reminder points at.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly, idempotent, openWorld), the description discloses important behavioral traits: reminders are personal and cannot show others' data, only upcoming reminders are returned (fired/completed ones are dropped), and an empty result does not mean nothing was scheduled. It also clarifies the distinction between reminder IDs and work package IDs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: a clear one-sentence summary, a brief usage section, a pitfalls subsection, and cross-references. Every sentence adds meaningful information without fluff, and the front-loaded purpose makes it easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (no parameters) and the presence of an output schema, the description covers all necessary context: personal scope, upcoming filter, return envelope structure, and pitfalls. It fully prepares the agent to use the tool correctly without needing to consult external documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4. The description properly clarifies that there are no parameters and that the output is the full list of personal upcoming reminders, which complements the empty input schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a specific verb and resource: "List your own upcoming work-package reminders." It clearly distinguishes this tool from siblings by scoping to personal and upcoming reminders, and cross-references related tools to avoid ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit use cases are provided: answering what you asked to be reminded about, checking before creating another reminder, and finding deferred work packages. Cross-references clearly state when to use set_work_package_reminder, list_notifications, and get_work_package instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_rolesList rolesARead-onlyIdempotent
List the roles this instance defines, with their ids.
This is the id-producing tool for create_membership.role_ids and
update_membership.role_ids — role names are never accepted there.
Roles are instance-wide definitions ('Member', 'Reader', 'Project
admin'); a membership binds one principal to one project with a set of
them.
Returns the standard list envelope with has_more: false: the role list
is small and fetched in full. Each item is {id, name}, plus
permissions when include_permissions=true.
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 include_permissions=true returns none, notes says so
rather than pretending the roles grant nothing.
Cross-references: create_membership / update_membership consume these
ids; list_memberships shows which roles are in use; list_permissions
answers what the current user may do, which is the more useful question
when a call just failed with 403.
| Name | Required | Description | Default |
|---|---|---|---|
| include_permissions | No | Add each role's full permission identifier array. Off by default because those arrays are long — a dozen roles can run to thousands of tokens. Turn it on only when the question is genuinely 'what does this role allow'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark it read-only, idempotent, and non-destructive, but the description adds substantial context: the list envelope with has_more:false, the item shape, the effect of include_permissions, configurable role names, assignability restrictions, and version-dependent permission arrays. This goes far beyond annotation defaults and warns of realistic pitfalls.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with a lead sentence, a clear note on id usage, a return-format paragraph, a Pitfalls section, and a Cross-references section. Every sentence adds value, no fluff, and important details are front-loaded. Length is justified by the richness of guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description still explains the envelope and item shape. It covers the tool's role in the wider workflow, its pitfalls, and cross-references to related tools. For a single-parameter list tool, this is as complete as one could reasonably need.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and already describes include_permissions well, so baseline is 3. The description adds extra practical context by explaining the performance cost (thousands of tokens) and advising when to enable it, which genuinely enhances the schema's meaning. Minor gap: no mention of default false beyond schema, but that's already there.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it lists instance-defined roles with their IDs, using a specific verb and resource. It distinguishes itself from sibling tools by explaining its role as the id-producing tool for membership operations, making its purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly frames this as the required source for role_ids in membership tools, says role names are never accepted, and names alternatives: list_memberships for usage, list_permissions for current user permissions, especially after a 403. Provides clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_time_entriesList time entriesARead-onlyIdempotent
List logged time, filtered server-side, with an optional accurate total.
Use it to answer "how much time went into this ticket?", "what did I
book last week?" or "how much did the team spend on project X in June?".
Filters combine with AND, so project_id + user='me' + a date range is
one call.
Returns the standard list envelope: items of {id, hours, spent_on, comment, user, activity, work_package, project} plus pagination.
hours is a float (1.5 = 1h30), never an ISO duration. With
sum_hours=true the envelope also carries sums.total_hours over
all matches and one groups bucket per activity with its own
count and sums.total_hours — those cover the whole filtered set,
so never add pages up yourself.
Pitfalls. Visibility is permission-bound: without the
view-all-time-entries permission you see only your own entries, and a
small total may mean "not allowed to see" rather than "nobody booked
time". work_package_id scopes to that one work package — child
work packages are not included, so a parent's roll-up needs a query
per child. The summing path stops at 2000 entries and says so in
notes; narrow the date range or the project when that happens
rather than trusting the number.
Cross-references: book time with log_time; correct an entry with
update_time_entry and remove one with delete_time_entry; the
activity ids and names valid in a project come from
get_project_metadata; the work package itself (including its
aggregated spent_hours) comes from get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| user | No | Whose time to list: a numeric user id, or the literal 'me' for the token owner. Omit for everyone you are allowed to see — on most instances that is only your own entries unless you hold the view-all-time-entries permission. | |
| to_date | No | Latest spent-on date, ISO YYYY-MM-DD, inclusive. | |
| fetch_all | No | Aggregate every page of rows into one result instead of returning page 1. Capped at 500 items with a note when the cap bites; mutually exclusive with page. sum_hours already reads every matching entry for its total, with or without fetch_all. | |
| from_date | No | Earliest spent-on date, ISO YYYY-MM-DD, inclusive. Combine with to_date for a range; either bound may be omitted for an open-ended one. | |
| page_size | No | Entries per page (max 100); the instance may clamp it lower and the returned pagination reports what actually came back. | |
| sum_hours | No | Compute an accurate total over EVERY matching entry (not just this page) and break it down per activity. Costs one request per 100 matches and is capped at 2000 entries — a cap hit is reported in 'notes'. Leave false when you only need rows. | |
| project_id | No | Only entries in this project (numeric id or identifier, from list_projects). Includes project-level entries that have no work package. | |
| activity_id | No | Only entries booked on this activity. Activity ids are instance-specific and come from get_project_metadata(project_id=...). | |
| work_package_id | No | Only entries booked on this work package. Ids come from search_work_packages / list_work_packages. The filter name differs between OpenProject versions; this tool probes the instance and uses the right one. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond annotations by disclosing permission-bound visibility, the 2000-entry summing cap, page_size clamping, and the nuance that a small total may reflect permissions rather than actual data. These details complement the readOnlyHint/idempotentHint annotations without any contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-structured with labeled sections (purpose, examples, response format, pitfalls, cross-references). It front-loads the core function and every sentence adds value, making it appropriately sized for a complex 10-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the response envelope, pagination, sums, group buckets, caps, permissions, and related tools, leaving very little unanswered for an agent. With an output schema present, this level of context is fully sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the input schema already documents all 10 parameters, the description adds meaningful cross-parameter behavior, such as how sum_hours aggregates across all matches, fetch_all caps at 500, and work_package_id excludes child work packages. These enrich the schema descriptions and justify a score above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List logged time, filtered server-side, with an optional accurate total,' a specific verb and resource, and immediately gives concrete example questions. It clearly distinguishes itself from sibling tools like log_time and update_time_entry in the cross-references.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description includes explicit 'Use it to answer...' usage scenarios and states that filters combine with AND. It lists alternatives for booking, correcting, and deleting entries, and gives exclusions such as permission limits and the exclusion of child work packages.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_versionsList versionsARead-onlyIdempotent
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
create_work_package/update_work_package need, to see which versions are still
open, or to review a release plan's dates. With project_id it answers "what can I
target in THIS project", which includes versions shared down from parent projects.
Returns the standard list envelope: items of {id, name, project, status, start_date, end_date, description, sharing, source} plus pagination and
notes. A project-scoped listing is fetched in full, so has_more is false.
Pitfalls: project is the project that DEFINES the version, which for a shared
version is not the project you asked about — assigning still works. status is
open/locked/closed and OpenProject refuses to put work packages into a closed
version. The instance-wide listing is capped at one page of 100; if more exist,
pagination.has_more is true and notes says so — narrow with project_id
rather than assuming you saw everything. include_sprints depends on the backlogs
module: where it is not installed the versions still come back and notes explains
the absence, so read notes before telling a user a project has no sprints.
Cross-references: create_version adds one, update_version moves its dates or
closes it, delete_version removes it; get_project_metadata(project_id=...)
returns the same versions alongside types and categories; to see what is IN a version
use list_work_packages with a version filter.
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Numeric project id or URL identifier to list the versions available in that project — including the ones shared into it from a parent. Omit it to list every version visible to you across the instance. | |
| include_sprints | No | Also read /projects/{id}/sprints from the backlogs module and merge the rows in with source='sprint'. Requires project_id. If backlogs is not installed the versions are still returned and 'notes' says why sprints are missing — the call does not fail. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, but the description adds rich behavioral context: shared versions' `project` refers to the defining project, closed versions refuse work packages, instance-wide listings are capped at 100 with pagination, and `include_sprints` depends on the backlogs module. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: it front-loads the purpose, then covers usage, return format, pitfalls, and cross-references in a clear, logical structure. There is no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers return envelope fields (`items`, `pagination`, `notes`), edge cases (shared versions, closed versions, missing backlogs module), and related tools. With an output schema present, this is more than sufficient for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the input schema already describes both parameters, the description adds operational meaning beyond the schema: `project_id` includes versions shared from parent projects, and `include_sprints` merges backlogs rows with `source='sprint'` and explains the behavior when backlogs is absent. This is a substantial value-add.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence 'List versions (releases, milestones, sprints) you can assign work packages to' precisely names the verb and resource, and the cross-reference section distinguishes it from create_version, update_version, delete_version, and list_work_packages. The purpose is unambiguous and well-differentiated from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use it ('Use it to turn 'Sprint 12' or 'release 2.1' into the version id...') and provides when-not-to-use guidance in the pitfalls ('narrow with project_id rather than assuming you saw everything'). The cross-references name exact alternatives for each adjacent task, such as get_project_metadata and list_work_packages.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_work_package_commentsList work package commentsARead-onlyIdempotent
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
{field, from, to} (for example
{"field": "Status", "from": "New", "to": "In progress"}).
Returns the standard list envelope: items plus
pagination{total,page,page_size,has_more} and notes.
Pitfalls. OpenProject's activities endpoint is unpaginated — this
tool fetches the entire journal on every call and pages it here, so
page/page_size cost the same upstream but keep the reply small.
Entries are ordered oldest first, so ask for the last page to see the
latest discussion. Comment text is cut at max_comment_chars and
marked truncated: true; pass that entry's id back as
activity_id to read it in full.
Cross-references: post a comment with add_work_package_comment; the
work package itself (description, custom fields, watchers) comes from
get_work_package; files referenced in a comment are listed by
list_attachments.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Work package id. Get it from search_work_packages, list_work_packages or get_work_package — never guess it. | |
| page | No | 1-based page number over the journal. Entries are oldest first, so the newest comments are on the LAST page. | |
| page_size | No | Journal entries per page, 1-100. Keep it small: comments are long. | |
| activity_id | No | Read exactly one journal entry, uncapped, instead of a page. The id comes from a previous call to this tool. Must belong to work package `id`. | |
| max_comment_chars | No | Per-comment character cap, 50-50000. A cut comment comes back with truncated=true and comment_length; re-read it in full via activity_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description adds significant behavior beyond that: the upstream endpoint is unpaginated, the tool fetches the full journal and pages locally, entries are oldest-first, and comments may be truncated with a recovery path via activity_id. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though long, the description is tightly structured with labeled 'Pitfalls' and 'Cross-references' sections. Every sentence adds operational value, and the main purpose is front-loaded. There is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for the tool's complexity: it explains the return envelope (items, pagination, notes), unusual upstream behavior, truncation, and links to related tools. With an output schema present, no further explanation of return values is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema covers 100% of parameters with descriptions, giving a baseline of 3. The description enriches this by explaining the practical implications: page/page_size meaning in the context of oldest-first ordering, cost trade-offs, truncation behavior tied to max_comment_chars, and the activity_id round-trip for reading full entries. This goes well beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Read the comment thread and change history of a work package.' It clearly distinguishes this from siblings like add_work_package_comment or get_work_package by stating exactly what is returned (comment entries, parsed field-change entries). The scope is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage guidance is provided: 'Use this whenever the question is "what did people say about this ticket" or "what changed on it"'. It also names alternatives for related actions (add_work_package_comment, get_work_package, list_attachments), helping the agent choose between tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_work_packagesList work packagesARead-onlyIdempotent
List work packages with structured filters — the workhorse read tool.
Use it for every "what is assigned to me", "what is overdue", "what is in this sprint"
question. Convenience queries are parameters here, not separate tools: overdue →
due_before=<today>; unassigned → assignee=['none']; nearly done →
percentage_done_min=80; subtasks of a ticket → parent_id=<id>.
Returns the standard list envelope: compact rows plus pagination, plus groups when
group_by was requested and sums when show_sums was requested. Groups and sums are
computed server-side over the whole filtered set, independent of paging — never re-add
them from the rows on one page.
Pitfalls: this returns open work packages only unless you pass status_scope or
status_ids, so say so when you report counts. status_ids overrides status_scope.
Status, type, priority and version ids differ per instance and must come from
get_project_metadata, never from memory.
For text lookups use search_work_packages; for one work package's description, custom
fields and children use get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| query | No | Optional free text, AND-combined with every other filter. Matches subject, description and comments. For text-only lookups prefer search_work_packages. | |
| author | No | Author (creator) user ids, or 'me'. 'none' is not valid here. | |
| project | No | Numeric project id or project identifier (URL slug) to scope the query. Both come from list_projects. Omit for a cross-project view. | |
| sort_by | No | Server-side sort as snake_case pairs, e.g. [['due_date','asc'],['priority','desc']]. An unknown key fails with the allowed set listed. | |
| watcher | No | Work packages watched by these user ids, or 'me'. | |
| assignee | No | Assignee filter: numeric user ids, the single value 'me', or the single value 'none' for unassigned work. Ids come from search_principals; get_instance_info gives the current user. | |
| group_by | No | Group the full filtered set by one snake_case column (e.g. 'status', 'assignee'). Counts in `groups` cover every page, not just this one. | |
| type_ids | No | Work package type ids (Task, Bug…); from get_project_metadata. | |
| due_after | No | Due on or after this ISO date (YYYY-MM-DD). | |
| fetch_all | No | Aggregate every page into one result instead of returning page 1. Capped at 500 items with a note when the cap bites; mutually exclusive with page. | |
| page_size | No | Results per page (max 100). | |
| parent_id | No | Direct children of this work package only. Mutually exclusive with top_level_only; use ancestor_id for the whole subtree. | |
| show_sums | No | Ask the server for totals (estimated/remaining/spent hours, story points) over the full filtered set. Never add up pages yourself. | |
| due_before | No | Due on or before this ISO date (YYYY-MM-DD). | |
| status_ids | No | Exact status ids. **Overrides status_scope** — the two never fight. Ids come from get_project_metadata. | |
| ancestor_id | No | Everything in this work package's subtree at any depth, unlike parent_id which is one level only. | |
| raw_filters | No | Escape hatch for filters this tool does not type, most importantly custom fields: [{'name': 'customField12', 'operator': '=', 'values': ['4']}]. Custom field names and option ids come from get_work_package_schema. | |
| responsible | No | Accountable user ids, 'me', or 'none' for no accountable user. | |
| start_after | No | Starts on or after this ISO date (YYYY-MM-DD). | |
| version_ids | No | Version / sprint ids; from get_project_metadata. | |
| priority_ids | No | Priority ids; from get_project_metadata. Never guess these — priority ids differ per instance. | |
| start_before | No | Starts on or before this ISO date (YYYY-MM-DD). | |
| status_scope | No | Status bucket: 'open' (default), 'closed' or 'all'. An explicit status filter is always sent, so the server's implicit open-only default never silently applies. Ignored when status_ids is given. | open |
| created_since | No | Created on or after this ISO date (YYYY-MM-DD). | |
| updated_since | No | Last changed on or after this ISO date (YYYY-MM-DD). | |
| top_level_only | No | Only work packages that have no parent. Excludes every subtask. | |
| milestones_only | No | Only milestone-type work packages. Resolved against this instance's own types (no hardcoded ids) and intersected with type_ids when both are given. | |
| percentage_done_max | No | Maximum progress percentage, 0-100. | |
| percentage_done_min | No | Minimum progress percentage, 0-100. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even though annotations already declare readOnly/openWorld/idempotent, the description adds critical behavioral context: it returns only open work packages unless status_scope/status_ids are passed, status_ids overrides status_scope, and groups/sums are computed server-side over the whole filtered set independent of paging. It also warns that IDs differ per instance and must come from get_project_metadata.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly structured with a clear opening label, use-case paragraph, return-envelope paragraph, pitfalls paragraph, and sibling-pointer paragraph. Every sentence delivers actionable information, and despite covering a complex 30-parameter tool, it remains readable and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (30 parameters, 0 required, rich schema, output schema, and many siblings), the description covers the essential use cases, limitations, return shape, and distinctions from adjacent tools. It is self-sufficient for an agent to know when and how to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds semantic value by translating natural-language queries into parameter combinations (overdue → due_before, unassigned → assignee=['none'], nearly done → percentage_done_min=80, subtasks → parent_id). It also clarifies cross-parameter interactions (status_ids overrides status_scope) and caveats like 'never re-add them from the rows on one page.'
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'List work packages with structured filters — the workhorse read tool', clearly identifying the verb, resource, and scope. It explicitly distinguishes itself from siblings by pointing to search_work_packages for text lookups and get_work_package for single-package details.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides concrete when-to-use guidance with example queries ('what is assigned to me', 'what is overdue', 'what is in this sprint') and maps them to specific parameters. It explicitly names alternatives: 'For text lookups use search_work_packages; for one work package's description, custom fields and children use get_work_package.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_timeLog 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: {id, hours, spent_on, comment, user, activity, work_package, project, created_at, updated_at, lock_version}. hours comes back as a float.
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 spent_hours
reflects the new entry on the next read.
Cross-references: the activities and ids this project accepts come from
get_project_metadata(project_id=...); review what is already booked
with list_time_entries; fix a mistake with update_time_entry or
delete_time_entry.
| Name | Required | Description | Default |
|---|---|---|---|
| hours | Yes | Duration in hours as a float: 1.5 is one and a half hours, 0.25 is fifteen minutes. Must be positive; OpenProject stores it to the minute. | |
| comment | No | What the time was spent on. Short and factual: it shows up in cost reports next to the hours. | |
| activity | No | Activity name ('Development') or numeric id. Omit to take the instance default from the form. Names are resolved against this project's allowed activities; an unknown name fails with the valid ones listed. | |
| spent_on | Yes | The date the work was done, ISO YYYY-MM-DD. Required and never inferred — 'today' on the server may not be today for the user. | |
| project_id | No | Project to book the time on when the work belongs to no single ticket (numeric id or identifier). Required when work_package_id is omitted. | |
| work_package_id | No | Work package to book the time on. Ids come from search_work_packages or list_work_packages. Either this or project_id is required; passing both books on the work package inside that project. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Time entry id. Feed it to update_time_entry or delete_time_entry. |
| user | No | User the time is booked for. |
| hours | No | Logged duration in hours as a float (1.5 = one and a half hours), converted from OpenProject's ISO 8601 duration. |
| comment | No | Free-text comment as entered (raw); html is dropped. |
| project | No | Project the entry belongs to. |
| activity | No | Time-entry activity (Development, Management, …); instance-defined. |
| spent_on | No | The date the work was done, ISO YYYY-MM-DD. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| lock_version | No | Optimistic-locking version, when this instance reports one for time entries; null means the resource is updated without a lock version. |
| work_package | No | Work package the time is booked on; null for project-level entries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the annotations by explicitly stating 'This is not idempotent', 'time is always booked for the token owner', and that logging time does not change the work package's status, estimate, or progress. It also details the form-endpoint validation and typed error behavior, providing valuable safety context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the core action, followed by usage triggers, a 'Pitfalls' section, and cross-references. Although it is long, each section is purposeful, and the use of paragraphs and labels keeps it scannable without wasting words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (6 parameters, 2 required, mutation semantics), the description is remarkably complete. It covers validation and error handling, return format, ownership constraints, non-idempotence, and related tools. The output schema is also explicitly described, so an agent has all necessary context for safe invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already has 100% description coverage for all six parameters, including required ones like hours and spent_on. The description adds some helpful examples and cross-references (e.g., 'half a day' for hours, getting activity ids from get_project_metadata), but these are pragmatic touches rather than new parameter semantics beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific action: 'Book time against a work package or a project.' This immediately distinguishes it from siblings like list_time_entries, update_time_entry, and delete_time_entry, which all have different verbs and targets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit trigger examples ('Use it when the user says "log 2 hours on #1234" or "book half a day to project X"') and names alternatives for related operations: get_project_metadata for allowed activities, list_time_entries for reviewing booked time, and update_time_entry/delete_time_entry for corrections.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_all_notifications_readMark all notifications readAIdempotent
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; reason and
project_id narrow that blast radius, they do not create a preview.
Check what is about to disappear with
list_notifications(unread_only=true, ...) using the same filters
first — there is no undo beyond re-marking individual ids unread.
Returns {marked, read, message}, where marked is how many unread
notifications matched the filters at the moment of the call (counted
immediately before the bulk update, so a notification arriving during
the call may be counted differently than it was marked).
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
mark_notifications(ids=[...], read=false).
Cross-references: preview or page the inbox with list_notifications;
mark a handful of rows with mark_notifications.
| Name | Required | Description | Default |
|---|---|---|---|
| reason | No | Only clear notifications with this trigger (mentioned, assigned, watched, …). Omit to clear every unread notification the filters allow. dateAlert is Enterprise-gated and is rejected with an explanatory hint on Community instances. | |
| project_id | No | Only clear notifications belonging to this project (numeric id or identifier, from list_projects). Omit to clear across all projects. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ids | No | The notification ids that were marked; null for the filter-based bulk tool. |
| read | Yes | True when they were marked read, false when marked unread. |
| marked | Yes | Number of notifications the call covered. For mark_notifications this is the number of ids sent; for mark_all_notifications_read it is how many unread notifications matched the filters when the call ran. |
| message | Yes | Human-readable confirmation of what happened. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavioral context beyond the annotations: there is no undo, the return value `marked` is counted immediately before the bulk update (with a possible race nuance), and the tool deliberately only marks read, with no reverse twin. This goes well beyond the annotations' `readOnlyHint`, `idempotentHint`, and `destructiveHint` by explaining the operational consequences.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but taut—every sentence earns its place: purpose, use cases, caveat, return semantics, and cross-references. It is front-loaded with the core purpose and structured logically, making it dense with operational guidance without any fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the rich annotations, and the presence of an output schema, the description covers all bases: when to use it, what it does, what to check beforehand, what it returns, and how to reverse the action. No important context for invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage for `reason` and `project_id` with clear descriptions. The tool description enriches this by explaining that both parameters 'narrow that blast radius' and 'do not create a preview', plus clarifying the default all-notifications behavior when omitted. This adds meaningful nuance beyond the schema, though it doesn't need to redefine the parameter formats.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a concrete verb and resource: 'Mark everything matching the filters as read', with the default scope clearly defined as 'the whole inbox'. It distinguishes this bulk operation from the per-id sibling `mark_notifications` and the read-only `list_notifications`, making the tool's purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use it ('clear my notifications', 'I have dealt with everything in project X') and recommends previewing with `list_notifications(unread_only=true, ...)` using the same filters first. It also directs the reverse operation to `mark_notifications(ids=[...], read=false)` and warns about the lack of undo, giving clear when-to and when-not-to guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_notificationsMark notifications read or unreadAIdempotent
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
list_notifications: pass the row id values from that tool.
Returns {marked, read, ids, message}. OpenProject answers the bulk
endpoint with 204 No Content, so marked is the number of ids the
call covered — ids that were already in the requested state, or that
belong to someone else's inbox, are simply not changed.
Pitfalls. Marking is idempotent, so a retry after a timeout is safe. Pass
the notification id, not resource.id — the work package id
underneath is a different number entirely. Unknown ids do not fail the
call, so do not treat success as proof that every id existed.
Cross-references: get the ids from list_notifications; to clear an
entire (optionally filtered) inbox in one go use
mark_all_notifications_read.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Notification ids to mark, e.g. [4711, 4712]. Required and non-empty — this tool never operates on 'everything'. Ids come from list_notifications (the row 'id', not resource.id). At most 200 per call. | |
| read | No | true (default) marks them read; false puts them back in the unread inbox. Both directions are safe here because the ids are explicit. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ids | No | The notification ids that were marked; null for the filter-based bulk tool. |
| read | Yes | True when they were marked read, false when marked unread. |
| marked | Yes | Number of notifications the call covered. For mark_notifications this is the number of ids sent; for mark_all_notifications_read it is how many unread notifications matched the filters when the call ran. |
| message | Yes | Human-readable confirmation of what happened. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes beyond annotations by describing the return structure (marked, read, ids, message), the 204 No Content behavior, the meaning of 'marked', the idempotent retry safety, the notification id vs resource.id pitfall, and that unknown ids do not fail the call. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with purpose, usage, returns, pitfalls, and cross-references. Every sentence adds value; while longer than a minimal description, the complexity of the API (204, idempotency, id pitfalls) justifies the length. Slightly dense but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 2 well-documented params, annotations, an output schema, and rich behavioral guidance, the description fully equips the agent to select and invoke the tool correctly. Includes all necessary caveats and alternatives.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already covers both parameters with descriptions (100% coverage), so baseline 3. The description adds critical semantics: ids come from the 'row id' of list_notifications, not resource.id, and clarifies the 'read' direction is safe with explicit ids.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States 'Mark specific notifications read (or unread) in one bulk request' – a specific verb+resource. Distinguishes from siblings by calling itself the 'id-consuming counterpart of list_notifications' and differentiating from mark_all_notifications_read.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use it after you have actually handled what a notification was about' and provides cross-references: 'get the ids from list_notifications' and 'to clear an entire inbox use mark_all_notifications_read'. This is clear when-to-use and alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_work_package_watcherRemove work package watcherAIdempotent
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 changed comes back null — do not report "removed" as
proof that they were subscribed. Removing another user needs the
delete-work-package-watchers permission; removing yourself only needs
view access. A 404 means the user id is unknown (or the work package
is), not that they were not watching. This does not unassign anyone and
does not remove them from the project.
Cross-references: get_work_package(include=['watchers']) shows who
watches today; add_work_package_watcher is the reverse.
| Name | Required | Description | Default |
|---|---|---|---|
| user_id | Yes | Numeric id of the watcher to remove. get_work_package(include=['watchers']) lists the current watchers with their ids. The string 'me' is not accepted here. | |
| work_package_id | Yes | Work package to unsubscribe the user from. Ids come from search_work_packages, list_work_packages or get_work_package. |
Output Schema
| Name | Required | Description |
|---|---|---|
| user | No | The watcher. 'id' is the user id; 'name' is filled in only when OpenProject returned the user resource (it does on add, not on remove). |
| changed | No | True when this call actually changed the watcher list, false when the user was already watching. Null when OpenProject does not report it — removals answer 204 whether or not the user was watching. |
| message | Yes | Human-readable confirmation. |
| watching | Yes | Watch state after the call: true after adding, false after removing. |
| work_package_id | Yes | Work package whose watcher list was changed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond annotations by disclosing critical behavioral nuances: the API returns 204 regardless of whether the user was actually watching, resulting in 'changed' being null; a 404 indicates an unknown user or work package, not non-membership; and the operation does not unassign or remove from project. These are exactly the kind of hidden behaviors agents need, and they complement the idempotentHint and destructiveHint annotations without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than necessary but earns its length through crucial pitfalls and permission details. It is well-structured with a clear 'Pitfalls' section that is immediately relevant to safe execution. No sentence is wasted, though a more compressed format could have achieved the same impact.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with subtle API behaviors, the description covers all essential context: operation, return value (though output schema exists), permission requirements, error semantics, and cross-references to related tools. The presence of an output schema does not detract; the description adds the non-obvious 'changed is null' detail that the output schema alone would not convey.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage of both parameters, including how to obtain IDs and the rejection of 'me' for user_id. The description adds no new parameter-level meaning beyond what the schema states; it merely restates and contextualizes. Per the rubric, baseline 3 is appropriate when schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Unsubscribe a user from a work package's notifications.' This clearly distinguishes it from siblings like add_work_package_watcher and get_work_package, and the cross-reference to add_work_package_watcher as the reverse further cements its unique purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool ('Use this to stop notifying someone who no longer needs the updates'), provides permission prerequisites (removing others requires delete-work-package-watchers permission, self-removal requires view access), and names alternatives (get_work_package with include=['watchers'] for listing current watchers, add_work_package_watcher as the reverse). This fully covers usage context and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_queryRun a saved queryARead-onlyIdempotent
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 — items of
compact work-package rows, pagination, plus groups when the query groups and
sums when it asks for totals — with one addition: query carries the stored
definition (name, project, readable filters, group_by, sort_by), so the rows
can be interpreted without a second call.
Pitfalls. groups and sums are computed server-side across the entire result set,
not the page in front of you — never re-add them from items. Omitting page_size
keeps the query's own page size, which may be much larger than 20. override_filters
replaces the stored filters instead of narrowing them, and never edits the saved
query. A 422 means the filter set is invalid for this query's context (a
project-scoped filter on a global query, an unknown custom field); violations names
the attribute.
Cross-references: find query ids with list_queries; equivalent ad-hoc filtering
lives in list_work_packages; open a single row with get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. Omit to use the page the stored query starts on (the first). | |
| query_id | Yes | Saved query id, from list_queries. It is the same id as in the UI's ?query_id= URL parameter. | |
| page_size | No | Rows per page (max 100). Omit to keep the query's stored page size, which can be larger or smaller than this tool's usual default. | |
| override_filters | No | Run the query with these filters instead of its stored ones, for this call only — e.g. [{'name': 'status', 'operator': 'o', 'values': []}] or [{'name': 'customField12', 'operator': '=', 'values': ['4']}]. This REPLACES the stored filters (the API cannot merge), so re-state anything you want to keep; 'query.filters' in the result shows what the stored ones were. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| query | Yes | The stored query definition, so the rows can be interpreted. |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral detail beyond the annotations: it states queries run on read and return rows 'as they are right now', explains the standard list envelope plus the `query` addition, and details pitfalls such as `groups`/`sums` being server-side computed, `page_size` omission behavior, `override_filters` replacing stored filters, and the 422 error meaning. This contextualizes the readOnly/idempotent annotations without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is efficiently structured: it leads with a purpose statement, then describes the result envelope, follows with a 'Pitfalls' paragraph highlighting key behavioral caveats, and ends with cross-references. Every sentence earns its place, and despite its length, there is no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter tool with an output schema, this description is exceptionally complete. It covers the full result shape, server-side computation quirks, pagination behavior, error semantics, and the meaning of the `query` field in the response. It also provides cross-tool references, making it sufficient for an agent to use safely and effectively.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema itself has 100% coverage with detailed descriptions, so the baseline is 3. The description adds meaningful nuance beyond the schema: it clarifies that `override_filters` replaces stored filters rather than narrowing them, and that omitting `page_size` keeps the query's own stored page size. This supplemental guidance enhances but doesn't fully replace schema documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Run a saved view and get its work packages', which is a specific verb+resource pairing that clearly identifies the tool's function. It also distinguishes itself from siblings by cross-referencing 'list_work_packages' for ad-hoc filtering and 'get_work_package' for opening a single row, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly provides usage guidance: it states the tool is the 'fastest way to answer with a team's own definition of the sprint or our bugs', and it names alternatives in the cross-references section ('find query ids with list_queries; equivalent ad-hoc filtering lives in list_work_packages; open a single row with get_work_package'). This gives clear when-to-use vs when-not-to-use context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_querySave a queryA
Save a filter set as a reusable OpenProject view the whole team can open.
Use it when a filter combination is worth keeping — "Overdue in Platform", "My open
bugs" — instead of rebuilding it every session: the saved view shows up in the
OpenProject UI as well, and run_query reproduces it exactly. Prove the filters with
list_work_packages first; whatever works there works here.
The call runs POST /queries/form before committing, so an invalid filter name, an
operator the filter does not support, or a project-scoped filter on a global view comes
back as violations naming the attribute — nothing is saved. Returns the stored
definition: {id, name, project, public, starred, filters (as readable sentences), group_by, sort_by, display_sums, updated_at, notes}. Keep the id: it is what
run_query takes.
Pitfalls. Filter values are ids, not names — 'Grace Hopper' is not a value, 12 is.
star=true is a second request after the query exists; if it fails the query is still
saved and notes says so, so never re-save on a starring failure. If OpenProject keeps
fewer filters than were sent, notes says that too — read filters rather than
assuming the view matches the request. Custom-field filters (customField12) are sent
as plain values because a list-typed one cannot be told apart from a text one without
asking the instance; if such a filter makes the call fail, nothing was saved — save that
view in the UI. Editing and deleting saved views is deliberately not offered here:
change or remove them in the OpenProject UI.
Cross-references: list_queries lists what already exists (and gives ids);
run_query(query_id=...) runs this view; list_work_packages is the ad-hoc equivalent
and the place to validate filters first; list_projects supplies project_id.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name the view is saved under, e.g. 'Overdue in Platform'. Names are not unique upstream, so a second save with the same name creates a second view. | |
| star | No | Also star (favorite) the view for the authenticated user, so it appears in their sidebar. Done as a second call after the query exists; if it fails the query is still saved and 'notes' says so. | |
| public | No | true shares the view with everyone who can see the project; false (default) keeps it private to the authenticated user. Sharing usually needs the 'manage public queries' permission. | |
| filters | Yes | The filters to store, in the same shape run_query's override_filters and list_work_packages' raw_filters take — e.g. [{'name': 'status', 'operator': 'o', 'values': []}, {'name': 'assignee', 'operator': '=', 'values': ['12']}]. Values are ids (or 'me'), not display names. Pass [] deliberately for a view that filters nothing: unlike a listing, a stored query with no filters shows every status. | |
| sort_by | No | Stored sort order, e.g. [['due_date', 'asc'], ['id', 'desc']]. Keys are the snake_case work-package columns list_work_packages sorts by; unknown keys are rejected locally with the allowed set listed. Omit for the default. | |
| group_by | No | Column to group the results by, e.g. 'status', 'assignee', 'type' or 'version'. Grouping is what makes run_query return 'groups' with per-group counts. Omit for a flat list. | |
| project_id | No | Numeric project id the view belongs to, from list_projects. Omit for a global (cross-project) view — but project-scoped filters such as version, category or subprojectId are then rejected. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Query id — pass it to run_query as query_id. |
| name | No | Query name as its author saved it. |
| notes | No | What happened beyond the create itself: a failed star, filters OpenProject did not keep. Empty when everything landed as asked. |
| public | No | True when shared with everyone who can see the project; false when private to its owner. |
| filters | No | The stored filters as readable sentences, e.g. 'Status open' or 'Assignee is (OR) Grace Hopper'. Empty when the query filters nothing. |
| project | No | Owning project, or null for a global (cross-project) query. |
| sort_by | No | Stored sort order, e.g. ['Finish date asc']. |
| starred | No | True when the current user starred (favorited) it. |
| group_by | No | Column the results are grouped by, when the query groups. |
| updated_at | No | ISO 8601 UTC timestamp. |
| display_sums | No | True when the query asks for totals; then the result 'sums' is populated. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint=false, openWorldHint=true, etc.), the description discloses the internal POST /queries/form validation call, the failure mode returning violations with nothing saved, the two-step star behavior, filter-count mismatch handling, and custom-field limitations. This gives the agent a realistic model of side effects and failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but structured: purpose, usage, behavior, pitfalls, cross-references. Each section adds essential operational knowledge, and the use of a 'Pitfalls' paragraph front-loads critical warnings. No wasted sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, usage, behavior, return format, error handling, side effects, and relationships to sibling tools. Combined with the exhaustive schema and output schema, the agent has everything needed to invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers 100% of parameters with detailed descriptions, so baseline is 3. The description adds value by highlighting semantic pitfalls (values are ids not names, customField12 handling, empty filters meaning 'show every status') that are not fully covered in the schema. However, much of the parameter detail overlaps with the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb-object: 'Save a filter set as a reusable OpenProject view the whole team can open.' It distinguishes from siblings by explicitly contrasting with run_query, list_queries, and list_work_packages, and notes editing/deleting is intentionally not offered.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use ('when a filter combination is worth keeping'), recommends validating with list_work_packages first, and names alternatives (list_queries, run_query, list_work_packages, list_projects) with specific roles. It also tells when not to use it (editing/deleting should be done in the UI).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_principalsSearch users, groups and placeholder usersARead-onlyIdempotent
Find users, groups and placeholder users, and get their ids.
This is the id-producing tool for every principal parameter in this
server: assignee/responsible on work packages, watcher ids, the
user_id of a time entry, and principal_id for create_membership.
Names are never accepted where an id is wanted — resolve here first,
and never guess a numeric id.
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: items of {id, name, type, email?, login?, status?} plus
pagination{total,page,page_size,has_more} and notes.
Pitfalls. email, login and status are only returned for user
principals the authenticated account may see — a null email means "not
visible to you", not "no email". Group principals can hold memberships
and be assigned work, so filter by type when you specifically need a
person. Matching is substring-based, so a short query matches broadly;
prefer the full name or the login. member_of_project filters by
membership, not by whether the person ever touched the project.
Cross-references: get_user for one user's full detail;
list_memberships for who holds which roles in a project;
create_membership to grant access; list_roles for the role ids that
grant needs.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. | |
| type | No | Restrict to one kind of principal. 'user' = people who log in, 'group' = user groups (assignable and grantable as a unit), 'placeholder' = licence-free stand-ins. Omit to search all three. | |
| query | No | Free text matched against name, login and email (substring, case-insensitive). Omit it to page through every visible principal. | |
| status | No | Restrict to an account status. 'active' is the usual filter when looking for someone to assign work to; 'locked' accounts keep old assignments but take no new ones. Groups and placeholder users have no status and are excluded when this is set. | |
| page_size | No | Principals per page, 1-100. | |
| member_of_project | No | Only principals who are members of this project. Numeric project id or the URL identifier; an identifier costs one extra lookup. Use it before assigning work — a user who is not a member usually cannot be assigned. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and idempotentHint annotations, the description discloses non-obvious behavior: email/login/status are only returned for visible users ('a null email means not visible to you, not no email'), group principals are assignable, matching is substring-based and broad, and member_of_project filters by membership rather than activity. These are valuable behavioral nuances not captured in annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than typical but well-organized into purpose, usage, pitfalls, and cross-references. Every sentence earns its place; there is no filler. The 'Pitfalls' section is particularly high-density. Could be slightly tightened, but structure aids scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no required parameters, an output schema for return envelope, and full schema coverage, the description still adds critical context: id resolution workflow, visibility semantics, and relationship to other tools. It is complete for an AI agent to select and invoke confidently, covering all decision-relevant aspects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description adds meaningful guidance over the schema: explains that type='group' includes assignable groups, warns about short queries matching broadly, and clarifies member_of_project usage ('Use it before assigning work — a user who is not a member usually cannot be assigned'). This elevates it above baseline, though not all params get equal attention.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Find users, groups and placeholder users, and get their ids', a specific verb+resource+result statement. It explicitly positions itself as 'the id-producing tool for every principal parameter in this server', distinguishing it from sibling tools like get_user, list_memberships, and create_membership.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use and when-not-to-use guidance: 'Names are never accepted where an id is wanted — resolve here first, and never guess a numeric id'. It also gives example queries ('who is Grace Hopper's account', 'which groups exist') and names alternatives: 'get_user for one user's full detail; list_memberships for who holds which roles; create_membership to grant access; list_roles for role ids'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_work_packagesSearch work packagesARead-onlyIdempotent
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 id into get_work_package, update_work_package or list_work_packages.
Returns the standard list envelope: compact rows (id, subject, type, status, priority,
assignee, project, dates, progress) plus pagination with total/page/page_size/has_more.
Pitfalls: search filters, it does not rank, so a broad query returns a lot — narrow it
with project_id, or switch to list_work_packages when you want structured filters
(assignee, due date, version) rather than text. Attachment-content matching in 'fulltext'
mode depends on instance database configuration and is reported honestly in notes.
For structured filtering use list_work_packages; for one work package's full detail
(description, custom fields, children) use get_work_package.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'quick' (default) matches subject, id, project name and type/status names — this is what the OpenProject header search runs and the right choice for 'find the ticket called X'. 'fulltext' additionally matches description text, comments and searchable custom fields; use it for 'which ticket mentions Y'. | quick |
| page | No | 1-based page number. | |
| query | Yes | Free text to look for. In 'quick' mode a bare number also matches a work package id, so '1234' finds #1234. | |
| page_size | No | Results per page (max 100). | |
| project_id | No | Restrict the search to one project: numeric id or the project identifier (the slug in the OpenProject URL). Both come from list_projects. Omit to search every project the user can see. | |
| status_scope | No | Which statuses to search. Defaults to 'all' because finding closed items is usually the point of a search; pass 'open' to hide finished work. An explicit status filter is always sent, so the server's implicit open-only default never applies. | all |
Output Schema
| Name | Required | Description |
|---|---|---|
| sums | No | Present only when show_sums was requested. |
| items | No | The page of results. |
| notes | No | Degradation markers: capped aggregations, unavailable modules, … |
| groups | No | Present only when group_by was requested. |
| pagination | Yes | Total/page/page_size/has_more. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that search filters and does not rank, recommends narrowing with project_id, and warns that fulltext attachment matching depends on instance database configuration. It also explains the status_scope sentinel behavior, which is not visible in annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four well-organized sentences front-load the purpose and usage, then cover return format, pitfalls, and alternatives without redundancy. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers when to use, what it returns, how to chain results, key caveats, and sibling tool differentiation. The output schema exists, so detailed return structure is not needed in the description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already includes rich descriptions for every parameter. The description's guidance to narrow with project_id and the status_scope behavior is already present in the schema, so the description adds little beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line 'Find work packages by text when you do not know their ids' clearly states the verb, resource, and the trigger condition, distinguishing it from list_work_packages (structured filters) and get_work_package (by id).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to 'Use this first whenever a user names a ticket instead of numbering it,' and names the follow-up tools to feed the id into. It also contrasts with list_work_packages for structured filters and get_work_package for full detail.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_project_favoriteSet project favoriteAIdempotent
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 {id, favorite, message} — favorite is the state now in effect. The call
is idempotent: favoriting an already-favorited project succeeds again.
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_projects(favorites_only=true) lists the current favorites
(and works on older instances too); get_project resolves an identifier first;
get_instance_info reports the detected OpenProject version.
| Name | Required | Description | Default |
|---|---|---|---|
| favorite | Yes | true adds the project to the authenticated user's favorites, false removes it. Required — there is no toggle, so read the current state with list_projects(favorites_only=true) if you do not know it. | |
| id_or_identifier | Yes | Numeric project id or URL identifier to favorite or un-favorite; both are accepted and come from list_projects or get_project. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Project id or identifier that was changed. |
| message | Yes | Human-readable confirmation. |
| favorite | Yes | The state now in effect: true when the project was favorited, false when the favorite was removed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (idempotentHint, readOnlyHint=false, destructiveHint=false), the description discloses per-user scoping, idempotent success on repeated calls, version-specific refusal/send behavior, and the deliberate 404 ambiguity. There is no contradiction with the annotations; the description adds substantial context the structured data does not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than the calibration examples but well-structured with sections for purpose, use case, return value, pitfalls, and cross-references. Every sentence carries meaningful information; the version pitfalls and 404 ambiguity are necessary for correct use, though some phrasing could be tightened. It is front-loaded with the core action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (version dependency, ambiguous 404, per-user scope), the description is remarkably complete: it covers the return shape, behavioral nuances, fallback instructions, and cross-tool references. The output schema exists, but the description still adds valuable context about version-specific behavior and error interpretation, leaving no significant gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters are already fully described in the schema (100% coverage), so the baseline is 3. The description adds a small but useful cross-reference that id_or_identifier comes from list_projects or get_project, and clarifies that favorite is not a toggle, though the schema already says most of this. Overall it adds some value beyond the schema but does not need to compensate for gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb ('Add or remove') and resource ('a project from the authenticated user's favorites'), making the action and scope unambiguous. It explicitly distinguishes itself from project status and work-package watchers, which are the closest sibling-tool concepts, so it is clearly differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides an explicit trigger ('when the user asks to pin, star or favorite a project they work in'), names alternatives (list_projects for reading favorites, web UI for older instances), and states exclusions (not project status, not a watcher). This gives the agent clear decision rules for when to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_work_package_reminderSet work package reminderAIdempotent
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 action
(created/updated/deleted/unchanged) and the resulting reminder.
Passing remind_at=null deletes the reminder. That is the
documented way to clear it rather than a destructive operation — nothing
but your own pending notification is removed, the work package and its
history are untouched — so this tool does not ask for confirm. Set a
new time to get it back.
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. remind_at must carry a timezone; a bare
'2026-08-03T09:00' is refused rather than guessed at. A reminder in the
past is rejected by the instance. Once a reminder has fired it disappears
from the API, so a later call creates a fresh one rather than reviving
it. Reminders are personal: you cannot set one for a colleague — add them
as a watcher or mention them in a comment instead.
Cross-references: list_reminders shows everything you have pending;
add_work_package_watcher notifies someone about changes rather than
at a chosen time; add_work_package_comment with an @-mention is how
you get another person's attention.
| Name | Required | Description | Default |
|---|---|---|---|
| note | No | Short text shown with the reminder ('check the staging deploy'). Omit to leave an existing note untouched; pass an empty string to clear it. | |
| remind_at | No | When to be reminded, as an ISO 8601 datetime WITH a timezone, e.g. '2026-08-03T09:00:00Z'. Pass null to delete the existing reminder. Omit the parameter entirely to keep the current time and only change the note. | __unchanged__ |
| work_package_id | Yes | Work package to be reminded about. Ids come from search_work_packages, list_work_packages or get_work_package. |
Output Schema
| Name | Required | Description |
|---|---|---|
| action | Yes | What actually happened: 'created' a new reminder, 'updated' the existing one, 'deleted' it, or 'unchanged' when there was nothing to clear. |
| message | Yes | Human-readable confirmation. |
| reminder | No | The reminder after the call; the deleted one when action='deleted', null when there was none. |
| work_package_id | Yes | Work package whose reminder was set or cleared. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses far more than annotations alone: it explains the upsert semantics (creates or updates), that passing null deletes the reminder, why no confirm is needed (only the personal notification is removed), the one-active-reminder-per-person limit, timezone requirements, past-rejection behavior, and that fired reminders disappear from the API. This is rich context beyond the readOnlyHint/idempotentHint annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average, but every sentence earns its place. It is front-loaded with the core purpose, followed by usage, a clear explanation of the null behavior, and a structured 'Pitfalls' section. Each paragraph has a distinct role, making it easy to scan and digest without wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description is exceptionally complete. It covers behavior, edge cases, personal nature, alternatives, and even mentions the return value ('Returns action (created/updated/deleted/unchanged) and the resulting reminder'). The output schema exists, so return details are not required, but the description still provides enough context for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage with detailed descriptions for all three parameters. The description does add extra nuance not in the schema: clarifying timezone refusal ('a bare '2026-08-03T09:00' is refused rather than guessed at'), past reminders being rejected, and the inability to stack reminders. This goes beyond a simple restatement, though the schema does most of the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Set, change or clear your personal reminder on a work package.' It immediately distinguishes the tool from siblings by clarifying it handles personal reminders, and the cross-references at the end (list_reminders, add_work_package_watcher, add_work_package_comment) reinforce the differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is given: 'Use this when something should resurface later' with concrete examples. The description also provides clear exclusions and alternatives: reminders are personal, so for a colleague use add_work_package_watcher or add_work_package_comment with an @-mention, and list_reminders shows all pending reminders.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
toggle_comment_reactionToggle 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 reacted, which says
whether you are now among them.
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 reacted from the result
instead. Reactions belong to the authenticated account; you cannot react
on someone else's behalf and cannot remove their reaction. Only comment
entries can be reacted to — a field-change journal entry ("Status changed
from New to In progress") is refused by OpenProject with a 400. The
feature needs OpenProject 16.0 or newer; an instance known to be older
is refused up front with a version hint rather than pretending to have
reacted, and one whose version is not readable answers 404 instead.
Cross-references: list_work_package_comments reads the thread and
produces activity ids; add_work_package_comment says something in
words when an emoji is not enough; get_instance_info reports the
detected OpenProject version.
| Name | Required | Description | Default |
|---|---|---|---|
| reaction | Yes | Which emoji to toggle. OpenProject accepts exactly eight: thumbs_up (👍), thumbs_down (👎), grinning_face_with_smiling_eyes (😄), confused_face (😕), heart (❤️), party_popper (🎉), rocket (🚀), eyes (👀). Passing the emoji character itself is rejected — use these names. | |
| activity_id | Yes | Id of the comment to react to. It comes from list_work_package_comments (the 'id' of an entry with kind='comment') or from add_work_package_comment's result — it is an activity id, not a work package id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | Degradation notes; always a list, usually empty. |
| message | Yes | Human-readable confirmation. |
| reacted | No | True when YOU now react with it, false when this call removed your reaction. Null when the authenticated user could not be determined. |
| reaction | Yes | The reaction this call toggled. |
| reactions | No | Every reaction on the comment after the toggle; always a list, empty once the last one is removed. |
| activity_id | Yes | Activity (journal entry) whose reactions these are. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark readOnlyHint=false, destructiveHint=false, and idempotentHint=false. The description goes far beyond that by explaining the toggle semantics (double-invocation leaves state unchanged), authentication ownership rules, the 400 rejection for journal entries, version requirements, and the 404/version-hint behavior. This is rich behavioral detail that helps the agent anticipate failures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is detailed but tightly organized: purpose, usage guidance, pitfalls, and cross-references. Every sentence carries operational value. While long, it is appropriately structured with clear sections and bolded pitfalls, and it front-loads the core purpose before diving into edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's toggle behavior, auth constraints, version dependency, and error modes, the description covers all these aspects comprehensively. It explains return state, references the output schema implicitly, and cross-links to the exact sources for the required activity_id. Nothing essential is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds crucial meaning: activity_id is 'an activity id, not a work package id' and comes from specific sibling tools, while reaction names must be used because 'Passing the emoji character itself is rejected.' These details prevent common invocation mistakes and exceed what the schema alone provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'React to a work-package comment with an emoji, or take your reaction back.' It clearly distinguishes this toggle from sibling tools like add_work_package_comment and edit_work_package_comment, and reinforces that with explicit cross-references.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit use-case guidance: 'Use this for the lightweight acknowledgement a comment does not deserve' and names alternatives: 'list_work_package_comments reads the thread and produces activity ids; add_work_package_comment says something in words when an emoji is not enough.' It also warns against blind retries, which is a key when-not-to-use pointer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_meetingUpdate meetingA
Change a meeting's title, time, place or invite list — or move its lifecycle state.
Use it to reschedule ("move Thursday's review to 15:00"), to publish a draft
(state='open'), to start or wrap up a running one (state='in_progress' /
'closed' — outcomes can only be recorded while it is in progress), or to fix the
participants. Only the parameters you pass are sent; omitted fields stay as they are.
Returns the updated meeting in the same shape as get_meeting, including the fresh
lock_version for a follow-up edit.
Pitfalls. participants replaces the entire set — a partial list silently uninvites
everyone else. A closed meeting accepts a state-only patch (reopening it) and nothing
else; any other change is rejected with a validation error until it is reopened. A
conflict error (409) means somebody edited the meeting since you read it — the error
carries the fresh lock_version and the differing fields, so re-read, decide, retry
deliberately. This needs the 'edit meetings' permission, and moving a meeting to
another project is deliberately not offered.
Cross-references: get_meeting for the current values and the lock_version;
create_meeting to schedule a new one; delete_meeting to remove one;
add_meeting_outcome for what an 'in_progress' state unlocks.
| Name | Required | Description | Default |
|---|---|---|---|
| state | No | New lifecycle state: 'open' publishes a draft to its participants (exactly what the UI's publish does), 'in_progress' starts it (required before outcomes can be recorded), 'closed' freezes it, 'cancelled' calls it off. There is no dedicated state endpoint upstream — this plain field is it. | |
| title | No | New meeting title. Omit to leave it alone; it cannot be cleared. | |
| location | No | New room name or meeting URL; REPLACES the stored one. Pass null or an empty string to clear it. Omit the parameter entirely (the default) to leave it untouched. | __unchanged__ |
| meeting_id | Yes | Numeric meeting id from list_meetings or get_meeting. Never a project id or an agenda item id. | |
| start_time | No | New start as ISO 8601 WITH a timezone: '2026-08-03T14:00:00Z' or '2026-08-03T16:00:00+02:00'. A time without an offset is rejected locally rather than booked in the wrong hour. Omit to keep the current time. | |
| lock_version | No | The lock_version you read from get_meeting. Pass it and the write fails loudly (409) if somebody else edited the meeting in the meantime. Omit it and the current version is fetched and echoed — still safe, just one more round trip and a slightly wider conflict window. | |
| participants | No | User ids of the FULL new invite list, from search_principals or a project's memberships. This REPLACES the whole set — anyone not listed is uninvited — so read the current list with get_meeting first and send it complete. Omit to leave the participants untouched. | |
| duration_minutes | No | New length in minutes (90 = one and a half hours); the end time is derived from it. Omit to keep the current duration. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Meeting id — what get_meeting and add_meeting_agenda_item take. |
| notes | No | Degradation notes: agenda items that could not be read, work packages this account may not see. |
| state | No | Lifecycle state: 'draft' (not yet opened to participants), 'open', 'in_progress', 'closed' or 'cancelled'. Cancelled meetings are excluded from listings. |
| title | No | Meeting title. |
| author | No | User who created the meeting. |
| project | No | Project the meeting belongs to. |
| end_time | No | ISO 8601 UTC end timestamp, derived from start plus duration. |
| location | No | Room name or meeting URL as typed by the organizer. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_time | No | ISO 8601 UTC start timestamp; null for an undated meeting. |
| updated_at | No | ISO 8601 UTC timestamp. |
| agenda_items | No | The agenda in order; always a list. Empty means either no agenda or an unreadable one — check 'notes' before concluding the meeting had none. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| participants | No | Invited users; always a list. Attendance is not exposed by API v3. |
| duration_hours | No | Scheduled length in hours (1.5 = 90 minutes); the wire sends an ISO duration, which is converted here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark readOnlyHint=false, destructiveHint=false, and idempotentHint=false, so the write nature is known. The description adds substantial behavioral context beyond that: partial update semantics ("Only the parameters you pass are sent; omitted fields stay as they are"), participants replacement behavior ("a partial list silently uninvites everyone else"), closed-meeting restrictions, optimistic locking (409 conflict), and permission requirements. It even reveals upstream behavior ("no dedicated state endpoint upstream — this plain field is it") and return shape. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear first sentence, a practical "Use it to..." list, a note on return shape, and a "Pitfalls" section. It front-loads the core purpose, uses compact bullet-like semicolon-separated examples, and every sentence earns its place. Despite its length, it is dense with actionable information and avoids fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (8 parameters, including lifecycle state transitions and concurrency controls) and the presence of an output schema, the description is remarkably complete. It covers usage scenarios, pitfalls, permission requirements, concurrency behavior, return value shape (referencing get_meeting), and what operations are deliberately not supported. The output schema handles return values, so the description's mention of the return shape is appropriate context, not redundancy.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, meaning every parameter already has a detailed schema description. However, the tool description adds further meaning beyond the schema: it explains the overall partial-update model (omitted fields unchanged), the significance of lock_version for concurrency control, the participants replacement pitfall, timezone requirements for start_time, and the "cannot be cleared" nuance for title. This enriches the agent's understanding beyond the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "Change a meeting's title, time, place or invite list — or move its lifecycle state." It clearly distinguishes this from siblings like create_meeting, get_meeting, and delete_meeting by enumerating the mutable attributes and lifecycle states. The cross-references at the end explicitly name sibling tools, reinforcing differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage context: "Use it to reschedule..., to publish a draft..., to start or wrap up..." and gives concrete example invocations. It also states when not to use it (e.g., "moving a meeting to another project is deliberately not offered") and cross-references get_meeting, create_meeting, delete_meeting, and add_meeting_outcome for related operations. This is exemplary guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_meeting_agenda_itemUpdate meeting agenda itemA
Edit one agenda item: retitle it, rewrite its notes, retime, reorder or re-link it.
Use it for "give that item 20 minutes", "move it to the top" (position=1), "let
Grace present it", or to fix a wrong work-package link. Only the parameters you pass
are sent; everything else is left exactly as it is.
Returns the updated item in the same shape as add_meeting_agenda_item, including the
fresh lock_version for a follow-up edit.
Pitfalls. Once the meeting is CLOSED its agenda is frozen — every write answers a
validation error until the meeting is reopened with update_meeting(state='open').
The item's type is fixed at creation: itemType is create-only upstream, so this tool
never sends it and a simple item stays a simple item. A 422 otherwise usually means
the presenter or work package is not visible in the project — violations names the
attribute. This needs the 'manage agendas' permission (403 otherwise), and a 409 means
a concurrent edit — the error carries the fresh lock_version.
Cross-references: get_meeting for the item ids, current values and lock versions;
delete_meeting_agenda_item to remove the item; add_meeting_outcome to record what
was decided under it.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | New markdown notes; they REPLACE the existing text, so read the current one with get_meeting first if you mean to extend it. Pass null or an empty string to clear. Omit the parameter entirely (the default) to leave it untouched. | __unchanged__ |
| title | No | New item title. Omit to leave it alone; it cannot be cleared, because simple items require one. | |
| position | No | New 1-based position within the item's section — this is how the agenda is reordered; the other items shift around it. Omit to keep the order. | |
| lock_version | No | The item's lock_version as read from get_meeting. Pass it and the write fails loudly (409) on a concurrent edit; omit it and the current version is fetched and echoed. | |
| presenter_id | No | User id of the new presenter, from search_principals or the meeting's participants. They must be able to view meetings in the project. Omit to leave the presenter alone. | |
| agenda_item_id | Yes | Numeric agenda item id from get_meeting's agenda_items (or from add_meeting_agenda_item's result). Not the meeting id, not a work package id. | |
| work_package_id | No | Re-point a work-package item at another work package, which must be visible to this account. Omit to leave the link alone; a simple item cannot be turned into a work-package one (itemType is create-only). | |
| duration_minutes | No | New planned length of this item in minutes (0-1440). Omit to leave it as it is. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Agenda item id (not the meeting id, not a work package id). |
| notes | No | Item notes as markdown (raw); html is dropped. |
| title | No | Item title. Empty for a work-package item, where the work package's subject is the title shown in the UI. |
| meeting | No | Meeting the item was added to. |
| section | No | Agenda section the item sits in, when the meeting uses sections. |
| outcomes | No | Outcomes recorded against this item; always a list, empty when none. |
| position | No | 1-based order within the agenda. |
| item_type | No | 'simple' for a free-text item, 'work_package' for a linked one. |
| presenter | No | User presenting this item. |
| created_at | No | ISO 8601 UTC timestamp. |
| lock_version | No | Optimistic-lock version. Echo it as update_meeting_agenda_item's lock_version so a concurrent edit fails loudly (409) instead of being overwritten. |
| work_package | No | Work package this item discusses. Null both when none is linked and when the linked one is invisible to this account — 'notes' says when the latter happened. |
| duration_minutes | No | Planned length in minutes; null when the organizer set none. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
While annotations indicate readOnlyHint=false and destructiveHint=false, the description adds rich behavioral context: only passed parameters are sent, the fresh lock_version is returned, closed meetings reject writes, itemType is create-only, permission requirements (403), and concurrency conflict semantics (409). This goes far beyond what annotations provide and avoids contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than the calibration examples, but every sentence earns its place: overview, examples, return value, pitfalls, and cross-references. It is well structured and front-loaded with the primary purpose, though the density could be slightly reduced without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the mutation complexity, the description covers partial update semantics, return shape, error cases (422, 403, 409), prerequisites (permissions, open meeting), and cross-references. Even with an output schema present, the behavioral and edge-case coverage is complete and actionable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage with detailed per-parameter descriptions. The tool description adds overarching context like 'Only the parameters you pass are sent' and the meaning of position for reordering, which clarifies interaction between parameters. Schema already carries the load, so the description only needs to add a little extra value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Edit one agenda item: retitle it, rewrite its notes, retime, reorder or re-link it.' This clearly distinguishes the tool from siblings like `add_meeting_agenda_item` and `delete_meeting_agenda_item`, using action-oriented language with concrete examples.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit usage scenarios ('Use it for...'), plus cross-references to related tools (`get_meeting`, `delete_meeting_agenda_item`, `add_meeting_outcome`). It also states when NOT to use it, e.g., when the meeting is CLOSED. This gives the agent clear decision-making guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_meeting_outcomeUpdate meeting outcomeA
Correct a recorded outcome's kind, text or linked work package.
Use it while the meeting still runs: fix a typo in the minutes, upgrade an information note to a decision, or attach the follow-up ticket that was created after the fact. Only the parameters you pass are sent.
Returns the updated outcome in the same shape as add_meeting_outcome.
Pitfalls. The same timing rule as every outcome write: this only works while the
meeting state is exactly 'in_progress' — once it is closed, the minutes are what they
are, and the write answers a validation error. Outcomes carry no lock_version
upstream, so there is nothing to echo and a simultaneous edit by somebody else is
silently overwritten — read the outcome via get_meeting first. This needs the
'manage outcomes' permission (403 otherwise).
Cross-references: get_meeting for the outcome id and current text;
add_meeting_outcome for the kind rules; delete_meeting_outcome to remove it.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | New kind: 'information', 'decision' or 'work_package'. Omit to keep the current one. Switching to 'work_package' needs a linked work package; switching to 'information' needs notes. | |
| notes | No | New outcome text as markdown; it REPLACES the stored text. Omit to leave it untouched. | |
| outcome_id | Yes | Numeric outcome id from get_meeting's agenda_items[].outcomes or from add_meeting_outcome's result. Not an agenda item id. | |
| work_package_id | No | Re-point the outcome at another work package, which must be visible to this account. Omit to leave the link alone. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Outcome id. |
| kind | No | Outcome kind as the instance defines it, e.g. 'decision'. |
| notes | No | Outcome text as markdown (raw); html is dropped. |
| author | No | User who recorded the outcome. |
| agenda_item | No | Agenda item the outcome is recorded against. |
| work_package | No | Work package the outcome points at, when one was linked. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations by disclosing the critical timing rule: 'this only works while the meeting state is exactly in_progress' and warns about validation errors after the meeting is closed. It also exposes concurrency behavior ('a simultaneous edit by somebody else is silently overwritten') and the required 'manage outcomes' permission (403 otherwise), which annotations do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, then organized into usage, pitfalls, and cross-references. Every sentence adds information: timing constraints, concurrency implications, permission requirements, return response shape, and related tools. Despite its length, it is tightly structured with no filler, making it appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's operational complexity (timing, permissions, concurrency), the description covers all necessary context: what happens on closed meetings, lack of lock_version, 403 condition, and the success response shape ('same shape as add_meeting_outcome'). With an output schema present, the description does not need to detail return fields, so this is complete for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% parameter descriptions, including omit behavior and constraints (e.g., 'Omit to keep the current one' and 'Switching to work_package needs a linked work package'). The description adds the partial-update rule 'Only the parameters you pass are sent,' but this is largely reinforced by the schema's per-field omission notes. No significant semantic gap exists, so a mid-range score is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Correct a recorded outcome's kind, text or linked work package.' It clearly identifies the tool as an update operation for existing meeting outcomes, distinguishing it from sibling tools like add_meeting_outcome and delete_meeting_outcome by focusing on correction/update.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'Use it while the meeting still runs: fix a typo in the minutes, upgrade an information note to a decision, or attach the follow-up ticket...' It also names cross-references such as get_meeting for retrieving the outcome id, add_meeting_outcome for kind rules, and delete_meeting_outcome for removal, providing clear alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_newsUpdate newsA
Correct or rewrite a published news entry.
Use it to fix a headline, refresh a weekly report in place, or clear a stale teaser. Only the parameters you pass are sent, so a concurrent edit to another field survives.
Returns the updated entry in the same shape as get_news.
Pitfalls: summary and description REPLACE the stored text — there is no
append. The entry's project and author are fixed at creation and cannot be updated
here; publish a new entry instead. News carries no lockVersion upstream, so there
is nothing to echo and no lock parameter: a simultaneous edit by somebody else is
silently overwritten, which is why reading with get_news first is worth it. The
'manage news' permission is required, so a 403 is about the account or a disabled
news module.
Cross-references: get_news for the current text and for can_manage;
delete_news when the entry should disappear entirely; create_news to publish
a follow-up instead of rewriting history.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | New headline, up to 256 characters. Omit to leave it alone; it cannot be cleared, because news requires a title. | |
| news_id | Yes | Numeric news id from list_news or get_news. | |
| summary | No | New teaser (plain text, up to 255 characters); REPLACES the existing one. Pass null or an empty string to clear it. Omit the parameter entirely (the default) to leave it untouched. | __unchanged__ |
| description | No | New markdown body; REPLACES the existing text rather than appending to it, so read the current one with get_news first if you mean to extend it. Pass null or an empty string to clear it. Omit to leave it untouched. | __unchanged__ |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | News id — what get_news, update_news and delete_news consume. |
| title | No | Headline of the announcement. |
| author | No | User who published it ({id, name}); set by the server. |
| project | No | Project the announcement belongs to ({id, name}). |
| summary | No | Short teaser OpenProject shows under the headline; may be empty. The full body is NOT here — read it with get_news(news_id=...). |
| can_manage | No | True when this account may change or delete this entry (OpenProject renders the update/delete links only with the 'manage news' permission). False means update_news and delete_news would fail with 403. |
| created_at | No | ISO 8601 UTC publication timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | The announcement body as markdown (raw); html is dropped. Empty string when the entry has only a headline and summary. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses important behaviors beyond annotations: partial-update semantics, summary/description replace rather than append, no lockVersion leading to silent overwrites, and required 'manage news' permission with 403 interpretation. This adds rich context not covered by readOnlyHint/idempotentHint flags.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured and front-loaded: the main purpose appears first, followed by use cases, pitfalls, and cross-references. Every sentence adds value, and the length is appropriate given the complexity of the tool's behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema (so return format need not be detailed), the description covers concurrency, replacement semantics, fixed fields, permission requirements, and related tools. It is complete for a complex update operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 100% of parameters with detailed meanings, so the baseline is 3. The description adds extra context about partial-parameter transmission and the absence of a lock parameter, enhancing understanding beyond the schema, but the schema already carries most of the burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Begins with 'Correct or rewrite a published news entry,' giving a specific verb and resource. The description lists concrete use cases (fix headline, refresh report, clear teaser) and cross-references get_news, delete_news, and create_news, clearly distinguishing it from sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('Use it to fix a headline...') and when not to: project/author cannot be changed (publish a new entry), delete_news for removal, create_news for follow-ups. This gives clear context and explicit alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_projectUpdate 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
active=false. The change is validated through POST /projects/{id}/form first,
so rejected values come back as violations naming the attribute.
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
lockVersion upstream, so there is no version to echo and no lock parameter here.
Returns the updated project in the same shape as get_project.
Pitfalls: description and status_explanation REPLACE the stored text rather
than appending to it. active=false archives, which is not deletion but does hide
the project and freeze its work packages. Changing identifier is deliberately not
offered — it breaks every existing link to the project.
Cross-references: get_project to read the current values first; delete_project
to remove a project for good; list_projects(active=false) to find archived ones.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New display name. Omit to leave the name alone. | |
| active | No | false ARCHIVES the project — it disappears from normal listings and everything in it becomes read-only, including its subprojects. true restores it. Omit to leave it alone. Archiving is usually admin-only. | |
| public | No | True publishes the project to every logged-in user, false makes it members-only. Omit to leave the visibility alone. | |
| parent_id | No | Move the project under another one: numeric id or identifier of the new parent. Pass null to detach it and make it top-level. Omit the parameter entirely (the default) to leave the hierarchy untouched. | __unchanged__ |
| description | No | New description in markdown; it REPLACES the existing text, so read the current one with get_project first if you mean to extend it. Pass an empty string to clear it. Omit to leave it alone. | |
| status_code | No | New project status: on_track, at_risk, off_track, not_started, finished or discontinued. Only these codes are accepted. Omit to leave the status alone. | |
| id_or_identifier | Yes | Numeric project id or URL identifier of the project to change; both are accepted and come from list_projects or get_project. | |
| status_explanation | No | Markdown note explaining the status, e.g. why the project is at risk. Replaces the previous explanation; pass an empty string to clear it. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Numeric project id; accepted by every project_id parameter. |
| name | No | Display name. |
| active | No | False for archived projects (read-only in the UI). |
| parent | No | Parent project, when this is a subproject. |
| public | No | True when visible to users without a membership. |
| created_at | No | ISO 8601 UTC timestamp. |
| identifier | No | URL slug from /projects/<identifier>; also accepted wherever an id is. |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
| status_code | No | Project status code, one of: on_track, at_risk, off_track, not_started, finished, discontinued. A code, never a translated label; null means no status has been set. |
| workspace_type | No | Workspace kind: 'project', 'program' or 'portfolio'. Pre-17 instances only have 'project'; on 17.x project listings mix all three kinds, so check this before treating a row as a plain project. |
| status_explanation | No | Free-text explanation of status_code, markdown (raw). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Description discloses validation via 'POST /projects/{id}/form' and violations, partial-update semantics ('Only the parameters you pass are sent — omitted fields are never rewritten'), absence of lockVersion, and pitfalls around REPLACE semantics and archiving. Annotations are minimal, so this context is vital and highly transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured with sections for use cases, validation, partial updates, return type, pitfalls, and cross-references. Every sentence adds unique value, so it earns its length; the core purpose is front-loaded in the first line.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers validation, partial-update behavior, return shape (via get_project reference), side effects of archiving, and intended alternatives. With an output schema present, no further return documentation is needed, and the description fully prepares an agent for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already documents all 8 parameters at 100% coverage, so baseline is 3. Description adds crucial operational meaning: 'Only the parameters you pass are sent' clarifies null/default semantics, and pitfalls like 'active=false archives' and 'description REPLACES' enrich the parameter behavior beyond schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Change a project's name, description, visibility, parent, status or archived state', a specific verb+resource+fields. It clearly distinguishes from siblings like update_work_package, create_project, and delete_project by naming project-specific attributes and the archived state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'Use it to record a status change... to rename or re-parent a project, to publish it, or to archive it'. It also provides exclusions: 'Changing ``identifier`` is deliberately not offered' and cross-references get_project/delete_project/list_projects for related tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_time_entryUpdate 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 log_time returns.
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 lock_version for time entries and some do not; this tool
reads the entry first and only echoes a lock version when one exists, so
a concurrent edit surfaces as a conflict error with the fresh state
rather than silently overwriting a colleague's correction. Editing time
inside a closed cost-reporting period is refused by OpenProject with a
validation error.
Cross-references: find the id with list_time_entries; remove the
entry entirely with delete_time_entry; the valid activity names come
from get_project_metadata.
| Name | Required | Description | Default |
|---|---|---|---|
| hours | No | New duration in hours as a float (1.5 = 1h30). Omit to leave it. | |
| comment | No | New comment text. Pass an empty string to clear it; omit to leave the existing comment untouched. | |
| activity | No | New activity as a name or numeric id. Names are resolved against the entry's own form, so an unknown one fails with the valid ones listed. | |
| spent_on | No | New spent-on date, ISO YYYY-MM-DD. Omit to leave it. | |
| time_entry_id | Yes | Id of the entry to correct. It comes from list_time_entries or from the log_time result — it is not a work package id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Time entry id. Feed it to update_time_entry or delete_time_entry. |
| user | No | User the time is booked for. |
| hours | No | Logged duration in hours as a float (1.5 = one and a half hours), converted from OpenProject's ISO 8601 duration. |
| comment | No | Free-text comment as entered (raw); html is dropped. |
| project | No | Project the entry belongs to. |
| activity | No | Time-entry activity (Development, Management, …); instance-defined. |
| spent_on | No | The date the work was done, ISO YYYY-MM-DD. |
| created_at | No | ISO 8601 UTC timestamp. |
| updated_at | No | ISO 8601 UTC timestamp. |
| lock_version | No | Optimistic-locking version, when this instance reports one for time entries; null means the resource is updated without a lock version. |
| work_package | No | Work package the time is booked on; null for project-level entries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint: false, etc.), the description discloses critical behaviors: partial update semantics ('only the parameters you pass are written'), lock_version handling and conflict errors rather than silent overwrites, and refusal to edit within closed cost-reporting periods. This adds substantial transparency beyond the structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with a clear purpose sentence, followed by usage, behavior, and cross-references in a logical order. Every sentence adds useful information; no filler or redundancy. Despite being long, it earns its space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description still usefully cross-references log_time's return shape. It covers edge cases (concurrency, validation errors, uneditable fields) and provides all necessary operational context. No significant gaps remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already has 100% coverage, but the description enriches it further: it clarifies the 'comment' empty-string behavior, explains 'activity' name resolution against the entry's own form, and clarifies that time_entry_id is not a work package id. This is additive value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Correct an existing time entry', a specific verb-resource pair that clearly distinguishes it from siblings like log_time (create) and delete_time_entry. It further clarifies the scope by listing everyday fixes (wrong duration, day, activity, comment), making the purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance ('everyday fixes'), clearly states what is not editable (work package and project) with an alternative action ('delete the entry and log it again'), and provides cross-references to list_time_entries, delete_time_entry, and get_project_metadata. This is exemplary usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_versionUpdate versionA
Change a version's name, dates, description, status or sharing.
Use it to move a sprint's dates, to close a finished release (status='closed'),
or to widen sharing so a subproject can use the version. The change is validated
through POST /versions/{id}/form first, so rejected values come back as
violations naming the attribute.
Only the parameters you pass are sent, so concurrent edits to other fields survive.
Versions carry no lockVersion upstream, so there is nothing to echo and no lock
parameter here — a 409 would mean the resource itself changed, not a stale version.
Returns the updated version in the same shape as create_version.
Pitfalls: end_date writes the API's endDate — it lands, unlike in the old
server. description REPLACES the stored text. Closing a version does not move or
unassign its work packages; they keep pointing at it. The defining project cannot be
changed — create a new version instead.
Cross-references: list_versions for ids and current values; delete_version
when the version must really disappear; update_work_package(version=...) to move
individual work packages between versions.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name. Omit to leave it alone. | |
| status | No | open, locked or closed. Closing a version keeps its work packages but stops new ones being assigned — it is the safe alternative to delete_version. Omit to leave the status alone. | |
| sharing | No | none, descendants, hierarchy, tree or system. Narrowing the sharing of a version other projects already use is rejected by the API. Omit to leave it alone. | |
| end_date | No | New finish date, ISO YYYY-MM-DD, written to the API's 'endDate'. Pass null to clear it. Omit the parameter to leave it untouched. | __unchanged__ |
| start_date | No | New start date, ISO YYYY-MM-DD. Pass null to clear it. Omit the parameter entirely (the default) to leave it untouched. | __unchanged__ |
| version_id | Yes | Numeric version id from list_versions or get_project_metadata(project_id=...).versions. | |
| description | No | New markdown description; REPLACES the existing text. Pass null or an empty string to clear it. Omit to leave it untouched. | __unchanged__ |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Version id — what update_version, delete_version and the work-package 'version' field consume. |
| name | No | Version name, e.g. 'Sprint 12' or '2.1'. |
| source | No | 'sprint' when the row came from the backlogs sprints endpoint, 'version' otherwise. Sprints ARE versions upstream, so a row can be both and is then reported as 'sprint'. |
| status | No | open, locked or closed. Locked and closed versions reject new work package assignments. |
| project | No | The project that DEFINES the version. A version shared from a parent project shows that parent here, not the project you asked about. |
| sharing | No | How far the version is shared: none, descendants, hierarchy, tree, system. |
| end_date | No | ISO date (YYYY-MM-DD); the version's finish date. |
| created_at | No | ISO 8601 UTC timestamp. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses behavior well beyond annotations: validation via POST /versions/{id}/form returning violations, partial-update semantics with concurrent edit survival, absence of lockVersion, 409 meaning resource-level change, end_date→endDate mapping, description replacement, and the effect of closing. This is rich, trustworthy context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured into lead, use cases, validation/concurrency, pitfalls, and cross-references. Almost every sentence earns its place, though a few points (e.g., end_date/endDate mapping) slightly duplicate the schema's own param descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 7 parameters, an output schema, and annotations, the description is fully complete: it explains behavior, side effects, validation failure mode, concurrency, pitfalls, and related tools. The return shape is adequately handled by referencing create_version's shape without over-explaining.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though schema coverage is 100%, the description adds crucial semantics: only passed parameters are sent, end_date maps to API endDate, description REPLACES text, null clears values, and omitted parameters stay untouched. This complements the schema rather than repeating it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: “Change a version's name, dates, description, status or sharing.” It clearly distinguishes itself from related tools by naming concrete use cases and cross-referencing list_versions, delete_version, and update_work_package for different intents.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: move sprint dates, close a release, widen sharing. It also states when NOT to use it (defining project cannot change—create a new version; delete_version when the version must disappear; update_work_package to move work packages), which is exemplary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_work_packageUpdate 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. Every convenience the old tooling spread across a dozen tools is a parameter here.
Returns the updated work package in full detail, including the new lock_version to use
for a follow-up edit.
Pitfalls: omitted parameters are left alone, while passing null clears a field
(assignee, responsible, version, parent, dates, description). A 409 error means somebody
else changed the work package first — the error carries the fresh lock_version and the
conflicting fields, so re-read, decide, and retry deliberately rather than blindly.
Status changes are validated against the workflow, so an invalid transition lists the
allowed targets.
Ids come from get_work_package / list_work_packages; status, priority, type and
version values come from get_project_metadata.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Work package id to change (the #1234 number). | |
| date | No | Milestone date (YYYY-MM-DD); only valid on milestone types. | __unchanged__ |
| type | No | New type as a name or numeric id. Cannot be cleared. | |
| notify | No | Send OpenProject notification emails for this change. | |
| status | No | New status as a name or numeric id. Validated through the form endpoint, so an invalid workflow transition comes back listing the statuses that *are* reachable from the current one. | |
| subject | No | New title. Omit to leave unchanged; cannot be cleared. | |
| version | No | Numeric version / sprint id; null removes it from the version. | __unchanged__ |
| assignee | No | Numeric user id to assign. Omit to leave unchanged; pass null (or 'none') to unassign — that sends a null href rather than a bogus user id. | __unchanged__ |
| due_date | No | ISO date (YYYY-MM-DD); null clears it. | __unchanged__ |
| priority | No | New priority as a name or numeric id. | |
| parent_id | No | Re-parent this work package under another id; null detaches it and makes it top level. This is the only hierarchy tool — there is no separate set/remove-parent tool. | __unchanged__ |
| start_date | No | ISO date (YYYY-MM-DD); null clears it. | __unchanged__ |
| description | No | New markdown body. Omit to leave unchanged; pass null to empty it. Replaces the whole description — read it with get_work_package first if you mean to append. | __unchanged__ |
| responsible | No | Numeric user id of the accountable person; null clears it. | __unchanged__ |
| lock_version | No | The `lock_version` you read from get_work_package. Pass it and the write fails loudly (409) if somebody else edited the work package in the meantime. Omit it and the current version is fetched and echoed — still safe, just one more round trip and a slightly wider conflict window. | |
| custom_fields | No | Custom field writes keyed by wire key or display name, e.g. {'Severity': 'High'}. Unknown or non-writable keys fail with the valid keys listed. Only the keys you pass are touched. | |
| estimated_hours | No | Estimate in hours as a decimal. | |
| percentage_done | No | Progress 0-100. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Work package id. |
| date | No | Milestone date (ISO YYYY-MM-DD); null for non-milestones. |
| type | No | Work package type. |
| notes | No | Degradation notes for this result. |
| author | No | Creating user. |
| parent | No | Parent work package. |
| status | No | Status. |
| project | No | Owning project. |
| subject | No | Subject line. |
| version | No | Version / sprint. |
| assignee | No | Assigned user or group. |
| category | No | Category. |
| due_date | No | ISO date (YYYY-MM-DD). |
| priority | No | Priority. |
| available | No | Feature availability for this WP: dev links, meetings, files. |
| created_at | No | ISO 8601 UTC timestamp. |
| display_id | No | Human-facing id as the instance renders it. Matches the numeric id unless the instance uses semantic identifiers (17.x, e.g. 'PROJ-42'); null when the instance predates it. |
| start_date | No | ISO date (YYYY-MM-DD). |
| updated_at | No | ISO 8601 UTC timestamp. |
| description | No | Description as markdown (raw); html is dropped. |
| responsible | No | Accountable user. |
| spent_hours | No | Logged time in hours. |
| lock_version | No | Optimistic-locking version; pass to update_work_package. |
| custom_fields | No | Always a list; empty when none are set. |
| project_phase | No | Project phase this work package sits in (16.1+, only when phases are active in the project and visible to this user); details via get_project_phase. |
| estimated_hours | No | Estimate in hours. |
| percentage_done | No | Progress, 0-100. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint false, destructiveHint false), the description discloses optimistic locking, null-clears-field semantics, omitted-parameters-left-alone, workflow validation, and 409 conflict details. It also states the return includes the new lock_version, going well beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured into purpose, use cases, return value, pitfalls, and data sources. Every sentence carries useful information; the length is justified by 18 parameters and complex concurrency semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a rich output schema and 18 parameters, the description covers all critical operational aspects: optimistic concurrency, null behavior, workflow validation, error handling, and source of identifiers/values. It is fully sufficient for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds essential global semantics: omitted parameters are untouched while null clears fields, and it explains the lock_version conflict window and the parent_id uniqueness as the only hierarchy tool. This adds meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Change any writable field of a work package' – a clear verb+resource statement. It then lists specific use cases (assign/unassign, status transitions, rescheduling) and explicitly contrasts with the old fragmented tooling, distinguishing it from siblings like create_work_package and delete_work_package.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly directs when to use this tool: 'Use it to assign or unassign, move a status forward, re-schedule, re-parent...' It also provides cross-references: IDs come from get_work_package/list_work_packages, and values from get_project_metadata. It explains the 409 conflict retry behavior and when to re-read, offering clear guidance versus alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_work_package_relationUpdate work package relationAIdempotent
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
follows relation reschedules the successor, so dates can move.
Cross-references: create_work_package_relation makes one;
delete_work_package_relation removes it;
get_work_package(include=['relations']) lists the ids.
| Name | Required | Description | Default |
|---|---|---|---|
| lag | No | New lag in working days; valid only while the relation is 'follows' or 'precedes'. Omit to leave it untouched, pass 0 to remove an existing lag. When lag is given without type, the current type is read first and a lag on a non-scheduling relation is refused before any write. | |
| type | No | New relation type. Omit to keep the current one. Switching to or from 'follows'/'precedes' changes whether the relation schedules dates. | |
| description | No | New note for the relation. Omit to leave it untouched; pass an empty string to clear it. | |
| relation_id | Yes | Id of the relation to change. It comes from create_work_package_relation or get_work_package(include=['relations']) — it is not a work package id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Relation id. Pass it to update_work_package_relation or delete_work_package_relation — it is not a work package id. |
| lag | No | Working days kept between the predecessor and the successor. Only follows/precedes relations carry one; null everywhere else. |
| type | No | Relation type as OpenProject stored it, read from the 'from' work package (e.g. 'follows' means 'from' is scheduled after 'to'). |
| description | No | Free-text note stored on the relation; null when unset. |
| reverse_type | No | The same relation read from the 'to' work package: 'follows' <-> 'precedes', 'blocks' <-> 'blocked', 'relates' <-> 'relates'. |
| to_work_package | No | Work package the relation points to. |
| from_work_package | No | Work package the relation starts at. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond annotations, including the plain-overwrite/no-conflict-detection behavior, the silent replacement of concurrent edits, and the rescheduling side effect of raising lag. Annotations only hint at idempotency and non-read-only, so this fills the gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear opening line, a 'Use it to' section, pitfalls, and cross-references. It is front-loaded with the purpose and each sentence adds value. Despite length, it remains focused and readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 4 parameters and an output schema, the description covers all critical aspects: what can be updated, what cannot be changed, concurrency behavior, side effects, and alternatives. The existing output schema covers return values, so no further explanation is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameter semantics are already strong. The description adds the runtime constraint that at least one of type, lag, or description must be given, and notes the scheduling implication of 'follows'/'precedes'. This goes beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Change an existing relation's type, lag or description.' It uses a specific verb and resource, and distinguishes itself from siblings by noting that the two work packages cannot be changed here and by cross-referencing create/delete relation tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit use cases ('widen the gap... correct a link... explain why two work packages are connected'), a clear exclusion ('The two work packages cannot be changed here'), and cross-references to alternative tools (create/delete/get). It also warns about the at-least-one-field requirement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upload_attachmentUpload 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.
| Name | Required | Description | Default |
|---|---|---|---|
| file_name | No | Name to store the file under, with extension. This is the only thing that decides the stored name: OpenProject ignores the multipart filename and re-detects the content type from the bytes. Defaults to the basename of file_path. | |
| file_path | Yes | Absolute path of the file to upload, on the machine running this server. Existence and size are checked locally before anything is transferred. | |
| description | No | Optional caption shown next to the file in OpenProject. | |
| container_id | Yes | Numeric id of the container: work package id, wiki page id, meeting id, document id, budget id, or activity id for 'comment'. | |
| container_type | Yes | Kind of object to attach the file to. Use 'comment' to attach to a work-package comment and pass its activity id as container_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Attachment id; pass it to download_attachment. |
| author | No | User who uploaded the file. |
| status | No | Virus-scan state: 'uploaded'/'scanned' are downloadable, 'quarantined' is not, anything else is still being scanned. |
| file_name | No | Stored file name including extension. |
| created_at | No | ISO 8601 UTC upload timestamp. |
| size_bytes | No | File size in bytes. |
| description | No | Caption stored with the file. |
| content_type | No | MIME type detected by OpenProject from the bytes. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses substantial behavioral detail beyond annotations: local existence/size pre-checks causing instant failure, the edit-permission requirement on the container, extension-restriction behavior with validation_failed and allowlist hint, and the filename source (file_name vs multipart). This fully surfaces operational traits without contradicting the annotation hints (readOnlyHint=false, destructiveHint=false).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence adds value: usage, pre-check behavior, return fields, pitfalls, alternative tool, and related tools. It is structured into clear scannable segments and avoids redundancy with the schema. For a tool with this complexity, the length is warranted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers permissions, error conditions (extension restrictions, oversized files), naming semantics, the return row and its downstream use (id feeds download_attachment), and guidance for the brand-new container case. With an output schema present and five parameters, nothing essential is left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and each parameter already has a thorough description, so the baseline is 3. The description adds extra semantics for file_name ('This is the only thing that decides the stored name... ignores the multipart filename') and clarifies container_id usage for comment (activity id). These additions go beyond the schema, justifying a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Attach a local file to a work package, wiki page, meeting, document, budget or comment.' This clearly distinguishes it from sibling tools like download_attachment, list_attachments, and delete_attachment while naming the exact containers it targets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is given: 'Use it when a file that already exists on the server's machine should be added to an existing container.' It also names an alternative with a concrete parameter hint: 'to give a brand-new work package its files use create_work_package(attachment_paths=[...]) instead.' Related tools are listed for reverse operations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
84 tool updates
v0.1.0- First observed
add_meeting_agenda_item - First observed
add_meeting_outcome - First observed
add_work_package_comment - First observed
add_work_package_watcher - First observed
cancel_recurring_meeting_occurrence - First observed
copy_project - First observed
create_meeting - First observed
create_news - First observed
create_project - First observed
create_recurring_meeting - First observed
create_version - First observed
create_work_package - First observed
create_work_package_relation - First observed
delete_attachment - First observed
delete_meeting - First observed
delete_meeting_agenda_item - First observed
delete_meeting_outcome - First observed
delete_news - First observed
delete_project - First observed
delete_recurring_meeting - First observed
delete_time_entry - First observed
delete_version - First observed
delete_work_package - First observed
delete_work_package_relation - First observed
download_attachment - First observed
edit_work_package_comment - First observed
execute_custom_action - First observed
get_document - First observed
get_github_pull_request - First observed
get_instance_info - First observed
get_job_status - First observed
get_meeting - First observed
get_news - First observed
get_project - First observed
get_project_metadata - First observed
get_project_phase - First observed
get_project_report_data - First observed
get_recurring_meeting - First observed
get_user - First observed
get_wiki_page - First observed
get_work_package - First observed
get_work_package_git_activity - First observed
get_work_package_schema - First observed
init_recurring_meeting_occurrence - First observed
list_attachments - First observed
list_budgets - First observed
list_documents - First observed
list_file_links - First observed
list_meetings - First observed
list_memberships - First observed
list_news - First observed
list_notifications - First observed
list_permissions - First observed
list_project_phase_definitions - First observed
list_projects - First observed
list_queries - First observed
list_recurring_meetings - First observed
list_reminders - First observed
list_roles - First observed
list_time_entries - First observed
list_versions - First observed
list_work_package_comments - First observed
list_work_packages - First observed
log_time - First observed
mark_all_notifications_read - First observed
mark_notifications - First observed
remove_work_package_watcher - First observed
run_query - First observed
save_query - First observed
search_principals - First observed
search_work_packages - First observed
set_project_favorite - First observed
set_work_package_reminder - First observed
toggle_comment_reaction - First observed
update_meeting - First observed
update_meeting_agenda_item - First observed
update_meeting_outcome - First observed
update_news - First observed
update_project - First observed
update_time_entry - First observed
update_version - First observed
update_work_package - First observed
update_work_package_relation - First observed
upload_attachment
TDQS
Each of the 84 tools targets a distinct resource+action, and close pairs like list_file_links/list_attachments or list_meetings/list_recurring_meetings are explicitly cross-referenced and disambiguated in their descriptions. There is no pair where an agent would be unsure which tool to pick after reading the descriptions.
The majority follow a consistent verb_noun pattern with CRUD verbs (list/get/create/update/delete). Minor inconsistencies exist: edit_work_package_comment vs update_*, remove_work_package_watcher vs delete_*, and add_* vs create_* for sub-resources, but these are readable and predictable overall.
84 tools is far beyond the 25 threshold for 'too many,' and even though the server covers an enormous domain (work packages, projects, meetings, time tracking, news, etc.), the sheer number imposes a heavy selection burden on agents and makes the surface harder to navigate coherently.
Major entities like work packages, projects, meetings, time entries, and versions have full CRUD, but there are significant gaps: membership management (create/update/delete) is entirely absent despite cross-references to those tools, and wiki, documents, file links, and budgets are read-only. Agents attempting common access-management or wiki-editing tasks will hit dead ends.
Maintenance
Related MCP Connectors
A MCP server built for developers enabling Git based project management with project and personal…
Related MCP Servers
- MIT
- AlicenseNot gradedqualityAmaintenanceOpen source MCP server specializing in easy, fast, and secure tools for Databases.16,318Apache 2.0
- AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceProduction-grade MCP server for universal reverse engineering automation.75GPL 3.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kar-thik/openproject-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server