openproject-ce-mcp 0.2.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.
Files changed (46) hide show
  1. openproject_ce_mcp-0.2.0/.gitignore +38 -0
  2. openproject_ce_mcp-0.2.0/.mcp.json.example +40 -0
  3. openproject_ce_mcp-0.2.0/CHANGELOG.md +253 -0
  4. openproject_ce_mcp-0.2.0/LICENSE +21 -0
  5. openproject_ce_mcp-0.2.0/PKG-INFO +482 -0
  6. openproject_ce_mcp-0.2.0/README.md +431 -0
  7. openproject_ce_mcp-0.2.0/configure_mcp.py +34 -0
  8. openproject_ce_mcp-0.2.0/docs/architecture.md +173 -0
  9. openproject_ce_mcp-0.2.0/docs/claude-desktop.md +100 -0
  10. openproject_ce_mcp-0.2.0/docs/claude.md +144 -0
  11. openproject_ce_mcp-0.2.0/docs/codex.md +114 -0
  12. openproject_ce_mcp-0.2.0/docs/cursor.md +50 -0
  13. openproject_ce_mcp-0.2.0/docs/github.md +116 -0
  14. openproject_ce_mcp-0.2.0/docs/tools.md +272 -0
  15. openproject_ce_mcp-0.2.0/img/claude.jpg +0 -0
  16. openproject_ce_mcp-0.2.0/img/codex.jpg +0 -0
  17. openproject_ce_mcp-0.2.0/img/cursor.jpg +0 -0
  18. openproject_ce_mcp-0.2.0/img/github.jpg +0 -0
  19. openproject_ce_mcp-0.2.0/img/openproject-ce-mcp-hero.jpg +0 -0
  20. openproject_ce_mcp-0.2.0/pyproject.toml +111 -0
  21. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/__init__.py +3 -0
  22. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/client.py +6860 -0
  23. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/config.py +340 -0
  24. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/models.py +1355 -0
  25. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/server.py +111 -0
  26. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/setup_cli.py +1016 -0
  27. openproject_ce_mcp-0.2.0/src/openproject_ce_mcp/tools.py +3006 -0
  28. openproject_ce_mcp-0.2.0/tests/fixtures/spec.md +1 -0
  29. openproject_ce_mcp-0.2.0/tests/integration/__init__.py +0 -0
  30. openproject_ce_mcp-0.2.0/tests/integration/conftest.py +131 -0
  31. openproject_ce_mcp-0.2.0/tests/integration/test_boards.py +23 -0
  32. openproject_ce_mcp-0.2.0/tests/integration/test_memberships.py +41 -0
  33. openproject_ce_mcp-0.2.0/tests/integration/test_meta.py +69 -0
  34. openproject_ce_mcp-0.2.0/tests/integration/test_news.py +55 -0
  35. openproject_ce_mcp-0.2.0/tests/integration/test_projects.py +55 -0
  36. openproject_ce_mcp-0.2.0/tests/integration/test_semantic_identifiers.py +64 -0
  37. openproject_ce_mcp-0.2.0/tests/integration/test_time_entries.py +75 -0
  38. openproject_ce_mcp-0.2.0/tests/integration/test_versions.py +54 -0
  39. openproject_ce_mcp-0.2.0/tests/integration/test_work_packages.py +147 -0
  40. openproject_ce_mcp-0.2.0/tests/test_client.py +5650 -0
  41. openproject_ce_mcp-0.2.0/tests/test_config.py +254 -0
  42. openproject_ce_mcp-0.2.0/tests/test_configure_mcp.py +599 -0
  43. openproject_ce_mcp-0.2.0/tests/test_registry.py +61 -0
  44. openproject_ce_mcp-0.2.0/tests/test_server.py +232 -0
  45. openproject_ce_mcp-0.2.0/tests/test_tools.py +1428 -0
  46. openproject_ce_mcp-0.2.0/uv.lock +1066 -0
@@ -0,0 +1,38 @@
1
+ # Python / build
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+
7
+ # Test / coverage
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ htmlcov/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+
16
+ # Type checking
17
+ .mypy_cache/
18
+
19
+ # Local credentials and config
20
+ .env
21
+ .mcp.json
22
+ /AGENTS.md
23
+
24
+ # Tool-local state
25
+ .claude/
26
+ .codex/
27
+ .vscode/
28
+ .DS_Store
29
+ CONTINUE.md
30
+
31
+ # Local backups
32
+ *.bak.*
33
+
34
+ # OpenProject source clones for the API source-check tool (tools/api-check/)
35
+ .op-sources/
36
+
37
+ # Internal review notes
38
+ SELF_REVIEW.md
@@ -0,0 +1,40 @@
1
+ {
2
+ "mcpServers": {
3
+ "openproject": {
4
+ "command": "openproject-ce-mcp",
5
+ "env": {
6
+ "OPENPROJECT_BASE_URL": "https://op.example.com",
7
+ "OPENPROJECT_API_TOKEN": "replace-with-your-token",
8
+
9
+ "OPENPROJECT_ALLOWED_PROJECTS_READ": "*",
10
+ "OPENPROJECT_ALLOWED_PROJECTS_WRITE": "",
11
+
12
+ "OPENPROJECT_ENABLE_PROJECT_READ": "true",
13
+ "OPENPROJECT_ENABLE_WORK_PACKAGE_READ": "true",
14
+ "OPENPROJECT_ENABLE_MEMBERSHIP_READ": "true",
15
+ "OPENPROJECT_ENABLE_VERSION_READ": "true",
16
+ "OPENPROJECT_ENABLE_BOARD_READ": "true",
17
+
18
+ "OPENPROJECT_HIDE_PROJECT_FIELDS": "",
19
+ "OPENPROJECT_HIDE_WORK_PACKAGE_FIELDS": "",
20
+ "OPENPROJECT_HIDE_ACTIVITY_FIELDS": "",
21
+ "OPENPROJECT_HIDE_CUSTOM_FIELDS": "",
22
+
23
+ "OPENPROJECT_ENABLE_ADMIN_WRITE": "false",
24
+
25
+ "OPENPROJECT_ENABLE_PROJECT_WRITE": "false",
26
+ "OPENPROJECT_ENABLE_WORK_PACKAGE_WRITE": "false",
27
+ "OPENPROJECT_ENABLE_MEMBERSHIP_WRITE": "false",
28
+ "OPENPROJECT_ENABLE_VERSION_WRITE": "false",
29
+ "OPENPROJECT_ENABLE_BOARD_WRITE": "false",
30
+
31
+ "OPENPROJECT_TIMEOUT": "12",
32
+ "OPENPROJECT_VERIFY_SSL": "true",
33
+ "OPENPROJECT_DEFAULT_PAGE_SIZE": "20",
34
+ "OPENPROJECT_MAX_PAGE_SIZE": "50",
35
+ "OPENPROJECT_MAX_RESULTS": "100",
36
+ "OPENPROJECT_LOG_LEVEL": "WARNING"
37
+ }
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,253 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. Versions
4
+ follow [semantic versioning](https://semver.org); 0.1.0 is the first tagged
5
+ release and 0.0.1 is the development baseline.
6
+
7
+ ---
8
+
9
+ ## 0.2.0 (unreleased)
10
+
11
+ First release published to PyPI. Supersedes the never-released 0.1.1 (its
12
+ package-rename and installer fixes are folded in here).
13
+
14
+ ### Added
15
+
16
+ - **PyPI distribution.** The package is installable with `pip` / `pipx` /
17
+ `uv tool install openproject-ce-mcp`. A GitHub Actions workflow publishes to
18
+ PyPI on a version tag via trusted publishing (OIDC, no stored token).
19
+ - **`openproject-ce-mcp configure` setup command** (plus the
20
+ `openproject-ce-mcp-setup` alias), shipped in the installed package. It
21
+ registers the server with detected MCP clients and writes `.mcp.json`. Scope is
22
+ auto-detected — a project directory gets a local `.mcp.json`, elsewhere the
23
+ server is registered user-wide — and can be forced with `--local` / `--global`.
24
+ - Top-level CLI: `openproject-ce-mcp --help` / `--version`; running with no
25
+ arguments still starts the stdio server, unchanged for MCP clients.
26
+ - `check_api.py --constants` verifies hardcoded enum/constant values (emoji
27
+ reactions, version statuses and their operators) against the OpenProject source
28
+ across versions, catching a value rename the presence check would miss.
29
+
30
+ ### Changed
31
+
32
+ - Renamed the package to **openproject-ce-mcp** (distribution name, import
33
+ package `openproject_ce_mcp`, and the `openproject-ce-mcp` command). The PyPI
34
+ name `openproject-mcp` is taken by an unrelated project; the new name is free
35
+ and states the Community-Edition focus. The MCP server key stays `openproject`,
36
+ so existing client configs do not change.
37
+ - Documentation leads with the PyPI install path; the `curl … | sh` source
38
+ installer is kept as an alternative. Uninstall is documented per install type.
39
+ - The `User-Agent` header now derives from the package version instead of a
40
+ hardcoded string.
41
+
42
+ ### Fixed
43
+
44
+ - The `curl … | sh` installer no longer crashes with `EOFError` on the first
45
+ prompt: `get.sh` attaches the controlling terminal, and the prompt helpers fall
46
+ back to defaults when stdin is not interactive.
47
+ - Re-running `configure --global` pre-fills from an existing client registration
48
+ instead of demanding the base URL and token again.
49
+ - `configure` warns before writing a token-bearing `.mcp.json` into an unrelated
50
+ project directory, and when the server command cannot be resolved to an absolute
51
+ path (which would fail for GUI clients that do not inherit the shell `PATH`).
52
+ - The Docker integration-test harness (`docker/test/up.sh`) runs on the Bash 3.2
53
+ that ships with macOS (no `declare -A`).
54
+
55
+ ---
56
+
57
+ ## 0.1.0 – 2026-07-01
58
+
59
+ ### Compatibility
60
+
61
+ - Reviewed for compatibility with OpenProject 17.5.1 / 17.5.0. No breaking API change
62
+ affects this server. The 17.5 change that replaces the `X-Requested-With` header check with
63
+ `Sec-Fetch-Site` applies to session authentication only; this server authenticates
64
+ with an API token (HTTP Basic auth) and is unaffected. The 17.4.1 security fixes
65
+ touch meeting, journal, and baseline endpoints that this server does not use.
66
+ - Verified against OpenProject 16.6 (classic), 17.4 (displayId), and 17.5 (semantic)
67
+ via the local Docker matrix, plus a source-level API audit across 16.0–17.5.
68
+
69
+ ### Added
70
+
71
+ - Single work package tools now accept a project-prefixed identifier (e.g. `PROJ-123`)
72
+ in addition to the numeric id (sent as either a number or a string); the bulk tools
73
+ remain numeric-only. OpenProject 17.5 lets administrators switch the displayed
74
+ identifier to a project-based format exposed via `displayId`, and its
75
+ `work_packages/{id}` endpoints resolve that form server-side. References are passed
76
+ through to the endpoint verbatim, so the behaviour degrades cleanly: on instances
77
+ without semantic identifiers a project-prefixed reference simply yields a 404
78
+ (surfaced as not-found), while numeric ids keep working on every supported version.
79
+ - Relation and parent writes resolve a project-prefixed reference to the numeric id
80
+ before building the HAL link, since link hrefs are not resolved by `displayId`.
81
+ - Interactive setup can detect installed MCP clients (Claude Code, Claude Desktop,
82
+ Codex, Cursor, VS Code/Copilot) and register the server in a client's user-wide
83
+ config. Registration merges rather than overwrites, backing up the existing file.
84
+ - `uninstall.sh` / `uninstall.ps1` and a `configure_mcp.py --uninstall` mode remove
85
+ the `openproject` entry from client configs (keeping other servers, with backups)
86
+ and clean up the local environment.
87
+ - `OPENPROJECT_ATTACHMENT_ROOT` confines attachment uploads to a directory (default:
88
+ the working directory); files outside it, and credential/config files such as
89
+ `.mcp.json` / `.env` / private keys even inside it, are refused.
90
+
91
+ ### Security
92
+
93
+ - Attachment uploads can no longer read arbitrary local files, closing a
94
+ credential-exfiltration path.
95
+ - `list_relations` is gated by the read scope and filtered by the project read
96
+ allowlist on both linked work packages; `update_relation`, `update_reminder`, and
97
+ `delete_reminder` apply the project write allowlist; `copy_project` validates its
98
+ destination; hidden work-package subjects no longer leak through relation tools.
99
+ - `OPENPROJECT_AUTO_CONFIRM_DELETE` now correctly governs the preview step for all
100
+ destructive deletes.
101
+
102
+ ### Docs
103
+
104
+ - Onboarding docs reworked: install-once/register-per-client model, per-client
105
+ config matrix, per-OS paths, verification steps, and gitignore reminders. Added a
106
+ Cursor guide and a generic "any other MCP client" note.
107
+
108
+ ---
109
+
110
+ ## 0.0.1 (development baseline)
111
+
112
+ Initial development baseline. The pre-release history is kept below as dated
113
+ milestones.
114
+
115
+ ### 2026-05-18
116
+
117
+ #### Compatibility
118
+
119
+ - Verified against OpenProject 17.4. No breaking API changes in 17.4.
120
+ - Work package responses now expose a `display_id` field (`displayId` in the API),
121
+ introduced in 17.4 as preparation for project-based identifiers in 17.5.
122
+ The numeric `id` remains the canonical identifier for all tool parameters; `display_id`
123
+ is informational and may show a project-prefixed form (e.g. `ABC-42`) once 17.5 is deployed.
124
+
125
+ #### Fixes
126
+
127
+ - Authentication header changed from `Bearer <token>` to `Basic base64(apikey:<token>)`,
128
+ aligning with the OpenProject API documentation. Both formats are accepted by OpenProject;
129
+ this change makes the implementation spec-compliant.
130
+
131
+ #### Bug fixes
132
+
133
+ - `list_work_packages`, `list_my_open_work_packages`, `list_versions`, and `list_projects`
134
+ now report `total` and `count` consistently when the read allowlist filters items out
135
+ of the API response. Previously `total` reflected the unfiltered server count while
136
+ `count`/`results` reflected the filtered set, producing responses like
137
+ `{"total": 8, "count": 0, "results": []}`. `next_offset` and `truncated` continue to
138
+ follow server-side pagination so callers still walk every page that may contain
139
+ allowed items.
140
+ - `list_work_packages` without an explicit `project` argument now correctly filters
141
+ results to allowed projects when `OPENPROJECT_ALLOWED_PROJECTS_READ` is restricted.
142
+ Previously the API returned all visible work packages and client-side filtering was
143
+ applied per-page, causing `total` to be unreliable. The server is now given a
144
+ project-id filter so only allowed work packages are returned from the start.
145
+ - Allowlist matching now resolves project names and hyphenated display names to their
146
+ canonical identifiers at startup, so HAL links that carry only the project id are
147
+ correctly matched against name-based allowlist entries.
148
+
149
+ #### Configuration
150
+
151
+ - `OPENPROJECT_ALLOWED_PROJECTS_READ` now accepts glob patterns in addition to exact
152
+ identifiers and names (e.g. `team-*` matches `team-alpha`, `team-beta`).
153
+
154
+ ---
155
+
156
+ ### 2026-04-08
157
+
158
+ #### Tools
159
+
160
+ - **Projects** — list, get, create, copy (with background job tracking), update, delete;
161
+ read admin context, project configuration, and lifecycle phase definitions/instances
162
+ - **Work packages** — list with structured filters (`project`, `type`, `version`,
163
+ `has_description`); free-text search with optional `project`, `status`, `open_only`,
164
+ `assignee_me` filters; get, create, subtask, update, delete; add comments; create/delete
165
+ relations; get relations and activity log; bulk create and bulk update; list own open
166
+ work packages
167
+ - **Watchers** — list, add, remove
168
+ - **Attachments** — list, get, upload, delete
169
+ - **File links** — list, delete (Nextcloud CE integration)
170
+ - **Time entries** — list, get, create, update, delete; list available activities
171
+ - **Versions** — list (global or project-scoped), get, create, update, delete
172
+ - **Boards** — list, get, create (basic and grouped), update, delete; list saved views,
173
+ get view
174
+ - **Memberships** — list, get, create, update, delete; list roles and principals; get
175
+ current user's project access
176
+ - **Users** — get current user; list, get, create, update, delete, lock, unlock
177
+ - **Groups** — list, get, create, update (full member-list replacement with add/remove
178
+ helpers), delete
179
+ - **Documents** — list, get, update (no create/delete endpoint in CE API)
180
+ - **News** — list, get, create, update, delete
181
+ - **Wiki pages** — get single page by id; no list tool (CE API v3 has no collection
182
+ endpoint — `GET /api/v3/projects/{id}/wiki_pages` is not implemented)
183
+ - **Categories** — list, get (no write API in CE)
184
+ - **Notifications** — list, mark single read, mark all read
185
+ - **Grids** — list, get, create, update, delete
186
+ - **User preferences** — get, update (always available — no write gate required)
187
+ - **Instance configuration** — get
188
+ - **Query metadata** — get filter, column, operator, sort-by; list/get filter-instance
189
+ schemas
190
+ - **Help texts** — list, get
191
+ - **Working days** — list working-day configuration; list non-working days
192
+ - **Custom options** — get
193
+ - **Relations (global)** — list, update
194
+ - **Actions & capabilities** — list
195
+ - **Text rendering** — render markdown or plain text to HTML via OpenProject API
196
+
197
+ #### Permission model
198
+
199
+ - Scoped read flags per chain: `OPENPROJECT_ENABLE_PROJECT_READ`,
200
+ `OPENPROJECT_ENABLE_WORK_PACKAGE_READ`, `OPENPROJECT_ENABLE_MEMBERSHIP_READ`,
201
+ `OPENPROJECT_ENABLE_VERSION_READ`, `OPENPROJECT_ENABLE_BOARD_READ` (all default `true`)
202
+ - Scoped write flags per chain: `OPENPROJECT_ENABLE_PROJECT_WRITE`,
203
+ `OPENPROJECT_ENABLE_WORK_PACKAGE_WRITE`, `OPENPROJECT_ENABLE_MEMBERSHIP_WRITE`,
204
+ `OPENPROJECT_ENABLE_VERSION_WRITE`, `OPENPROJECT_ENABLE_BOARD_WRITE` (all default `false`)
205
+ - `OPENPROJECT_ENABLE_ADMIN_WRITE` — dedicated opt-in for instance-wide user and group
206
+ management; never activated by project-scoped write flags (default `false`)
207
+ - No global shortcut flags — each scope must be enabled explicitly
208
+ - Two-layer safety model: MCP env-var gates (ceiling) + OpenProject server-side role
209
+ permissions (final authority); a `403` from OpenProject surfaces as a tool error
210
+
211
+ #### Architecture
212
+
213
+ - Five-module layout: `server.py`, `config.py`, `client.py`, `models.py`, `tools.py`
214
+ - All policy logic (read gates, write gates, project scoping, field hiding) concentrated
215
+ in `client.py` for easier security review
216
+ - Preview/confirm two-step pattern for all writes and deletes; bypassable globally via
217
+ `OPENPROJECT_AUTO_CONFIRM_WRITE` or per class via `OPENPROJECT_AUTO_CONFIRM_DELETE`
218
+ - Project allowlists matched case-insensitively against identifier, name, and numeric ID;
219
+ hyphenated name variant tested for HAL-embedded links
220
+ - Field hiding per entity type via `OPENPROJECT_HIDE_<ENTITY>_FIELDS`; hidden fields are
221
+ rejected on writes too
222
+ - HAL responses normalized into compact dataclasses; raw payloads never forwarded to MCP
223
+ clients
224
+ - Pagination bounded by `OPENPROJECT_DEFAULT_PAGE_SIZE`, `OPENPROJECT_MAX_PAGE_SIZE`,
225
+ `OPENPROJECT_MAX_RESULTS`
226
+ - Form validation against OpenProject schema endpoints before create/update writes
227
+
228
+ #### Test coverage
229
+
230
+ - 152 unit tests (httpx mock transport, no network)
231
+ - Integration test suite (`tests/integration/`) against a live OpenProject instance;
232
+ excluded from the default run, opt in with `-m integration`
233
+
234
+ #### Scope
235
+
236
+ - Community Edition only — Enterprise features (Placeholder Users, Budgets, Portfolios,
237
+ Programs, Custom Actions, Baseline Comparisons) are not implemented
238
+ - Nextcloud file links included (CE feature; returns empty list gracefully if Nextcloud
239
+ not connected)
240
+ - Project lifecycle phases included (read-only; degrades gracefully if unavailable)
241
+
242
+ #### Known API notes
243
+
244
+ - `GET /api/v3/projects/{id}/wiki_pages` is not implemented in OpenProject v3;
245
+ `list_wiki_pages` is therefore not provided. Individual pages are accessible via
246
+ `get_wiki_page`.
247
+ - Project-scoped endpoints for work packages and versions are deprecated in OpenProject
248
+ 17.2 in favour of workspace-scoped alternatives; the deprecated paths remain in use as
249
+ the workspace-scoped alternatives are not yet stable in CE.
250
+ - Relations use the canonical `/api/v3/relations` endpoint with a filter instead of the
251
+ redirecting project-scoped path.
252
+ - Groups PATCH requires a complete `_links.members` array (full replacement); the client
253
+ fetches the current list and applies adds/removes before sending.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jürgen Tauschl
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.