Skip to main content
Glama
matbour

usemotion-mcp-server

by matbour
README.md
# usemotion-mcp-server

MCP server for the [Motion](https://www.usemotion.com) API, running on [Bun](https://bun.sh). Supports two transports, selected with the `MCP_TRANSPORT` environment variable:

- `stdio` (default) — local use; reads `MOTION_API_KEY` from the environment
- `http` — streamable HTTP, deployable to Vercel; each client sends its own Motion API key as a request header (`X-API-Key` or `Authorization: Bearer`), which is forwarded to the Motion API for that request only. The server stores no credentials; requests without a key get a 401.

## Setup

1. Create an API key in Motion (Settings → API).
2. For stdio mode, create a `.env` file in the project root:

   ```sh
   cp .env.example .env   # then fill in MOTION_API_KEY
   ```

3. Install dependencies:

   ```sh
   bun install
   ```

## Build

Compile a standalone binary (no Bun required at runtime):

```sh
bun run compile   # produces bin/usemotion-mcp-server
```

The binary still auto-loads `.env` from the directory it runs in; otherwise set `MOTION_API_KEY` in its environment.

## Usage

### HTTP mode (local)

```sh
MCP_TRANSPORT=http bun run src/index.ts   # serves http://localhost:3000/api/mcp (PORT to override)
```

Clients authenticate per request:

```sh
claude mcp add --transport http motion http://localhost:3000/api/mcp --header "X-API-Key: your-key-here"
```

### Deploy to Vercel

`api/mcp.ts` + `vercel.json` (`"bunVersion": "1.x"`) make the repo deployable as-is:

```sh
vercel deploy   # then connect clients to https://<your-app>.vercel.app/api/mcp
```

Do not set `MOTION_API_KEY` on the deployment — keys come from client headers and are never stored.

### stdio mode — Claude Code

```sh
claude mcp add motion -- bun run /path/to/usemotion-mcp-server/src/index.ts
```

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "motion": {
      "command": "bun",
      "args": ["run", "/path/to/usemotion-mcp-server/src/index.ts"],
      "env": { "MOTION_API_KEY": "your-key-here" }
    }
  }
}
```

Note: Bun auto-loads `.env` from the working directory, so the `env` block is only needed if the server isn't started from the project root.

## Tools

| Tool | Description |
|------|-------------|
| `list_tasks` | List tasks with filters (workspace, project, status, assignee, name, label) |
| `get_task` | Get a task by id |
| `create_task` | Create a task (supports auto-scheduling, priority, labels, markdown description) |
| `update_task` | Update a task |
| `delete_task` | Delete a task |
| `move_task` | Move a task to another workspace |
| `unassign_task` | Remove a task's assignee |
| `list_projects` | List projects in a workspace |
| `get_project` | Get a project by id |
| `create_project` | Create a project |
| `list_workspaces` | List workspaces |
| `list_users` | List users (find assignee ids) |
| `get_current_user` | Get the user tied to the API key |
| `list_statuses` | List task statuses in a workspace |
| `get_schedules` | Get schedules used by auto-scheduling |
| `list_comments` | List comments on a task |
| `create_comment` | Comment on a task |

## Notes

- Motion's rate limit is 12 requests/minute on individual plans; the client retries 429s with backoff (honors `Retry-After`).
- List endpoints are cursor-paginated: pass `cursor` from the previous response's `meta.nextCursor`.

## Development

```sh
bun run typecheck             # tsc --noEmit
bun run scripts/smoke.ts      # stdio smoke test: lists tools, then create → get → update → delete a task
bun run scripts/smoke-http.ts # HTTP smoke test: 401 without key, then list tools + workspaces via header auth
                              # (start the server first: MCP_TRANSPORT=http bun run src/index.ts)
```