clickup-mcp
This server integrates ClickUp via the MCP protocol, providing tools to manage workspaces, spaces, folders, lists, tasks, comments, and Docs.
Workspaces: List all available workspaces/teams.
Spaces: List spaces within a workspace and get details of a specific space.
Folders: Create, update, delete, get details, and list folders within a space.
Lists: Create folderless lists in a space, create lists within a folder, update lists, and get list details.
Tasks: Get by ID, search with filters, create, update, delete, and move tasks between lists.
Comments: Get comments for a task and add new comments to a task.
Docs: Retrieve a single page from a ClickUp Doc (v3 API).
Connection Testing: Verify your ClickUp credentials and configuration are working correctly.
Deployment options include stdio mode (for Claude Desktop), HTTP mode for single-tenant or multi-tenant (gateway) deployments, with token-based auth via environment variable or per-request header (X-Clickup-Token).
Provides tools for managing ClickUp workspaces, spaces, folders, lists, tasks, and comments, enabling automated project management operations through the ClickUp API.
Click on "Install 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., "@clickup-mcplist all spaces in my workspace"
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.
clickup-mcp
ClickUp MCP server for Claude — exposes ClickUp tasks, spaces, folders, lists, and comments as MCP tools.
Tech stack: Python 3.12 + uv + FastMCP (Starlette/FastAPI)
Quick Start
# Install dependencies
cd D:\leo\mcp-server\clickup-mcp
uv sync
# Run in HTTP/gateway mode (the default) — each request supplies its own
# token via the X-Clickup-Token header, nothing to set locally
uv run clickup-mcp
# Or run in stdio mode for local single-user tools like Claude Desktop
$env:MCP_TRANSPORT="stdio"
$env:CLICKUP_API_TOKEN="pk_xxxxx"
uv run clickup-mcpRelated MCP server: Clickup Universal MCP Server
Configuration
Copy .env.example to .env and fill in your values:
Variable | Default | Description |
|
|
|
| — | ClickUp personal API token ( |
|
| HTTP server port |
|
| API base URL |
Note: there is no AUTH_MODE setting anymore. HTTP transport is always
gateway mode (per-request X-Clickup-Token header, enforced by middleware);
stdio transport is always local-token mode. Credential source now follows
transport directly, so there is no way to accidentally run an HTTP/gateway
deployment that falls back to a shared env-var token.
Get your API token: ClickUp → Settings → Apps → API Token
Claude Desktop Setup
Add to claude_desktop_config.json:
{
"mcpServers": {
"clickup": {
"command": "uv",
"args": ["run", "--directory", "D:/leo/mcp-server/clickup-mcp", "clickup-mcp"],
"env": {
"CLICKUP_API_TOKEN": "pk_xxxxx"
}
}
}
}Transport Modes
HTTP — gateway / multi-tenant (default)
uv run clickup-mcp
# Each request must include: X-Clickup-Token: pk_xxxxx
# A request with no header gets 401 — there is no env-var fallback.stdio (Claude Desktop / CLI, local dev only)
$env:MCP_TRANSPORT="stdio"
$env:CLICKUP_API_TOKEN="pk_xxxxx"
uv run clickup-mcpAvailable Tools (28)
Tool | Description |
| List all workspaces/teams |
| List workspace members, flattened to id/username/email/team_id/role — resolve a person's email to the user_id |
| List spaces in a workspace, or across every workspace the token can see when |
| Get space details |
| List folders in a space |
| List folderless lists in a space |
| Get folder details |
| List lists in a folder |
| Create a folder |
| Update a folder |
| Delete a folder |
| Get list details |
| Create list in a folder |
| Create list in a space |
| Update a list |
| Get task by native ID, or by custom ID with the workspace resolved automatically |
| Search tasks with filters; searches every workspace the token can see when |
| List a person's tasks across ALL visible workspaces in one call, by email or user_id — no team_id, no manual pagination/dedup needed |
| Create a task |
| Update a task |
| Delete a task |
| Move task to a different list |
| Get task comments |
| Add a comment to a task |
| Get a single page from a Doc (v3); |
| Upload a file (e.g. an image) as an attachment on a task |
| Upload a file and post it inline inside a new task comment, in one call |
| List all EOS Rocks (quarterly goals) org-wide in one call, normalized to a fixed status enum |
Workspace (team) IDs are resolved from the token
No tool requires a workspace/team ID. GET /team already tells the server every
workspace the API token is authorized for, so the server resolves it rather than
asking the caller to supply an ID it has no way of knowing.
Omit
team_idand the call covers every workspace the token can reach. Reads fan out and merge; each returned row carries its ownteam_id.Pass a
team_idthe token cannot see and, when only one workspace exists, it is substituted and the response reportsteam_id_corrected.When several workspaces exist and the given ID matches none, the error lists the legal ones in
authorized_workspaces— so one retry is enough. Writes (clickup_attach_task_file) never guess between workspaces.
A workspace the token cannot reach is reported as invalid_argument, not
unauthorized; only a genuinely bad token yields unauthorized.
Finding a person's ClickUp user ID
Use clickup_list_members. ClickUp's native GET /team response embeds a
full member list per team (teams[].members[].user.{id,username,email}), but
clickup_get_workspaces strips that out to keep its response small, so it is
not the place to look up people. clickup_list_members reads the same
underlying endpoint and projects the member list to a flat, purpose-built
shape (id/username/email/team_id/role) so callers don't have to dig it out of
the full workspace/team object themselves. clickup_list_tasks_for_person
uses the same underlying lookup internally to resolve email -> user_id.
Known gap: ClickUp's team-member object has no reliable "is this member
deactivated" field — clickup_list_members does not return an active
field, since nothing real would back it (the only status field present
on the raw object, invited_by.status, describes the inviter, not the
member).
clickup_search_tasks already returns status.type
Like every other read tool here, clickup_search_tasks and
clickup_get_task pass through ClickUp's raw task object unmodified —
including the status object's type field (open / custom / closed /
done), which is the only reliable way to tell whether a custom-named
status counts as done. No code change was needed for this; it was already
there. clickup_list_tasks_for_person surfaces it explicitly as
status_type on each returned task for convenience.
How EOS Rocks are represented in this ClickUp workspace
Confirmed 2026-08-18 by inspecting a real rock task's fields directly (not
guessed): Rocks are regular ClickUp tasks living in a list literally named
"Rocks" (found under Space "Company" > Folder "EOS Traction"), each
carrying dedicated custom fields: Quarter (dropdown, "Q1 2024".."Q4 2026"),
Rocks Status (On Hold / Off Track / On Track / Completed / Blocked / At
Risk), Rock Type (Company / Individual / Departmental / Team Rock),
Department, and progress via either Progress (manual) or Progress %
(auto, checklist-rollup). This is neither the ClickUp Goals API nor a
plain task list with no metadata — it's tasks-plus-custom-fields.
clickup_list_rocks_for_org discovers every list named "Rocks" (by name,
not a hardcoded ID, in case spaces/folders get reorganized) across every
workspace visible to the token, reads these fields, and normalizes them:
quarter: ClickUp's "Q3 2026" label is converted to2026-Q3(and back, for thequarterinput filter).status: ClickUp's 6 raw options are mapped down to the 5-value contract (on_track/off_track/done/missed/open) — see the_STATUS_MAPcomment inrocks.pyfor the exact mapping and whymissedis never emitted (nothing in ClickUp's data distinguishes "ran out of time" from generic "off track"; deriving it from an overdue due_date would be an unconfirmed business-logic assumption, so it isn't done here). The raw ClickUp label (e.g."At Risk") is also returned asstatus_raw, alongside the normalizedstatus, so a UI can show ClickUp's own wording without it looking out of sync with the mapped value.measurable: no dedicated field exists on this list. Falls back to the task description;nullif that's empty too (never fabricated).weekly_status: no structured source was found anywhere (not a custom field, nothing comment-derived either) — always returned as[]. If the org starts tracking this in ClickUp some other way, revisit.owner_email/owner_user_id(optional filter args): this tool is org-wide by default (every rock returned), but a caller building a single-person view can pass either to scope the results to one owner's rocks — the org-wide fetch itself still happens (this doesn't reduce upstream ClickUp API calls), it just narrows what's returned. Omit both for the original org-wide behavior.
Discovery performance: parallel, not sequential
clickup_list_rocks_for_org's list-discovery walk (every workspace, every
space, each space's folderless lists + folder lists) runs concurrently via
asyncio.gather, not one request after another. A sequential version of
this was measured to time out against MSPbots' own workspace (15+ spaces
x 2 calls each, run one at a time, comfortably exceeded the caller's MCP
timeout) — parallelizing brought it down to ~7s. ClickUp's rate limit
(developer.clickup.com/docs/rate-limits) is a per-minute budget with no
separate burst cap (100/min on the lowest plan tier), and this fires on
the order of 2 x (space count) requests once, so a few dozen concurrent
calls stays well inside it even combined with other concurrent usage of
the same token — api_client.py's shared retry logic (see below) also
backs off on a real 429 rather than assuming this burst is the only
traffic on the token.
Rate-limit retries are centralized, not per-tool
Every tool's HTTP calls go through ClickUpClient's shared _request
method (api_client.py), which retries 429/500/502/503/504
with backoff — added here once so every tool benefits, since a token's
rate-limit budget is shared across whatever else is calling the ClickUp
API with it, not dedicated to any single tool. On a 429, the delay
prefers ClickUp's own X-RateLimit-Reset header (a Unix timestamp for
when the per-minute window resets — the header ClickUp's rate-limit docs
actually document) over a generic Retry-After or blind exponential
backoff, so retries wait exactly as long as ClickUp says to, not a guess.
Attachments and images
ClickUp's REST API has no way to attach a file directly to a comment —
only to a task (POST /task/{task_id}/attachment, what
clickup_attach_task_file wraps). There is also no delete/update
attachment endpoint; re-uploading adds a new attachment rather than
replacing the old one, and removing one requires the ClickUp web/desktop
app. Confirmed by checking ClickUp's own official MCP server's tool
descriptions too — same split (a Create Task Comment tool with no
attachment support, and a separate Attach File to Task tool).
To make an image show up inline inside a comment, the underlying
trick is: upload the file to the task first, then reference the
returned URL from the file's response using Markdown image syntax in
the comment text — ClickUp's comment renderer inlines it as a real
image, not just a link. clickup_create_comment_with_image does both
steps in one call:
clickup_create_comment_with_image(task_id, file_content_base64, filename)
# internally:
# 1. POST /task/{task_id}/attachment -> {"url": "...", ...}
# 2. POST /task/{task_id}/comment comment_text = ""To do it manually instead (e.g. to add other text around the image), call the two tools yourself:
1. result = clickup_attach_task_file(task_id, file_content_base64, filename)
-> result["url"] is the uploaded file's URL
2. clickup_create_task_comment(
task_id,
comment_text=f""
)API Reference
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Tools
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides integration with ClickUp's API, allowing you to retrieve task information and manage ClickUp data through MCP-compatible clients.
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Clickup's project management tools through the MCP protocol, allowing task and project operations via natural language.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI applications to interact with ClickUp's project management API through the MCP protocol, supporting resources like Teams, Spaces, Goals, and Key Results.4MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage ClickUp workspaces, teams, spaces, folders, lists, tasks, and custom fields via 29 MCP tools with full CRUD operations.825MIT
Related MCP Connectors
ClickUp MCP — wraps the ClickUp REST API v2 (BYO API key)
Monday.com MCP — wraps the Monday.com GraphQL API (BYO API key)
Manage feature requests, votes, roadmaps, and changelogs from any MCP client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MSPbotsAI/clickup-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server