Status: Work in progress (WIP). Contributions are welcome while the tool surface and integration story stabilise.
OpenProject MCP Server
A Model Context Protocol (MCP) server that gives AI assistants high-fidelity access to OpenProject API v3. The server exposes curated tools for project discovery, work package collaboration, time tracking, attachments, wiki content, and user resolution—all through a fast asynchronous Python stack optimised for LLM workflows.
Table of Contents
- Overview
- Architecture at a Glance
- Tool Catalog
- Repository Layout
- Quickstart
- Configuration
- Running the Server
- Working With MCP Clients
- Testing & Quality
- Troubleshooting
- Known Limitations & Roadmap
- Contributing
- License
Overview
- Purpose-built for MCP: Ships with a
FastMCPimplementation that can be dropped into Claude Desktop, Cursor, modelcontextclient, or any other MCP host. - Async & resilient: Uses
httpxwith request retries, exponential backoff, timeout tuning, and sanitised error handling. - Rich tool surface: LLMs can comment on work packages, search across content, manage attachments, resolve users, log time, and query wiki pages.
- Typed contracts: Pydantic models validate every tool input to protect the OpenProject API from malformed prompts.
- Pyproject-only tooling:
uvdrives dependency management, ensuring reproducible environments and quick install times.
Architecture at a Glance
| Layer | Purpose | | --- | --- | | openproject_mcp.main | Entry point that configures logging, loads settings, and boots the MCP server on stdio. | | openproject_mcp.server | Creates the FastMCP instance, registers system_ping, and loads all domain-specific tool modules. | | openproject_mcp.config.Settings | Pydantic settings model fed by .env/environment variables (URL, token, timeouts, pagination defaults). | | openproject_mcp.client.OpenProjectClient | Async HTTP client with retry/backoff logic, uniform headers, and error mapping to domain exceptions. | | openproject_mcp.tools.* | Tool implementations grouped by OpenProject domain (work packages, attachments, queries, time entries, users, wiki, projects). | | tests/ | Extensive async pytest suite covering positive flows, permission failures, validation errors, and stdio smoke tests. |
Tool Catalog
Each tool is exposed via the MCP protocol—LLMs call them exactly as defined here.
System
system_ping: Quick readiness check (returns{"ok": true}) to let MCP clients validate connectivity.
Work Packages (src/openproject_mcp/tools/work_packages.py)
add_comment: Post Markdown comments with optional watcher notifications.search_content: Search work packages and/or projects, optionally capturing attachment matches.append_work_package_description: Append Markdown to an existing description while handling optimistic locking.get_work_package_statuses: List all statuses available to the authenticated user.get_work_package_types: List every type globally or scoped to a project.resolve_status: Turn a human status name into an ID/disambiguation payload.resolve_type: Resolve a type name inside a project, including fallback when the type exists but is disabled there.
Attachments (src/openproject_mcp/tools/attachments.py)
attach_file_to_wp: Upload local files to work packages via multipart/form-data.list_attachments: Enumerate attachments on a work package.download_attachment: Download binary content (optionally persisting to disk) with base64 responses for in-memory use.get_attachment_content: Fetch metadata plus a preview slice using HTTP range requests to save bandwidth.
Projects (src/openproject_mcp/tools/projects.py)
get_project_memberships: Fetch project member/role mappings with pagination and multi-page follow mode.resolve_project: Resolve names/identifiers with disambiguation hints so LLMs can pick the right project.
Queries (src/openproject_mcp/tools/queries.py)
list_queries: List saved queries (global or project-scoped).run_query: Execute saved queries and support prompt-time filter overrides.
Time Entries (src/openproject_mcp/tools/time_entries.py)
list_time_entries: Filter by project, work package, user, and date span using native OpenProject operators.log_time: Convert decimal hours to ISO-8601 durations and create time entries (including optional user, activity, and timestamps).
Users (src/openproject_mcp/tools/users.py)
resolve_user: Search active principals by name.get_user_by_id: Retrieve a single user with full metadata.
Wiki (src/openproject_mcp/tools/wiki.py)
get_wiki_page: Retrieve wiki metadata, versions, and cross-links.attach_file_to_wiki: Upload files to wiki pages.list_wiki_page_attachments: List attachments on a wiki page.
Repository Layout
├── src/openproject_mcp
│ ├── main.py # CLI entry point (stdio server)
│ ├── server.py # FastMCP factory + tool registration
│ ├── config.py # Pydantic settings (env-driven)
│ ├── client.py # Resilient httpx/OpenProject client
│ ├── errors.py # Domain-specific exceptions and sanitisation
│ ├── utils/logging.py # Log configuration helper
│ └── tools/ # Tool families grouped by domain
├── tests/ # Async pytest suite and smoke tests
├── pyproject.toml # uv/PEP 621 metadata + tooling config
├── uv.lock # Locked dependency graph
├── requirements.txt # Convenience export (mirrors pyproject deps)
└── env_example.txt # Template for `.env`
Quickstart
- Install prerequisites
- Python 3.10+
uvfor blazing-fast installs (curl -LsSf https://astral.sh/uv/install.sh | sh)- Access to an OpenProject instance and a personal API token
- Clone the repository
git clone https://github.com/your-org/openproject-mcp-ai-integration.git
cd openproject-mcp-ai-integration
- Install dependencies
uv sync # creates .venv and installs runtime + dev deps
- Configure environment
cp env_example.txt .env
# edit .env with your OpenProject URL + API token
- Run a smoke test
uv run python -m openproject_mcp.main # prints nothing and blocks while serving stdio
Stop with Ctrl+C once you confirm the server boots without configuration errors.
Configuration
openproject_mcp.config.Settings consumes the following environment variables (case-insensitive thanks to Pydantic). Assign them in .env or via your MCP client configuration.
| Variable | Required | Description | Default | | --- | --- | --- | --- | | OPENPROJECT_URL / OPENPROJECT_BASE_URL | Yes | Base URL to your OpenProject instance (no /api/v3). | — | | OPENPROJECT_API_KEY / OPENPROJECT_API_TOKEN | Yes | Personal API token with API v3 access. | — | | LOG_LEVEL | Optional | Python logging level (DEBUG, INFO, …). | INFO | | CONNECT_TIMEOUT | Optional | Seconds allowed to establish TCP/TLS connections. | 10.0 | | READ_TIMEOUT | Optional | Seconds allowed for responses. | 10.0 | | MAX_RETRIES | Optional | Retry attempts for retryable status codes/timeouts. | 3 | | PAGE_SIZE_DEFAULT | Optional | Default pagination size for helper logic. | 25 | | PAGE_SIZE_MAX | Optional | Hard ceiling for page sizes. | 200 |
Additional keys in env_example.txt (OPENPROJECT_PROXY, TEST_CONNECTION_ON_STARTUP) are placeholders for future enhancements and are currently ignored.
Running the Server
The project exposes a console script named openproj-mcp (configured in pyproject.toml). Run it through uv to ensure dependencies are resolved via the project virtual environment:
uv run openproj-mcp
- The process blocks and communicates over stdio per MCP specification.
- Use
LOG_LEVEL=DEBUGto surface HTTP requests/responses during debugging. - Health-check via
system_pingfrom your MCP client before invoking other tools.
Working With MCP Clients
Claude Desktop (macOS & Windows)
Add an entry to claude_desktop_config.json:
{
"mcpServers": {
"openproject": {
"command": "uv",
"args": ["run", "openproj-mcp"],
"env": {
"OPENPROJECT_URL": "https://your-instance.openproject.com",
"OPENPROJECT_API_KEY": "sk_...",
"LOG_LEVEL": "INFO"
}
}
}
}
Restart Claude Desktop and confirm the server appears in the MCP tool list. Other MCP hosts (Cursor, Continue, etc.) follow the same pattern: point to uv run openproj-mcp and provide the environment through their configuration UI.
Testing & Quality
The repo includes async-first tests plus static analysis hooks.
| Task | Command | | --- | --- | | Run entire test suite | uv run pytest | | Focus on a specific test | uv run pytest tests/test_work_packages.py -k add_comment | | Type checking | uv run mypy src | | Linting | uv run ruff check | | Formatting (check/fix) | uv run black --check src tests / uv run black src tests | | Coverage report | uv run pytest --cov=src/openproject_mcp --cov-report=term-missing |
Helpful docs live in tests/TESTING_CHECKLIST.md (e.g., the checklist for add_comment).
Troubleshooting
| Symptom | Likely Cause | Suggested Fix | | --- | --- | --- | | AuthError: Authentication failed | Invalid or revoked API token. | Regenerate the token under My account → Access tokens and update .env/client config. | | PermissionError: Permission denied | The token lacks project/work package rights. | Grant the user the required OpenProject roles or run the operation in a project the user can access. | | 404 Resource not found | Wrong project/work package ID or the user cannot view it. | Double-check IDs via UI or resolve_project/search_content. | | Stalled commands | Corporate proxies or SSL interception. | Run curl manually to confirm connectivity; proxy support is not yet wired, so a direct connection is required for now. | | Slow responses | Large queries or wide attachment downloads. | Use filters (pageSize, limit, date ranges) or get_attachment_content for previews before downloading entire files. |
Set LOG_LEVEL=DEBUG to trace HTTP calls. The server mutes noisy httpx logs when running at higher log levels.
Known Limitations & Roadmap
- Proxy configuration and startup connectivity tests are stubbed in
.env_examplebut not implemented. - Time entry activities must be referenced by ID (e.g., check OpenProject → Administration → Time tracking). A dedicated lookup tool is planned.
- There is no high-level permission inspection yet; use OpenProject’s UI to confirm rights if you receive 403 errors.
- Only stdio transport is provided today. Socket or HTTP transports would require additional glue around
FastMCP. - The tool surface focuses on read/write operations tested in
tests/. If you need new API coverage (e.g., versions, relations), PRs are welcome.
Contributing
- Fork & clone the repo.
- Create a feature branch:
git checkout -b feature/<topic>. - Run
uv run ruff check,uv run black,uv run mypy, anduv run pytestbefore committing. - Submit a PR describing the motivation, testing performed, and any OpenProject prerequisites.
Please avoid committing secrets—.env is gitignored, and errors.py scrubs sensitive tokens from exception text.
Credits & Origins
This codebase was originally forked from the openproject-mcp-server project by AndyEverything (MIT-licensed). Under Oleksandr Pometun's maintenance it has since been heavily refactored into a distinct implementation with a different architecture, tooling setup, and feature scope aimed at a production-ready MCP server and portfolio reference.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Acknowledgements
- Inspired by the Model Context Protocol community work.
- Integrates with OpenProject API v3.












