openproject-mcp
Provides tools for managing OpenProject projects, work packages, relations, attachments, users, notifications, watchers, boards, and reference data via the OpenProject REST API.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@openproject-mcp show open work packages assigned to me"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
openproject-mcp
An MCP server that exposes the OpenProject REST API (v3) as tools usable by Claude Desktop, Claude Code, Cursor, and any other MCP client.
Install
Before you start: you need an OpenProject API token. Get one from My account > Access tokens > API in your OpenProject instance.
Pick your MCP client and run one command:
Claude Code
claude mcp add openproject \
--env OPENPROJECT_BASE_URL=https://your-instance.openproject.com \
--env OPENPROJECT_API_KEY=your-token \
-- npx -y github:OliverRhyme/openproject-mcpClaude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"openproject": {
"command": "npx",
"args": ["-y", "github:OliverRhyme/openproject-mcp"],
"env": {
"OPENPROJECT_BASE_URL": "https://your-instance.openproject.com",
"OPENPROJECT_API_KEY": "your-token"
}
}
}
}Cursor
Add to .cursor/mcp.json in your project root:
{
"mcpServers": {
"openproject": {
"command": "npx",
"args": ["-y", "github:OliverRhyme/openproject-mcp"],
"env": {
"OPENPROJECT_BASE_URL": "https://your-instance.openproject.com",
"OPENPROJECT_API_KEY": "your-token"
}
}
}
}Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"openproject": {
"command": "npx",
"args": ["-y", "github:OliverRhyme/openproject-mcp"],
"env": {
"OPENPROJECT_BASE_URL": "https://your-instance.openproject.com",
"OPENPROJECT_API_KEY": "your-token"
}
}
}
}Any other MCP client (generic stdio)
Run this as the server process:
OPENPROJECT_BASE_URL=https://your-instance.openproject.com \
OPENPROJECT_API_KEY=your-token \
npx -y github:OliverRhyme/openproject-mcpThe server speaks JSON-RPC over stdio. No build step needed — npx fetches, installs, and runs it directly from GitHub.
Related MCP server: OpenProject MCP Server
Configuration
Variable | Required | Default | Notes |
| yes | -- | e.g. |
| yes | -- | From My account > Access tokens > API |
| no |
| Default page size for list endpoints |
| no |
| HTTP request timeout in milliseconds |
Features
Projects -- list, get, create, update, delete, count
Work packages -- list, get, create, update (with
lockVersion), delete, activity/comment thread, inline file attachments on comments, countRelations -- list, get, create, delete (blocks, precedes, relates, duplicates, etc.)
Attachments -- list, get (with download), upload (with auto-embed in description or comment), delete
Users -- current user, list, get
Notifications -- list, get, mark read, mark all read
Watchers -- list, add, remove watchers on work packages
Boards -- list and get Kanban-style boards
Reference data -- work package types, statuses, priorities, versions
Raw passthrough -- call any GET endpoint under
/api/v3directlyFilter, sort, group, and paginate via OpenProject's native query syntax
Output optimization -- field selection, description truncation,
hasMorepagination flag, lightweight count tools
Tools reference
All tools are prefixed op_ to avoid collisions with other MCP servers.
Projects
Tool | Description |
| List projects with filter/sort/paginate |
| Get project by id or identifier slug |
| Create a new project |
| Patch project fields |
| Delete a project (destructive, async) |
| Count projects matching filters |
Work packages
Tool | Description |
| List work packages, optionally scoped to a project |
| Get a single work package by id |
| Create a work package (requires subject, projectId, typeId) |
| Patch work package fields (requires |
| Delete a work package (destructive) |
| List comments and change history |
| Add a comment, optionally attaching and embedding a file inline |
| Count work packages matching filters |
Relations
Tool | Description |
| List relations on a work package (blocks, precedes, duplicates, etc.) |
| Get a single relation by id |
| Create a relation between two work packages |
| Delete a relation |
Attachments
Tool | Description |
| List attachments on a work package |
| Get attachment metadata; optionally download to a local path |
| Upload a local file; optionally embed in a comment or WP description |
| Delete an attachment (destructive) |
Users
Tool | Description |
| Get the user tied to the configured API key |
| List users with filter/sort/paginate |
| Get a user by id |
Notifications
Tool | Description |
| List in-app notifications for the current user |
| Get a single notification by id |
| Mark one notification as read |
| Mark all notifications as read |
Watchers
Tool | Description |
| List users watching a work package |
| Add a user as a watcher |
| Remove a user from watching |
Boards
Tool | Description |
| List Kanban-style boards for a project |
| Get board details including column config |
Reference data
Tool | Description |
| List work package types (Task, Bug, Feature, etc.) |
| List all work package statuses |
| List all priorities |
| List versions/milestones, optionally per project |
| Raw GET against any |
Usage patterns
Filter syntax
List endpoints accept OpenProject's structured filter format:
{
"projectIdOrIdentifier": "web",
"filters": [
{ "field": "status_id", "operator": "o", "values": null },
{ "field": "assignee", "operator": "=", "values": ["42"] },
{ "field": "type", "operator": "=", "values": ["1"] }
],
"sortBy": [["updatedAt", "desc"]],
"pageSize": 50
}Common operators:
Operator | Meaning |
| Equals |
| Not equals |
| Contains (substring) |
| Open statuses |
| Closed statuses |
| Greater or equal |
| Less or equal |
| Any (not empty) |
| None (empty) |
Filter values are always strings, even for numeric ids: "values": ["42"], not [42].
Output optimization
List tools return summarized output by default. Several options keep responses small:
fields -- Return only specific fields per element:
// op_list_work_packages
{ "fields": ["id", "subject", "status"] }
// -> elements contain only { id, subject, status }raw -- Get the full HAL+JSON document from OpenProject:
// op_get_work_package
{ "id": 17, "raw": true }Count tools -- When you only need a number, use op_count_work_packages or op_count_projects instead of listing:
// op_count_work_packages
{ "projectIdOrIdentifier": "web", "filters": [{ "field": "status_id", "operator": "o", "values": null }] }
// -> { "total": 42 }Pagination -- All list responses include hasMore: true|false so you know if there are more pages. Max pageSize is 100; default is 25.
Truncation -- Project descriptions are truncated to 200 chars in list mode. Activity comments are truncated to 500 chars by default; pass full: true to op_list_work_package_activities for complete text.
Updating work packages (lockVersion)
OpenProject uses optimistic locking. You must pass the current lockVersion
when updating a work package -- fetch it first with op_get_work_package:
// 1. Get current state
// op_get_work_package { "id": 17 }
// -> { ..., "lockVersion": 4 }
// 2. Update with lockVersion
// op_update_work_package
{
"id": 17,
"lockVersion": 4,
"statusId": 7,
"percentageDone": 50
}Uploading and embedding attachments
Upload a file and auto-embed it as an image in a comment:
// op_upload_attachment
{
"workPackageId": 17,
"filePath": "/path/to/screenshot.png",
"embedIn": "comment",
"embedText": "Here's the updated design:"
}Or attach a file when commenting:
// op_comment_work_package
{
"id": 17,
"comment": "Fixed in latest build, see attached screenshot.",
"attachFilePath": "/path/to/screenshot.png"
}Creating relations
Link work packages with dependency or reference relations:
// op_create_relation
{
"fromId": 17,
"toId": 23,
"type": "blocks"
}Relation types: relates, duplicates, blocks, precedes, follows,
includes, partOf, requires.
Local development
git clone https://github.com/OliverRhyme/openproject-mcp.git
cd openproject-mcp
npm install
npm run devCommand | Purpose |
| Install dependencies |
| Compile TypeScript to |
| Run the compiled server (requires build) |
| Watch-mode server via |
| Run test suite |
| Run tests in watch mode |
| Type-check without emitting |
Smoke test
Verify the server boots and lists tools without a real OpenProject instance:
OPENPROJECT_BASE_URL=https://example.openproject.com OPENPROJECT_API_KEY=fake \
node dist/index.js <<'EOF'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0.0.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
EOFLicense
MIT
Available Tools
37 toolsop_add_watcherAdd watcherC
Add a user as a watcher to a work package.
Inputs: workPackageId (required), userId (required).
op_api_passthroughRaw API passthroughA
Escape hatch: call any GET endpoint under /api/v3 directly. Path should start with "/" (e.g. "/queries/42"). Use sparingly — prefer the typed tools above.
Inputs: path (required).
op_comment_work_packageComment on work packageB
Add a markdown comment to a work package activity stream. Optionally attach a file and embed it inline using attachFilePath.
Inputs: id (required), comment (required), notify, attachFilePath, +1 more.
op_create_projectCreate projectA
Create a new project. Requires admin or "add project" permission. Pass either a parent identifier or numeric id via _links.parent.href if creating a sub-project.
Inputs: name (required), identifier, description, public, +1 more.
op_create_relationCreate relationA
Create a relation between two work packages. Types: relates, duplicates, blocks, precedes, follows, includes, partOf, requires.
Inputs: fromId (required), toId (required), type (required), description.
op_create_work_packageCreate work packageA
Create a work package in a project. At minimum provide subject, projectId, and typeId. When patching dates use ISO YYYY-MM-DD strings.
Inputs: projectId (required), subject (required), typeId (required), description, +8 more.
op_current_userCurrent userA
Return the user associated with the configured API key.
Inputs: no named parameters.
op_delete_attachmentDelete attachmentC
Delete an attachment. Destructive.
Inputs: id (required).
op_delete_projectDelete projectA
Delete a project. Destructive; requires admin permission. The deletion is processed asynchronously by OpenProject.
Inputs: idOrIdentifier (required).
op_delete_relationDelete relationB
Delete a relation between work packages.
Inputs: id (required).
op_delete_work_packageDelete work packageA
Permanently delete a work package. Destructive.
Inputs: id (required).
op_get_attachmentGet attachmentA
Fetch attachment metadata by id. Set saveTo to download the file content to a local path and also return embedLocations showing where the file is referenced in the work package (description and comments).
Inputs: id (required), raw, saveTo.
op_get_boardGet boardA
Fetch a single board (grid) by id, including its widget/column configuration.
Inputs: id (required), raw.
op_get_notificationGet notificationB
Fetch a single notification by id.
Inputs: id (required), raw.
op_get_projectGet projectB
Get a single project by numeric id or identifier slug.
Inputs: idOrIdentifier (required), raw.
op_get_relationGet relationC
Fetch a single relation by id.
Inputs: id (required), raw.
op_get_userGet userA
Fetch a user by numeric id.
Inputs: id (required), raw.
op_get_work_packageGet work packageB
Fetch a single work package by numeric id with full HAL detail.
Inputs: id (required), raw.
op_list_attachmentsList attachmentsB
List attachments on a work package.
Inputs: workPackageId (required), raw.
op_list_boardsList boardsA
List boards (Kanban-style) for a project. Boards are stored as grids scoped to /projects/{id}/boards.
Inputs: projectIdOrIdentifier (required), raw.
op_list_notificationsList notificationsA
List in-app notifications for the current user. Common filters: "readIAN" ("=" "t"/"f"), "reason" ("=" "assigned"/"mentioned"/"watched").
Inputs: filters, sortBy, offset, pageSize, +1 more.
op_list_prioritiesList prioritiesA
List all priorities defined on the instance.
Inputs: no named parameters.
op_list_projectsList projectsA
List OpenProject projects visible to the API key. Supports OpenProject filter syntax. Common fields: "active" ("=" true/false), "name_and_identifier" ("~" substring), "parent_id" ("=" id).
Inputs: filters, sortBy, offset, pageSize, +1 more.
op_list_relationsList work package relationsC
List relations for a work package. Relation types: relates, duplicates, duplicated, blocks, blocked, precedes, follows, includes, partOf, requires, required.
Inputs: workPackageId (required), raw.
op_list_statusesList statusesA
List all work package statuses defined on the instance.
Inputs: no named parameters.
op_list_typesList work package typesA
List work package types (Task, Bug, Feature, Milestone, etc.). Optionally scope to a project.
Inputs: projectIdOrIdentifier.
op_list_usersList usersC
List users visible to the current API key. Supports filters such as "name" ("~" search), "status" ("=" "active"/"locked"/"invited"), "login" ("=" exact).
Inputs: filters, sortBy, offset, pageSize, +1 more.
op_list_versionsList versions (milestones)B
List versions, optionally filtered to a project.
Inputs: projectIdOrIdentifier.
op_list_watchersList watchersC
List users watching a work package.
Inputs: workPackageId (required), raw.
op_list_work_package_activitiesList work package activitiesB
List activities (comments and changes) on a work package, oldest first.
Inputs: id (required), raw.
op_list_work_packagesList work packagesA
List work packages, optionally scoped to a project. Supports OpenProject filter syntax. Common filter fields: "status_id", "assignee", "project", "type", "priority", "subject" ("~" search), "updatedAt".
Inputs: projectIdOrIdentifier, filters, sortBy, groupBy, +3 more.
op_mark_all_notifications_readMark all notifications readA
Mark all notifications as read for the current user.
Inputs: no named parameters.
op_mark_notification_readMark notification readC
Mark a single notification as read.
Inputs: id (required).
op_remove_watcherRemove watcherB
Remove a user from watching a work package.
Inputs: workPackageId (required), userId (required).
op_update_projectUpdate projectB
Patch an existing project. Only include the fields you want to change.
Inputs: idOrIdentifier (required), name, description, public, +1 more.
op_update_work_packageUpdate work packageA
Patch fields on a work package. You MUST pass the current lockVersion (fetch it first with op_get_work_package) or OpenProject will reject the update with a stale-object error.
Inputs: id (required), lockVersion (required), subject, description, +8 more.
op_upload_attachmentUpload attachmentA
Upload a file from the local filesystem as an attachment on a work package. Provide the absolute path to the file. Optionally override the file name. Set embedIn to "comment" or "description" to also insert an image reference () into the work package.
Inputs: workPackageId (required), filePath (required), fileName, description, +2 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 37 tools
Each tool targets a distinct resource and action (e.g., op_create_project, op_delete_work_package, op_list_boards). Even similar operations like add/remove watchers are clearly differentiated. The escape hatch (op_api_passthrough) is explicitly noted as distinct.
All tools follow the consistent pattern op_verb_noun (snake_case), with clear verbs like create, delete, get, list, update, etc. No mixing of conventions or ambiguous prefixes.
37 tools is above the typical ideal range, but each tool serves a specific and necessary operation for OpenProject's domain (projects, work packages, attachments, users, notifications, etc.). The count reflects the complexity of the platform rather than poor scoping.
The tool surface covers most core workflows: CRUD for projects and work packages, attachments, relations, notifications, and user management. Minor gaps exist (e.g., no update for relations or users), but these are likely intentional due to permissions or scope.
Maintenance
Related MCP Connectors
Manage Timequip projects, tasks, comments, members, and dashboards through MCP.
Manage Pipefork API projects, drafts, connections, secrets, and deployments through MCP.
Read and write Mission Control state via MCP — projects, tasks, subtasks, templates, status updates.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with self-hosted OpenProject instances through the MCP protocol, supporting CRUD operations for projects and tasks (work packages) with pagination and filtering capabilities.8MIT
- AlicenseBqualityCmaintenanceEnables AI assistants to manage OpenProject work packages, projects, and time tracking. It provides comprehensive tools for creating, updating, and querying tasks and project metadata through the OpenProject API.1159 npm1MIT
- AlicenseNot gradedqualityCmaintenanceProvides MCP tools for interacting with Redmine, including issues, projects, wiki pages, time entries, and more.15 npm1MIT
- AlicenseNot gradedqualityCmaintenanceMCP server enabling AI agents to interact with OpenProject API v3 for project management, including creating and managing work packages, projects, comments, time entries, boards, and user dashboards.59 npmMIT