openproject-mcp-server 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openproject_mcp_server-0.1.0/.gitignore +27 -0
- openproject_mcp_server-0.1.0/CHANGELOG.md +48 -0
- openproject_mcp_server-0.1.0/LICENSE +21 -0
- openproject_mcp_server-0.1.0/PKG-INFO +499 -0
- openproject_mcp_server-0.1.0/README.md +471 -0
- openproject_mcp_server-0.1.0/SECURITY.md +44 -0
- openproject_mcp_server-0.1.0/openproject_mcp/__init__.py +5 -0
- openproject_mcp_server-0.1.0/openproject_mcp/__main__.py +127 -0
- openproject_mcp_server-0.1.0/openproject_mcp/auth.py +54 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/__init__.py +17 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/cache.py +147 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/errors.py +439 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/filters.py +571 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/hal.py +342 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/http.py +457 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/locking.py +192 -0
- openproject_mcp_server-0.1.0/openproject_mcp/client/payloads.py +300 -0
- openproject_mcp_server-0.1.0/openproject_mcp/config.py +273 -0
- openproject_mcp_server-0.1.0/openproject_mcp/observability.py +127 -0
- openproject_mcp_server-0.1.0/openproject_mcp/projections.py +311 -0
- openproject_mcp_server-0.1.0/openproject_mcp/prompts.py +972 -0
- openproject_mcp_server-0.1.0/openproject_mcp/py.typed +0 -0
- openproject_mcp_server-0.1.0/openproject_mcp/resources.py +324 -0
- openproject_mcp_server-0.1.0/openproject_mcp/server.py +142 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/__init__.py +61 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/_forms.py +172 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/_shared.py +606 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/attachments.py +1053 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/git_activity.py +751 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/metadata.py +1045 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/modules_collab.py +1306 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/news.py +654 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/notifications.py +546 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/people.py +1045 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/projects.py +1363 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/queries.py +687 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/reporting.py +1075 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/time_entries.py +906 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/versions.py +702 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/work_packages.py +1539 -0
- openproject_mcp_server-0.1.0/openproject_mcp/tools/wp_collaboration.py +1695 -0
- openproject_mcp_server-0.1.0/openproject_mcp/version_probe.py +295 -0
- openproject_mcp_server-0.1.0/pyproject.toml +115 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
|
|
8
|
+
# Environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# Tooling caches
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.hypothesis/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# uv.lock is intentionally tracked
|
|
22
|
+
|
|
23
|
+
# default download target of download_attachment
|
|
24
|
+
openproject-downloads/
|
|
25
|
+
|
|
26
|
+
# Claude Code local settings
|
|
27
|
+
.claude/
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
While the version is 0.x, MINOR releases may change the MCP tool surface
|
|
8
|
+
(tools renamed or removed, parameter or result-envelope semantics changed);
|
|
9
|
+
PATCH releases are fixes and strictly additive changes only.
|
|
10
|
+
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-07-30
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Initial release: 72 MCP tools for the OpenProject API v3 — work packages
|
|
18
|
+
(search/list/get/create/update/delete), comments, relations, watchers,
|
|
19
|
+
reminders and comment reactions, attachments (upload and download with
|
|
20
|
+
virus-quarantine handling, a size cap and SHA-256 digests), git/GitHub/GitLab
|
|
21
|
+
activity, projects and memberships, saved queries, notifications, time
|
|
22
|
+
tracking, versions and sprints, people, metadata and schemas, meetings, wiki,
|
|
23
|
+
news, documents, budgets, and reporting.
|
|
24
|
+
- 4 server-rendered prompts (`weekly_report`, `daily_standup`, `triage_inbox`,
|
|
25
|
+
`groom_backlog`) and 3 resource templates (work package, project,
|
|
26
|
+
attachment).
|
|
27
|
+
- stdio transport (default) and an HTTP transport that binds `127.0.0.1` by
|
|
28
|
+
default and refuses to start unless `OPENPROJECT_MCP_AUTH_TOKENS` is set (or
|
|
29
|
+
`OPENPROJECT_MCP_INSECURE=1` is passed explicitly, for local development
|
|
30
|
+
only).
|
|
31
|
+
- Per-request bearer-token verification on the HTTP transport: every request
|
|
32
|
+
must carry `Authorization: Bearer <token>` matching one of the configured
|
|
33
|
+
`OPENPROJECT_MCP_AUTH_TOKENS` values (comma-separated, e.g. one token per
|
|
34
|
+
client); missing or invalid tokens are rejected with a 401 and a
|
|
35
|
+
`WWW-Authenticate` header. Token comparison is constant-time. A set-but-empty
|
|
36
|
+
token list (for example `OPENPROJECT_MCP_AUTH_TOKENS=,`) refuses to start
|
|
37
|
+
rather than serving an open endpoint.
|
|
38
|
+
- Deployment gating: `OPENPROJECT_MCP_READ_ONLY=1` serves read tools only,
|
|
39
|
+
`OPENPROJECT_MCP_ADMIN_TOOLS=1` enables the three membership-write tools
|
|
40
|
+
(hidden by default), and `OPENPROJECT_MCP_DISABLE` drops whole tool groups.
|
|
41
|
+
- Version-adaptive behavior across OpenProject 14 LTS through 17.x: the server
|
|
42
|
+
probes the instance (internal comments, reactions, project favorites, filter
|
|
43
|
+
dialects) and degrades explicitly instead of failing silently.
|
|
44
|
+
|
|
45
|
+
Configuration note: the server binds only the documented `OPENPROJECT_*` /
|
|
46
|
+
`OPENPROJECT_MCP_*` environment variable names. Bare unprefixed names (e.g.
|
|
47
|
+
`READ_TIMEOUT`, `API_KEY`) are ignored — pre-release git installs also read
|
|
48
|
+
some unprefixed names, so rename any such variables when upgrading.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kar-thik <mail@karthikv.dev>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openproject-mcp-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for OpenProject: 72 tools for work packages, attachments, git activity, time tracking, meetings and reporting over the API v3
|
|
5
|
+
Project-URL: Homepage, https://github.com/kar-thik/openproject-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/kar-thik/openproject-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/kar-thik/openproject-mcp/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/kar-thik/openproject-mcp/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: kar-thik <mail@karthikv.dev>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,claude,fastmcp,llm,mcp,model-context-protocol,openproject,project-management
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Office/Business :: Groupware
|
|
20
|
+
Classifier: Topic :: Software Development
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Requires-Dist: fastmcp<4,>=3.4.5
|
|
24
|
+
Requires-Dist: httpx[http2]>=0.28.1
|
|
25
|
+
Requires-Dist: pydantic-settings>=2
|
|
26
|
+
Requires-Dist: pydantic>=2.12
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# OpenProject MCP Server
|
|
30
|
+
|
|
31
|
+
<!-- mcp-name: io.github.kar-thik/openproject-mcp-server -->
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/openproject-mcp-server/)
|
|
34
|
+
[](https://github.com/kar-thik/openproject-mcp/actions/workflows/ci.yml)
|
|
35
|
+
|
|
36
|
+
An MCP ([Model Context Protocol](https://modelcontextprotocol.io/)) server for the
|
|
37
|
+
[OpenProject](https://www.openproject.org/) API v3. It gives Claude and any other MCP client
|
|
38
|
+
72 tools covering work packages, comments and relations, attachments, git/PR activity, projects,
|
|
39
|
+
saved queries, notifications, time tracking, versions, people and memberships, meetings, news,
|
|
40
|
+
documents, budgets and reporting — plus 4 report/workflow prompts and 3 resource templates.
|
|
41
|
+
Built on FastMCP 3.x and httpx (HTTP/2).
|
|
42
|
+
|
|
43
|
+
Design principles, all enforced in code:
|
|
44
|
+
|
|
45
|
+
- **Structured everything.** Every tool returns a typed model, so clients get an
|
|
46
|
+
`outputSchema` and machine-readable `structuredContent`, not prose. Errors come back as a
|
|
47
|
+
JSON envelope with a stable `type`, the upstream `http_status`, a `message` and a `hint`
|
|
48
|
+
describing how to correct the call.
|
|
49
|
+
- **Honest degradation.** OpenProject instances differ by version, installed modules and
|
|
50
|
+
permissions. Tools report what they could not see as in-band notes — a missing module yields
|
|
51
|
+
an empty page with an explanation, never a fake success or a bare traceback.
|
|
52
|
+
- **Safe by default.** A read-only mode, admin-gated membership writes, per-group tool
|
|
53
|
+
disabling, a `confirm=true` guard on every destructive tool, TLS always verified, and
|
|
54
|
+
credentials that never appear in logs.
|
|
55
|
+
- **Version-adaptive.** Targets OpenProject 14 LTS through 17.x; API differences are detected
|
|
56
|
+
by a lazy, cached feature probe instead of assumptions (see
|
|
57
|
+
[Supported OpenProject versions](#supported-openproject-versions)).
|
|
58
|
+
|
|
59
|
+
## Requirements
|
|
60
|
+
|
|
61
|
+
- Python >= 3.12
|
|
62
|
+
- An OpenProject instance, version 14 LTS through 17.x
|
|
63
|
+
- An OpenProject API key: in OpenProject, go to **My account → Access tokens** and generate an
|
|
64
|
+
API token
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
The distribution name is `openproject-mcp-server`. It installs two identical console scripts,
|
|
69
|
+
`openproject-mcp-server` and `openproject-mcp`; the long form is canonical (an unrelated PyPI
|
|
70
|
+
package also installs a bin named `openproject-mcp`).
|
|
71
|
+
|
|
72
|
+
Run one-shot with [uv](https://docs.astral.sh/uv/), no install step:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
uvx openproject-mcp-server
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or install persistently:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
uv tool install openproject-mcp-server
|
|
82
|
+
# or
|
|
83
|
+
pip install openproject-mcp-server
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The minimal configuration is two environment variables:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
export OPENPROJECT_URL=https://openproject.example.com
|
|
90
|
+
export OPENPROJECT_API_KEY=your-api-key
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Validate the configuration without starting the server:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
openproject-mcp-server --check
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`--check` verifies the configuration and exits; it does not contact your instance. Once
|
|
100
|
+
connected through a client, call the `get_instance_info` tool for a live end-to-end check.
|
|
101
|
+
When configuration is missing or invalid, the server prints the specific problems to stderr
|
|
102
|
+
and exits with code 2 — never a traceback.
|
|
103
|
+
|
|
104
|
+
### Claude Code
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
claude mcp add openproject \
|
|
108
|
+
--env OPENPROJECT_URL=https://openproject.example.com \
|
|
109
|
+
--env OPENPROJECT_API_KEY=your-api-key \
|
|
110
|
+
-- uvx openproject-mcp-server
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Claude Desktop and other MCP clients
|
|
114
|
+
|
|
115
|
+
Add to `claude_desktop_config.json` (or your client's equivalent `mcpServers` config):
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"openproject": {
|
|
121
|
+
"command": "uvx",
|
|
122
|
+
"args": ["openproject-mcp-server"],
|
|
123
|
+
"env": {
|
|
124
|
+
"OPENPROJECT_URL": "https://openproject.example.com",
|
|
125
|
+
"OPENPROJECT_API_KEY": "your-api-key"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### From source
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
git clone https://github.com/kar-thik/openproject-mcp
|
|
136
|
+
cd openproject-mcp
|
|
137
|
+
uv sync
|
|
138
|
+
uv run openproject-mcp-server
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
Configuration is entirely environment-driven. The table below is the authoritative reference:
|
|
144
|
+
the server binds **exactly these 25 names and no others**. Bare, unprefixed names such as
|
|
145
|
+
`READ_TIMEOUT` or `API_KEY` are deliberately ignored (a stray variable in your shell cannot
|
|
146
|
+
change or break the server), as is any other unknown variable. A `.env` file in the server's
|
|
147
|
+
working directory is read with the same names; real environment variables take precedence.
|
|
148
|
+
From-source users can start from
|
|
149
|
+
[`.env.example`](https://github.com/kar-thik/openproject-mcp/blob/main/.env.example).
|
|
150
|
+
|
|
151
|
+
| Variable | Default | Purpose |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `OPENPROJECT_URL` | — (required) | Instance root URL, e.g. `https://openproject.example.com`. A trailing `/api/v3` is tolerated and stripped. |
|
|
154
|
+
| `OPENPROJECT_API_KEY` | — (required*) | API key from **My account → Access tokens**. Sent as HTTP Basic `apikey:<token>`. |
|
|
155
|
+
| `OPENPROJECT_OAUTH_TOKEN` | unset | OAuth bearer token, as an alternative to the API key. *One of the two credentials is required. |
|
|
156
|
+
| `OPENPROJECT_MCP_ACCEPT_LANGUAGE` | unset | Sent as the `Accept-Language` header; OpenProject localizes validation messages accordingly. |
|
|
157
|
+
| `OPENPROJECT_MCP_CA_BUNDLE` | 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. |
|
|
158
|
+
| `OPENPROJECT_MCP_READ_ONLY` | `false` | Serve read tools only: every write, destructive and admin tool is removed at startup. |
|
|
159
|
+
| `OPENPROJECT_MCP_ADMIN_TOOLS` | `false` | Expose the three admin-gated membership write tools (hidden by default). |
|
|
160
|
+
| `OPENPROJECT_MCP_DISABLE` | empty | Comma-separated group tags to remove whole tool groups at startup (see below). |
|
|
161
|
+
| `OPENPROJECT_MCP_INSECURE` | `false` | Allow `--transport http` to start without auth tokens. Local development only. |
|
|
162
|
+
| `OPENPROJECT_MCP_DOWNLOAD_DIR` | `./openproject-downloads` | Directory where `download_attachment` writes files (created if missing; default is relative to the server's working directory). |
|
|
163
|
+
| `OPENPROJECT_MCP_MAX_DOWNLOAD_MB` | `100` | Size cap for attachment downloads, in MiB. |
|
|
164
|
+
| `OPENPROJECT_MCP_CACHE_TTL` | `300` | TTL in seconds for the metadata cache (statuses, types, priorities, schemas). |
|
|
165
|
+
| `OPENPROJECT_MCP_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL` (case-insensitive). |
|
|
166
|
+
| `OPENPROJECT_MCP_LOG_FORMAT` | `text` | `text` or `json`. Logs always go to stderr (stdout belongs to the stdio transport). |
|
|
167
|
+
| `OPENPROJECT_MCP_LOG_BODIES` | `false` | Log request/response bodies — only at DEBUG level, with credentials redacted. Development use only. |
|
|
168
|
+
| `OPENPROJECT_MCP_OTEL` | `false` | Reserved for OpenTelemetry tracing. Accepted but not yet wired to anything in this release; setting it produces no traces. |
|
|
169
|
+
| `OPENPROJECT_MCP_HTTP_HOST` | `127.0.0.1` | Bind address for `--transport http`. |
|
|
170
|
+
| `OPENPROJECT_MCP_HTTP_PORT` | `8000` | Port for `--transport http`. |
|
|
171
|
+
| `OPENPROJECT_MCP_AUTH_TOKENS` | unset | Comma-separated bearer tokens accepted by `--transport http` (e.g. one per client). Every request must carry `Authorization: Bearer <token>` with one of them; anything else gets a 401. Without it (and without `OPENPROJECT_MCP_INSECURE=1`), the HTTP transport refuses to start. See [Transports](#transports). |
|
|
172
|
+
| `OPENPROJECT_MCP_CONNECT_TIMEOUT` | `10` | Seconds to wait for a TCP/TLS connection to OpenProject. |
|
|
173
|
+
| `OPENPROJECT_MCP_READ_TIMEOUT` | `30` | Seconds to wait for response data. |
|
|
174
|
+
| `OPENPROJECT_MCP_WRITE_TIMEOUT` | `60` | Seconds to wait while sending request data (uploads). |
|
|
175
|
+
| `OPENPROJECT_MCP_POOL_TIMEOUT` | `5` | Seconds to wait for a free connection from the pool. |
|
|
176
|
+
| `OPENPROJECT_MCP_MAX_CONNECTIONS` | `10` | Connection pool size toward OpenProject. |
|
|
177
|
+
| `OPENPROJECT_MCP_MAX_RETRIES` | `3` | Retry budget for idempotent requests. |
|
|
178
|
+
|
|
179
|
+
Secrets (`OPENPROJECT_API_KEY`, `OPENPROJECT_OAUTH_TOKEN`, `OPENPROJECT_MCP_AUTH_TOKENS`) are
|
|
180
|
+
held in memory as Pydantic `SecretStr` values and are never written to logs; the
|
|
181
|
+
`Authorization` header is redacted in every log record.
|
|
182
|
+
|
|
183
|
+
### Limiting what the model can do
|
|
184
|
+
|
|
185
|
+
Three settings shrink the tool surface at startup (the tool list is fixed for the lifetime of
|
|
186
|
+
the process):
|
|
187
|
+
|
|
188
|
+
- `OPENPROJECT_MCP_READ_ONLY=1` serves only the 35 read tools.
|
|
189
|
+
- `OPENPROJECT_MCP_ADMIN_TOOLS=1` reveals the three membership write tools
|
|
190
|
+
(`create_membership`, `update_membership`, `delete_membership`); they are hidden by default.
|
|
191
|
+
- `OPENPROJECT_MCP_DISABLE` drops whole groups to cut prompt cost, e.g.
|
|
192
|
+
`OPENPROJECT_MCP_DISABLE=meetings,news`. The valid group tags are:
|
|
193
|
+
`work_packages`, `wp_collaboration`, `attachments`, `git_activity`, `projects`, `queries`,
|
|
194
|
+
`notifications`, `time_entries`, `versions`, `people`, `metadata`, `meetings`, `wiki`,
|
|
195
|
+
`documents`, `budgets`, `news`, `reporting` — the same tags that head each section of the
|
|
196
|
+
tool catalog below.
|
|
197
|
+
|
|
198
|
+
Independent of all three, every destructive tool (the eight permanent deletes) requires an
|
|
199
|
+
explicit `confirm=true` argument before it acts.
|
|
200
|
+
|
|
201
|
+
## Transports
|
|
202
|
+
|
|
203
|
+
- **stdio** (default) — what Claude Code, Claude Desktop and most local MCP clients use.
|
|
204
|
+
Logs go to stderr; stdout carries only the protocol.
|
|
205
|
+
- **Streamable HTTP** — `openproject-mcp-server --transport http`. Binds `127.0.0.1:8000` by
|
|
206
|
+
default; override with `--host`/`--port` or `OPENPROJECT_MCP_HTTP_HOST`/
|
|
207
|
+
`OPENPROJECT_MCP_HTTP_PORT`. The HTTP transport refuses to start without authentication
|
|
208
|
+
configured: set `OPENPROJECT_MCP_AUTH_TOKENS` to a comma-separated list of bearer tokens,
|
|
209
|
+
or — for local development only — set `OPENPROJECT_MCP_INSECURE=1`.
|
|
210
|
+
|
|
211
|
+
With tokens configured, every HTTP request to the MCP endpoint must carry an
|
|
212
|
+
`Authorization: Bearer <token>` header whose token matches one of the configured values
|
|
213
|
+
(compared in constant time); requests with a missing, malformed or unknown token are
|
|
214
|
+
rejected with a 401 and a `WWW-Authenticate` header. Multiple tokens are supported —
|
|
215
|
+
for example one per client, so each can be revoked independently. Tokens must be ASCII,
|
|
216
|
+
and the endpoint applies no rate limiting, so use long random values — for example
|
|
217
|
+
`python -c "import secrets; print(secrets.token_urlsafe(32))"`.
|
|
218
|
+
`OPENPROJECT_MCP_INSECURE=1` remains a development-only escape hatch that leaves the
|
|
219
|
+
endpoint open without authentication.
|
|
220
|
+
|
|
221
|
+
The server does not terminate TLS, so tokens would otherwise cross the network in
|
|
222
|
+
plaintext: keep the default `127.0.0.1` bind, and if you must expose the HTTP transport
|
|
223
|
+
beyond localhost, put it behind a TLS-terminating reverse proxy (for example nginx or
|
|
224
|
+
Caddy).
|
|
225
|
+
|
|
226
|
+
## Tools
|
|
227
|
+
|
|
228
|
+
72 tools: 35 read, 34 write and 3 admin-gated writes. The admin tools stay hidden unless
|
|
229
|
+
`OPENPROJECT_MCP_ADMIN_TOOLS=1`; the 8 destructive tools additionally require `confirm=true`
|
|
230
|
+
on every call. Each section heading names the group tag accepted by
|
|
231
|
+
`OPENPROJECT_MCP_DISABLE`.
|
|
232
|
+
|
|
233
|
+
### Work packages (`work_packages`)
|
|
234
|
+
|
|
235
|
+
| Tool | Kind | What it does |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| `search_work_packages` | Read | Find work packages by text when you do not know their ids. |
|
|
238
|
+
| `list_work_packages` | Read | List work packages with structured filters — the workhorse read tool. |
|
|
239
|
+
| `get_work_package` | Read | Read one work package in full: description, dates, custom fields, parent and progress. |
|
|
240
|
+
| `create_work_package` | Write | Create a work package, validated through OpenProject's own form endpoint first. |
|
|
241
|
+
| `update_work_package` | Write | Change any writable field of a work package, with optimistic locking. |
|
|
242
|
+
| `delete_work_package` | Write (destructive) | Permanently delete a work package and everything attached to it. |
|
|
243
|
+
|
|
244
|
+
### Comments, relations, watchers, reminders (`wp_collaboration`)
|
|
245
|
+
|
|
246
|
+
| Tool | Kind | What it does |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `list_work_package_comments` | Read | Read the comment thread and change history of a work package. |
|
|
249
|
+
| `add_work_package_comment` | Write | Post a comment on a work package. |
|
|
250
|
+
| `edit_work_package_comment` | Write | Rewrite the text of an existing work-package comment. |
|
|
251
|
+
| `add_work_package_watcher` | Write | Subscribe a user to a work package's notifications. |
|
|
252
|
+
| `remove_work_package_watcher` | Write | Unsubscribe a user from a work package's notifications. |
|
|
253
|
+
| `create_work_package_relation` | Write | Link two work packages (blocks, follows, duplicates, relates, ...). |
|
|
254
|
+
| `update_work_package_relation` | Write | Change an existing relation's type, lag or description. |
|
|
255
|
+
| `delete_work_package_relation` | Write (destructive) | Remove the link between two work packages. |
|
|
256
|
+
| `toggle_comment_reaction` | Write | React to a work-package comment with an emoji, or take the reaction back. |
|
|
257
|
+
| `set_work_package_reminder` | Write | Set, change or clear your personal reminder on a work package. |
|
|
258
|
+
| `list_reminders` | Read | List your own upcoming work-package reminders. |
|
|
259
|
+
| `execute_custom_action` | Write | Run an instance-defined one-click action on a work package. |
|
|
260
|
+
|
|
261
|
+
### Attachments and file links (`attachments`)
|
|
262
|
+
|
|
263
|
+
| Tool | Kind | What it does |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| `list_attachments` | Read | List the files attached to one container. |
|
|
266
|
+
| `download_attachment` | Read | Download an attachment's bytes to a file on the machine running this server. |
|
|
267
|
+
| `upload_attachment` | Write | Attach a local file to a work package, wiki page, meeting, document, budget or comment. |
|
|
268
|
+
| `delete_attachment` | Write (destructive) | Permanently delete one attached file from OpenProject. |
|
|
269
|
+
| `list_file_links` | Read | List the external-storage files (Nextcloud, OneDrive/SharePoint) linked to a work package. |
|
|
270
|
+
|
|
271
|
+
### Git and pull requests (`git_activity`)
|
|
272
|
+
|
|
273
|
+
| Tool | Kind | What it does |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| `get_work_package_git_activity` | Read | Show the code behind a work package: commits, pull/merge requests and CI status. |
|
|
276
|
+
| `get_github_pull_request` | Read | Read one linked GitHub pull request in full, including its CI check runs. |
|
|
277
|
+
|
|
278
|
+
### Projects (`projects`)
|
|
279
|
+
|
|
280
|
+
| Tool | Kind | What it does |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| `list_projects` | Read | List projects, filtered server-side, one page at a time. |
|
|
283
|
+
| `get_project` | Read | Read one project in full. |
|
|
284
|
+
| `create_project` | Write | Create a project, validated through OpenProject's own form endpoint first. |
|
|
285
|
+
| `update_project` | Write | Change a project's name, description, visibility, parent, status or archived state. |
|
|
286
|
+
| `delete_project` | Write (destructive) | Schedule the permanent deletion of a project and everything inside it. |
|
|
287
|
+
| `copy_project` | Write | Copy a project — its settings, and optionally its work packages — into a new one. |
|
|
288
|
+
| `get_job_status` | Read | Check whether a background job (a project copy, a scheduled deletion) has finished. |
|
|
289
|
+
| `set_project_favorite` | Write | Add or remove a project from the authenticated user's favorites (OpenProject 17+). |
|
|
290
|
+
|
|
291
|
+
### Saved queries (`queries`)
|
|
292
|
+
|
|
293
|
+
| Tool | Kind | What it does |
|
|
294
|
+
|---|---|---|
|
|
295
|
+
| `list_queries` | Read | List the saved work-package views (queries) this user can open. |
|
|
296
|
+
| `run_query` | Read | Run a saved view and get its work packages as they are right now. |
|
|
297
|
+
| `save_query` | Write | Save a filter set as a reusable OpenProject view the whole team can open. |
|
|
298
|
+
|
|
299
|
+
### Notifications (`notifications`)
|
|
300
|
+
|
|
301
|
+
| Tool | Kind | What it does |
|
|
302
|
+
|---|---|---|
|
|
303
|
+
| `list_notifications` | Read | Read the authenticated user's OpenProject inbox. |
|
|
304
|
+
| `mark_notifications` | Write | Mark specific notifications read (or unread) in one bulk request. |
|
|
305
|
+
| `mark_all_notifications_read` | Write | Mark everything matching the filters as read — the whole inbox by default. |
|
|
306
|
+
|
|
307
|
+
### Time tracking (`time_entries`)
|
|
308
|
+
|
|
309
|
+
| Tool | Kind | What it does |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| `list_time_entries` | Read | List logged time, filtered server-side, with an optional accurate total. |
|
|
312
|
+
| `log_time` | Write | Book time against a work package or a project. |
|
|
313
|
+
| `update_time_entry` | Write | Correct an existing time entry. |
|
|
314
|
+
| `delete_time_entry` | Write (destructive) | Permanently delete a logged time entry. |
|
|
315
|
+
|
|
316
|
+
### Versions and sprints (`versions`)
|
|
317
|
+
|
|
318
|
+
| Tool | Kind | What it does |
|
|
319
|
+
|---|---|---|
|
|
320
|
+
| `list_versions` | Read | List versions (releases, milestones, sprints) you can assign work packages to. |
|
|
321
|
+
| `create_version` | Write | Create a version (release, milestone or sprint) inside a project. |
|
|
322
|
+
| `update_version` | Write | Change a version's name, dates, description, status or sharing. |
|
|
323
|
+
| `delete_version` | Write (destructive) | Permanently delete a version. |
|
|
324
|
+
|
|
325
|
+
### People and memberships (`people`)
|
|
326
|
+
|
|
327
|
+
| Tool | Kind | What it does |
|
|
328
|
+
|---|---|---|
|
|
329
|
+
| `search_principals` | Read | Find users, groups and placeholder users, and get their ids. |
|
|
330
|
+
| `get_user` | Read | Read one user's profile: name, login, email, admin flag and status. |
|
|
331
|
+
| `list_memberships` | Read | List who has access to which project, and with which roles. |
|
|
332
|
+
| `create_membership` | Admin write | Grant a principal one or more roles in a project. |
|
|
333
|
+
| `update_membership` | Admin write | Replace the roles of an existing membership. |
|
|
334
|
+
| `delete_membership` | Admin write (destructive) | Revoke a principal's access to a project. |
|
|
335
|
+
| `list_roles` | Read | List the roles this instance defines, with their ids. |
|
|
336
|
+
|
|
337
|
+
The three membership write tools require `OPENPROJECT_MCP_ADMIN_TOOLS=1` (and an OpenProject
|
|
338
|
+
account with the Manage members permission).
|
|
339
|
+
|
|
340
|
+
### Instance metadata and schemas (`metadata`)
|
|
341
|
+
|
|
342
|
+
| Tool | Kind | What it does |
|
|
343
|
+
|---|---|---|
|
|
344
|
+
| `get_instance_info` | Read | Check the OpenProject connection and report what this instance supports. |
|
|
345
|
+
| `get_project_metadata` | Read | List the ids and names (types, statuses, priorities, ...) that are actually valid on this instance. |
|
|
346
|
+
| `get_work_package_schema` | Read | Show which fields a work package of this type accepts in this project. |
|
|
347
|
+
| `list_permissions` | Read | List what the authenticated user is allowed to do, globally or in one project. |
|
|
348
|
+
|
|
349
|
+
### Meetings (`meetings`)
|
|
350
|
+
|
|
351
|
+
| Tool | Kind | What it does |
|
|
352
|
+
|---|---|---|
|
|
353
|
+
| `list_meetings` | Read | List meetings: what is coming up, what already ran. |
|
|
354
|
+
| `get_meeting` | Read | Read one meeting in full: participants, the agenda, and any recorded outcomes. |
|
|
355
|
+
| `create_meeting` | Write | Schedule a meeting in a project and optionally invite participants. |
|
|
356
|
+
| `add_meeting_agenda_item` | Write | Add one item to a meeting's agenda, optionally pinned to a work package. |
|
|
357
|
+
|
|
358
|
+
### Wiki (`wiki`)
|
|
359
|
+
|
|
360
|
+
| Tool | Kind | What it does |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `get_wiki_page` | Read | Read a wiki page's identity and project — not its content (API v3 does not expose page bodies). |
|
|
363
|
+
|
|
364
|
+
### Documents (`documents`)
|
|
365
|
+
|
|
366
|
+
| Tool | Kind | What it does |
|
|
367
|
+
|---|---|---|
|
|
368
|
+
| `list_documents` | Read | List the documents visible to you, across every project. |
|
|
369
|
+
| `get_document` | Read | Read one document with its full description text. |
|
|
370
|
+
|
|
371
|
+
### Budgets (`budgets`)
|
|
372
|
+
|
|
373
|
+
| Tool | Kind | What it does |
|
|
374
|
+
|---|---|---|
|
|
375
|
+
| `list_budgets` | Read | List a project's budgets — their ids and names, which is all API v3 exposes. |
|
|
376
|
+
|
|
377
|
+
### News (`news`)
|
|
378
|
+
|
|
379
|
+
| Tool | Kind | What it does |
|
|
380
|
+
|---|---|---|
|
|
381
|
+
| `list_news` | Read | List project news — the announcements a team publishes on its project overview. |
|
|
382
|
+
| `get_news` | Read | Read one news entry in full, including the markdown body. |
|
|
383
|
+
| `create_news` | Write | Publish a news announcement in a project. |
|
|
384
|
+
| `update_news` | Write | Correct or rewrite a published news entry. |
|
|
385
|
+
| `delete_news` | Write (destructive) | Permanently delete a news entry. |
|
|
386
|
+
|
|
387
|
+
### Reporting (`reporting`)
|
|
388
|
+
|
|
389
|
+
| Tool | Kind | What it does |
|
|
390
|
+
|---|---|---|
|
|
391
|
+
| `get_project_report_data` | Read | Aggregate everything a status report needs about one project and one date window. |
|
|
392
|
+
|
|
393
|
+
## Prompts and resources
|
|
394
|
+
|
|
395
|
+
Four prompt templates render live OpenProject data into ready-to-use briefings:
|
|
396
|
+
|
|
397
|
+
- **weekly_report** — a weekly status report for one project: done / in progress / planned,
|
|
398
|
+
hours and impediments.
|
|
399
|
+
- **daily_standup** — today's standup for one project: yesterday's movement, what is due
|
|
400
|
+
today, and what is blocked.
|
|
401
|
+
- **triage_inbox** — groups your unread notifications by reason and suggests actions.
|
|
402
|
+
- **groom_backlog** — sweeps a project's open backlog for unassigned, stale and unestimated
|
|
403
|
+
work.
|
|
404
|
+
|
|
405
|
+
Three resource templates expose OpenProject objects at stable URIs:
|
|
406
|
+
|
|
407
|
+
- `openproject://work_package/{id}` — one work package as JSON.
|
|
408
|
+
- `openproject://project/{identifier}` — one project as JSON (identifier slug or numeric id).
|
|
409
|
+
- `openproject://attachment/{id}` — the attachment's bytes with the detected MIME type.
|
|
410
|
+
|
|
411
|
+
## Supported OpenProject versions
|
|
412
|
+
|
|
413
|
+
The server targets OpenProject **14 LTS through 17.x**. Instead of assuming one API dialect,
|
|
414
|
+
it probes the instance lazily on first need and caches the result for an hour. The
|
|
415
|
+
version-dependent surfaces:
|
|
416
|
+
|
|
417
|
+
- **Internal (private) comments** need OpenProject >= 16. Older servers silently ignore the
|
|
418
|
+
internal flag, so below 16 the server refuses with a clear error rather than posting a
|
|
419
|
+
comment publicly that you asked to keep internal.
|
|
420
|
+
- **Emoji reactions** (`toggle_comment_reaction`) need OpenProject >= 16; detected from the
|
|
421
|
+
version, tolerant of a 404 at call time.
|
|
422
|
+
- **Project favorites** (`set_project_favorite`) need OpenProject >= 17; same tolerance.
|
|
423
|
+
- **Time-entry filters**: the work-package filter is probed (`entityId`, falling back to the
|
|
424
|
+
pre-15.x `workPackage` spelling).
|
|
425
|
+
- **Permission contexts** (`list_permissions`): the context prefix is probed (`p{id}`, falling
|
|
426
|
+
back to the `w{id}` spelling introduced in 17.2).
|
|
427
|
+
- **Meetings time filter**: OpenProject 17.6 changed the wire dialect; `list_meetings`
|
|
428
|
+
discovers which spelling the instance accepts and caches it.
|
|
429
|
+
|
|
430
|
+
Features that depend on optional instance modules (meetings, news, documents, budgets, wiki,
|
|
431
|
+
backlogs, GitHub/GitLab integration, external storages) degrade honestly when the module is
|
|
432
|
+
absent: list tools return an empty page with an in-band note naming the missing module, and
|
|
433
|
+
detail tools return a structured error explaining both possible readings of the 404.
|
|
434
|
+
|
|
435
|
+
## Security
|
|
436
|
+
|
|
437
|
+
- The API key and OAuth token are held as `SecretStr` and never logged; `Authorization` and
|
|
438
|
+
cookie headers are redacted from every log record. Request/response bodies are only logged
|
|
439
|
+
at DEBUG level with `OPENPROJECT_MCP_LOG_BODIES=1`, for development.
|
|
440
|
+
- The `Authorization` header is stripped whenever a redirect leaves the OpenProject origin —
|
|
441
|
+
attachment downloads redirect to presigned object-storage URLs, and the credential must not
|
|
442
|
+
travel there.
|
|
443
|
+
- TLS is always verified. Private CAs are supported via `OPENPROJECT_MCP_CA_BUNDLE`; an
|
|
444
|
+
insecure-TLS switch deliberately does not exist.
|
|
445
|
+
- Attachment downloads respect OpenProject's virus scanner: quarantined files are never
|
|
446
|
+
fetched and produce a structured `attachment_quarantined` error. Downloads are capped by
|
|
447
|
+
`OPENPROJECT_MCP_MAX_DOWNLOAD_MB`, stored under sanitized file names (directory separators
|
|
448
|
+
and traversal sequences are neutralized), and reported with a SHA-256 of the bytes.
|
|
449
|
+
- Destructive tools require `confirm=true` and are annotated so clients can ask the user
|
|
450
|
+
first.
|
|
451
|
+
- The HTTP transport refuses to start without `OPENPROJECT_MCP_AUTH_TOKENS` configured and
|
|
452
|
+
verifies the `Authorization: Bearer` header on every request — missing or invalid tokens
|
|
453
|
+
are rejected with a 401, and token comparison is constant-time. The server does not
|
|
454
|
+
terminate TLS: keep the default `127.0.0.1` bind, and put a TLS-terminating reverse proxy
|
|
455
|
+
in front if the port must be reachable from anywhere else (see [Transports](#transports)).
|
|
456
|
+
|
|
457
|
+
## Troubleshooting
|
|
458
|
+
|
|
459
|
+
| Symptom | What it means and what to do |
|
|
460
|
+
|---|---|
|
|
461
|
+
| Server exits with "cannot start, configuration is incomplete" | Run `openproject-mcp-server --check` and fix the problems it lists; the variable reference is the [Configuration](#configuration) section above. |
|
|
462
|
+
| 401 authentication_failed | The API key is wrong, revoked, or belongs to a blocked account. Generate a fresh token under **My account → Access tokens**. |
|
|
463
|
+
| 403 permission_denied | The account is authenticated but lacks a role permission for that project, or the relevant module is disabled for it. |
|
|
464
|
+
| 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. |
|
|
465
|
+
| TLS certificate errors | Your instance uses a private CA: point `OPENPROJECT_MCP_CA_BUNDLE` at its PEM bundle. There is no option to disable verification. |
|
|
466
|
+
| `--transport http` refuses to start | Set `OPENPROJECT_MCP_AUTH_TOKENS` (comma-separated bearer tokens), or `OPENPROJECT_MCP_INSECURE=1` for local development only — see [Transports](#transports). |
|
|
467
|
+
| 401 from the MCP HTTP endpoint itself | The request's `Authorization: Bearer <token>` header is missing or does not match any configured `OPENPROJECT_MCP_AUTH_TOKENS` entry. Check the client's token for typos; distinct from the upstream `401 authentication_failed` above, which is about the OpenProject API key. |
|
|
468
|
+
| Proxies | Standard `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` variables are honored by the underlying HTTP client. |
|
|
469
|
+
|
|
470
|
+
When reporting an issue, please include your OpenProject version, the output of
|
|
471
|
+
`openproject-mcp-server --version`, and the output of `openproject-mcp-server --check`.
|
|
472
|
+
|
|
473
|
+
## Development
|
|
474
|
+
|
|
475
|
+
```sh
|
|
476
|
+
git clone https://github.com/kar-thik/openproject-mcp
|
|
477
|
+
cd openproject-mcp
|
|
478
|
+
uv sync --group dev
|
|
479
|
+
uv run pytest # entire suite runs offline (respx-mocked HTTP), no instance needed
|
|
480
|
+
uv run ruff check .
|
|
481
|
+
uv run ruff format --check .
|
|
482
|
+
uv run pyright # strict mode
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
The suite currently contains no tests that need a live OpenProject instance; an opt-in
|
|
486
|
+
`integration` marker is registered and reserved for any that are added later. The
|
|
487
|
+
full technical specification lives in
|
|
488
|
+
[SPEC.md](https://github.com/kar-thik/openproject-mcp/blob/main/SPEC.md).
|
|
489
|
+
|
|
490
|
+
Releasing is documented in
|
|
491
|
+
[CONTRIBUTING.md](https://github.com/kar-thik/openproject-mcp/blob/main/CONTRIBUTING.md).
|
|
492
|
+
|
|
493
|
+
## License and trademark
|
|
494
|
+
|
|
495
|
+
MIT — see [LICENSE](https://github.com/kar-thik/openproject-mcp/blob/main/LICENSE).
|
|
496
|
+
|
|
497
|
+
This is a community project. It is not affiliated with, endorsed by, or supported by
|
|
498
|
+
OpenProject GmbH. "OpenProject" is a trademark of OpenProject GmbH and is used here only to
|
|
499
|
+
indicate interoperability.
|