Skip to main content
Glama
README.md
# focalboard-mcp

[![CI](https://github.com/giordano137/focalboard-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/giordano137/focalboard-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)

An [MCP](https://modelcontextprotocol.io) server for [Focalboard](https://github.com/mattermost-community/focalboard),
the self-hosted Trello/Notion/Asana alternative. Lets an MCP client (Claude Code, Claude Desktop, etc.) read and
manage boards and cards on a self-hosted Focalboard instance.

Property values are matched by human-readable name and label (e.g. `"Status": "Done"`), not by Focalboard's
internal property/option ids, so the model doesn't need to know your board's schema up front. Unknown property/option
names get a "did you mean" suggestion instead of a bare error.

## Why another Focalboard MCP server?

A few already exist. This one is built directly against Focalboard's server source (`server/api/*.go`,
`server/model/*.go`, and the webapp's block components) rather than reverse-engineered from another wrapper, and
goes further on feature depth: comments, checklists (with the `contentOrder` bookkeeping Focalboard's UI needs to
actually show them), image attachments returned as real MCP image content, an instance-wide search, and a fuzzy
`find_cards`.

### Focalboard quirks this handles (found by reading the source, not guessing)

- Cards have a dedicated, flat-properties API (`/boards/{id}/cards`, `PATCH /cards/{id}`) distinct from the generic
  blocks API.
- Creating a block via the generic blocks API requires client-supplied non-zero `createAt`/`updateAt` timestamps, or
  the server 400s.
- Login itself needs the `X-Requested-With` CSRF header, not just authenticated requests.
- Checklist items only render in Focalboard's UI if you also append them to the card's `contentOrder`.
- A card's actual body text lives in separate `text` content blocks, not on the card itself.
- Image attachments come back from Focalboard's file endpoint as `application/octet-stream` regardless of the real
  file type, so `get_card` derives the MIME type from the filename extension instead of trusting that header.

## Scope

Covers boards, cards, comments, checklists, and image attachments — enough for day-to-day project planning. It does
not cover board/template creation, member management, or sharing/permissions; PRs welcome if you need those.

## Compatibility

Targets standalone Focalboard (Personal Server / Team Edition) using its normal `/api/v2` username+password login.
Two deployment modes reject that login entirely (confirmed in `server/api/auth.go`) and won't work with this server:

- **Mattermost plugin mode** — Focalboard running as a Boards plugin inside Mattermost authenticates through
  Mattermost instead; the standalone login endpoint is disabled.
- **Single-user mode** — instances configured with a fixed `FOCALBOARD_SINGLE_USER_TOKEN` also reject username/
  password login.

If your instance runs one of those, `pnpm smoke` will fail fast with a clear error instead of doing anything
destructive.

## Tools

| Tool | Description |
|---|---|
| `list_teams` | List teams visible to the authenticated user |
| `list_boards` | List boards for a team (`teamId` optional if the instance has only one team) |
| `get_board` | Get a board's properties (columns) and their options |
| `search_boards` | Search board titles across the whole instance |
| `list_cards` | List cards on a board, with properties resolved to names/labels |
| `find_cards` | Fuzzy-search card titles on a board |
| `get_card` | Get a card by id, including properties, body content, comments, checklist items, and any attached images |
| `create_cards` | Create one or more cards on a board |
| `update_card` | Update a card's title and/or properties |
| `delete_card` | Delete a card |
| `add_comment` | Add a comment to a card |
| `add_checklist_item` | Add a checklist item to a card |
| `set_checklist_item` | Check/uncheck a checklist item |

## Getting Started

This isn't published to npm (yet) — you clone and build it locally, point it at your Focalboard instance, and
register it with your MCP client. Requires Node.js >= 20.

### 1. Clone and build

```bash
git clone https://github.com/giordano137/focalboard-mcp.git
cd focalboard-mcp
pnpm install
pnpm build
```

This produces `dist/index.js`, the actual server your MCP client will run.

### 2. Configure credentials

```bash
cp .env.example .env
```

Edit `.env` with your instance's details:

```bash
FOCALBOARD_HOST=https://your-focalboard-instance.example
FOCALBOARD_USERNAME=your-username
FOCALBOARD_PASSWORD=your-password
```

`.env` is gitignored — it never gets committed. The server authenticates via Focalboard's session login
(`POST /api/v2/login`) and re-authenticates automatically if the session expires; see [Security](#security) for why
credentials belong in this file and not on the command line.

Sanity-check the connection before registering anything (read-only, touches nothing):

```bash
pnpm smoke
```

### 3. Register with your MCP client

**Claude Code**, registered once, available in every session (`-s user`) — not tied to this repo's directory:

```bash
claude mcp add focalboard -s user -- \
  node --env-file=/absolute/path/to/focalboard-mcp/.env /absolute/path/to/focalboard-mcp/dist/index.js
```

New registrations need a fresh Claude Code session to be picked up — an already-running session won't see it.

**Claude Desktop** (or any other MCP client that reads a `mcpServers` JSON config): add this to
`claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "focalboard": {
      "command": "node",
      "args": [
        "--env-file=/absolute/path/to/focalboard-mcp/.env",
        "/absolute/path/to/focalboard-mcp/dist/index.js"
      ]
    }
  }
}
```

Then restart the client. Either way: use `--env-file` pointing at your `.env`, not `-e`/`--env` flags with the
credentials inline — those end up in plaintext in the client's own config file and shell history.

### 4. Use it

Once registered, there's nothing to invoke manually — just talk to your MCP client and it picks the right tools.
A few examples of what that looks like:

> "What boards do I have on Focalboard?"

Calls `list_teams` (skipped automatically if you only have one team), then `list_boards`.

> "Show me everything that isn't Done on the Kaizen board"

Calls `get_board` to see the `Status` property's valid options, then `list_cards` and filters by the resolved
`Status` value — no need to know Focalboard's internal property/option ids.

> "Create a card 'Fix login bug' with Status Todo and Priority High"

Calls `create_cards` with `properties: {"Status": "Todo", "Priority": "High"}` — plain names and labels, matched
case-insensitively. A typo like `"Statuss"` or `"Todoo"` comes back with a "did you mean" suggestion instead of a
bare error.

> "What's in the 'Redesign' card, including any screenshots?"

Calls `get_card`, which returns properties, body text, comments, and checklist items as JSON, plus any attached
images inline as actual image content the model can see — not just a filename.

> "Mark 'write tests' as done on that card and add a comment that it's ready for review"

Calls `set_checklist_item` and `add_comment`.

## Development

```bash
pnpm dev         # run from source with tsx
pnpm typecheck
pnpm lint
pnpm test        # watch mode
pnpm test:run    # single run
pnpm smoke       # read-only sanity check against your real instance
pnpm backup      # export all teams/boards/cards to backups/*.json (gitignored)
pnpm write-test  # create/update/comment/checklist/delete a throwaway card on a test board, end to end
```

CI runs typecheck/lint/test/build on Node 20 and 22 for every push and PR (`.github/workflows/ci.yml`).

## Contributing

Issues and PRs welcome — see [Scope](#scope) for what's not covered yet. Keep changes covered by tests; `pnpm smoke`,
`pnpm write-test`, and `pnpm backup` are also useful for verifying against a real instance before opening a PR.

## Security

Credentials are only ever read from environment variables (see `.env.example`) — never pass them as CLI flags to an
MCP client, since those tend to land in the client's own config file and shell history in plaintext. There's
currently no supported way to use a scoped, long-lived personal access token instead of a full username/password
session login. Please report security issues via [GitHub's private vulnerability reporting](https://github.com/giordano137/focalboard-mcp/security/advisories/new) rather than a public issue.

## License

[MIT](./LICENSE)

TDQS

B3.2/5.0

Scored across 13 tools

Disambiguation4/5

Most tools have distinct purposes, but 'find_cards' and 'list_cards' both retrieve cards, and 'search_boards' overlaps with 'list_boards', potentially causing confusion for an agent if descriptions aren't carefully parsed.

Naming Consistency4/5

All tools use snake_case verb_noun naming, but pluralization is inconsistent (e.g., 'create_cards' vs 'delete_card') and some compound nouns (e.g., 'add_checklist_item') break the simple pattern.

Tool Count5/5

13 tools is well-scoped for a board/card management server, covering core operations without being overwhelming or too sparse.

Completeness3/5

Covers many operations on cards and boards, but lacks board creation, update, or deletion, and missing comment/checklist item deletion, creating notable gaps in the lifecycle.

Maintenance

ActivityStale
ResponsivenessNo issues