Skip to main content
Glama

ClickUp MCP Server

by Nazruden
# Sage's AI Journal for @nazruden/clickup-server This journal captures key learnings, patterns, and specific instructions encountered during the development of the ClickUp MCP Server. ## Learnings & Patterns ### 1. PowerShell Environment Variable Syntax (Critical for Local Testing) - **Date:** 2024-07-15 - **Context:** When running commands like `npx @modelcontextprotocol/inspector ...` or `npm run dev ...` locally in PowerShell, environment variables need to be set using PowerShell syntax. - **Pattern:** ```powershell $env:VAR_NAME='value'; your_command --here ``` - **Incorrect (Bash/Zsh syntax):** ```bash VAR_NAME=value your_command --here ``` - **Example (from project):** ```powershell $env:CLICKUP_PERSONAL_TOKEN='pk_xxxxxxxx'; npx @modelcontextprotocol/inspector node --loader ts-node/esm src/index.ts ``` Or, for `npm run dev` if it needs the token directly (though `dotenv` handles it from `.env`): ```powershell $env:CLICKUP_PERSONAL_TOKEN='pk_xxxxxxxx'; npm run dev ``` - **Tags:** `powershell`, `environment-variables`, `local-development`, `testing`, `mcp-inspector` ### 2. MCP Inspector and Stdio Logging (Critical for MCP Server Dev) - **Date:** 2024-07-15 - **Context:** The `MCP Inspector` (and any MCP client using `StdioServerTransport`) expects **only valid MCP JSON messages** on `stdout` from the server. - **Problem:** Any other output (e.g., `console.log`, `logger.debug` from the server if not redirected, or verbose output from tools like `ts-node-dev`) sent to `stdout` will corrupt the MCP message stream, leading to JSON parsing errors in the Inspector (e.g., `SyntaxError: Unexpected token ... is not valid JSON`). - **Solution/Mitigation:** - Ensure the application logger (e.g., `winston` in this project) is configured to write to `stderr` or a file when in "production" or "stdio" mode, especially for `debug` and `info` levels if they are too verbose for `stdout`. - For `npm run dev` which uses `ts-node-dev`: - `ts-node-dev` might output its own messages (like compilation status) to `stdout`. Investigate `ts-node-dev` options (e.g., `--quiet`, `--transpile-only --no-cache` with manual restarts for less output, or redirecting its specific output if possible) to minimize non-MCP output. - The command `npx @modelcontextprotocol/inspector node --loader ts-node/esm src/index.ts` directly invokes node with `ts-node/esm` loader, which might be cleaner than `npm run dev` if `ts-node-dev` proves too noisy. - The MCP SDK's `StdioServerTransport` should only send MCP protocol messages via `process.stdout.write()`. - **Observed Errors (MCP Inspector):** `SyntaxError: Unexpected end of JSON input`, `SyntaxError: Unexpected token '>', "> @nazrude"... is not valid JSON`, `SyntaxError: Unexpected token 'I', "[INFO]..." is not valid JSON`. - **Tags:** `mcp`, `stdio`, `mcp-inspector`, `logging`, `debugging`, `ts-node-dev`, `stdout`, `stderr` ### 3. ClickUp API v2 - Spaces & Folders Endpoints - **Date:** 2024-07-15 - **Context:** Reference for implemented Space and Folder management tools. Note that `team_id` in API v2 refers to Workspace ID. - **Endpoints Used:** - **Spaces:** - `GET /team/{team_id}/space` (Get Spaces) - `POST /team/{team_id}/space` (Create Space) - `GET /space/{space_id}` (Get Space) - `PUT /space/{space_id}` (Update Space) - `DELETE /space/{space_id}` (Delete Space) - **Folders:** - `GET /space/{space_id}/folder` (Get Folders) - `POST /space/{space_id}/folder` (Create Folder) - `GET /folder/{folder_id}` (Get Folder) - `PUT /folder/{folder_id}` (Update Folder) - `DELETE /folder/{folder_id}` (Delete Folder) - **Tags:** `clickup-api`, `api-v2`, `spaces`, `folders`, `reference` ### 4. Jest Mocking for Axios in Service Tests - **Date:** 2024-07-15 - **Context:** The pattern for unit testing `ClickUpService` methods that use `axios`. - **Pattern (`src/__tests__/services/clickup.service.test.ts`):** 1. `jest.mock("axios");` at the top level. 2. `const mockedAxios = axios as jest.Mocked<typeof axios>;` to get typed mock. 3. In `beforeEach`: - Clear mocks: `mockedAxios.create.mockClear(); mockedAxios.get.mockClear();` etc. - Mock `axios.create()` to return a mocked AxiosInstance: ```typescript const mockAxiosInstance = { defaults: { headers: { common: {} } }, interceptors: { request: { use: jest.fn(), eject: jest.fn() }, response: { use: jest.fn(), eject: jest.fn() }, }, get: mockedAxios.get, // Point to top-level mocks post: mockedAxios.post, put: mockedAxios.put, delete: mockedAxios.delete, } as unknown as jest.Mocked<typeof axios>; mockedAxios.create.mockReturnValue(mockAxiosInstance); ``` 4. In individual tests, mock specific calls: `mockedAxios.get.mockResolvedValueOnce(mockResponse);` or `mockedAxios.post.mockRejectedValueOnce(new Error("API Error"));`. - **Tags:** `jest`, `unit-testing`, `mocking`, `axios`, `typescript` ### 5. TypeScript Type Casting for MCP Tool Arguments - **Date:** 2024-07-15 - **Context:** Safely casting `request.params.arguments` (which is `Record<string, unknown> | undefined`) in `src/index.ts` tool handlers to specific parameter types. - **Pattern:** Use `as unknown as <SpecificType>`. ```typescript // Example for clickup_get_spaces handler const args = request.params.arguments as unknown as GetSpacesParams; if (!args.team_id || typeof args.team_id !== "string") { throw new Error("Team ID (Workspace ID) is required."); } // Now 'args' can be safely used as GetSpacesParams ``` - **Reasoning:** This tells TypeScript that the developer is asserting the type after performing necessary runtime checks (like `if (!args.team_id ...)`). Direct casting `as SpecificType` might hide potential errors if the object shape doesn't actually match. - **Tags:** `typescript`, `type-casting`, `mcp-sdk`, `tool-handling` ## [YYYY-MM-DD] - Refactoring and Tool Expansion Cycle **Tags:** #refactoring, #testing, #mocking, #architecture, #mcp **Learnings:** 1. **Architecture - Resource-Based Structure:** Refactored the monolithic `ClickUpService`, tool definitions/handlers in `index.ts`, and `clickup.service.test.ts` into resource-specific files (e.g., `task.service.ts`, `task.tools.ts`, `task.service.test.ts`). - **Pattern:** Place service logic in `src/services/resources/`, tool definitions/handlers in `src/tools/`, service tests in `src/__tests__/services/resources/`, and tool handler tests in `src/__tests__/tools/`. - **Benefit:** Improved modularity, readability, and maintainability. Smaller file sizes make components easier to understand and test. - **Implementation:** `ClickUpService` now acts as a facade, instantiating resource services and providing accessors. `index.ts` imports tools/handlers from `src/tools/*`. 2. **Testing - Service Layer vs. Tool Handlers:** - Service layer tests (`*.service.test.ts`) focus on mocking the direct external dependency (`axios`) and verifying API call parameters and response handling. - Tool handler tests (`*.tools.test.ts`) focus on mocking the internal dependency (`ClickUpService` and its resource services) and verifying input validation, correct service method calls, and MCP response formatting. 3. **Testing - Mocking Nested Services in Handlers:** Mocking `ClickUpService` and specifically its resource service getters (like `viewService`) for tool handler tests proved complex. - **Challenge:** Ensuring the mocked `ClickUpService` instance was correctly typed and that its mocked getters returned objects containing the specifically defined method mocks (`jest.fn()`) caused persistent linter errors. - **Current (Paused) Approach:** Using `jest.mock()` on the service, instantiating the mocked class, and then directly assigning the mock implementation object to `(mockInstance as any).resourceService = { method1: mockFn1, ... }` seemed the most promising but still hit type issues. Further investigation needed when resuming. - **Key Takeaway:** Testing functions that orchestrate calls across different service instances requires careful mock setup, especially with getters involved. 4. **MCP - Stdio Logging:** Need to remain vigilant about potential interference between server logs (`console.log`, `logger.debug`, etc.) and MCP message parsing when running via stdio, especially during development (`npm run dev`) and integration testing with MCP Inspector. - **Mitigation:** Ensure critical logging goes to stderr or a file, or configure `ts-node-dev`/logging library appropriately if stdout logging is unavoidable during stdio operation. 5. **ClickUp API V2 Notes:** - Docs API appears less mature/stable than other parts (Search, Page retrieval/editing implemented). - View API allows fetching views by parent (Team, Space, Folder, List) and getting tasks within a view (with pagination). - Custom Field setting requires careful handling of `value` based on field type; `value_options` is used for specifics like date+time. --- _This journal will be updated as new significant learnings or patterns emerge._

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/Nazruden/clickup-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server