Openbook

Board MCP server

Connect Claude or ChatGPT to one Kanban board over MCP — read the columns, write stories, move cards, leave comments, and run a dev/QA loop between agents.

What the board MCP server is

MCP is a standard for giving a model tools over HTTP. That page is about Openbook using somebody else's MCP server. This one is the other direction: every Kanban board in Openbook is an MCP server, and you can point your own Claude or ChatGPT at it.

Once connected, the assistant is not describing your board — it is working it. It reads the columns, writes user stories into one, picks a card out of another, moves cards between columns, and leaves comments on them. That is enough for a whole loop: a model writes stories, a development agent takes one from a column and builds it, a QA agent tests it and comments, the developer reads the comments and fixes. Round and round until the card reaches Done, with the board as the shared memory none of them has to hold.

Three things make that safe enough to hand to a machine.

A token is one board. The credential names exactly one board, and no tool takes a board as a parameter. There is nothing an assistant can substitute to reach a second board — not an id, not a name. A card key from another board comes back as "no card on this board".

Reading and writing are separate keys. A read token can call every reading tool and none of the writing ones. Connect a model you are still learning to trust with a read token and the worst case is that it tells you something wrong.

Nothing impersonates a person. A card or comment written through a token is signed with that token's name — "Claude", "QA agent" — wherever Openbook shows an author, and cards are flagged as machine-written. No row is ever attributed to a member's account.

Turn it on and mint a token

The MCP server shares the Board API's switch and its tokens.

  1. Open the Kanban room and click Share on the board.
  2. Flip API access on. (The Public board toggle above it is unrelated — either can be on without the other.)
  3. Under Tokens, name the token for the agent that will hold it — "Claude", "Dev agent", "QA agent" — and choose what it may do:
    • Read only — can read every card on the board: descriptions, assignees, dates, labels, checklists. Cannot change anything.
    • Can write — everything a read token can do, plus create cards, edit them, move them between columns, and post comments.
  4. Click Create token and copy it. The Share dialog shows which kind each existing token is.

One token per agent is the point. Delete one and exactly that agent is cut off, immediately, while the others keep working. Flipping API access off cuts all of them off at once without deleting anything.

A write token is a key to the board, it does not expire, and anything holding it is editing your work. Mint one per agent, and delete it when that agent is done.

Tokens minted before write scopes existed are read-only, and stay that way. There is no way for an old token to become a write token by accident — you mint a new one.

The endpoint

POST https://app.openbook.work/kanban/api/mcp
Authorization: Bearer <token>

Streamable HTTP, protocol version 2025-06-18. The path is the same wherever Openbook runs; on a self-hosted install, replace app.openbook.work with your own host.

The token travels in the Authorization header and only there — unlike the read-only Board API, there is no ?token= query form. A write credential in a URL ends up in browser history, proxy logs and screenshots, and this one can move your work.

Opening the URL in a browser answers with a short description of the server rather than a 404, which is a quick way to check you have the host right.

Add it to Claude

Claude Desktop. Open its configuration file — Settings → Developer → Edit Config — and add:

{
  "mcpServers": {
    "openbook-board": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://app.openbook.work/kanban/api/mcp",
        "--header", "Authorization: Bearer obk_your_token_here"
      ]
    }
  }
}

Restart Claude Desktop. The board's tools appear under the connector icon in the composer.

Claude Code. One command:

claude mcp add --transport http openbook-board \
  https://app.openbook.work/kanban/api/mcp \
  --header "Authorization: Bearer obk_your_token_here"

Add it to ChatGPT

In ChatGPT, custom MCP connectors live under Settings → Connectors → Advanced → Developer mode, then Create:

  • Name — "Openbook board", or the board's name.
  • MCP server URLhttps://app.openbook.work/kanban/api/mcp
  • Authentication — API key / access token, with the token you minted. ChatGPT sends it as Authorization: Bearer <token>, which is what this server expects.

Some ChatGPT surfaces only expose search-and-fetch connectors. If yours will not accept a custom MCP URL, the Board API still gives it a read-only view of the same board.

The tools

Nine tools. Reads work with either kind of token; the four writing tools require a write token and tell a read-only caller exactly why they refused.

A card is named by its key — an opaque string like lCGmgWBfd8xw that comes back from list_cards. Its reference number works too, with or without a hash: 7 or #7.

A column is named the way it appears on the board — "In Progress", "QA" — case-insensitively, or by the key from list_columns. There is also a holding area for cards that are off the board entirely, keyed __backlog. It has a deliberately odd name because most boards here also have a real column called Backlog, and the two must not be confused: the word "backlog" means the real column whenever the board has one.

board_overview

The whole board in one call — every column in order, every card with description, assignee, dates, priority, story points, labels, tags and checklist. No parameters. Identical to what the Board API returns.

list_columns

The board's columns, left to right, with how many cards each holds. No parameters.

{
  "board": "Product Roadmap",
  "columns": [
    { "key": "col40", "name": "Backlog",     "position": 0, "card_count": 2, "wip_limit": null },
    { "key": "col41", "name": "To Do",       "position": 1, "card_count": 0, "wip_limit": null },
    { "key": "col42", "name": "In Progress", "position": 2, "card_count": 1, "wip_limit": 3    },
    { "key": "col43", "name": "QA",          "position": 3, "card_count": 0, "wip_limit": null },
    { "key": "col44", "name": "Done",        "position": 4, "card_count": 7, "wip_limit": null }
  ]
}

list_cards

Cards in a compact shape — enough to choose one, not enough to work from. Filters combine.

Parameter Type Meaning
column string Only cards in this column.
label string Only cards carrying this label (exact, case-insensitive).
updated_since string Only cards changed at or after this ISO timestamp or YYYY-MM-DD date.
include_backlog boolean Default true. False hides cards that are off the board.
limit integer Default 50, maximum 200.
{
  "total_matching": 1,
  "returned": 1,
  "cards": [
    {
      "key": "lCGmgWBfd8xw",
      "reference": "#1",
      "title": "Universal login",
      "column": "QA",
      "in_backlog": false,
      "priority": "high",
      "story_points": 3,
      "assignee": null,
      "due_date": null,
      "labels": ["story"],
      "author": "Claude",
      "updated_at": "2026-09-11T01:08:28.814Z"
    }
  ]
}

When more cards match than the limit allows, the response says so and suggests narrowing the filter. author is set only on cards written by a token; a card a person made has none.

get_card

One card in full: everything above, plus the description, checklist, the whole comment thread oldest-first, tags, and any linked pull requests.

Parameter Type Required
card string yes
{
  "key": "lCGmgWBfd8xw",
  "reference": "#1",
  "title": "Universal login",
  "column": "QA",
  "description": "As a returning user I want one sign-in across spaces so that I stop re-entering my password.",
  "priority": "high",
  "story_points": 3,
  "labels": ["story"],
  "tags": [],
  "ai_generated": true,
  "author": "Claude",
  "checklist": [
    { "title": "SSO redirect works",          "done": true },
    { "title": "Logout clears every session", "done": false }
  ],
  "comments": [
    { "author": "Dev agent", "by_person": false, "text": "Built. Branch `ob-1/universal-login`. Ready for QA.", "at": "2026-09-11T01:08:28.852Z" },
    { "author": "QA agent",  "by_person": false, "text": "Logout only clears the current tab. Sending it back.", "at": "2026-09-11T01:08:28.887Z" }
  ],
  "pull_requests": [],
  "created_at": "2026-09-11T01:08:28.814Z"
}

by_person is the field an agent should read before replying: false means another machine wrote it.

create_card (write)

Parameter Type Required Meaning
title string yes One line naming the work.
description string The body of the card. Markdown is fine.
column string Where it starts. Omitted, the card goes to the holding area, off the board.
priority string low, medium (default), high, critical.
story_points integer
labels string[]
assignee string An email address or a person's name. Openbook emails them if the name matches an account.
due_date string YYYY-MM-DD.
checklist string[] Checklist items in order — acceptance criteria belong here.
{
  "name": "create_card",
  "arguments": {
    "title": "Universal login",
    "description": "As a returning user I want one sign-in across spaces so that I stop re-entering my password.",
    "column": "Backlog",
    "priority": "high",
    "story_points": 3,
    "labels": ["story"],
    "checklist": ["SSO redirect works", "Logout clears every session"]
  }
}
{
  "created": true,
  "key": "lCGmgWBfd8xw",
  "reference": "#1",
  "column": "Backlog",
  "attributed_to": "Claude"
}

attributed_to is the token's name, and it is what the board shows as the card's author.

update_card (write)

Takes card plus any of title, description, priority, story_points, labels, assignee, due_date, checklist. Fields you leave out keep their current value — a one-field edit cannot blank the rest.

labels and checklist are the exception: passing either replaces the whole list, and replacing a checklist unticks everything. Read the card first and send the full list you want.

An empty string clears assignee or due_date. This tool does not move cards — that is move_card, which respects the board's rules.

move_card (write)

Parameter Type Required
card string yes
column string yes
position integer
{ "moved": true, "key": "lCGmgWBfd8xw", "column": "QA", "position": 0 }

If the board enforces a workflow, a move the rules forbid is refused with the reason, exactly as it would be for a person dragging the card. Entering a column runs that column's automations, also exactly as it would for a person.

add_comment (write)

Takes card and text. The comment is signed with the token's name.

{ "posted": true, "key": "lCGmgWBfd8xw", "author": "QA agent", "at": "2026-09-11T01:08:28.887Z" }

list_comments

Takes card and an optional limit (the most recent N, still oldest-first).

The loop: three agents, one board

Here is the whole thing, using a board with columns Backlog → To Do → In Progress → QA → Done.

Mint three write tokens — name them Claude, Dev agent and QA agent — and give one to each assistant. The names are not decoration: they are what appears on every card and comment those agents write, and they are how the agents tell each other's work apart in a thread.

1. Writing the stories

Connect your own Claude or ChatGPT with the Claude token, and ask for work in plain language:

Using the Openbook board tools, write the user stories for universal login into the Backlog column. One card per story. Put the story itself in the description as "As a … I want … so that …", and put the acceptance criteria in the checklist. Label them story. Before you start, call list_columns so you use the board's real column names.

That last sentence earns its place. A model that guesses at column names spends a turn discovering it guessed wrong.

2. The development agent

Give the dev agent the Dev agent token and a standing instruction:

You have Openbook board tools for one Kanban board. Your queue is the To Do column.

  1. list_cards with column: "To Do". If it is empty, stop and say so.
  2. Take the top card. get_card on it and read the description, the checklist and every comment — a card that has been through QA before will have feedback on it, and that feedback is your actual task.
  3. move_card it to In Progress before you start, so nobody else picks it up.
  4. Build it.
  5. add_comment saying what you did, and name the branch or pull request.
  6. move_card it to QA.

Never move a card to Done yourself. QA decides that.

3. The QA agent

Give the QA agent the QA agent token and its own:

You have Openbook board tools for one Kanban board. Your queue is the QA column.

  1. list_cards with column: "QA".
  2. get_card on the top one. Test every checklist item against what was built, and read the comments for what the developer says they changed.
  3. If everything passes: add_comment saying what you tested, then move_card to Done.
  4. If anything fails: add_comment with exactly what failed and how to reproduce it — this is the developer's entire brief, so be specific — then move_card back to In Progress.

Never fix anything yourself. Comment and move.

4. It goes round

The dev agent's step 2 is what closes the loop: a card coming back to In Progress already carries the QA agent's comment, and reading comments before working is the instruction that makes the second lap different from the first. Run both agents on a schedule, or start each by hand, and a story walks itself from Backlog to Done.

Three habits make the difference between a loop that runs and one that stalls:

  • Name the column in the instruction, not the task. "Your queue is the QA column" survives; "check for anything I sent you" does not.
  • Insist on a comment with every move. A card that changes column with no explanation leaves the next agent guessing, and guessing is where loops go wrong.
  • Give each agent exactly one column it is allowed to move cards out of. That is what stops two agents fighting over the same card.

Everything both agents did stays visible on the board: the comment thread is the transcript, signed with each agent's name, and anyone on the team can open the card and read what happened without asking either of them.

Limits and refusals

  • Rate limit: 120 requests a minute per token, counted per token, answered with HTTP 429 and how long to wait.
  • List sizes: 50 cards or comments by default, 200 at most. A truncated list says how many more matched.
  • Text: titles up to 500 characters, descriptions and comments up to 20,000.
  • Wrong names are refused with the board's real column names in the message, so an assistant can usually correct itself in the same turn.
  • A read-only token calling a writing tool is told which token to mint instead, rather than failing silently.
  • HTTP 403 means API access is switched off for the board; 401 means the token is unknown or missing.

What it cannot do

The MCP server works cards, and only cards. It cannot create or delete columns, delete cards, change board settings, publish the board, touch another board, or reach anything outside the Kanban room. Deleting is deliberately absent: an agent that can delete a card can destroy work with no undo, and nothing in the loop above needs it.

For a read-only feed of the same board into a script or a dashboard, use the Board API — same switch, same tokens. To go the other way and give Openbook's own build agent tools from somebody else's MCP server, see MCP servers.