Problem / motivation
The server has no tools for sprints or the product backlog. Agile project
management — the core agentic-PM use case — needs to see what's in the current
sprint, what's sitting in the backlog, and (for planning) to move work between them.
OpenProject 17.3+ exposes sprints in the API v3, so we can support this natively.
Background — verified against a live instance (not assumptions)
Spiked on ephemeral openproject/openproject:17 (coreVersion 17.5.1):
GET /api/v3/sprints → 200 (HAL collection, empty on a fresh instance).
GET /api/v3/projects/{id}/sprints → 200.
- Sprint resource fields:
id, name, status (in_planning | active |
completed), startDate, finishDate, description{raw},
_links.definingWorkspace (the project).
- The endpoint answered without enabling Backlogs, but per the API docs it is
gated by a "view sprints"/backlogs permission, so tools must degrade gracefully
when a project has no sprints / the module is off.
Proposed tools (clean implementation — NOT a port)
Read-only first (highest value, lowest risk):
| Tool |
Description |
list_sprints |
List sprints, optionally by project_id and status |
get_sprint |
One sprint's details by ID |
list_sprint_work_packages |
Work packages in a sprint (with story-point total) |
list_backlog_work_packages |
Work packages not in any sprint (product backlog) |
Optional write path (separate, only if verified):
sprint_id and story_points on create_work_package / update_work_package
(+ remove_from_sprint to send a WP back to the backlog).
Design
- New
src/tools/sprints.py; client methods get_sprints(project_id, filters, offset, page_size) and get_sprint(sprint_id) in src/client.py; register in
src/server.py. Follow existing tool conventions (Pydantic where inputs are
structured, format_*, try/except → format_error, full docstrings).
- Reuse the existing
get_work_packages(project_id, filters, ...) for the two
WP-listing tools rather than adding bespoke client code.
Open questions to resolve DURING implementation (verify live, don't assume)
- WP query filter for sprint. Is a work package filtered by sprint via a
sprint filter identifier, a version-based filter, or something else? (Recall
the time-entries entity_id surprise — verify the exact identifier and the
"no sprint" / backlog operator against the instance.)
- Story points field. Confirm the WP attribute name (
storyPoints) and whether
it is readable/writable via the API on a stock instance.
- Sprint assignment write path (only if we build the write tools): how a WP is
assigned to a sprint in the payload (_links.sprint? a version link?), and
whether it needs the Backlogs module active.
- How to create a sprint for the integration test (likely a rails helper, since
the API is read-only for sprints).
Acceptance criteria
- The 4 read-only tools register and return sensible output (and a clear empty-state
message when a project has no sprints / backlog).
- Live smoke on ephemeral OpenProject 17.5.1: create a sprint + assign a WP (via a
rails helper), then list_sprints, get_sprint, list_sprint_work_packages
(shows the WP + story points), and list_backlog_work_packages (shows an
unassigned WP) all return correct data.
- Network-free unit tests for formatting + filter construction, in the existing style.
- Any WP write params (sprint_id/story_points) are proven to round-trip before merge,
or dropped.
Out of scope
Prior art / attribution
Independent, clean implementation. Upstream PR
AndyEverything/openproject-mcp-server#25 (@MacherelR) and issue AndyEverything#20 surfaced the
17.3+ sprint API and are acknowledged as inspiration; no code is ported from them.
Problem / motivation
The server has no tools for sprints or the product backlog. Agile project
management — the core agentic-PM use case — needs to see what's in the current
sprint, what's sitting in the backlog, and (for planning) to move work between them.
OpenProject 17.3+ exposes sprints in the API v3, so we can support this natively.
Background — verified against a live instance (not assumptions)
Spiked on ephemeral
openproject/openproject:17(coreVersion 17.5.1):GET /api/v3/sprints→ 200 (HAL collection, empty on a fresh instance).GET /api/v3/projects/{id}/sprints→ 200.id,name,status(in_planning|active|completed),startDate,finishDate,description{raw},_links.definingWorkspace(the project).gated by a "view sprints"/backlogs permission, so tools must degrade gracefully
when a project has no sprints / the module is off.
Proposed tools (clean implementation — NOT a port)
Read-only first (highest value, lowest risk):
list_sprintsproject_idandstatusget_sprintlist_sprint_work_packageslist_backlog_work_packagesOptional write path (separate, only if verified):
sprint_idandstory_pointsoncreate_work_package/update_work_package(+
remove_from_sprintto send a WP back to the backlog).Design
src/tools/sprints.py; client methodsget_sprints(project_id, filters, offset, page_size)andget_sprint(sprint_id)insrc/client.py; register insrc/server.py. Follow existing tool conventions (Pydantic where inputs arestructured,
format_*, try/except →format_error, full docstrings).get_work_packages(project_id, filters, ...)for the twoWP-listing tools rather than adding bespoke client code.
Open questions to resolve DURING implementation (verify live, don't assume)
sprintfilter identifier, aversion-based filter, or something else? (Recallthe time-entries
entity_idsurprise — verify the exact identifier and the"no sprint" / backlog operator against the instance.)
storyPoints) and whetherit is readable/writable via the API on a stock instance.
assigned to a sprint in the payload (
_links.sprint? a version link?), andwhether it needs the Backlogs module active.
the API is read-only for sprints).
Acceptance criteria
message when a project has no sprints / backlog).
rails helper), then
list_sprints,get_sprint,list_sprint_work_packages(shows the WP + story points), and
list_backlog_work_packages(shows anunassigned WP) all return correct data.
or dropped.
Out of scope
(API error-handling rewrite, model restructuring, search changes, a stray
findfile) — explicitly not included.
Prior art / attribution
Independent, clean implementation. Upstream PR
AndyEverything/openproject-mcp-server#25(@MacherelR) and issue AndyEverything#20 surfaced the17.3+ sprint API and are acknowledged as inspiration; no code is ported from them.