API reference contents
API reference
Documentation
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
| Variable | Required | Default | Meaning |
|---|---|---|---|
OUTCOMEDEV_API_KEY | Yes | — | The key, beginning od_. Without it every tool fails. |
OUTCOMEDEV_BASE_URL | No | https://outcomedev.com | The 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.
| Tool | Arguments | Calls |
|---|---|---|
get_desk | — | GET /desk |
list_work | status?, kind?, limit?, cursor? | GET /work |
get_work | id | GET /work/{id} |
complete_work | id, note? | POST /work/{id}/complete |
snooze_work | id, days | POST /work/{id}/snooze |
prepare_message | id | POST /work/{id}/draft |
run_work | id | POST /work/{id}/run |
list_arrangements | — | GET /arrangements |
get_arrangement | id | GET /arrangements/{id} |
add_note | arrangement_id, body, channel? | POST /arrangements/{id}/notes |
list_units | arrangement?, type? | GET /units |
list_connections | — | GET /connections |
list_domains | — | GET /domains |
get_permissions | — | GET /permissions |
list_watches | — | GET /watches |
list_activity | limit? | GET /activity |
list_events | limit? | GET /events |
get_me | — | GET /me |
ask_desk | text | POST /chat |
Notes on individual tools
-
complete_worksends things. It is the Approve button. A row whose action isapprovehas 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_workcan take minutes and is refused with403 not_permittedunless the desk was granted that class of work. An assistant will be told which permission is missing. -
prepare_messagemay decline. A refusal comes back asdrafted: falsewith a reason, which is a success. An assistant that treats it as a failure will call it repeatedly. -
ask_deskcan take four minutes. A client with a short timeout will cut it off. -
snooze_workrequiresdays, 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:
-
get_desk— the assistant learns the blocks and the kinds of work this desk uses, so it names them the way the business does. -
list_workwithlimit— the queue. The assistant reads each row'saction.type,labelandallowed. -
get_workon 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. -
get_permissionswhen a row showsallowed: 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
| Symptom | Cause and fix |
|---|---|
| No tools appear in the client | The 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 failure | OUTCOMEDEV_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 client | Desktop 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_desk | Both 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 error | Correct 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 names | The 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 assistant | They should, immediately. Every tool call reads live. If they do not, the client is caching a resource read, not the tool result. |
Related
- 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.