- OpenProject MCP
OpenProject MCP
⚠️ This is an early-stage project. Do not use it productively – contributions welcome!
OpenProject MCP Server
A Model Context Protocol (MCP) server that provides seamless integration with OpenProject API v3. This server enables LLM applications to interact with OpenProject for project management, work package tracking, and task creation.
Features
- 🔌 Full OpenProject API v3 Integration
- 📋 Project Management: List and filter projects
- 📝 Work Package Management: Create, list, and filter work packages
- 🏷️ Type Management: List available work package types
- 🔐 Secure Authentication: API key-based authentication
- 🌐 Proxy Support: Optional HTTP proxy configuration
- 🚀 Async Operations: Built with modern async/await patterns
- 📊 Comprehensive Logging: Configurable logging levels
Prerequisites
- Python 3.10 or higher
- uv (fast Python package manager)
- An OpenProject instance (cloud or self-hosted)
- OpenProject API key (generated from your user profile)
Installation
1. Install uv (if not already installed)
macOS/Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
Windows:
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
Alternative (using pip):
pip install uv
2. Clone and Setup the Project
git clone https://github.com/yourusername/openproject-mcp.git
cd openproject-mcp
3. Create Virtual Environment and Install Dependencies
# Create virtual environment and install dependencies in one command
uv sync
Alternative (manual steps):
# Create virtual environment
uv venv
# Install dependencies
uv pip install -r requirements.txt
4. Configure Environment
# Copy the environment template
cp env_example.txt .env
Edit .env and add your OpenProject configuration:
OPENPROJECT_URL=https://your-instance.openproject.com
OPENPROJECT_API_KEY=your-api-key-here
Configuration
Environment Variables
| Variable | Required | Description | Example |
|---|---|---|---|
OPENPROJECT_URL | Yes | Your OpenProject instance URL | https://mycompany.openproject.com |
OPENPROJECT_API_KEY | Yes | API key from your OpenProject user profile | 8169846b42461e6e... |
OPENPROJECT_PROXY | No | HTTP proxy URL if needed | http://proxy.company.com:8080 |
LOG_LEVEL | No | Logging level (DEBUG, INFO, WARNING, ERROR) | INFO |
TEST_CONNECTION_ON_STARTUP | No | Test API connection when server starts | true |
Getting an API Key
- Log in to your OpenProject instance
- Go to My account (click your avatar)
- Navigate to Access tokens
- Click + Add to create a new token
- Give it a name and copy the generated token
Usage
Running the Server
Using uv (recommended):
uv run python openproject-mcp.py
Alternative (manual activation):
# Activate virtual environment
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Run the server
python openproject-mcp.py
Note: If you renamed the file from openproject_mcp_server.py, update your configuration accordingly.
Integration with Claude Desktop
Add this configuration to your Claude Desktop config file:
Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"openproject": {
"command": "/path/to/your/project/.venv/bin/python",
"args": ["/path/to/your/project/openproject-mcp.py"]
}
}
}
Note: Replace /path/to/your/project/ with the actual path to your project directory.
Alternative with uv (if uv is in your system PATH):
{
"mcpServers": {
"openproject": {
"command": "uv",
"args": ["run", "python", "/path/to/your/project/openproject-mcp.py"]
}
}
}
Why use the direct Python path? The direct Python path approach is more reliable because:
- It doesn't require
uvto be in the system PATH - It avoids potential issues with
uv runtrying to install the project as a package - It's simpler and more straightforward for MCP server configurations
Available Tools
1. test_connection
Test the connection to your OpenProject instance.
Example:
Test the OpenProject connection
2. list_projects
List all projects you have access to.
Parameters:
active_only(boolean, optional): Show only active projects (default: true)
Example:
List all active projects
3. list_work_packages
List work packages with optional filtering.
Parameters:
project_id(integer, optional): Filter by specific projectstatus(string, optional): Filter by status - "open", "closed", or "all" (default: "open")
Example:
Show all open work packages in project 5
4. list_types
List available work package types.
Parameters:
project_id(integer, optional): Filter types by project
Example:
List all work package types
5. create_work_package
Create a new work package.
Parameters:
project_id(integer, required): The project IDsubject(string, required): Work package titletype_id(integer, required): Type ID (e.g., 1 for Task)description(string, optional): Description in Markdown formatpriority_id(integer, optional): Priority IDassignee_id(integer, optional): User ID to assign to
Example:
Create a new task in project 5 titled "Update documentation" with type ID 1
6. list_users
List all users in the OpenProject instance.
Parameters:
active_only(boolean, optional): Show only active users (default: true)
7. get_user
Get detailed information about a specific user.
Parameters:
user_id(integer, required): User ID
8. list_memberships
List project memberships showing users and their roles.
Parameters:
project_id(integer, optional): Filter by specific projectuser_id(integer, optional): Filter by specific user
9. list_statuses
List all available work package statuses.
10. list_priorities
List all available work package priorities.
11. get_work_package
Get detailed information about a specific work package.
Parameters:
work_package_id(integer, required): Work package ID
12. update_work_package
Update an existing work package.
Parameters:
work_package_id(integer, required): Work package IDsubject(string, optional): Work package titledescription(string, optional): Description in Markdown formattype_id(integer, optional): Type IDstatus_id(integer, optional): Status IDpriority_id(integer, optional): Priority IDassignee_id(integer, optional): User ID to assign topercentage_done(integer, optional): Completion percentage (0-100)
13. delete_work_package
Delete a work package.
Parameters:
work_package_id(integer, required): Work package ID
14. list_time_entries
List time entries with optional filtering.
Parameters:
work_package_id(integer, optional): Filter by specific work packageuser_id(integer, optional): Filter by specific user
15. create_time_entry
Create a new time entry.
Parameters:
work_package_id(integer, required): Work package IDhours(number, required): Hours spent (e.g., 2.5)spent_on(string, required): Date when time was spent (YYYY-MM-DD format)comment(string, optional): Comment/descriptionactivity_id(integer, optional): Activity ID
16. update_time_entry
Update an existing time entry.
Parameters:
time_entry_id(integer, required): Time entry IDhours(number, optional): Hours spentspent_on(string, optional): Date when time was spentcomment(string, optional): Comment/descriptionactivity_id(integer, optional): Activity ID
17. delete_time_entry
Delete a time entry.
Parameters:
time_entry_id(integer, required): Time entry ID
18. list_time_entry_activities
List available time entry activities.
19. list_versions
List project versions/milestones.
Parameters:
project_id(integer, optional): Filter by specific project
20. create_version
Create a new project version/milestone.
Parameters:
project_id(integer, required): Project IDname(string, required): Version namedescription(string, optional): Version descriptionstart_date(string, optional): Start date (YYYY-MM-DD format)end_date(string, optional): End date (YYYY-MM-DD format)status(string, optional): Version status (open, locked, closed)
21. create_project
Create a new project.
Parameters:
name(string, required): Project nameidentifier(string, required): Project identifier (unique)description(string, optional): Project descriptionpublic(boolean, optional): Whether the project is publicstatus(string, optional): Project statusparent_id(integer, optional): Parent project ID
Example:
Create a new project named "Website Redesign" with identifier "web-redesign"
22. update_project
Update an existing project.
Parameters:
project_id(integer, required): Project IDname(string, optional): Project nameidentifier(string, optional): Project identifierdescription(string, optional): Project descriptionpublic(boolean, optional): Whether the project is publicstatus(string, optional): Project statusparent_id(integer, optional): Parent project ID
23. delete_project
Delete a project.
Parameters:
project_id(integer, required): Project ID
24. get_project
Get detailed information about a specific project.
Parameters:
project_id(integer, required): Project ID
25. create_membership
Create a new project membership.
Parameters:
project_id(integer, required): Project IDuser_id(integer, optional): User ID (required if group_id not provided)group_id(integer, optional): Group ID (required if user_id not provided)role_ids(array, optional): Array of role IDsrole_id(integer, optional): Single role ID (alternative to role_ids)notification_message(string, optional): Optional notification message
Example:
Add user 5 to project 2 with role ID 3 (Developer role)
26. update_membership
Update an existing membership.
Parameters:
membership_id(integer, required): Membership IDrole_ids(array, optional): Array of role IDsrole_id(integer, optional): Single role IDnotification_message(string, optional): Optional notification message
27. delete_membership
Delete a membership.
Parameters:
membership_id(integer, required): Membership ID
28. get_membership
Get detailed information about a specific membership.
Parameters:
membership_id(integer, required): Membership ID
29. list_project_members
List all members of a specific project.
Parameters:
project_id(integer, required): Project ID
Example:
List all members of project 5
30. list_user_projects
List all projects a specific user is assigned to.
Parameters:
user_id(integer, required): User ID
31. list_roles
List all available roles.
Example:
List all available roles in the OpenProject instance
32. get_role
Get detailed information about a specific role.
Parameters:
role_id(integer, required): Role ID
33. set_work_package_parent
Set a parent for a work package (create parent-child relationship).
Parameters:
work_package_id(integer, required): Work package ID to become a childparent_id(integer, required): Work package ID to become the parent
Example:
Set work package 15 as a child of work package 10
34. remove_work_package_parent
Remove parent relationship from a work package (make it top-level).
Parameters:
work_package_id(integer, required): Work package ID to remove parent from
35. list_work_package_children
List all child work packages of a parent.
Parameters:
parent_id(integer, required): Parent work package IDinclude_descendants(boolean, optional): Include grandchildren and all descendants (default: false)
Example:
List all children of work package 10 including descendants
36. create_work_package_relation
Create a relationship between work packages.
Parameters:
from_id(integer, required): Source work package IDto_id(integer, required): Target work package IDrelation_type(string, required): Relation type (blocks, follows, precedes, relates, duplicates, includes, requires, partof)lag(integer, optional): Lag in working days (for follows/precedes)description(string, optional): Optional description of the relation
Example:
Create a "blocks" relation where work package 5 blocks work package 8
37. list_work_package_relations
List work package relations with optional filtering.
Parameters:
work_package_id(integer, optional): Filter relations involving this work package IDrelation_type(string, optional): Filter by relation type
38. update_work_package_relation
Update an existing work package relation.
Parameters:
relation_id(integer, required): Relation IDrelation_type(string, optional): New relation typelag(integer, optional): Lag in working daysdescription(string, optional): Optional description
39. delete_work_package_relation
Delete a work package relation.
Parameters:
relation_id(integer, required): Relation ID
40. get_work_package_relation
Get detailed information about a specific work package relation.
Parameters:
relation_id(integer, required): Relation ID
Development
Setting up Development Environment
# Install development dependencies
uv sync --extra dev
# Or install manually
uv pip install -e ".[dev]"
Running Tests
uv run pytest tests/
Code Formatting
# Format code
uv run black openproject-mcp.py
# Lint code
uv run flake8 openproject-mcp.py
Adding Dependencies
# Add a new dependency
uv add package-name
# Add a development dependency
uv add --dev package-name
# Update dependencies
uv sync
Tool Compatibility & Test Results
✅ Fully Working Tools (39/41)
All these tools have been tested and work correctly with admin privileges:
Core Project Management:
test_connection,check_permissions,list_projects,create_project,update_projectdelete_project,get_project
Work Package Management:
list_work_packages,list_types,create_work_package,update_work_packagedelete_work_package,get_work_package,list_statuses,list_priorities
Work Package Hierarchy & Relations:
set_work_package_parent,remove_work_package_parent,list_work_package_childrencreate_work_package_relation,list_work_package_relations,update_work_package_relationdelete_work_package_relation,get_work_package_relation
User & Membership Management:
list_users,get_user,create_membership,update_membership,delete_membershipget_membership,list_project_members,list_user_projects,list_roles,get_role
Time Tracking:
list_time_entries,create_time_entry,update_time_entry,delete_time_entry
Project Versions:
list_versions,create_version
⚠️ Partially Working Tools
list_memberships: Works globally and withproject_idfiltering. User ID filtering (user_id) may not be supported in all OpenProject instances.
❌ Endpoint Limitations with Workarounds
list_time_entry_activities: Returns 404 but time entry activities ARE functional! Use these predefined activity IDs:- Management (ID: 1): Administrative and planning tasks
- Specification (ID: 2): Requirements and documentation
- Development (ID: 3): Coding and implementation
- Testing (ID: 4): Quality assurance and testing
Example: create_time_entry with activity_id: 3 for Development work
Permission Requirements
Most create/update/delete operations require appropriate permissions:
- Project Operations: Require global "Create project" and "Edit project" permissions. Deletion typically requires admin rights
- Work Package Operations: Require "Create/Edit work packages" permission in target projects
- Work Package Relations & Hierarchy: Require "Edit work packages" permission for creating/modifying parent-child relationships and dependencies
- Membership Management: Require "Manage members" permission for target projects
- Time Entry Operations: Require time tracking permissions
- Version Management: Require project admin or version management permissions
- User Operations: Admin privileges may be needed for comprehensive user management
- Role Management: Read-only operations generally available; admin privileges may be needed for detailed role information
Use the check_permissions tool to diagnose permission-related issues.
Troubleshooting
Connection Issues
- 401 Unauthorized: Check your API key is correct and active
- 403 Forbidden: Ensure your user has the necessary permissions
- 404 Not Found: Verify the OpenProject URL and that resources exist
- Proxy Errors: Check proxy settings and authentication
Debug Mode
Enable debug logging by setting:
LOG_LEVEL=DEBUG
Common Issues
- No projects found: Ensure your API user has project view permissions
- SSL errors: May occur with self-signed certificates or proxy SSL interception
- Timeout errors: Increase timeout or check network connectivity
Security Considerations
- Never commit your
.envfile to version control - Use environment variables for sensitive data
- Rotate API keys regularly
- Use HTTPS for all OpenProject connections
- Configure proxy authentication securely if needed
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built for the Model Context Protocol
- Integrates with OpenProject
- Inspired by the MCP community
Support
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Server Config
{
"mcpServers": {
"openproject": {
"command": "/path/to/your/project/.venv/bin/python",
"args": [
"/path/to/your/project/openproject-mcp.py"
]
}
}
}