COR MCP Server
The COR MCP Server exposes the full COR REST API as 61 MCP tools, enabling AI agents to manage projects and related entities across 11 functional areas:
Projects: List, create, read, update, delete projects; manage collaborators, costs, labels, ratecards, templates, and profitability.
Tasks: Search (with rich filters), get pending tasks, create/read/update/delete tasks; manage collaborators and labels.
Time Tracking: Log hours against tasks, search time entries, get entries by date, change entry status (approve/reject/pending), and accept suggested hours.
Clients: List, create, read, update, delete clients; retrieve client fees.
Contracts: List, get, and create contracts; manage contract positions.
Messaging: Read and post messages on projects and tasks (with @mention support).
Team & Users: View your profile, list/get users, list/create teams, add/remove users from teams, retrieve working time data.
Ratecards: List, get, and create ratecards.
Allocations: Get resource allocations by project, create/update allocations, delete allocations.
Products: List and create products.
Labels: Retrieve labels, optionally filtered by entity type (project, task, or user).
The server supports both user credentials (email/password) and client credentials (API key/secret) with OAuth token auto-refresh, dual transport (stdio for agents, sse for HTTP remote access), and Docker deployment.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@COR MCP Server list my open tasks"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
⬡ COR MCP Server
61 MCP tools for Project COR — the project management platform for creative and professional teams.
Exposes the complete COR REST API surface as MCP tools — usable from any MCP-compatible client: Hermes Agent, Claude Desktop, Cursor, Claude Code, Cline, Windsurf, and more.
✨ Features
61 MCP tools — full CRUD for all COR entities across 11 modules
Dual authentication — pick what suits you:
User Credentials (email + password) — quick personal use
Client Credentials (API key + client secret) — server-to-server
Async everywhere —
httpx+asyncio, non-blocking I/ODual transport —
stdio(default for agents) andsse(HTTP for remote access)OAuth token auto-refresh — never worry about expiry mid-session
Docker support — one-command deployment
Hermes Agent skill included — see
skills/cor-mcp-setup/24 passing tests — fully mocked, no live credentials required
Entity Coverage
Module | Tools | Covers |
Projects | 14 | CRUD, collaborators, costs, labels, ratecards, templates, profitability |
Tasks | 10 | Search, CRUD, collaborators, labels |
Team & Users | 8 | Profile, users, teams, working time |
Clients | 6 | CRUD, fees |
Contracts | 5 | CRUD, positions |
Time Tracking | 5 | Log, search, status, accept suggested |
Messaging | 4 | Project + task messages |
Allocations | 3 | CRUD |
Ratecards | 3 | CRUD |
Products | 2 | CRUD |
Labels | 1 | By entity type |
Related MCP server: Redmine MCP Server
🚀 Quick Start
Prerequisites
Python 3.11+
uv (recommended) or pip
COR account with credentials
Install & Run
# Clone
git clone https://github.com/fxckcode/mcp-cor.git
cd mcp-cor
# Install with uv (recommended)
uv sync
# Or with pip
pip install -e .Configure
cp .env.example .envThen choose one auth mode in .env:
Mode A — Email + Password (recommended for personal use)
COR_EMAIL=your.email@company.com
COR_PASSWORD=your_password
COR_API_URL=https://api.projectcor.com/v1Mode B — API Key + Client Secret (for server-to-server)
COR_API_KEY=your_api_key
COR_CLIENT_SECRET=your_client_secret
COR_API_URL=https://api.projectcor.com/v1The server auto-detects which mode you configured. Email/password takes priority when both are set.
Run
# stdio transport (default — for MCP agents)
uv run cor-mcp-server
# SSE/HTTP transport (for remote access)
uv run cor-mcp-server --transport sse --host 0.0.0.0 --port 8000
# Verbose logging
uv run cor-mcp-server --verbose🔐 Authentication
Two modes supported. The server auto-detects which to use.
User Credentials (Mode A)
POST https://api.projectcor.com/v1/auth/login
Content-Type: application/x-www-form-urlencoded
email=user@corp.com&password=*****Returns JWT access_token + refresh_token. Auto-refresh built in.
Client Credentials (Mode B)
POST https://api.projectcor.com/v1/oauth/token?grant_type=client_credentials
Authorization: Basic base64(api_key:client_secret)Returns access_token (1-hour TTL). Cached and auto-refreshed with a 60-second safety margin.
🔌 Connecting from MCP Clients
Add to ~/.hermes/config.yaml:
mcp_servers:
cor:
command: "uv"
args: ["run", "--directory", "/path/to/cor-mcp-server", "python", "-m", "cor_mcp_server"]
env:
COR_EMAIL: "your.email@company.com"
COR_PASSWORD: "your_password"
COR_API_URL: "https://api.projectcor.com/v1"
timeout: 180
connect_timeout: 60Then restart or run /reload-mcp.
Add to claude_desktop_config.json:
{
"mcpServers": {
"cor": {
"command": "uv",
"args": ["run", "--directory", "/path/to/cor-mcp-server", "python", "-m", "cor_mcp_server"],
"env": {
"COR_EMAIL": "your.email@company.com",
"COR_PASSWORD": "your_password",
"COR_API_URL": "https://api.projectcor.com/v1"
}
}
}
}In MCP Servers settings, add:
Name: COR
Type: command
Command:
uv run --directory /path/to/cor-mcp-server python -m cor_mcp_serverEnvironment variables:
COR_EMAIL,COR_PASSWORD,COR_API_URL
env COR_EMAIL=your@email.com COR_PASSWORD=your_pass COR_API_URL=https://api.projectcor.com/v1 \
npx @anthropic/claude-code --mcp "uv run --directory /path/to/cor-mcp-server python -m cor_mcp_server"Docker
docker build -t cor-mcp-server .
# stdio mode
docker run -i --rm \
-e COR_EMAIL=your@email.com \
-e COR_PASSWORD=your_password \
-e COR_API_URL=https://api.projectcor.com/v1 \
cor-mcp-server
# SSE mode
docker run -i --rm \
-p 8000:8000 \
-e COR_EMAIL=your@email.com \
-e COR_PASSWORD=your_password \
-e COR_API_URL=https://api.projectcor.com/v1 \
cor-mcp-server --transport sse🛠 Available Tools
All tool names are prefixed with cor_ for clean discoverability.
Projects (14 tools)
Tool | Description |
| List projects with filters (clientId, status, health, dates) |
| Get project details |
| Create a new project |
| Update a project (partial) |
| Delete a project |
| Get project collaborators |
| Add collaborator to project |
| Remove collaborator from project |
| Get project costs/estimates |
| Add cost to project |
| Get project labels |
| Get project ratecard |
| Get available project templates |
| Get project profitability |
Tasks (10 tools)
Tool | Description |
| Search tasks with filters (projectId, clientId, status, text, dates, labels) |
| Get my pending tasks |
| Get task details |
| Create a new task |
| Update a task |
| Delete a task |
| Get task collaborators |
| Sync (replace) task collaborators |
| Add label to task |
| Remove label from task |
Team & Users (8 tools)
Tool | Description |
| Get current user profile |
| List users with filters |
| Get user details |
| List teams |
| Create a new team |
| Add users to a team |
| Remove users from a team |
| Get working time for users |
Clients (6 tools)
Tool | Description |
| List clients |
| Get client details |
| Create a new client |
| Update a client |
| Delete a client |
| Get client fees |
Contracts (5 tools)
Tool | Description |
| List contracts |
| Get contract details |
| Create a new contract |
| Get contract positions |
| Create position in contract |
Time Tracking (5 tools)
Tool | Description |
| Log hours against a task |
| Search time entries with filters |
| Get time entries by date |
| Change time entry status |
| Accept suggested hours |
Messaging (4 tools)
Tool | Description |
| Get messages on a task |
| Post a message on a task |
| Get messages on a project |
| Post a message on a project |
Ratecards (3 tools)
Tool | Description |
| List ratecards |
| Get ratecard details |
| Create a new ratecard |
Allocations (3 tools)
Tool | Description |
| Get allocations for a project |
| Create/update a resource allocation |
| Delete a resource allocation |
Products (2 tools)
Tool | Description |
| List products |
| Create a new product |
Labels (1 tool)
Tool | Description |
| Get labels (filter by entity type: project, task, user) |
🏗 Architecture
┌──────────────────────┐ ┌───────────────────────────┐ ┌──────────────────────┐
│ MCP Client │ │ COR MCP Server │ │ COR REST API │
│ (Hermes / Claude / │◄───►│ (FastMCP + httpx) │────►│ api.projectcor.com │
│ Cursor / etc.) │ │ │ │ /v1 │
└──────────────────────┘ ├───────────────────────────┤ └──────────────────────┘
│ Auth auto-detection │
│ • Email/Password (prio) │
│ • API Key + Secret │
└───────────────────────────┘🧑💻 Development
# Install dev deps
uv sync --dev
# Run tests (24 passing tests — no live API needed, uses mocking)
uv run pytest tests/ -v
# Test server startup
uv run cor-mcp-server --help
# Lint
uv run ruff check .Adding New Tools
To add a new tool to the COR MCP Server:
Create or edit a tool module in
cor_mcp_server/tools/(e.g.,reports.py).Write an async function with the
cor_prefix pattern:from ..context import get_client async def cor_list_reports(page: int = 1, per_page: int = 20) -> str: """List reports. Args: page: Page number (default: 1) per_page: Results per page (default: 20) """ client = get_client() data = await client.get("/reports", page=page, per_page=per_page) import json return json.dumps(data, indent=2, ensure_ascii=False, default=str)Register the tool in
cor_mcp_server/server.py:from .tools import reports as _reports mcp.tool( name="cor_list_reports", description=_reports.cor_list_reports.__doc__, )(_reports.cor_list_reports)Add tests in
tests/test_tools.pyfollowing the existing patterns (import assertion + tool count update).Update the tool count in
tests/test_tools.pyif needed.
The get_client() function provides a fully-authenticated CORClient instance with automatic Bearer token handling. All tools return JSON strings for consistent serialization.
Test Structure
Tests are in tests/test_tools.py and cover:
TestTokenStore— Thread-safe token caching, expiry, and refresh logicTestCORClient— HTTP methods (GET, POST, PUT, DELETE), pagination, auth headersTestToolFunctions— Import verification for all 61 tool functions across 11 modulesTestServerCreation— Server factory creates a valid FastMCP instance
All tests use mocking — no live COR credentials required to run them.
📁 Project Structure
cor-mcp-server/
├── pyproject.toml # Dependencies and metadata
├── README.md # This file
├── LICENSE # MIT license
├── .env.example # Env var template (both auth modes)
├── Dockerfile # Container deployment
├── skills/ # Hermes Agent skills
│ └── cor-mcp-setup/
│ └── SKILL.md # Skill: setup + usage guide
├── handoff-personal-hermes.md # Self-contained instructions for personal Hermes
├── cor_mcp_server/
│ ├── __init__.py # Package init
│ ├── __main__.py # CLI entry point
│ ├── server.py # FastMCP server + tool registration (all 61 tools)
│ ├── auth.py # OAuth 2.0 dual-mode (user creds + client creds)
│ ├── client.py # COR API HTTP client (httpx, async)
│ ├── context.py # Shared client context / dependency injection
│ ├── models.py # Pydantic models (14 entity types)
│ └── tools/
│ ├── __init__.py
│ ├── projects.py # 14 tools
│ ├── tasks.py # 10 tools
│ ├── time_tracking.py # 5 tools
│ ├── clients.py # 6 tools
│ ├── contracts.py # 5 tools
│ ├── messaging.py # 4 tools
│ ├── team.py # 8 tools
│ ├── labels.py # 1 tool
│ ├── ratecards.py # 3 tools
│ ├── allocations.py # 3 tools
│ └── products.py # 2 tools
├── tests/
│ ├── __init__.py
│ └── test_tools.py # 24 tests for auth, client, and tools
├── .github/ # GitHub community files
│ ├── dependabot.yml # Dependency auto-updates
│ ├── ISSUE_TEMPLATE/ # Bug report + feature request templates
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/ # CI + Release pipelines
├── .gitignore
├── .gitattributes
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
└── SECURITY.md📄 License
👤 Author
Alejandro Duran — Omnicom Media Group
Available Tools
61 toolscor_accept_suggested_hoursC
Accept suggested hours for a given date.
Args: datetime_unix: Unix timestamp for the date
Inputs: datetime_unix (required). Returns: result
cor_add_project_collaboratorC
Add a collaborator to a project.
Args: project_id: The project ID user_id: The user ID to add
Inputs: user_id (required), project_id (required). Returns: result
cor_add_project_costC
Add a cost entry to a project.
Args: project_id: The project ID data: Cost data (description, amount, date, etc.)
Inputs: data (required), project_id (required). Returns: result
cor_add_task_labelC
Add a label to a task.
Args: task_id: The task ID label_id: The label ID to add
Inputs: task_id (required), label_id (required). Returns: result
cor_add_team_usersB
Add users to a team.
Args: team_id: The team ID user_ids: List of user IDs to add
Inputs: team_id (required), user_ids (required). Returns: result
cor_change_hours_statusB
Change the status of a time entry.
Args: hours_id: The time entry ID status: New status (e.g. "approved", "rejected", "pending")
Inputs: status (required), hours_id (required). Returns: result
cor_create_clientB
Create a new client.
Args: data: Client data (name, email, phone, notes, etc.)
Inputs: data (required). Returns: result
cor_create_contractB
Create a new contract.
Args: data: Contract data (name, clientId, startDate, endDate, value, status, etc.)
Inputs: data (required). Returns: result
cor_create_contract_positionA
Create a position in a contract.
Args: contract_id: The contract ID data: Position data (title, rate, hours, etc.)
Inputs: data (required), contract_id (required). Returns: result
cor_create_productB
Create a new product.
Args: data: Product data (name, description, rate, etc.)
Inputs: data (required). Returns: result
cor_create_projectB
Create a new project.
Args: data: Project data (name, clientId, description, startDate, endDate, status, etc.)
Inputs: data (required). Returns: result
cor_create_ratecardC
Create a new ratecard.
Args: data: Ratecard data (name, description, rates, etc.)
Inputs: data (required). Returns: result
cor_create_taskA
Create a new task.
Args: data: Task data (title, projectId, description, deadline, priority, etc.)
Inputs: data (required). Returns: result
cor_create_teamC
Create a new team.
Args: data: Team data (name, description, etc.)
Inputs: data (required). Returns: result
cor_delete_allocationB
Delete a resource allocation.
Args: allocation_id: The allocation ID
Inputs: allocation_id (required). Returns: result
cor_delete_clientC
Delete a client.
Args: client_id: The client ID
Inputs: client_id (required). Returns: result
cor_delete_projectB
Delete a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_delete_taskC
Delete a task.
Args: task_id: The task ID
Inputs: task_id (required). Returns: result
cor_get_allocations_by_projectC
Get resource allocations for a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_clientB
Get a single client by ID.
Args: client_id: The client ID
Inputs: client_id (required). Returns: result
cor_get_client_feesC
Get fees for a client.
Args: client_id: The client ID
Inputs: client_id (required). Returns: result
cor_get_contractA
Get a single contract by ID.
Args: contract_id: The contract ID
Inputs: contract_id (required). Returns: result
cor_get_contract_positionsC
Get positions for a contract.
Args: contract_id: The contract ID
Inputs: contract_id (required). Returns: result
cor_get_hours_by_dateB
Get time entries for a specific date.
Args: datetime_unix: Unix timestamp for the date
Inputs: datetime_unix (required). Returns: result
cor_get_labelsC
Get labels, optionally filtered by entity type.
Args: entity_type: Entity type filter - "project", "task", "user", or empty for all
Inputs: entity_type. Returns: result
cor_get_my_pending_tasksB
Get my pending tasks.
Inputs: no named parameters. Returns: result
cor_get_my_profileA
Get the current user's profile.
Inputs: no named parameters. Returns: result
cor_get_projectB
Get a single project by ID.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_collaboratorsB
Get collaborators for a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_costsC
Get costs/estimates for a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_labelsC
Get labels assigned to a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_messagesC
Get messages for a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_profitabilityC
Get profitability data for a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_ratecardC
Get the ratecard assigned to a project.
Args: project_id: The project ID
Inputs: project_id (required). Returns: result
cor_get_project_templatesC
Get available project templates.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_get_ratecardB
Get a single ratecard by ID.
Args: ratecard_id: The ratecard ID
Inputs: ratecard_id (required). Returns: result
cor_get_taskB
Get a single task by ID.
Args: task_id: The task ID
Inputs: task_id (required). Returns: result
cor_get_task_collaboratorsC
Get collaborators for a task.
Args: task_id: The task ID
Inputs: task_id (required). Returns: result
cor_get_task_messagesC
Get messages for a task.
Args: task_id: The task ID
Inputs: task_id (required). Returns: result
cor_get_userA
Get a single user by ID.
Args: user_id: The user ID
Inputs: user_id (required). Returns: result
cor_get_working_timeC
Get working time for users.
Inputs: no named parameters. Returns: result
cor_list_clientsB
List clients.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_list_contractsA
List contracts.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_list_productsC
List products.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_list_projectsA
List projects with optional filters (clientId, status, health, dates).
Args: page: Page number (default: 1) per_page: Results per page (default: 20) filters: Optional dict with filter fields
Inputs: page, filters, per_page. Returns: result
cor_list_ratecardsC
List ratecards.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_list_teamsC
List teams.
Args: page: Page number (default: 1) per_page: Results per page (default: 20)
Inputs: page, per_page. Returns: result
cor_list_usersC
List users with optional filters.
Args: page: Page number (default: 1) per_page: Results per page (default: 20) filters: Optional dict with filter fields
Inputs: page, filters, per_page. Returns: result
cor_log_hoursC
Log hours against a task.
Args: task_id: The task ID to log hours against data: Time entry data (hours, date, notes, etc.)
Inputs: data (required), task_id (required). Returns: result
cor_post_project_messageB
Post a message on a project (with optional @mentions in content).
Args: project_id: The project ID content: Message content text
Inputs: content (required), project_id (required). Returns: result
cor_post_task_messageB
Post a message on a task (with optional @mentions in content).
Args: task_id: The task ID content: Message content text
Inputs: content (required), task_id (required). Returns: result
cor_remove_project_collaboratorC
Remove a collaborator from a project.
Args: project_id: The project ID user_id: The user ID to remove
Inputs: user_id (required), project_id (required). Returns: result
cor_remove_task_labelB
Remove a label from a task.
Args: task_id: The task ID label_id: The label ID to remove
Inputs: task_id (required), label_id (required). Returns: result
cor_remove_team_usersC
Remove users from a team.
Args: team_id: The team ID user_ids: List of user IDs to remove
Inputs: team_id (required), user_ids (required). Returns: result
cor_save_allocationC
Create or update a resource allocation.
Args: data: Allocation data (projectId, userId, allocationPercentage, startDate, endDate, etc.)
Inputs: data (required). Returns: result
cor_search_tasksA
Search tasks with filters (projectId, clientId, status, text, dates, labels).
Args: filters: Dict with filter fields like projectId, status, text, etc.
Inputs: filters. Returns: result
cor_search_time_entriesC
Search time entries with filters (userId, projectId, dates, status).
Args: filters: Dict with filter fields
Inputs: filters. Returns: result
cor_sync_task_collaboratorsA
Sync (replace) collaborators for a task.
Args: task_id: The task ID user_ids: List of user IDs to set as collaborators
Inputs: task_id (required), user_ids (required). Returns: result
cor_update_clientC
Update an existing client.
Args: client_id: The client ID data: Fields to update
Inputs: data (required), client_id (required). Returns: result
cor_update_projectB
Update an existing project (partial update).
Args: project_id: The project ID data: Fields to update
Inputs: data (required), project_id (required). Returns: result
cor_update_taskC
Update an existing task (partial update).
Args: task_id: The task ID data: Fields to update
Inputs: data (required), task_id (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 61 tools
Each tool targets a distinct resource and action, with clear separation between read/write/delete for different entities. No two tools have overlapping purposes.
All tools follow the 'cor_verb_noun' pattern consistently. Underscores and ordering are uniform, making the set predictable.
With 61 tools, the server is bloated for typical MCP usage. This number exceeds reasonable scope and may cause agent confusion or inefficiency.
Core entities (clients, projects, tasks) have full CRUD, but contracts, ratecards, and products lack update/delete operations, leaving notable gaps.
Maintenance
Related MCP Connectors
One MCP server for all your MCP servers: code mode, per-tool permissions, approvals and audit.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Related MCP Servers
- AlicenseAqualityDmaintenanceExposes the OpenProject REST API as MCP tools for project management, including creating and managing projects, work packages, relations, attachments, users, notifications, watchers, boards, and reference data.3759 npmMIT
- AlicenseBqualityDmaintenanceMCP server for Redmine project management, enabling tools for managing projects, issues, users, time entries, groups, memberships, versions, wiki, news, attachments, search, and Agile sprints via the Redmine REST API.8921 npm1MIT
- AlicenseBqualityDmaintenanceMCP server to interact with the Shortcut project management API, providing 73 tools for managing stories, epics, iterations, milestones, projects, and more.73MIT
- AlicenseNot gradedqualityDmaintenanceMCP server exposing Basecamp CLI commands as tools for project management, todos, messages, and more.MIT