Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to the Bonsai MCP server

Vendor MCP
Open markdown

The Bonsai MCP connector routes your AI agent's tool calls to Bonsai's own MCP server through Scalekit. Each user signs in to Bonsai once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 49 tools.

Tools
49
What they doRead · write · destructive
23 · 20 · 623 read20 write6 destructive
Users sign in with
OAuth app
Scalekit's or your own

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Bonsai MCP connection

    In AgentKit > Connections, create a Bonsai MCP connection. The name you give it is the connection_name your code passes. See Configure connections.

    Scalekit credentials are available for Bonsai MCP server, so you don't need to register an OAuth app.

  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'bonsaimcp'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Bonsai MCP:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'bonsaimcp_list_board_groups',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user signs in once. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • bonsaimcp_get_companyFetch a single CRM company by its id, in the same shape `list_companies` returns per row.Read-only

    Get Company

    Fetch a single CRM company by its id, in the same shape `list_companies` returns per row. Use it when you already hold a company id — from `list_companies`, a contact's `company_ids` or `default_company_id`, an invoice's `company_id`, or `create_company` — instead of paging through the list. Returns `id`, `name`, `default_contact_id`, `domains`, `created_at`, the profile fields `description`, `industry`, `company_size`, `source`, `linkedin`, `instagram`, `twitter` and `facebook`, `avatar_url` (the company logo, null when it has none), and `url`, a direct link to the company in the Bonsai app. Archived and deleted companies are reported as not found, just as `list_companies` leaves them out.

    Inputs

    idintegerrequired
    Id of the company to fetch — from `list_companies`, a contact's `company_ids`, or an invoice's `company_id`.
  • bonsaimcp_get_docFetch a single document by its id — the `id` that `list_docs` or `create_doc` returned — in the same shape both of them use.Read-only

    Get Doc

    Fetch a single document by its id — the `id` that `list_docs` or `create_doc` returned — in the same shape both of them use. Read it again after the user acts in Bonsai to see where the document stands: its `status` (`draft` until it is sent from the Bonsai app, then `sent`, and `completed` once everybody has signed, or `expired` if it lapsed first), how far each of the `recipients` has got (`pending`, `viewed` once they open it, `completed` once they are done), and which `unassigned_roles` still need somebody. Also returns `title`, `doc_number`, `deal_id`, `project_ids`, `created_at`, `updated_at`, and `url`, a direct link to the document in the Bonsai app. Signing links are never included. Templates are not documents: the id of a `list_templates` entry is not accepted here.

    Inputs

    idintegerrequired
    Id of the document to fetch — the `id` from `list_docs` or `create_doc`.
  • bonsaimcp_get_noteFetch a single note by its id, including what it says as `content_plain_text` — the field a list response always leaves out, so read a note here whenever you need its body.Read-only

    Get Note

    Fetch a single note by its id, including what it says as `content_plain_text` — the field a list response always leaves out, so read a note here whenever you need its body. Also returns `title`, `date` (the day the note is about, which is what the app sorts and groups by), `visibility` (`everyone` or `author_only`), `created_by_member_id`, `created_at`, `updated_at`, and `note_links` — the company, project or contact records the note is attached to, empty for a note that stands on its own in the workspace or whose records this user cannot reach. Resolve ids via `list_notes`. Anyone can read a note they wrote; reading someone else's needs permission to read notes.

    Inputs

    idstringrequired
    Id of the note to fetch. Resolve via `list_notes`.
  • bonsaimcp_get_taskFetch a single task from Bonsai by its UUID.Read-only

    Get Task

    Fetch a single task from Bonsai by its UUID.

    Inputs

    uuidstringrequired
    UUID of the task to fetch.
  • bonsaimcp_list_board_groupsList board groups (pipeline stages for deals, project groups for projects) in the user's Bonsai company.Read-only

    List Board Groups

    List board groups (pipeline stages for deals, project groups for projects) in the user's Bonsai company. Use to resolve group names to UUIDs for filtering deals and projects.

    Inputs

    namestring
    Case-insensitive substring match on the board group name.
    pageinteger
    Page number (1-indexed). Default 1.default 1
    page_sizeinteger
    Items per page (max 100). Default 25.default 25
    resource_typestring
    Board type to narrow to. One of "Deal" or "Project". Omitted returns all boards.one of DealProject
  • bonsaimcp_list_commentsList the comments on a task or a deal, paginated, newest first.Read-only

    List Comments

    List the comments on a task or a deal, paginated, newest first. Provide exactly one parent — `task_id` (a task UUID) or `deal_id` (a deal id). By default only user-authored comments are returned; pass `kind` = `events` for system-generated activity (status changes, assignments) or `all` for both. Each comment carries `body_plain_text` (HTML stripped), its `kind`, `created_at`, the parent `commentable_id`, and `author_member_id` (null when the author is not a company member). Returns a `not_found` error when the parent does not exist or is not accessible.

    Inputs

    deal_idinteger
    Integer id of the parent deal whose comments to list. Provide exactly one of `task_id` or `deal_id`. Resolve deal ids via `list_deals`.
    kindstring
    Which comment kinds to return: `user_created` (default) for user-authored comments, `events` for system-generated activity (status changes, assignments), or `all` for both. Omit to default to `user_created`.one of user_createdeventsall
    pageinteger
    Page number (1-indexed). Default 1.default 1
    page_sizeinteger
    Items per page (max 100). Default 25.default 25
    task_idstring
    UUID of the parent task whose comments to list. Provide exactly one of `task_id` or `deal_id`. Resolve task UUIDs via `list_tasks`, `get_task`, or `list_subtasks`.
  • bonsaimcp_list_companiesList CRM companies in the user's Bonsai account.Read-only

    List Companies

    List CRM companies in the user's Bonsai account. Use to find existing companies before creating new ones or when resolving company_id for projects and invoices.

    Inputs

    namestring
    Free-text search over company name (case-insensitive).
    pageinteger
    Page number (1-indexed). Default 1.default 1
    page_sizeinteger
    Items per page (max 100). Default 25.default 25
  • bonsaimcp_list_company_activitiesList what happened with a CRM company, newest first: its own events plus the events of every contact on it, merged into one stream — documents, invoices, emails, meetings, notes, deals, forms, client portal events, and changes to the company and its contacts.Read-only

    List Company Activities

    List what happened with a CRM company, newest first: its own events plus the events of every contact on it, merged into one stream — documents, invoices, emails, meetings, notes, deals, forms, client portal events, and changes to the company and its contacts. Each occurrence appears once, even when it touched several contacts. A document is the exception: it is recorded per recipient, so one sent to two contacts is two activities (a legacy one, `details.legacy` true, appears once). Each activity carries `id`, `type` (`group.action`, e.g. `invoice.paid`), `occurred_at`, a one-line `summary`, the `actor` who did it (a team `member`, a `contact`, or `system`), the `subject` record it is about with a `url` to open it in Bonsai (an email, meeting or form has no page of its own, so its `url` opens the contact or company page), `contact_id`, `company_id`, and type-specific `details` (a changed field with its old and new values, an invoice amount, an email subject, …) or null. Only the activity types this user may read are returned. Email subjects and meeting titles are null when the mailbox they came from does not share them. Filter by `type` and by an `occurred_from` / `occurred_to` window of UTC days. Pages stop at 40: to read further back, call again with the same filters, `page` 1, and `occurred_to` set to the day of the last activity you read (the date in its `occurred_at`). The activity of that day you already read comes back again, and activity arriving while you page can repeat across pages, so dedupe by `id`. When all 40 pages fall on one day, paging cannot get past it: split that day into calls narrowed by `type`. Narrow to some of the contacts on the company with `contact_id`, which also leaves out the company's own activity and, like `type`, splits a day too busy to page through. Resolve `company_id` via `list_companies` or `get_company`, or from a contact's `company_ids` (`list_contacts`). Archived and deleted companies are reported as not found.

    Inputs

    company_idintegerrequired
    Id of the company whose activity to list — from `list_companies`, `get_company`, or a contact's `company_ids`.
    contact_idstring
    Keep only the activity about these contacts, leaving out the company's own activity. Every id must be a contact on this company — resolve via `list_contacts` with `company_id`. Single value or array.
    occurred_fromstring
    Keep activity on or after this day (YYYY-MM-DD, UTC), inclusive.
    occurred_tostring
    Keep activity on or before this day (YYYY-MM-DD, UTC), inclusive. Past page 40, move it back to the date in the `occurred_at` of the last activity read.
    pageinteger
    Page number (1-indexed), at most 40. Default 1. To read further back, set `occurred_to` to the day of the last activity read (the date in its `occurred_at`) and start again from page 1.default 1
    page_sizeinteger
    Items per page (max 100). Default 25.default 25
    typestring
    Activity types (`invoice.paid`) or groups (`invoice`) to keep; a group stands for every type in it that this user may read. Single value or array; an array keeps activity matching any of them. A type this user may not read, or a group with no type they may, is refused, and the error names it.