This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MCP server bridging Motion's task management API with LLMs via the Model Context Protocol. Supports both local (stdio) and remote (Cloudflare Workers) deployment.
# Build & Run
npm run build # Compile TypeScript to dist/
npm run mcp # Start MCP server (compiled)
npm run mcp:dev # Start MCP server (ts-node, no build needed)
npm run type-check # Type checking without emitting files
# Testing (vitest)
npm test # Run all unit tests
npm test -- handlers.task # Run a single test file by name fragment
npm test -- --reporter=verbose # Run with verbose output
npm run test:integration # Integration tests (requires MOTION_API_KEY in .env)
# Worker (Cloudflare)
npm run worker:dev # Local Worker at http://localhost:8787
npm run worker:deploy # Deploy to Cloudflare
npm run worker:type-check # Type-check Worker code (separate tsconfig)
# Verify both entry points compile
npm run build && npm run worker:type-check
# Quick startup smoke test
timeout 3s npm run mcpUses vitest. Unit tests in tests/**/*.spec.ts, integration tests in tests/integration/**/*.integration.test.ts.
- Unit tests: 10s timeout, mocked services,
tests/setup.tssilences console.error - Integration tests: 60s timeout, sequential execution (rate limits), requires
MOTION_API_KEYenv var - Handler tests mock
MotionApiServiceandWorkspaceResolver; service returns useListResult<T>shape ({ items, truncation })
Both share all handlers, services, tools, and utilities — they differ only in transport and API key source.
- Stdio Server (
src/mcp-server.ts): UsesServerfrom MCP SDK. API key fromMOTION_API_KEYenv var. - Cloudflare Worker (
src/worker.ts): UsesMcpAgentfrom Cloudflare Agents SDK with Durable Objects. API key from Worker secret. Auth via secret token in URL path (/mcp/SECRET).
Command pattern: each resource type has a handler extending BaseHandler. HandlerFactory routes tool calls to handlers by tool name.
Handlers access this.motionService (API client) and this.workspaceResolver (name-to-ID resolution). Operations are dispatched via switch(operation) in handle().
ToolRegistry: Manages definitions and tier mappingsToolConfigurator: Validates config string, returns enabled tool setToolDefinitions: JSON Schema definitions for all tools
Configured via MOTION_MCP_TOOLS env var:
minimal(3): motion_tasks, motion_projects, motion_workspacesessential(7): + motion_users, motion_search, motion_comments, motion_schedulescomplete(10, default): + motion_custom_fields, motion_recurring_tasks, motion_statusescustom:tool1,tool2: Exact tool selection
Centralized Motion API client (https://api.usemotion.com/v1). Features:
- Workspace name-to-ID and project name resolution
- Caching with per-resource TTLs (defined in
src/utils/constants.tsasCACHE_TTL) - Retry with exponential backoff (GET requests only, config in
RETRY_CONFIG) - Pagination with configurable limits (
LIMITS.MAX_PAGES,LIMITS.MAX_PAGE_SIZE) - All logging to stderr in JSON format (MCP compliance)
- sanitize.ts:
sanitizeTextContent,sanitizeName,sanitizeDescription,sanitizeCommentContent— strip HTML/script tags, normalize whitespace. Length limits are caller's responsibility. - responseFormatters.ts: Consistent MCP response formatting for all resource types (tasks, projects, workspaces, comments, etc.)
- constants.ts:
LIMITS,RETRY_CONFIG,CACHE_TTL,createMinimalPayload()(strips undefined/empty strings, preservesnulland[]),VALID_PRIORITIES,parseFilterDate() - workspaceResolver.ts: Workspace name resolution with fuzzy matching
- parameterUtils.ts: Parameter parsing (auto-scheduling, duration, labels)
- validator.ts: Runtime input validation via AJV against tool JSON schemas
- jsonSchemaToZod.ts: Converts JSON Schema to Zod for Worker entry point (excluded from main build)
| Tool | Operations |
|---|---|
| motion_tasks | create, list, get, update, delete, move, unassign |
| motion_projects | create, list, get |
| motion_workspaces | list, get |
| motion_users | list, current |
| motion_search | content |
| motion_comments | list, create |
| motion_custom_fields | list, create, delete, add_to_project, remove_from_project, add_to_task, remove_from_task |
| motion_recurring_tasks | list, create, delete |
| motion_schedules | list |
| motion_statuses | list |
- Two tsconfigs:
tsconfig.json(CommonJS, excludesworker.tsandjsonSchemaToZod.ts) andtsconfig.worker.json(ES2022, bundler resolution, Workers types) - Stdio server compiles to
dist/; Wrangler bundles Worker separately - Changes to shared code (handlers, services, tools, utils) affect both entry points — always verify both builds pass
- Create handler class extending
BaseHandlerinsrc/handlers/ - Implement
handle()method with operation switch - Register in
HandlerFactory - Add tool schema in
ToolDefinitions.ts(auto-registered viaallToolDefinitions) - Add to appropriate tier in
ToolRegistry.ts