Skip to main content
Glama
README.md
# trello-mcp-server

[![Checks](https://github.com/perkarlo/trello-mcp-server/actions/workflows/checks.yml/badge.svg)](https://github.com/perkarlo/trello-mcp-server/actions/workflows/checks.yml)

Trello for Claude — one implementation, two clients.

- **MCP server** (`build/index.js`) — Claude Desktop, over stdio
- **CLI** (`build/cli.js`) — Claude Code, via the `trello` skill

Runs locally against your own Trello API key. No third-party server sees your boards.

![trello CLI against a sample board: boards, cards sorted by value, one card in full](docs/cli.png)

## Setup

```bash
npm install
npm run build
```

Credentials are read from `TRELLO_KEY` / `TRELLO_TOKEN`, falling back to
`~/.config/trello/credentials.json`:

```json
{"key": "...", "token": "..."}
```

Get an API key at <https://trello.com/power-ups/admin>, then a token from:

```
https://trello.com/1/authorize?expiration=never&scope=read,write&response_type=token&name=ClaudeDesktop&key=YOUR_KEY
```

The `read,write` scope cannot delete a board or a list.

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "trello": {
      "command": "node",
      "args": ["/absolute/path/to/trello-mcp-server/build/index.js"],
      "env": {
        "TRELLO_ALLOW_WRITE": "false"
      }
    }
  }
}
```

Flip `TRELLO_ALLOW_WRITE` to `"true"` when you want Claude to be able to create and move
cards. While it is off, the write tools are not merely refused — they are never advertised.

### Claude Code

A skill can invoke the CLI directly by absolute path:

```bash
node /absolute/path/to/trello-mcp-server/build/cli.js boards
```

## Tools (MCP)

25 tools in three permission classes. With the default `TRELLO_ALLOW_WRITE=false`, only
the 11 non-write ones are advertised.

| Tool | Class | |
|---|---|---|
| `trello_list_boards` | READ | every open board |
| `trello_get_board` | READ | lists + cards, 15 cards per list by default |
| `trello_get_board_meta` | READ | lists, labels, members, custom field defs in one call |
| `trello_list_cards` | READ | filter by label, sort/filter by custom field |
| `trello_get_card` | READ | full detail incl. checklists and comments |
| `trello_search_cards` | READ | archived hidden unless asked for |
| `trello_get_due` | READ | `{upcoming, overdue}`, kept separate |
| `trello_get_timeline` | READ | cards carrying start/due |
| `trello_get_activity` | READ | what changed since a cursor; returns `nextSince` |
| `trello_list_saved_searches` | READ | list, or run one by name |
| `trello_export_board` | REPORT | JSON or CSV into `TRELLO_EXPORT_DIR`, timestamped, mode 600, never overwrites; returns the path, not the contents |
| `trello_create_card` | WRITE | desc, dates, labels, members, checklist, fields, template |
| `trello_update_card` | WRITE | title, desc, dates, due-complete, `isTemplate` |
| `trello_move_card` | WRITE | list, optionally cross-board |
| `trello_archive_card` | WRITE | `restore` un-archives |
| `trello_add_comment` | WRITE | notifies other members on a shared board |
| `trello_set_custom_field` | WRITE | set or clear a value on a card |
| `trello_manage_checklist` | WRITE | create, add items, check/uncheck, per-item due + assignee |
| `trello_set_card_label` | WRITE | add/remove |
| `trello_set_card_member` | WRITE | add/remove |
| `trello_add_attachment` | WRITE | URL only; file upload is CLI-only |
| `trello_create_board` | WRITE | own lists instead of To Do / Doing / Done; `fields` installs value/points/boost |
| `trello_create_list` | WRITE | a list on a board that already exists |
| `trello_create_label` | WRITE | names one of the six unnamed defaults before adding a new one |
| `trello_create_custom_field` | WRITE | defines the field itself; a list field needs its options up front |

Every board / list / card / label / member argument takes a **fuzzy name** — `northwind`
finds "Northwind Consulting", Turkish characters are folded. A name matching more than one
thing fails and lists the candidates instead of guessing.

## CLI

```
me · boards · lists · board · cards · card · add · move · update · archive · comment
checklist · check · label · assign · search · due · newlist · newboard · newlabel
labels · members · newfield · fields · field · checkitem · timeline · activity
attachments · attach · export · saved · templates · refresh
```

`trello <command> --help` for one command, `trello` for the list. Output is
human-readable Turkish; `--json` switches any command to raw JSON.

```bash
trello cards "Development" --label trello-mcp-server --where "points=1"
trello newboard "Roadmap" --lists "Backlog,TODO,Done" --fields
trello newlabel "Roadmap" "infra" --color sky
trello newfield "Roadmap" "risk" --options "low,medium,high"
trello update "aylık kira" --as-template      # then: trello add ... --template "aylık kira"
trello export "Development" --csv           # → trello-development.export.csv; never overwrites
```

`newboard --fields` installs the account's `value`/`points`/`boost` scheme; `newfield`
defines any other custom field (`--type` is inferred as `list` when `--options` is given).

`--label` takes several names — OR between them, AND with `--where`. Repeated `list`-kind
flags accumulate, so `--label a --label b` and `--label a b` mean the same thing. Their
first value may start with a dash (`--checklist "--where ile calisir"`); for a later one,
use `--flag=value`.

The CLI **ignores `TRELLO_ALLOW_WRITE`**. It is driven by a human at a terminal or by
Claude Code, which runs its own confirmation step; the permission classes exist to
constrain the tool surface exposed to Claude Desktop.

## Configuration

| Variable | Default | Meaning |
|---|---|---|
| `TRELLO_KEY` | — | API key (or `key` in the credentials file) |
| `TRELLO_TOKEN` | — | Token (or `token` in the credentials file) |
| `TRELLO_ALLOW_READ` | `true` | Read tool class |
| `TRELLO_ALLOW_WRITE` | `false` | Write tool class — off by default |
| `TRELLO_ALLOW_REPORT` | `true` | Export/report tool class |
| `TRELLO_CACHE_TTL` | `900` | Board/list cache seconds; card data is never cached |
| `TRELLO_EXPORT_DIR` | `~/.local/share/trello/exports` | Where the MCP export tool writes; the model cannot name a path |
| `LOG_LEVEL` | `info` | |

Permission gating applies to the **MCP server only**. The CLI is invoked by a human or by
Claude Code, which does its own confirmation, so it is always fully enabled.

The MCP server keeps its board/list cache in memory. The CLI exits after every command, so
it persists the same cache to `~/.cache/trello/mcp-cache.json` (mode 600) and clears it on
`trello refresh`. Cache I/O is best-effort and can never fail a command.

## Safety

- **No delete.** Archiving is the destructive limit and is reversible in the Trello UI.
- **Secrets are redacted.** Trello echoes the full request URL — key and token included —
  inside error bodies; every message leaving the process is masked.
- **Ambiguity is never guessed.** A name matching two boards returns both and fails, so
  the caller can ask rather than act on the wrong one.
- **Archived cards never resolve.** Trello's search returns them; they are filtered out,
  so a write cannot land on an invisible card.
- **Exports never overwrite.** Files are created with `wx` and mode 600: an existing file
  or a symlink planted at the destination fails the export instead of being replaced, and
  the dump (every comment, the last 1000 actions) is readable by the owner only. Over MCP
  the model cannot name a destination at all; a `path` argument is refused, and the file
  lands in `TRELLO_EXPORT_DIR` under a timestamped name.
- **CSV cells cannot be formulas.** A cell starting with `=`, `+`, `-`, `@`, tab or CR gets
  an apostrophe prefix, so a card named `=HYPERLINK(...)`, planted by any board member or
  through email-to-board, opens as text in Excel, Numbers and Sheets.
- **Default export names end in `.export.json` / `.export.csv`.** Add `*.export.*` to your
  own `.gitignore`; this repo's already has it, so a dump run inside a repo never lands
  in a commit by accident.

## Development

```bash
npm run dev            # tsc --watch
npm run test:unit      # mocked, no network
npm run test:readonly  # live, read-only
```

See [CLAUDE.md](CLAUDE.md) for architecture and conventions.

## License

MIT