MCP

MCP

Reference article: Part of the API reference. Nimbus has two MCP servers. One uses an API key and can work across the organization. The other belongs to an App install and stays in one room.

An MCP client such as Cursor or Claude connects to Nimbus over HTTP and receives a catalog of tools, each with a description and a JSON Schema, so the model knows how to call them. You do not paste this page into the client. The server sends the catalog on initialize and tools/list.

Note: Partially available. The Nimbus MCP server is on integration at https://mcp.gonimbus.ai. Staging and production do not serve /v1/mcp or /v1/apps/mcp yet. tools/list on the host you connect to is the catalog that host actually has.

Which server

Nimbus MCP App MCP
Use it when An agent should act as a person: tasks, rooms, the workstream agent, loops, runs, the wiki. An agent should act as one App, in one room.
Endpoint POST https://mcp.gonimbus.ai or POST /v1/mcp on the API origin POST /v1/apps/mcp
Credential API key, Authorization: Bearer nmb_sk_… or nmb_rk_… Install token, Authorization: Bearer nmb_it_…
Acts as The person who created the key The App, in that install's room
Catalog tools/list on that host Four message tools, below

There is no OAuth login for either server. A client that can only complete an OAuth handshake, and cannot send a bearer header, cannot connect. OAuth you see elsewhere in Nimbus is for connecting someone else's server, such as Linear, to Nimbus. It does not sign Cursor in.

Transport

Both servers speak MCP over streamable HTTP, and both are stateless:

  • Send JSON-RPC 2.0 requests as the body of a POST. You can send one request, or a batch as a JSON array.
  • Responses are plain JSON (Content-Type: application/json), never a server-sent event stream.
  • A body containing only notifications gets 202 with no body.
  • GET and DELETE return 405: there's no server-to-client stream and no session to end.

Supported protocol versions: 2025-06-18, 2025-03-26, and 2024-11-05. If initialize asks for another version, the server answers with 2025-06-18.

Supported methods: initialize, ping, tools/list, tools/call, and notifications.

initialize returns a short instructions string: how tasks are named, that Frames render as cards, and which tool starts an agent turn. tools/list returns every tool's name, description, and inputSchema. Field descriptions inside the schema are the argument docs. A tool that fails returns isError: true and a JSON error the model can read, including insufficient_scope when the key lacks that tool's scope.

Nimbus MCP

POST https://mcp.gonimbus.ai
Authorization: Bearer nmb_sk_…

The same handler answers POST /mcp on that host, and POST /v1/mcp on the API origin for the lane (https://api-integration.gonimbus.ai/v1/mcp on integration). Create the key on the Developers page. A secret key (nmb_sk_…) can call every tool. A restricted key (nmb_rk_…) needs the scope named on the tool. Write includes read. Posts and tasks appear under the key's name, as the person who created the key.

{
  "mcpServers": {
    "nimbus": {
      "type": "http",
      "url": "https://mcp.gonimbus.ai",
      "headers": {
        "Authorization": "Bearer nmb_sk_…"
      }
    }
  }
}

Keep the key out of files you commit. The server's serverInfo.version is the public API date (2026-09-25), not a count of tools. Trust tools/list.

Tasks

Tasks use identifiers like ENG-42. Statuses are triage, backlog, todo, in_progress, in_review, done, and canceled. Scope: tasks:read or tasks:write.

Tool What it does
whoami Who the key acts as, and which organization. Requires tasks:read.
task_projects_list, task_projects_create, task_projects_update Projects. The key (ENG) prefixes every identifier and cannot be changed later. The default project is Tasks (TASK); list always includes it.
task_labels_list Labels already in use. Reuse those spellings.
tasks_summary Open tasks assigned to the caller, and how many of those are overdue. For every task in the workspace, use tasks_search.
tasks_search, tasks_get Find tasks (the whole workspace, unless assignees is set), or read one with its parent, subtasks, comments and history. History is status, assignees, priority, due date, labels, parent and archive, not title or description edits.
tasks_create, tasks_update, tasks_update_many Create or change tasks. Omit project to use Tasks (TASK). parent is another task's identifier. description is markdown, including LaTeX. Pass source_key to make a repeat create update the same task. Anyone who can see a task can update it; only the creator can archive or restore it. Title and description edits are saved and left off the history. A history change returns an event id.
tasks_comment, tasks_undo Comment, or reverse one event. Only the creator can undo. Undoing creation archives the task. A second undo of the same event is refused.
task_suggestions_accept, task_suggestions_dismiss Turn a Sync's follow-ups on a message into tasks, or dismiss them.
tasks_from_todo Save a to-do card's items as tasks in that room.
task_import_sources_list, task_import_projects_list, task_imports_start, task_imports_list Import from Linear, Jira, GitHub, Asana, ClickUp, Trello, or monday.com. Connecting the tracker still happens in the Nimbus app.

A task can sit on the workspace, or in one room or one workstream, not both.

Rooms

Scope: messages:read or messages:write.

Tool What it does
rooms_list Public rooms, and private rooms the key's creator belongs to.
messages_post Post Markdown, a Frame, or both. reply_to posts in a thread.
messages_read Latest messages, or one message and its thread. Includes task suggestions and to-do cards.
messages_update Replace the text of a message this key posted.
files_post Attach a file (base64 or text). Files are scanned. The default ceiling is 25 MB.

Agent, loops, and runs

These tools call the same operations as the agent, loops, and runs REST endpoints.

Tool Scope What it does
agent_ask agent:write Ask the workstream agent. Returns immediately, usually still queued. Spends NTUs the same way a question in the app does.
agent_get agent:read Poll until status is completed (output_text is the answer) or failed. Pass conversation back to agent_ask to continue.
agent_cancel agent:write Stop a response that is still running.
loops_list, loops_get, loop_executions_list loops:read Shared loops and their executions. Private loops are hidden.
loops_run loops:write Run a loop now. Does not create or edit a loop. The execution's run id is what the run tools take.
runs_list, runs_get runs:read Runs this key can see.
runs_report runs:read The report, once the run has written one.

Wiki, decisions, connections, and workstreams

Tool Scope What it does
documents_search documents:read Search the Company Wiki by meaning. Returns passages, not whole files.
decisions_list decisions:read Decisions already recorded. It does not approve or reject.
connections_list, connections_get connections:read Which apps are connected and whether they work. Never returns credentials, and does not call the app.
workstreams_list, workstreams_get workstreams:read Workstreams this key can see.
workstreams_create, workstreams_update workstreams:write Create a workstream, rename it, or archive it.
workstreams_delete workstreams:write Move a workstream to the trash. Prefer archive when you only want to hide it.

The Nimbus MCP server does not call a connector, approve a human-in-the-loop checkpoint, start a Sync, author a loop, or read billing.

App MCP

Every App install comes with its own MCP server, so an agent can act in the App's room: post messages and Frames, update its own messages, and read the conversation. It acts as the App, in that one room, and nowhere else.

POST https://api.gonimbus.ai/v1/apps/mcp
Authorization: Bearer nmb_it_…

Use the install's mcp_url and its MCP token (nmb_it_…), both from the install response. The token says which install, App, and room you are, so every install shares the same URL. Transport is the same as the Nimbus MCP server, above.

Connecting an App

Most MCP clients that support remote HTTP servers with custom headers can connect with a configuration like this:

{
  "mcpServers": {
    "nimbus": {
      "type": "http",
      "url": "https://api.gonimbus.ai/v1/apps/mcp",
      "headers": {
        "Authorization": "Bearer nmb_it_7Kp2Rt9Vx4Lb8Nc1Wz6Hd3Qm5Fs0Yj2Ea7Gu4Ci9"
      }
    }
  }
}

The exact keys vary by client, so check your client's documentation for how it names the server type, URL, and headers. Keep the token out of files you commit; most clients can read it from an environment variable.

Calling the server directly

You don't need an MCP library to use it. Initialize, then call a tool:

::: code-group

MCP_URL='https://api.gonimbus.ai/v1/apps/mcp'
TOKEN='nmb_it_7Kp2Rt9Vx4Lb8Nc1Wz6Hd3Qm5Fs0Yj2Ea7Gu4Ci9'

curl "$MCP_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"deploy-bot","version":"1.0.0"}}}'

curl "$MCP_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"messages_post","arguments":{"text":"Deploy **v2.14** finished on production."}}}'
let nextId = 1
const rpc = async (method: string, params: object = {}) => {
  const response = await fetch('https://api.gonimbus.ai/v1/apps/mcp', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.NIMBUS_MCP_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: nextId++, method, params }),
  })
  if (!response.ok) throw new Error(`Nimbus MCP ${response.status}: ${await response.text()}`)
  const reply = await response.json()
  if (reply.error) throw new Error(`JSON-RPC ${reply.error.code}: ${reply.error.message}`)
  return reply.result
}

await rpc('initialize', { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'deploy-bot', version: '1.0.0' } })

const result = await rpc('tools/call', {
  name: 'messages_post',
  arguments: { text: 'Deploy **v2.14** finished on production.' },
})
if (result.isError) throw new Error(result.content[0].text)
const message = result.structuredContent
import itertools
import os

import requests

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['NIMBUS_MCP_TOKEN']}"
ids = itertools.count(1)


def rpc(method, params=None):
    response = session.post(
        "https://api.gonimbus.ai/v1/apps/mcp",
        json={"jsonrpc": "2.0", "id": next(ids), "method": method, "params": params or {}},
    )
    response.raise_for_status()
    reply = response.json()
    if "error" in reply:
        raise RuntimeError(f"JSON-RPC {reply['error']['code']}: {reply['error']['message']}")
    return reply["result"]


rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "deploy-bot", "version": "1.0.0"}})

result = rpc("tools/call", {"name": "messages_post", "arguments": {"text": "Deploy **v2.14** finished on production."}})
if result.get("isError"):
    raise RuntimeError(result["content"][0]["text"])
message = result["structuredContent"]

:::

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{ "type": "text", "text": "{\"id\":\"2c7e9a41-5b3d-4f8e-a6c1-9d0b4e2f7a58\",\"object\":\"message\",…}" }],
    "structuredContent": {
      "id": "2c7e9a41-5b3d-4f8e-a6c1-9d0b4e2f7a58",
      "object": "message",
      "author": { "type": "app", "id": "app_5Hc2Lq9Rv4Tx7Nb1Mz8Kw3Pd", "name": "Deploy Bot" },
      "text": "Deploy **v2.14** finished on production.",
      "frame": null
    }
  }
}

A successful tool call returns its output twice: as JSON text in content, for clients that only read text, and as structuredContent. The structuredContent above is shortened.

App tools

Tool names use underscores, not dots, because some common MCP clients reject dots in tool names. tools/list returns each tool's full JSON Schema. These four tools are the App server only. The Nimbus MCP server's messages_update edits a message the API key posted, not a message an App posted.

messages_post

Posts text, a Frame, or both to the room, as the App. Returns the created Message object, with its frame.

Argument Type Description
text string, optional Markdown, 1–16,000 characters.
frame object, optional A Frame. Send text, frame, or both.
reply_to string, optional The ID of a message in this room, to post in its thread.

messages_update

Replaces the text or Frame of a message this App posted, in this room. A field you leave out keeps its current value. Returns the updated message.

Argument Type Description
message_id string The message to update.
text string, optional New Markdown text, 1–16,000 characters.
frame object, optional A new Frame. Send text, frame, or both.

Updating a message the App didn't post fails with message_not_owned. A message that isn't in this room fails with resource_missing.

messages_read

Reads the room's latest messages, or one message and its thread.

Argument Type Description
message_id string, optional Read this message and its replies, instead of the latest messages.
limit integer, optional How many messages to return, 1–50. Default 20.

Without message_id, it returns { "messages": [ … ] }, newest first. With it, it returns { "message": { … }, "replies": [ … ] }. Every message includes its frame.

surfaces_info

Says which App this is, which organization it's in, and which room it's installed in. Takes no arguments.

{
  "object": "app_install",
  "install": "ins_8Wd3Kp6Rm1Vt4Lx9Qb2Nh7Cz",
  "app": { "id": "app_5Hc2Lq9Rv4Tx7Nb1Mz8Kw3Pd", "name": "Deploy Bot" },
  "organization": { "id": "acme", "name": "Acme Corp" },
  "surface": { "kind": "workstream", "id": "3f6c2a1e-8b4d-4e2f-9a7c-5d1b0e6f2a94", "workspace": "default" }
}

Errors

Errors come back in three ways, depending on where they happen:

Where How it comes back Examples
Authentication and HTTP An HTTP error status with the usual error body. 401 missing_install_token, invalid_install_token, install_revoked; 429 rate_limited; 400 invalid_json.
Protocol A JSON-RPC error object, with HTTP 200. -32600 invalid request, -32601 unknown method, -32602 unknown tool, -32603 internal error.
Tool A normal tool result with "isError": true, whose text is the error as JSON, so the model can read it and correct itself. frame_invalid, parameter_invalid, message_not_owned, resource_missing.
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"type\":\"permission_error\",\"code\":\"message_not_owned\",\"message\":\"An App can only update messages it posted.\",\"param\":\"message_id\",\"doc_url\":\"https://docs.gonimbus.ai/api-reference/errors#message_not_owned\"}"
      }
    ],
    "isError": true
  }
}

Each install has its own rate limit allowance, shared by its MCP calls and its incoming webhook, and reported in the usual RateLimit-* headers.

FAQ

Which URL do I give Cursor?

For an agent that should keep tasks and ask the workstream agent, use https://mcp.gonimbus.ai with an API key. For an agent that should post as an App, use the install's mcp_url with that install's nmb_it_… token. They are not interchangeable.

Does the client need this page to know the tools?

No. initialize and tools/list are the catalog. This page is the map of what those tools are for. If a tool named here is missing from tools/list, that host has not been deployed with it yet.

Can the App MCP server reach other rooms?

No. The install token belongs to one room. To act in another room, install the App there and use that install's token. The Nimbus MCP server can reach the rooms and workstreams its key is allowed to see.

Can I send several requests at once?

Yes. Send a JSON array of JSON-RPC requests, and the response is an array of their responses. Notifications in the batch get no response.

Can an MCP client ask the Nimbus agent?

The App token cannot. It only posts, updates its own messages, and reads. The Nimbus MCP server can, with agent_ask and an API key that has agent:write. You can also call the agent endpoints over REST.

What happens when I rotate the credentials?

The old token or key stops working immediately. App calls fail with 401 invalid_install_token. API-key calls fail with 401 invalid_api_key or api_key_revoked. Update the header in the client's configuration.

Is there a session to keep alive?

No. Each request stands alone, so you can call tools/call without keeping a connection open. Calling initialize first is still good practice, and most clients do it for you. agent_ask returns before the answer is ready. Poll agent_get. That is a new request, not a held connection.