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 the parts that can be connected from a terminal.
Seeing what is connected
brainbase agent connections
✓ slack Acme Corp
brainbase agent disconnect slack
· github not connected
GitHub renders the App-manifest confirmation page itself, so connecting it needs a browser. Use the web app.
· meeting not connected
brainbase agent connect meeting
· phone not connected
Connecting phone means buying a Twilio number, which is a purchase rather than an authorization. Use the web app.
Each line shows whether the integration is connected and, when it is, the identity behind it — the Slack workspace, the GitHub account, the meeting bot's name, the phone number. Under it is either the command that changes it or the reason it cannot be changed from here.
This is also what explains a surprising unpack. Five capability flags decide which built-in MCP servers get installed locally — memory, browser, and the three integrations 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 phone, which has 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. In the web app, open the agent's Slack setup and copy the generated manifest into Slack's app builder, then install it 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 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 and phone
Neither can be completed from a terminal today, and neither is an OAuth gap:
- GitHub uses the GitHub App *manifest* flow. GitHub itself renders the confirmation page and performs the redirect, so a browser is unavoidable.
- Phone means buying a Twilio number and provisioning SIP trunks — a purchase rather than an authorization.
brainbase agent connections still reports both, so whether they are set up is answerable from a terminal even though changing them is not.
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
Authorize OAuth-backed servers in the web app; the CLI cannot run that flow yet.
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. Authorizing an OAuth-backed MCP server still has to happen in the web app; 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.
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
# 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.
--json authorises the disconnect on its own, terminal or not. What changed is that an *absent* terminal no longer counts as agreement — --json is a flag you typed, and it has never prompted at a terminal either.
Disconnecting removes the same record the web app's disconnect removes, 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.