npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@d4works/mcp-clickup

v0.8.0

Published

MCP server for ClickUp task management

Readme

@d4works/mcp-clickup

MCP (Model Context Protocol) server for ClickUp task management. Allows Claude to read task details from ClickUp and help with implementation.

Features

  • Fetch task details (description, comments, attachments, custom fields, etc.)
  • Create tasks and subtasks, with the target list derived from an existing task
  • Support for multiple task ID formats (URL, custom ID, number)
  • Instructions for Claude to analyze tasks before implementation

Configuration

1. Environment Variables

| Variable | Required | Description | | --------------------- | -------- | ----------------------------------------------------------------------- | | CLICKUP_API_TOKEN | Yes | Your ClickUp API token | | CLICKUP_TEAM_ID | Yes | Your ClickUp team/workspace ID | | CLICKUP_TASK_PREFIX | No | Task prefix for number-only input (e.g., "PROJ") | | CLICKUP_SPACE_ID | No | Numeric space ID — skips the space lookup done by assign-release | | CLICKUP_DEFAULT_LIST_ID | No | List ID create-task falls back to when the call names no target list | | CLICKUP_READONLY | No | 1 or true — register only read tools and refuse every write to ClickUp (see Read-only mode) | | CLICKUP_ALLOWED_TOOLS | No | Comma-separated allowlist of tool names to register; combined with CLICKUP_READONLY as an intersection |

2. Get Your API Token

  1. Open ClickUp → Click your avatar → Settings
  2. Go to Apps section
  3. Generate or copy your API Token

3. Find Your Team ID

  1. Open ClickUp → Go to any workspace
  2. Look at the URL: https://app.clickup.com/{TEAM_ID}/...
  3. The number in the URL is your Team ID

4. Create MCP Configuration

Create .mcp.json in your project root.

First, store your API token in a local file that's gitignored:

echo "export CLICKUP_API_TOKEN=pk_your_token_here" >> .claude/.env.local

Then configure .mcp.json to source this file before running the server:

{
  "mcpServers": {
    "clickup": {
      "command": "bash",
      "args": [
        "-c",
        "export $(cat .claude/.env.local | xargs) && npx -y @d4works/mcp-clickup"
      ],
      "env": {
        "CLICKUP_TEAM_ID": "your_team_id",
        "CLICKUP_TASK_PREFIX": "PROJ"
      }
    }
  }
}

Note: If you have CLICKUP_API_TOKEN exported in your global ~/.zshrc or ~/.bashrc, you can simplify the command to just npx -y @d4works/mcp-clickup.

5. Read-only mode

For an agent that may look things up in ClickUp but must never change anything, start the server with CLICKUP_READONLY=1:

{
  "mcpServers": {
    "clickup": {
      "command": "bash",
      "args": [
        "-c",
        "export $(cat .claude/.env.local | xargs) && npx -y @d4works/mcp-clickup"
      ],
      "env": {
        "CLICKUP_TEAM_ID": "your_team_id",
        "CLICKUP_TASK_PREFIX": "PROJ",
        "CLICKUP_READONLY": "1"
      }
    }
  }
}

In read-only mode:

  • Only the read tools are registered: test-connection, get-task, get-task-statuses, get-release-options, find-list. The write tools (create-task, set-task-status, set-task-release, rename-task, append-task-description, add-task-comment, assign-release, remove-release) are not listed at all, so the client never sees them. A call to one of them by name is answered with an error.
  • As a second line of defence, the HTTP client refuses every request to the ClickUp API other than GET with a read-only mode error, so nothing can be written even if a tool were mis-classified.
  • On start the server logs the mode and the active tools to stderr (never the token).

CLICKUP_ALLOWED_TOOLS narrows the set further, e.g. CLICKUP_ALLOWED_TOOLS=get-task,find-list. When both variables are set the result is the intersection — the allowlist can never re-enable a write tool in read-only mode. Names that match no tool are reported as a warning on stderr and ignored.

Usage

Ask Claude to work on a task using any of these formats:

> Work on task ABC-123
> Work on task 123
> Work on task https://app.clickup.com/t/12345/ABC-123

Claude will:

  1. Fetch task details from ClickUp
  2. Analyze the task (description, comments, attachments)
  3. Propose an implementation plan
  4. Wait for your approval before writing code

Available Tools

test-connection

Test connection to ClickUp API.

get-task

Get task details from ClickUp (description, status, comments, attachments, custom fields, checklists, etc.).

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number

create-task

Create a new task (or subtask) in ClickUp. Only used with explicit user confirmation.

The fastest way to use it is likeTaskId: point at any existing task in the right list — the task currently being worked on, for instance — and the new task is filed next to it. No list ID, no extra configuration, nothing to look up.

Parameters:

  • name (string, required): Task title
  • description (string, optional): Task body, written as markdown
  • listId (string, optional): Target list ID (look one up with find-list)
  • likeTaskId (string, optional): Existing task whose list becomes the target — URL, custom ID (ABC-123), or number
  • parentTaskId (string, optional): Existing task to hang the new one under as a subtask — URL, custom ID (ABC-123), or number
  • status (string, optional): Initial status; must match one of the target list's statuses
  • assignees (string[], optional): E-mail addresses or numeric user IDs
  • tags (string[], optional): Tag names — ClickUp only attaches tags that already exist in the space
  • priority (number, optional): 1 = urgent, 2 = high, 3 = normal, 4 = low

Target list is taken from the first source that is given, in this order:

  1. parentTaskId — a subtask always lives in its parent's list, so an additional listId is ignored and reported
  2. listId
  3. likeTaskId
  4. CLICKUP_DEFAULT_LIST_ID from the environment

When none of them is available, nothing is created and the error names all four.

Behavior:

  • The answer always carries the custom ID (ABC-123), the internal ID and the URL — the custom ID is what branch names, commit prefixes and time entries key off. When the space does not generate one, the answer says so outright instead of leaving it blank
  • Everything checkable is checked before the task exists: an invalid status fails with the list's valid statuses, an unknown assignee e-mail fails with the workspace members, and neither creates anything
  • The description is sent as markdown_content, so headings, lists and links survive. A workspace that rejects the field falls back to the plain-text description and the answer says the formatting was lost
  • A custom ID passed as parentTaskId is translated to the internal ID the API requires
  • A rate-limited call (HTTP 429) is retried once, honoring Retry-After

find-list

List the team's spaces, folders and lists with their IDs, so a listId can be found without opening ClickUp in a browser. Only needed when no existing task can serve as likeTaskId — a brand new project with no tasks yet, typically.

Parameters:

  • spaceId (string, optional): Restrict the walk to a single space
  • query (string, optional): Case-insensitive substring matched against space, folder and list names. A hit on a space or folder name keeps all the lists below it

A space that cannot be read is reported in place, so restricted access to one space does not hide the rest of the workspace.

get-task-statuses

Get available statuses for a task. Use this before updating status to see valid options.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number

set-task-status

Set the status of a ClickUp task. Only used with explicit user confirmation.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • status (string, required): New status name (must match one of the available statuses)

get-release-options

Get available Release dropdown options for a task. Use this to see valid values before setting Release.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • fieldName (string, optional): Name of the dropdown field (default: "Release")

set-task-release

Set the Release dropdown value on a ClickUp task. Only used with explicit user confirmation.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • release (string, required): Release value to set (must match one of the available options)
  • fieldName (string, optional): Name of the dropdown field (default: "Release")

rename-task

Rename an existing ClickUp task. Changes the title only — the description, status, tags and custom fields stay untouched. The new name is written verbatim, so include any reference prefix (e.g. [ABC-123] ) yourself. The response shows the old and the new name. Only used with explicit user confirmation.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • name (string, required): New task name (non-empty)

append-task-description

Append markdown content below the existing description of a ClickUp task. The existing description is always kept — there is no replace mode. Only used with explicit user confirmation.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • content (string, required): Markdown content to append below the current description

add-task-comment

Add a comment to a ClickUp task. Only used with explicit user confirmation.

Parameters:

  • taskId (string, required): Task URL, custom ID (ABC-123), or number
  • comment (string, required): The comment text to add to the task
  • notifyAll (boolean, optional): Notify all task members about the comment (default: false)

assign-release

Tag one or more tasks with a release version and optionally move them to a status. Only used with explicit user confirmation.

The version is stored as a space tag, not as a dropdown custom field — the public ClickUp API cannot add options to a dropdown, but it can create tags. The tag is created on the space the first time a version is used, then attached to each task.

Parameters:

  • version (string, required): Release version used verbatim as the tag name — case and spaces matter (e.g. "v1.17.4 (100)")
  • taskIds (string[], required, min 1): Task URLs, custom IDs (ABC-123), or numbers
  • status (string, optional): Status to set on every task (must match one of the available statuses)
  • tagColor (object, optional): { bg, fg } as 6-digit hex including #. Applied only when the tag is created for the first time (defaults: #4A78FF / #FFFFFF)

Behavior:

  • Idempotent — re-running the same call neither duplicates the space tag nor the tag on a task
  • Errors are collected per task, so one bad identifier does not abort the rest of the release; the call is only reported as failed when every task fails
  • The space is resolved from the first task that can be read, unless CLICKUP_SPACE_ID is set. All tasks in one call are expected to live in the same space — a task from another space is reported as failed, because its space does not have the tag

remove-release

The counterpart to assign-release: it detaches a release version tag from tasks instead of attaching it. Use it when a version was tagged by mistake — a failed build that never shipped, a typo in the version, or a re-tag that left both the old and the new version on the task. Only used with explicit user confirmation.

assign-release deliberately never replaces older version tags. Deleting them as a side effect of a write would mean a mis-derived commit range could destroy correct tags, so removal is this separate, explicit tool.

Parameters:

  • version (string, required): Release version matched verbatim against the tag name — case and spaces matter (e.g. "v1.17.4 (101)")
  • taskIds (string[], required, min 1): Task URLs, custom IDs (ABC-123), or numbers
  • deleteTagIfUnused (boolean, optional, default false): After removing the tag from the listed tasks, also delete it from the space — but only when no task uses it any more

Behavior:

  • By default the tag is only detached from the listed tasks and stays available in the space
  • Idempotent — re-running the same call changes nothing and reports every task as removed: false with a reason
  • A tag that is not on a task is not an error: that task reports removed: false with reason: "tag not present on task" and the rest of the batch continues
  • A tag that does not exist in the space is not an error either — nothing is changed and the result says so
  • Errors are collected per task, so one bad identifier does not abort the rest; the call is only reported as failed when every task fails

Deleting the tag from the space is a separate, off-by-default step because it is irreversible and strips the tag from every task in the space at once. It only happens when all of the following hold, and the result explains which one blocked it otherwise:

  • Every task in the call succeeded — an unreadable task might still carry the tag
  • A search across the space (including closed and archived tasks) finds no other task using it, after subtracting the tasks this call just untagged

License

MIT