
37signals now ships an official Basecamp MCP server inside the open-source Basecamp CLI and also maintains the full REST API. They are not two doors into the same room. One is a local process that borrows a single person's CLI login; the other is a REST surface where you own the OAuth app, the account selection, and every token. This guide compares what each path lets an agent do, why neither enforces least privilege at the Basecamp layer, and how to run a Basecamp agent across many customer accounts in production.
Your agent needs to work inside your customers' Basecamp accounts: post a Monday status message, chase overdue to-dos, move a card from In progress to Done when a pull request merges. You notice 37signals now ships an official MCP server and also maintains the full REST API. They are not two doors into the same room. One is a local process that borrows a single person's CLI login; the other is a REST surface where you own the OAuth app, the account selection, and every token. Here's how to pick.
The official Model Context Protocol (MCP) server is a subcommand of the Basecamp CLI, MIT licensed and maintained by 37signals. basecamp mcp serves MCP over stdin and stdout, so you register it as a local server: claude mcp add basecamp -- basecamp mcp. It authenticates with whatever account basecamp auth login stored. That login opens a browser, or uses an OAuth device flow when the issuer advertises one, and falls back to Launchpad's authorization code flow otherwise. Tokens land in the OS keyring, or in credentials.json in the config directory when no keyring is available, and refresh tokens rotate on each refresh.
The server exposes sixteen domain gateway tools: basecamp_projects, basecamp_todos, basecamp_cards, basecamp_messages, basecamp_campfires, basecamp_boosts, basecamp_schedules, basecamp_files, basecamp_people, basecamp_automation, basecamp_reports, basecamp_everything, basecamp_clientside, basecamp_forwards, basecamp_account, and basecamp_recordings. Each one takes an action and params, and a describe action returns the schema for each action on demand. --read-only serves only read actions, and --domains projects,todos narrows the tool list and fails closed on unknown names. The CLI is pre-1.0 and 37signals does not label the MCP server's maturity.
There is also mcp.basecamp.com, which responds to MCP requests with a sign-in challenge and publishes OAuth metadata. The CLI README notes that the basecamp plugin name is reserved for a hosted connector backed by Basecamp's own MCP server. As of late October 2026 that hosted server has no public documentation and no self-registration path for Claude or ChatGPT custom connectors. Community servers fill the gap, such as stefanoverna/basecamp-mcp with 47 tools, and each one asks you to register your own Launchpad OAuth app.
The Basecamp API is a JSON REST API where every path sits under the account ID, as in https://3.basecampapi.com/999999999/projects.json. It covers projects, to-dos and to-do lists, card tables, message boards, comments, Campfires, documents and vaults, schedules, check-ins, client visibility, email forwards, reports, search, and webhooks. 37signals also publishes an OpenAPI spec and SDK for code generation.
Auth is OAuth 2.0 through Launchpad. You register an app at launchpad.37signals.com/integrations, send users to https://launchpad.37signals.com/authorization/new, and exchange the code at /authorization/token with grant_type=authorization_code. Basic auth is not supported. Every request must also carry a User-Agent header naming your app and a contact address, or Basecamp returns 400 Bad Request.
Coverage is close to parity. The CLI wraps the same API that you would call directly, and its sixteen gateways reach most of it. The gap is not what the agent can do inside a chat. It is everything outside one: event triggers, other users, and a token you can scope down.
The Basecamp data model is deep. A to-do lives in a to-do list, which lives in a to-do set, which you find in the project's dock array under the entry named todoset. Pagination is geared: 15 results on page 1, 30 on page 2, 50 on page 3, then 100 per page, and you must follow the Link header rather than build page URLs. A model that has to discover all of that from raw endpoint docs burns turns doing it.
The gateway design hides it. Sixteen tools in context instead of a hundred, with describe loading an action's schema only when the model needs it, keeps the decision space small. Composite helpers do work no single endpoint does: basecamp_recordings summarize builds a compact view from an event feed row.
Writing the schema is the hard part. Not the API call.
The tradeoff is ownership and scope. The schemas track the CLI's release cadence, which reached v0.13.2 on October 13, 2026, and pre-1.0 software is allowed to change its action names. And the server always acts as the person who ran basecamp auth login on that machine.
The CLI server has no auth configuration of its own. It reuses the stored CLI login, so the MCP server is exactly as powerful as that user, across every Basecamp account their identity can reach. That suits a developer driving Claude Code against their own projects. It does not map to a multi-tenant product: there is no way to hand the local server a different user's credential per request, and the refresh token sits in a keyring or a JSON file on a laptop.
--read-only deserves precision. It removes write actions from the tool list. The token underneath is unchanged, so anything else on that machine that reads credentials.json holds full write access. Tool filtering limits the model. It does not limit the credential.
On the direct path you register a Launchpad app and run the Authorization Code flow per user. Four details shape the design:
What the user can't do, the agent can't do. With a full token, the reverse also holds: whatever the user can do, the agent can. Least privilege for a Basecamp agent is enforced at your tool layer or not at all.
In a multi-tenant B2B agent, MCP gives you a credential per user and the direct API gives you a credential per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.
Basecamp adds its own wrinkle. A 404 does not always mean "not found." Basecamp returns 404 for deleted content, for content the user lost access to, and for inactive accounts, which it flags with a Reason: Account Inactive header. An agent that treats every 404 as a missing record will keep retrying against a customer who churned.
Recommended reading: OAuth vs API keys for AI agents
37signals owns the tool schemas, the action dispatch, and the token refresh inside the CLI. You own the machine. The server runs only while a chat session is open on a computer that is awake, so nothing happens when a to-do is assigned at 2am. You own the CLI version, too: upgrading from one pre-1.0 release to the next can change an action your prompts depend on.
You also own the credential sprawl. Every teammate who installs the CLI and runs basecamp auth login creates another long-lived Basecamp grant on another laptop. When that person leaves, disabling their SSO account does not reach the refresh token in their keyring.
You own schemas, pagination, retries, and the token lifecycle. Rate limits are dynamic and layered by method and window. The first one most integrations hit is 50 requests per 10 seconds per IP address; a 429 carries a Retry-After header in seconds. Because the limit is per IP, an agent fleet behind one NAT gateway shares a single budget across every customer. Retry 500, 502, 503, and 504 with exponential backoff, and do not retry 404s. Use ETag and If-None-Match on polls so unchanged resources return 304 Not Modified with no body.
Webhooks replace polling, with constraints. They are created per project, the payload URL must be HTTPS, and you choose from 18 recording types including Todo, Message, Comment, Kanban::Card, and Schedule::Entry. Basecamp retries a failed delivery up to 10 times with exponential backoff, does not follow 3xx redirects, and then deactivates the webhook. A deploy that breaks your receiver for a few hours silently ends the event stream. Payloads carry a generic recording summary, so a to-do event omits assignees and completed, and your agent makes a follow-up read. When an agent acted on a user's behalf, the payload includes a top-level performed_by person with personable_type Agent, which is useful for keeping your agent from reacting to its own writes.
The REST API has no version prefix in its URLs, and 37signals extends it in place. The reference now calls it the Basecamp 5 API, flat routes such as /todos/1069479521.json are canonical while project-scoped /buckets/ routes are legacy, and new personable_type values can appear without a version change. On the direct path you read the changelog and update your code. On the MCP path, 37signals absorbs those changes in the CLI, and you absorb the CLI's own changes in return.
Both paths hand you a Launchpad OAuth grant per user. Neither hands you a vault. A multi-tenant agent with 80 customers and 6 Basecamp users each is 480 refresh tokens, each tied to one Launchpad identity that may span several Basecamp accounts, each with full access. Each one must be stored encrypted, refreshed before its 2-week access token lapses, kept out of logs and LLM context, and deleted when a user disconnects or a customer churns.
The MCP path does not shrink that number. It moves the tokens onto laptops, one CLI login at a time, where you cannot rotate or revoke them centrally. The tempting shortcut on the API path is one admin's grant per customer. That collapses 480 tokens into 80 and turns every agent action into an action by that admin, with that admin's access to every project, including the HR and finance projects nobody else can see. Fewer secrets, larger blast radius per secret.
Scalekit's basecamp connector handles the Launchpad OAuth flow, stores each user's tokens in the token vault, and refreshes them, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; the token never enters the agent runtime or the LLM context.
Recommended reading: why a token vault matters for AI agent workflows
Scalekit's basecamp connector is an API-path connector with 89 tools: 60 read, 28 write, and 1 destructive (basecamp_todo_uncomplete). No tool deletes content. It authenticates with your own Basecamp OAuth app: each user signs in to Basecamp once, and Scalekit stores and refreshes their tokens as that user's connected account. basecamp_account_get confirms which Basecamp account a connection is using.
In the Scalekit dashboard, go to AgentKit > Connections, create a Basecamp connection, and copy its redirect URI. Register an app at launchpad.37signals.com/integrations, add that redirect URI, and paste the app's client ID and client secret into the Basecamp connection.
The connection name you choose is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error. Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env.
get_or_create_connected_account returns the user's connected account. If it is not ACTIVE, get_authorization_link returns a Basecamp consent link. The user approves once on Launchpad; Scalekit exchanges the code and stores the tokens.
This is not a catalog load. The agent asks for the tools this user's connected account is authorized to call, then narrows them to the six a status-report agent needs. The 28 write tools never reach the model, so it cannot complete, reassign, or reschedule a to-do while summarizing one. The Basecamp token is still full; the tool surface is where least privilege actually gets enforced.
The loop runs until the model stops requesting tools. Every tool call executes with this user's Basecamp credential, so the agent sees only the projects this user can see.
For pipelines, skip the model. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This job, triggered when a release ships, files a follow-up to-do in a known list and assigns it with a due date.
Comments work on any commentable recording: a to-do, message, document, card, or schedule entry. Pass the recording's ID as recording_id. Content is Basecamp rich text, so use HTML tags such as div, strong, and a.
For a weekly planning digest, basecamp_upcoming_schedule_get returns schedule entries and dated to-dos and cards across every project in a date window.
The connector does not manage Basecamp webhooks. For event-driven agents, register a project webhook against Basecamp directly, and use the payload's recording.id as the input to basecamp_todo_get or basecamp_comments_list under the same connected account.
At 89 tools and roughly 200 tokens each, the full basecamp connector costs around 17,800 tokens of definitions before the agent does any work. Scoping a status agent to six tools cuts that by more than 90%. Overreach is the second risk: an agent that holds basecamp_project_update, basecamp_card_move, and basecamp_todo_update alongside its read tools can reschedule or re-stage a customer's work on a misread instruction. A Scalekit virtual MCP server declares exactly which tools an agent role can see, and session tokens keep each run bound to one user's connected accounts. Docs: set up a virtual MCP server.
One server definition serves every user. Before each run, check that the user's accounts are active and mint a short-lived session token scoped to them. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.
The agent sees four tools. The endpoint is static; the identity is per-user. Compare that with the CLI server: same MCP transport, but reachable over HTTP from any agent runtime, backed by a vaulted credential per user instead of one laptop's login, and with no server for you to deploy or host.
Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one of your users and one customer. When a customer asks which agent run moved a card to Done or posted a comment on their client-visible message board, the answer is a lookup, not an investigation. Reads matter as much as writes: Basecamp projects hold contracts, client correspondence, and HR discussions, and your execution log is the record of which user's agent read which project, and when.
Basecamp's own records help on the receiving end. Webhook payloads and events include performed_by when an agent acted on someone's behalf, so a customer's admin can see the action was agent-driven. A shared credential breaks the chain on your side: if every run uses one admin's grant, Basecamp attributes every action to that admin, and you can only prove that the shared token did it. Shared credentials are a single-user solution. They do not survive a second user.
Recommended reading: audit trails for agent auth
If your agent is one person's assistant inside Claude Code or Cursor, triaging their own to-dos and drafting messages on demand, the official basecamp mcp server is the shortest path, and --read-only is a sensible default. If your agent reacts to webhooks, runs on a schedule, or acts for more than one customer, build against the Basecamp API: you get per-user grants, account selection you control, and a rate-limit budget you can plan for. 37signals may document its hosted MCP endpoint next, and the transport question will shift again. The need to store, refresh, and revoke one full-scope Launchpad grant per user, per customer, will not, and that's what needs production-grade infrastructure.
Yes, inside the open-source Basecamp CLI as basecamp mcp, added in v0.10.0 on September 3, 2026. It runs locally over stdio, authenticates with whatever account basecamp auth login stored, and exposes sixteen domain gateway tools such as basecamp_todos and basecamp_messages, each taking an action and params. A hosted endpoint at mcp.basecamp.com answers with an OAuth challenge, but 37signals has not documented it.
OAuth 2.0 only, issued through 37signals Launchpad. You register an app, send users to the authorization page, and exchange the code for an access token that lasts 2 weeks, with a refresh token. One grant covers every Basecamp account the user belongs to, so the first call after consent reads authorization.json and picks the right bc3 account. Every request must also carry a User-Agent header naming your app and a contact address.
No. Basecamp-issued tokens carry a single documented scope, full. The CLI's --read-only flag removes write actions from the tool list but leaves the token unchanged, so anything else on that machine that reads the stored credentials holds full write access. Least privilege for a Basecamp agent is enforced at your tool layer or not at all.
The first limit most integrations hit is 50 requests per 10 seconds per IP address, with a Retry-After header on 429. Webhooks are created per project with an HTTPS payload URL across 18 recording types. Basecamp retries a failed delivery up to 10 times with exponential backoff and then deactivates the webhook, so a receiver that is down for a few hours silently ends the event stream.
When it reacts to webhooks, runs on a schedule, or acts for more than one customer. The CLI server runs only while a chat session is open on a computer that is awake, acts as the person who logged in, and cannot take a different user's credential per request, so it fits one person's interactive assistant and nothing multi-tenant.
The basecamp connector exposes 89 tools over the Basecamp API with your own Launchpad OAuth app. Each user signs in once, Scalekit stores and refreshes the tokens in the token vault, basecamp_account_get confirms which account a connection uses, a virtual MCP server narrows the tools per agent role, and every execution returns an execution_id attributed to that user. Webhooks are registered against Basecamp directly.
Browse the Scalekit Basecamp connector and the Basecamp connector docs with the full tool list. See Scalekit pricing, or talk to us.