Home › Documentation › API › MCP server

The MCP server

@outcomedev/mcp exposes the desk as an MCP server, so an assistant can read and work the queue without anybody writing a client.

What MCP is

The Model Context Protocol is a standard way for an assistant to be told what tools it has and how to call them. Instead of every product inventing its own integration, an assistant that speaks MCP can use any server that speaks MCP.

This server is one of those. Point an MCP-speaking assistant at it, give it a key, and it gains fifteen named tools for reading and working the desk.

That is the whole of it. There is no separate account, no separate permission model and no second API: every tool below calls the same endpoints documented in this reference, through the same key.

Installing

npm install -g @outcomedev/mcp

Or run it without installing, which is what the client configurations below do:

npx -y @outcomedev/mcp

The package has no dependencies. It runs over stdio by default, which is what every desktop client shown here expects, and it can also run over HTTP for a client that connects to a URL instead of spawning a process.

Environment variables

VariableRequiredDefaultMeaning
OUTCOMEDEV_API_KEYYes—The key, beginning od_. Without it every tool fails.
OUTCOMEDEV_BASE_URLNohttps://outcomedev.comThe deployment to talk to. Change it for a self-hosted instance.

The key is read from the environment and never from an argument, so it does not appear in a process list. Create one for the assistant specifically, as described in Authentication — one key per integration, so that revoking the assistant does not break anything else.

Configuration

Claude Desktop

Edit claude_desktop_config.json — on macOS at ~/Library/Application Support/Claude/claude_desktop_config.json — and restart the application.

{
  "mcpServers": {
    "outcomedev": {
      "command": "npx",
      "args": ["-y", "@outcomedev/mcp"],
      "env": {
        "OUTCOMEDEV_API_KEY": "od_K7fQ2mVx8sT1pR4nB6wY3zA"
      }
    }
  }
}

Claude Code

One command, which writes the same configuration in the right place:

claude mcp add outcomedev \
  --env OUTCOMEDEV_API_KEY=od_K7fQ2mVx8sT1pR4nB6wY3zA \
  -- npx -y @outcomedev/mcp

Or as JSON, for a project-scoped .mcp.json:

{
  "mcpServers": {
    "outcomedev": {
      "command": "npx",
      "args": ["-y", "@outcomedev/mcp"],
      "env": {
        "OUTCOMEDEV_API_KEY": "od_K7fQ2mVx8sT1pR4nB6wY3zA"
      }
    }
  }
}

Cursor

Edit ~/.cursor/mcp.json for every project, or .cursor/mcp.json inside one project:

{
  "mcpServers": {
    "outcomedev": {
      "command": "npx",
      "args": ["-y", "@outcomedev/mcp"],
      "env": {
        "OUTCOMEDEV_API_KEY": "od_K7fQ2mVx8sT1pR4nB6wY3zA"
      }
    }
  }
}

VS Code

Add the server to .vscode/mcp.json in a workspace, or run MCP: Add Server from the command palette.

{
  "servers": {
    "outcomedev": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@outcomedev/mcp"],
      "env": {
        "OUTCOMEDEV_API_KEY": "od_K7fQ2mVx8sT1pR4nB6wY3zA"
      }
    }
  }
}

Put the key in the environment, not in the file, where the client allows it. Several of these configurations end up in a repository or in a synced settings folder. A key in a committed .mcp.json is a leaked key, and the remedy is the same as anywhere else: revoke it and make another.

The tools

Fifteen, and they map one-to-one onto the endpoints in this reference. The names are what an assistant sees; the endpoints are what they call.

ToolArgumentsCalls
get_desk—GET /desk
list_workstatus?, kind?, limit?, cursor?GET /work
get_workidGET /work/{id}
complete_workid, note?POST /work/{id}/complete
snooze_workid, daysPOST /work/{id}/snooze
prepare_messageidPOST /work/{id}/draft
run_workidPOST /work/{id}/run
list_arrangements—GET /arrangements
get_arrangementidGET /arrangements/{id}
add_notearrangement_id, body, channel?POST /arrangements/{id}/notes
list_unitsarrangement?, type?GET /units
list_connections—GET /connections
list_domains—GET /domains
get_permissions—GET /permissions
list_watches—GET /watches
list_activitylimit?GET /activity
list_eventslimit?GET /events
get_me—GET /me
ask_desktextPOST /chat

Notes on individual tools

  • complete_work sends things. It is the Approve button. A row whose action is approve has a message prepared, and completing it sends that message to a real counterparty. An assistant calling this has done something that cannot be undone.
  • run_work can take minutes and is refused with 403 not_permitted unless the desk was granted that class of work. An assistant will be told which permission is missing.
  • prepare_message may decline. A refusal comes back as drafted: false with a reason, which is a success. An assistant that treats it as a failure will call it repeatedly.
  • ask_desk can take four minutes. A client with a short timeout will cut it off.
  • snooze_work requires days, between 1 and 365.

The composition as a resource

The desk's composition is also exposed as an MCP resource at:

outcomedev://desk

A resource is something a client can read and put in front of a model without the model having to decide to call a tool for it. Reading this resource gives the assistant the business's own vocabulary — the names of the blocks, the kinds of work, the fields on its units — before it starts talking about them. That is the difference between an assistant saying "your filing" and saying the word the desk actually uses.

It is the same payload as GET /desk, described in The desk.

The prompt

The server offers one MCP prompt:

review_desk

A prompt is a starting point a person can invoke deliberately, rather than something the model chooses. review_desk asks the assistant to look at the desk — what is open, what is overdue, what is waiting, what is asking for a decision — and report back. It is the thing to reach for at the start of a day, or when somebody asks a general question about how a desk is doing and no specific tool obviously fits.

A worked example

A person types this into an assistant that has the server configured:

What needs me on the desk today?

What happens underneath, in order:

  1. get_desk — the assistant learns the blocks and the kinds of work this desk uses, so it names them the way the business does.
  2. list_work with limit — the queue. The assistant reads each row's action.type, label and allowed.
  3. get_work on the rows that look urgent — the message bodies and the history, so it can say what a row is actually waiting for rather than reading a status.
  4. get_permissions when a row shows allowed: false — to say what the desk would need before it could act on that class of work.

And what it answers with:

Four things need a person.

1. #47 Renew the business licence - overdue by 2 days. A message is written and
   waiting for approval. The draft asks the council for the renewal fee and the
   document list.
2. #61 Confirm the second instalment - a person has to sign this one. It cannot be
   delegated, so the desk prepares it and stops.
3. #72 How does this one get done? There is nobody recorded on the row to write to,
   so nothing can be drafted until that is answered.
4. #58 File the quarterly return - the desk could finish this itself, but it has not
   been granted File. Everything else in that class is waiting on the same grant.

Nothing else is asking. Six rows are waiting on somebody outside, the oldest for
nine days.

Two things about that answer are worth noting, because they are what the server is for. The assistant used the desk's own block names and kinds rather than inventing synonyms, and it distinguished "the desk is allowed to do this" from "somebody must". The second distinction is allowed and action.type, which are on every row and are the reason an agent reading this API does not have to guess.

If the person then says approve the first one, the assistant calls complete_work with id: 47, and the message goes out through the owner's own mailbox. That is the point at which the assistant is acting, not reading.

Troubleshooting

SymptomCause and fix
No tools appear in the clientThe client did not start the server, or it exited at once. Run npx -y @outcomedev/mcp in a terminal with the key set and see what it prints.
Every tool returns an authentication failureOUTCOMEDEV_API_KEY is missing, misspelled, or the key is revoked. Check it with curl -s "$OUTCOMEDEV_BASE_URL/api/v1/" -H "Authorization: Bearer $OUTCOMEDEV_API_KEY".
An npx command works in a terminal and not from the clientDesktop clients often run with a minimal PATH and no shell profile. Use an absolute path to the node binary, or install the package globally and point command at outcomedev-mcp.
Tools time out on run_work or ask_deskBoth are genuinely slow — minutes, and up to four for ask_desk. Raise the client's MCP timeout if it has one; otherwise prefer list_work and get_work for reading.
run_work returns a permission errorCorrect and expected. The desk has not been granted that class of work. It is granted in the product, by the owner, and no configuration of this server can substitute for it.
An answer mentions the wrong business name or wrong block namesThe server is pointed at a different desk's key. Each key is one desk; check get_desk.
Changes made in the product do not show in the assistantThey should, immediately. Every tool call reads live. If they do not, the client is caching a resource read, not the tool result.
  • Authentication — making the key this server needs.
  • Work — the fields the tools return.
  • Code examples — the same calls, if a client is being written instead.