Connecting integrations
An agent's instructions describe how it behaves; its connections decide what it can reach. A cloned repo gives a teammate the first half — the manifest carries instructions, skills, and MCP server definitions, but never credentials. This page covers the second half: seeing what an agent is wired to, and connecting each surface — chat, Slack, meetings, GitHub, Linear and phone — and its OAuth-backed MCP servers from a terminal.
Seeing what is connected
brainbase agent connections
✓ chat connected
brainbase agent disconnect chat
✓ slack Acme Corp
brainbase agent disconnect slack
· meeting not connected
brainbase agent connect meeting
· github not connected
brainbase agent connect github
· linear not connected
brainbase agent connect linear
· phone not connected
brainbase agent surface phone search; then brainbase agent surface phone purchase <number> --yes
Each line shows whether the integration is connected and, when it is, the identity behind it — the Slack workspace, the GitHub account, the Linear workspace, the meeting bot's name, the phone number. Under it is the command that changes it. Against an older control plane that still reserves a surface for the web app, the line gives that reason instead.
This is also what explains a surprising unpack. Six capability flags decide which built-in MCP servers get installed locally — memory, browser, and slack, meeting, github and linear from the list above — so an agent that cannot see your Slack workspace is usually an agent with no Slack install rather than a broken harness.
The two are related but not the same list, and this command is not reading those flags: it reads the installation records directly. That is why it covers chat and phone, which have no capability flag, omits memory and browser, which are not integrations, and can say *which* workspace or account is behind a connection rather than just that there is one.
Asserting connections in CI
--json makes the state machine-readable, so a pipeline can refuse to ship an agent that would come up unable to talk to anything.
brainbase agent connections --json \
| jq -e '.integrations[] | select(.name == "slack") | .connected' \
|| { echo "Slack is not connected"; exit 1; }
Under --json, the connection commands print their result on stdout on success and {"error": "…"} on failure, exiting non-zero either way when something went wrong. --json also suppresses prompting even on a real terminal, so an automated run fails immediately naming the flag it needed instead of waiting for input nobody will give.
.error alone. A folder that is not linked to an agent exits non-zero but prints a normal-looking body — {"linked": false, "integrations": []} from agent connections, and {"linked": false, "servers": []} from mcp list, neither carrying agent_id. A check that only looks for error reads that as success and ships an agent with no connections, which is the exact failure this section exists to prevent. Test the exit code, or test .linked.Two commands sit outside the contract, both deliberately. mcp check prints plain text on stderr with no JSON when the check itself could not run, because the platform reads "missing JSON plus a non-zero exit" as check_status: failed. An unknown subcommand behaves the same way.
Slack
Slack uses a per-agent custom app rather than an OAuth redirect, so there is nothing to authorize in a browser. You create the app in Slack, then hand the CLI its credentials.
- Create the Slack app. Generate the app manifest with
brainbase agent surface slack manifest(or copy it from the agent's Slack setup in the web app), paste it into Slack's app builder, then install the app to your workspace. - Collect two values. The bot token (
xoxb-…) from *OAuth & Permissions*, and the signing secret from *Basic Information*. - Connect. Run
brainbase agent connect slackwith the two values. The control plane validates the token against Slack before storing anything, and reads the workspace identity from Slack's answer rather than trusting what you pass.
brainbase agent surface slack manifest --app-name "Release Bot" --output slack-manifest.json
brainbase agent connect slack \
--bot-token xoxb-… \
--signing-secret …
A bad or revoked token fails immediately with the reason. Re-running the command rotates the stored credentials in place, so it is also how you refresh them.
Keeping credentials out of argv
Anything on a command line is visible to other processes through ps and lands in your shell history. Both values can come from the environment instead — but note that typing export VAR=xoxb-… at a prompt puts the secret in history too. Read it without echoing, or expand it from a secret manager, so only the command is recorded and never the value.
Typed in, without echoing. read prints nothing of its own, so the prompt has to be written explicitly or you are staring at a blank line — and the portable spelling is printf, since bash wants read -rsp and zsh rejects it.
printf 'Slack bot token: ' >&2; read -rs BRAINBASE_SLACK_BOT_TOKEN; echo >&2
printf 'Slack signing secret: ' >&2; read -rs BRAINBASE_SLACK_SIGNING_SECRET; echo >&2
export BRAINBASE_SLACK_BOT_TOKEN BRAINBASE_SLACK_SIGNING_SECRET
brainbase agent connect slack
Or expanded from a secret manager. Assign before exporting: export VAR=$(cmd) reports success even when cmd fails, which would quietly hand the command an empty credential.
BRAINBASE_SLACK_BOT_TOKEN=$(vault kv get -field=bot_token secret/slack) || exit 1
BRAINBASE_SLACK_SIGNING_SECRET=$(vault kv get -field=signing_secret secret/slack) || exit 1
export BRAINBASE_SLACK_BOT_TOKEN BRAINBASE_SLACK_SIGNING_SECRET
brainbase agent connect slack
Or on stdin, which avoids the environment entirely and is usually the cleanest option for a secret manager. A JSON object supplies both; a bare value supplies the single field that is still missing.
# both values, as JSON
vault kv get -format=json -field=data secret/slack \
| brainbase agent connect slack
# one value, with the other passed as a flag
brainbase agent connect slack --bot-token xoxb-… < signing-secret.txt
connect does make one read call first — it asks the control plane which integrations it may change — so it can fail on an unreachable control plane even when every credential was supplied locally.Meetings
A meeting connection is the display name the agent joins calls under. There is no third-party handshake at all.
brainbase agent connect meeting --bot-name "Notetaker"
brainbase agent connect meeting --bot-name "Notetaker" --bot-image-url https://…
Without --bot-name the CLI prompts in a terminal, and exits non-zero naming the flag when there is no terminal to prompt on.
GitHub
GitHub uses a GitHub App. agent connect github starts the setup the web app runs, prints the link, opens it, and waits until the installation is live. GitHub renders its own confirmation pages, so the approving still happens in a browser — the terminal starts it and sees it through.
brainbase agent connect github
brainbase agent connect github --app-name "Release Bot" --github-org acme
# in CI: get the link, hand it on, and resume later
brainbase agent connect github --json
brainbase agent surface github wait --installation <installation-id>
--app-name names the GitHub App to create and --github-org installs it on an organization rather than your account. Both apply only when the agent has no app yet; once it has one, the link goes straight to that app's install page. An agent that is already connected is reported as it is, with nothing started.
Linear
Linear uses an OAuth app you create in your Linear workspace, so it has a credentials step like Slack and a browser step like GitHub.
- Create the Linear app.
brainbase agent surface linear manifestprints the app settings and the Linear page to create it on;--openopens that page, and--output <file>writes the settings to a file. - Connect. Run
brainbase agent connect linear --client-id <id>. The client secret and webhook signing secret resolve the same way as Slack's credentials: flag, thenBRAINBASE_LINEAR_CLIENT_SECRET/BRAINBASE_LINEAR_WEBHOOK_SIGNING_SECRET, then stdin, then a masked prompt. - Approve in the browser. The command opens Linear's authorization page and waits for the installation, as GitHub's does.
brainbase agent surface linear manifest --open
brainbase agent connect linear --client-id <client-id>
brainbase agent surface linear reconnect # reuse the stored client id
While Linear is connected, agent pull installs the built-in Linear MCP server alongside the agent's own.
Phone
Connecting phone buys a phone number for the agent, which starts recurring charges. It is two steps, and the purchase needs --yes every time, at a terminal or not.
brainbase agent surface phone search --area-code 415
brainbase agent surface phone purchase +14155550123 --yes
brainbase agent surface phone whitelist add +14155550199 --label "On-call"
brainbase agent surface phone release --yes
1 with purchase_outcome_unknown rather than guessing. Provisioning may still be running. Check brainbase agent surface phone status before buying again, or you may end up paying for two numbers.Provider setup needs a login session
Connecting GitHub or Linear, generating the Linear manifest, disconnecting Linear, and searching, buying or releasing a phone number go through the web app, which holds the provider credentials. Those steps need a brainbase login session and refuse a personal access token: unset BRAINBASE_TOKEN for them. Everything else — status, settings, Slack, meetings, chat, the phone whitelist — goes through the control plane and accepts a PAT. See agent surface for every action.
OAuth-backed MCP servers
Declaring an MCP server in brainbase.agent.yaml and pushing it *configures* the server. For OAuth-backed servers it does not *authorize* it. mcp list shows which are waiting on that, and when a live authorization lapses.
brainbase mcp list
✓ linear remote · authorized · expires in 22h
! notion remote · needs authorization
✗ sentry remote · authorization expired — reconnect
! grafana remote · authorized · unreachable
· filesystem local · disabled
Run `brainbase mcp connect <name>` to authorize a server.
Authorization and reachability are reported separately, because a server can hold a perfectly good token and still be down — grafana above. The expired state is the one worth watching: without it, a dead refresh lease shows up only as a tool that quietly stopped working. brainbase mcp list --json carries oauth_token_expires_at and last_status, so a scheduled job can warn before a lease lapses. See mcp list for the full state table.
Authorize a server, or replace an expired grant, with mcp connect. It prints a single-use link, opens it, and waits while you approve on the provider's consent screen. A server the agent inherits from an orchestration is the exception: it is still authorized in the web app.
brainbase mcp connect notion
mcp list reports what the cloud holds — whether a server is authorized and until when. mcp check probes the servers from inside the sandbox to see whether they respond right now. A server can be authorized and unreachable, or reachable and unauthorized.Disconnecting
# at a terminal: asks first, and --yes skips the prompt
brainbase agent disconnect slack
brainbase agent disconnect meeting --yes
# chat, github, linear and phone always need --yes
brainbase agent disconnect github --yes
# no terminal and no --yes: exits non-zero naming --yes, install untouched
brainbase agent disconnect slack < /dev/null
Disconnecting is destructive, so consent has to be explicit. At a terminal you get the confirmation prompt, and --yes skips it. In scripts, CI and agent sandboxes there is nobody to ask — and rather than reading that absence as agreement, the command refuses: it exits non-zero naming --yes and leaves the install exactly as it was. Pass --yes to authorise it. See destructive commands for the rule and the full list of commands it covers.
For Slack and meetings, --json authorises the disconnect on its own, terminal or not: it is a flag you typed, and it has never prompted at a terminal either. Chat, GitHub, Linear and phone never prompt and need --yes every time, at a terminal and with --json alike — releasing a phone number gives it up for good.
Disconnecting removes the same record the web app's disconnect removes (for chat, it turns chat off), and is idempotent, so tearing down something that was never connected still succeeds. After connecting or disconnecting, run agent pull to pick up or drop the matching built-in MCP server locally.