Skip to main content
Glama
openproject-mcp

openproject-mcp

by OliverRhyme

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-mcp

Claude 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-mcp

The 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

OPENPROJECT_BASE_URL

yes

--

e.g. https://community.openproject.org

OPENPROJECT_API_KEY

yes

--

From My account > Access tokens > API

OPENPROJECT_PAGE_SIZE

no

25

Default page size for list endpoints

OPENPROJECT_TIMEOUT_MS

no

30000

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, count

  • Relations -- 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/v3 directly

  • Filter, sort, group, and paginate via OpenProject's native query syntax

  • Output optimization -- field selection, description truncation, hasMore pagination flag, lightweight count tools

Tools reference

All tools are prefixed op_ to avoid collisions with other MCP servers.

Projects

Tool

Description

op_list_projects

List projects with filter/sort/paginate

op_get_project

Get project by id or identifier slug

op_create_project

Create a new project

op_update_project

Patch project fields

op_delete_project

Delete a project (destructive, async)

op_count_projects

Count projects matching filters

Work packages

Tool

Description

op_list_work_packages

List work packages, optionally scoped to a project

op_get_work_package

Get a single work package by id

op_create_work_package

Create a work package (requires subject, projectId, typeId)

op_update_work_package

Patch work package fields (requires lockVersion)

op_delete_work_package

Delete a work package (destructive)

op_list_work_package_activities

List comments and change history

op_comment_work_package

Add a comment, optionally attaching and embedding a file inline

op_count_work_packages

Count work packages matching filters

Relations

Tool

Description

op_list_relations

List relations on a work package (blocks, precedes, duplicates, etc.)

op_get_relation

Get a single relation by id

op_create_relation

Create a relation between two work packages

op_delete_relation

Delete a relation

Attachments

Tool

Description

op_list_attachments

List attachments on a work package

op_get_attachment

Get attachment metadata; optionally download to a local path

op_upload_attachment

Upload a local file; optionally embed in a comment or WP description

op_delete_attachment

Delete an attachment (destructive)

Users

Tool

Description

op_current_user

Get the user tied to the configured API key

op_list_users

List users with filter/sort/paginate

op_get_user

Get a user by id

Notifications

Tool

Description

op_list_notifications

List in-app notifications for the current user

op_get_notification

Get a single notification by id

op_mark_notification_read

Mark one notification as read

op_mark_all_notifications_read

Mark all notifications as read

Watchers

Tool

Description

op_list_watchers

List users watching a work package

op_add_watcher

Add a user as a watcher

op_remove_watcher

Remove a user from watching

Boards

Tool

Description

op_list_boards

List Kanban-style boards for a project

op_get_board

Get board details including column config

Reference data

Tool

Description

op_list_types

List work package types (Task, Bug, Feature, etc.)

op_list_statuses

List all work package statuses

op_list_priorities

List all priorities

op_list_versions

List versions/milestones, optionally per project

op_api_passthrough

Raw GET against any /api/v3 path (escape hatch)

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)

o

Open statuses

c

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 dev

Command

Purpose

npm install

Install dependencies

npm run build

Compile TypeScript to dist/

npm start

Run the compiled server (requires build)

npm run dev

Watch-mode server via tsx (no build)

npm test

Run test suite

npm run test:watch

Run tests in watch mode

npm run typecheck

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"}
EOF

License

MIT

Available Tools

37 tools
op_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

    A3.5/5.0

    Scored across 37 tools

    Disambiguation5/5

    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.

    Naming Consistency5/5

    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.

    Tool Count4/5

    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.

    Completeness4/5

    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

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers