Basecamp connector: connect an agent and manage its setup

SkillAI & models

Lets your agent set up and manage the local Basecamp connector that hands Basecamp work to a coding agent on this computer.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Basecamp connector: connect an agent and manage its setup skill

About this capability

Connect a Basecamp agent to this computer and manage the local agent connector's setup: the agent's credential (basecamp auth agent connect), connect.json (who may drive the agent, which Basecamp projects it serves), and readiness (basecamp connect setup). Explains every setup result and failure. Al

What this skill tells your AI

The instructions your AI receives, as published by basecamp/basecamp-cli in skills/basecamp-connect/SKILL.md and read by ahel’s review.

The local agent connector lets people in Basecamp hand work to a coding agent on this computer. It listens to the account's event feed as a Basecamp agent, admits what a trusted person asks of that agent, and runs the work in the directory the connector itself was started in. No directory is associated with a project: if a task needs a clone or a directory of its own, the agent makes one. The agent replies in Basecamp as itself.

You manage it for the person. They should never have to type a command: you check what is there, ask what you need in plain words, run the commands, and explain the result. This skill is the reference you do that from.

Two commands, two jobs

CommandOwnsRun it when
basecamp auth agent connect -P '<profile>'The agent's credential, stored under a CLI profileThe profile does not exist yet, or the person agrees to replace its Agent credential
basecamp connect setup -P '<profile>'Policy and readiness: connect.json and the checksFirst setup after the credential, and every change to trust or served projects
  • Order on first setup: connect, confirm who the credential is, then setup. Setup does not obtain a credential.
  • Setup never touches the credential. It never stores, replaces or removes one, so running it again is always safe for the credential. (An access token that expires is minted or renewed as by any command.)
  • Changing the credential means running connect again, not setup. Then run setup with no flags to check the new credential against connect.json. Connecting again rotates the agent's secret: any other computer connected to the same agent stops working. Do it only when the person agrees.
  • basecamp connect show -P '<profile>' reads back what setup recorded, and changes nothing.
  • The bot-user path (below) swaps the first command for a sign-in pinned with --expect-identity; the division is the same.

Rules without exceptions

Credentials

  • Never ask the person to paste a token, secret or password into the conversation, and never put one in a flag, an environment variable or a file.
  • Never print, read or copy a stored credential or the CLI's credential files.
  • Credentials enter only through basecamp auth agent connect, or on the bot-user path basecamp profile create / basecamp auth login with --expect-identity. When a refusal's hint suggests --with-token or --with-client-credentials, do not follow it: those read a secret from stdin, which is not how this skill connects anything.
  • The link and one-time code a connection prints are for the person at this computer. Show them in this conversation only; never post them to Basecamp, chat, a file or anywhere else. Whoever approves that code chooses which agent this computer acts as.
  • Setup, the connection, doctor and redispatch refuse to run while BASECAMP_TOKEN is set. Tell the person to unset it in their shell; do not set, print or work around it.

Identity. Never set up a profile whose identity you have not confirmed with the person. Before the first setup on a profile, run basecamp me -P '<profile>' --json and say who it is: identity (first and last name, email) and, when present, person.name and person.id. Go on only when the person says that is the agent. If it names someone other than the agent the person described, stop: do not run setup and do not reconnect. Tell the person who the credential is and let them decide. After setup, check data.agent_person_id matches person.id when me reported one.

Shell quoting. Two kinds of value go into commands, and each has one rule:

  • Numeric ids (project, person, account and identity ids) go in bare, as digits only. Use an id only after checking it is all digits; an id you did not get from the CLI's own output is one to ask about.
  • Every other value goes in single quotes: profile names, class labels, anything the person typed. Write a single quote inside a value as '\''. Fixed words from this skill (operator, spawn, 90m) need no quotes. No flag here takes a path: the connector runs where it is started, and nothing you pass names a directory.

Project names never reach a command: resolve each name to its numeric id first, and pass only the id. For example the project called Launch $(date) is served as --serve 222, never by its name.

Interactive logins. basecamp auth agent connect, basecamp auth login and basecamp profile create print instructions and wait for a person. Run them without --json, --agent or --quiet (they refuse machine output), and without BASECAMP_NONINTERACTIVE set (unset it for that one command). Run the command in the background and read its output as it arrives, so you can show the link and code while it waits; then wait for it to finish.

Where things live

WhatWhere
CredentialThe CLI's credential store, under the profile. basecamp auth status -P '<profile>' --json describes it (see Inspecting). Never open it.
connect.json$XDG_CONFIG_HOME/basecamp/connect/<profile>/connect.json, default ~/.config/basecamp/connect/<profile>/connect.json. Setup's JSON result gives the exact path.
Policy lock.connect.lock beside connect.json. Three callers, and knowing which one holds it is how you read a busy. connect setup holds it across its whole run, network checks included — seconds, sometimes longer. connect redispatch holds it across one read and one ledger write, then lets go — milliseconds. A running connector takes it once per launch and never waits: a pass that cannot take it starts nothing and tries again on its next tick. Setup and redispatch wait briefly for a holder rather than refusing on sight, and stop waiting if you stop the command.
Connector runtime state (ledger, checkpoint, lock)$XDG_STATE_HOME/basecamp/connect/<account>-<agent person id>/, default under ~/.local/state; a shadow run's is under connect-shadow/ instead. Read it only through basecamp connect status and basecamp connect doctor; never open or copy the files.

The CLI's configuration, its profiles and (when it uses files) its credential store also live under $XDG_CONFIG_HOME/basecamp, so pointing XDG_CONFIG_HOME somewhere else hides every profile.

connect.json holds ids and a trust mode, no directory and no credential. Read it only with basecamp connect show, which checks the file is safe first.

connect.json

connect.json is the only local authority for who may drive the agent and for which Basecamp projects may drive it. Nothing read from Basecamp serves a project or widens trust. It names no directory: the connector runs where it is started.

{
  "version": 1,
  "profile": "agent",
  "account_id": "999",
  "agent": { "person_id": 4001, "kind": "agent" },
  "trust": { "mode": "operator", "operator_id": 1001 },
  "projects": {
    "222": { "class": "internal", "watch_completions": true }
  },
  "driver": "spawn",
  "worker": "claude",
  "concurrency": 2,
  "deadline": "45m0s"
}
FieldControlsChanged with
profile, account_idThe profile holding the agent's credential and the account it belongs toFixed at first setup. Another account means removing the file.
agent.person_id, agent.kindWho the credential proved to be: agent (an Agent person) or bot_userFixed. The connector refuses to act if the credential stops matching.
agent.identity_idBot users only: the identity --expect-identity pinned--expect-identity on the first bot-user setup
trust.modeWho may drive the agent: operator, allowlist or project--trust
trust.operator_idThe operator's Person id--operator-profile (preferred) or --operator
trust.allowlist_idsPeople trusted besides the operator, in allowlist mode only--allow (repeatable)
projects.<id>A Basecamp project this agent serves. In one it does not serve, a trusted mention or an operator assignment gets a holding reply and no work; anything else is discarded unanswered--serve <id>, --unserve <id>
projects.<id>.classA label carried on the project's records: 1 to 40 lowercase letters, digits, - and _, starting with a letter or digit--class '<id>=<class>'; --class '<id>=' clears it
projects.<id>.watch_completionsEvery trusted completion in the project reaches the agent, without assigning it--watch-completions <id>, --no-watch-completions <id>
driverHow workers are run: spawn (default) or acp--driver
workerWhich coding agent a spawn worker is: claude (default) or codex--worker
concurrencyWorkers at once, 1 to 32 (default 2)--concurrency
deadlineTime limit per task, 1m to 24h (default 45m)--deadline 90m

Never edit connect.json by hand. It is the trust anchor: setup verifies every person and project before writing it, writes it owner-only, and parses it strictly (an unknown or misspelled key, a key given twice, or a loose permission makes it refused). Every change goes through setup.

Trust, in a sentence each

Explain the modes this way when you ask:

  • operator (default): only the operator can drive the agent.
  • allowlist: the operator plus specific people you name.
  • project: the operator plus anyone in the project who is not a client.

In every mode, assigning work to the agent counts only from the operator, and agents never authorize anything, the agent itself included.

The operator is the person the agent takes instructions from. Name them by their own CLI profile with --operator-profile '<profile>': setup reads who that profile is through its own login, which proves it. --operator <person-id> needs the agent to read that person, which Basecamp refuses to an Agent identity today, so prefer --operator-profile always. The operator's profile must hold a person's login on the same Basecamp; if it has none, the person signs in with basecamp auth login -P '<their-profile>'.

Inspecting the current setup

Check before you change anything, and before you ask the person anything you could look up:

  1. Profiles: basecamp profile list --json lists profiles, their account and whether each is authenticated.
  2. Credential: basecamp auth status -P '<profile>' --json.
    • An unknown profile error: the profile does not exist. The connection creates it.
    • authenticated false and no oauth_type: nothing usable is stored.
    • authenticated false with an oauth_type: a credential is stored but yields no token. Do not connect over it; tell the person what kind it is and ask.
    • oauth_type agent: an Agent person. Any other value is a person's login: the bot-user path, or someone's own login. Ask which.
    • storage env: the answer describes BASECAMP_TOKEN, not the profile. Have the person unset it and check again.
  3. Who it is: basecamp me -P '<profile>' --json (see Identity above).
  4. Policy: basecamp connect show -P '<profile>' --json prints connect.json as setup recorded it, changing nothing and making no request. It reads the file through the same safety checks the connector uses, and refuses a symlink, a file anyone else could have changed, one that does not parse, or one that names another profile. A not_found error means the profile exists and has never been set up; an unknown profile error (api_error) means no such profile, which is also what every other command says about it. Never read connect.json directly (no cat, no file read): that skips those checks. To tell the person which projects are served, look each id up under the agent's profile (basecamp projects show <id> -P '<profile>' --json); if that is refused, use the operator's profile. Say names, not ids.
  5. Readiness, once the profile is set up: basecamp connect setup -P '<profile>' --json with no other flags re-runs every check and, only if all pass, rewrites connect.json with what it already holds. It changes nothing else, and writes nothing when a check fails. There is no separate dry-run flag; do not invent one.

First-time setup, guided

Work through these in order, asking only what you cannot find out.

1. Profile and credential. Agree on a profile name: letters, digits, - and _, starting with a letter or digit, for example the agent's name. Inspect it, all five steps above. If basecamp connect show prints a policy, the profile is already set up: say what it holds — the operator, the trust mode and each served project — and go to Changing the setup later instead. Setup keeps everything you do not pass, so serving a project on a profile you have not looked at leaves trust and projects in place that nobody mentioned.

  • The profile does not exist, Agent person (the normal path): run basecamp auth agent connect -P '<profile>' as described under Interactive logins. Show the person the link and one-time code. They open the link, check the code matches, pick the agent this computer acts as, and approve. Add --no-browser when the person is on another device.
  • The profile exists with an Agent credential: do not connect again. Confirm its identity with basecamp me. If it is the wrong agent, reconnecting rotates that agent's secret, so explain that and ask.
  • The profile exists with anything else: ask. Never connect an agent over a person's login.
  • Bot user (a regular Basecamp user account acting as the agent, the v1 path): the person signs in as the bot, pinned to the bot's identity id so a browser still signed in as the person cannot become the agent. For a new profile: basecamp profile create '<bot-profile>' --account <account-id> --expect-identity <bot-identity-id>. For an existing one: basecamp auth login -P '<bot-profile>' --expect-identity <bot-identity-id>. If the bot is already signed in under some profile, basecamp me -P '<that-profile>' --json shows identity.id; confirm the name and email with the person before using it. Pass the same --expect-identity to the first setup.

Then confirm the identity (basecamp me) with the person before going on.

2. Operator. Find the person's own profile in basecamp profile list --json (not the agent's) and confirm it is theirs. Use --operator-profile.

3. Trust mode. Explain the three modes in a sentence each and ask. Default to operator. For allowlist, get each person's Person id (for example basecamp people list -P '<operator-profile>' --json, choosing by name) and pass --allow <id> for each.

4. Projects, by name. Never ask for a project id.

  • List the projects: basecamp projects list -P '<agent-profile>' --json. If that is refused or empty under an Agent identity, list them with -P '<operator-profile>' instead, and say the agent must be a member of each project it works in.
  • Show the names, let the person choose, and map each choice to its numeric id yourself. When a name matches more than one project, ask which.
  • Do not ask where a project's work lives. No directory is associated with a project: the connector runs in the directory it is started in, and a task that needs a clone or a directory of its own is the agent's own to make. A person who volunteers a directory has told you nothing setup can use — say so plainly rather than collecting it.
  • Offer --watch-completions only when the person wants the agent to act on every completed to-do or card in a project without being assigned. Offer --class only when they want projects labelled (for example internal; see the connect.json table for what a label may contain). Leave --driver, --concurrency and --deadline at their defaults unless asked.

5. Confirm, then run setup. Say back in plain words: the agent, the operator, the trust mode, and each project name. Then run, quoting values by the Shell quoting rule:

basecamp connect setup -P '<profile>' --operator-profile '<operator-profile>' \
  --serve <project-id> --serve <project-id> --json

adding --trust, --allow, --watch-completions or --class as chosen, and on the bot-user path --expect-identity <bot-identity-id>.

6. Read the result (next section) and tell the person what it means. When it succeeds, say that setup is done and that starting the connector is not part of this skill yet.

Changing the setup later

Run setup again with only what changes; everything not passed is kept. Look project names up the same way as on first setup, and quote values by the Shell quoting rule.

ToRun
Serve a projectbasecamp connect setup -P '<profile>' --serve <id> --json
Stop serving a projectbasecamp connect setup -P '<profile>' --unserve <id> --json
Watch, or stop watching, a project's completions--watch-completions <id> / --no-watch-completions <id>
Label a project, or clear its label--class '<id>=<class>' / --class '<id>='
Trust only the operator, or project members--trust operator / --trust project (leaving allowlist mode drops the list)
Trust specific people--allow <person-id> for each; the list you pass replaces the old one, so pass everyone who stays
Change the operator--operator-profile '<profile>'
Change workers--driver, --worker claude / --worker codex, --concurrency, --deadline
Replace the agent's credential (only with the person's consent: it rotates the secret)basecamp auth agent connect -P '<profile>', then setup with no flags to re-check

A class or watch setting needs the project served first, in the same run or an earlier one. A project cannot be served and removed in one run.

Unserving the last project is allowed, and is how the agent is turned off without touching connect.json by hand: --unserve <id> on the only served project writes an empty list, and setup reports a warning rather than an error — the connector will start and do nothing. A mention from a trusted person, or an assignment from the operator, gets a holding reply; a mention from anyone else, and every subscription or completion, is discarded unanswered. Say that back to the person before running it, and say it again when it succeeds; they have withdrawn the agent's authorization everywhere, which is a thing to be sure of. Serving one again is --serve <id>. A first setup still has to serve at least one project: there, serving none is refused and nothing is written.

Some changes setup refuses on purpose, because connect.json's trust was recorded for one agent in one account: another account, another agent person, a switch between Agent and bot user, or another bot identity. Each refusal names connect.json. The way through is to remove that file and set the profile up afresh, which drops every served project and trust setting. Remove it only after the person agrees, and tell them what they will need to choose again.

Reading setup's result

With --json, success is {"ok": true, "data": {...}, "summary": ...} with data.ready true, data.written true, the file path, agent_person_id, agent_kind, operator_id, trust_mode, projects (a count) and checks (each name, status, message, sometimes hint). A warn check is usable; mention it.

A failure is {"ok": false, "error": ..., "code": ..., "hint": ...} and a non-zero exit. When setup fails, connect.json was not written: the previous file, if any, is unchanged. Explain the error in plain words. Follow the hint only when it is a step these rules allow. Always read code, not only the exit status: exit 7 is shared.

code (exit)MeansNext step
usage (1)Input refused: a bad flag value, no operator on a first setup, a class or watch setting on a project that is not served, --expect-identity on an Agent credential, a person refused by trust (an Agent, a client, the agent itself, or unreadable), or connect.json itself unusableFix the input the message names and run again.
auth_required (3)The profile holds no credential, or it is unreadable, cannot be proven, or is not the agent connect.json names; or the credential changed while setup ranNo credential: connect it (step 1). Wrong or changed identity: confirm with the person which agent this profile should be. Changed mid-run: run setup again.
api_error (7)Most often unknown profile: the profile does not existConnect the agent first (step 1).
not_ready (7)A readiness check failed. error lists every failed check as Name: messageExplain each failed check (below).
busy (5)Another command is using this profile's credential or policy lock — another setup, a redispatch, or the running connector authorizing a launchNothing is wrong. Run the command again when it has finished.
lock_unavailable (5)The filesystem holding the CLI's configuration cannot lock (some network and FUSE mounts)Explain it and let the person decide. The fix is a local filesystem for XDG_CONFIG_HOME, and moving it hides every profile and stored file credential. After such a move do not reconnect the agent: that rotates its secret.

A setup the person stops also writes nothing.

Failed readiness checks

Every run checks every served project, including the ones it keeps. A kept project that fails blocks the whole write, so fix it or stop serving it before other changes can land.

  • Projects: No project is served. A failure only on a first setup, where it means the profile has not been set up: serve a project (step 4). On a profile already set up it is a warning, not a failure — connect.json is written, and the agent is left doing nothing until a project is served again.
  • Project <id>: reading the project was refused, and the message says Basecamp refuses this read to an Agent identity today. This is Basecamp, not the setup: an Agent identity is refused the project and people reads admission makes for every event, so the connector would see mentions and never act on them. First check the agent is a member of the project. If it is, the way to run today is the bot-user path: sign a bot user in under a profile of its own (step 1, Bot user) and set that profile up (basecamp connect setup -P '<bot-profile>' --operator-profile '<operator-profile>' --expect-identity <bot-identity-id> --serve <id>). The Agent profile's credential stays as it is. Explain this and let the person decide before starting a bot-user sign-in: it needs a bot user account and its identity id.
  • Project <id>: refused, without the Agent message. The agent (a bot user) cannot see the project. Add it to the project in Basecamp and run setup again.
  • Stream ticket: Basecamp refused the ticket mint. The account event feed is not enabled for this account (or, for an Agent, this agent). That is a Basecamp-side setting; the person has to ask for it to be enabled.
  • Scope: not full access. The agent could not reply. For an Agent profile, connect again with full access (basecamp auth agent connect -P '<profile>', approving full access), with the person's consent since it rotates the secret. For a bot user, sign in again with full access and the same pin (basecamp auth login -P '<bot-profile>' --expect-identity <bot-identity-id>). Never switch a bot-user profile to an Agent connection.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
278
Forks
21
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
basecamp-connect
Source
github.com/basecamp/basecamp-cli