npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aexol-studio/basecamp-to-llm

v2.0.5

Published

Basecamp MCP server, CLI, and typed TypeScript SDK for LLM-friendly project data

Readme

@aexol-studio/basecamp-to-llm

Basecamp MCP server and CLI for AI-assisted project management. Connect your IDE (Codex, Cursor, OpenCode) to Basecamp via the Model Context Protocol.

Requires Node.js 20 or newer. CI tests the Node.js 20 and 22 LTS lines; newer compatible Node.js releases are not intentionally excluded.

Features

  • MCP Server — 14 tools for managing Basecamp projects, cards, steps, comments, and attachments directly from your IDE
  • CLI — Authenticate, list projects, call any Basecamp API endpoint, and run SDK actions from the terminal
  • Typed SDK — Modular TypeScript SDK covering projects, card tables, people, comments, steps, messages, and todos
  • OAuth2 Authentication — Secure token-based auth with automatic refresh
  • Enriched Cards — Fetch cards with full comment history, visual attachments (images), and formatted text output for LLM context
  • Kanban Management — Create tasks with checklists, move cards between columns, update assignees and due dates

Quick Start

1. Create a Basecamp OAuth App

  1. Go to https://launchpad.37signals.com/integrations
  2. Create a new integration
  3. Set the redirect URI to http://localhost:8787/callback
  4. Note your client_id and client_secret

2. Environment Variables

export BASECAMP_CLIENT_ID="your_client_id"
export BASECAMP_CLIENT_SECRET="your_client_secret"
export BASECAMP_REDIRECT_URI="http://localhost:8787/callback"
export BASECAMP_USER_AGENT="Your App Name ([email protected])"
# Optional when your authorization includes multiple Basecamp accounts
export BASECAMP_ACCOUNT_ID="your_account_id"

These are shell exports: run them in the same shell (or shell startup file) that launches the CLI. The package does not load .env files automatically. MCP clients may instead set the variables in their server configuration.

3. Install

npm install @aexol-studio/basecamp-to-llm

4. Authenticate

npx @aexol-studio/basecamp-to-llm auth --open

Token cache security

OAuth tokens are cached in .basecamp/basecamp-token.json under the current working directory. Treat this file as sensitive and do not commit it. If your setup has an older or root-level token cache such as .basecamp-token, ignore that too:

# Basecamp OAuth token cache
.basecamp/
.basecamp-token*

MCP Server Setup

Install as a dev dependency in your project:

npm install --save-dev @aexol-studio/basecamp-to-llm

The package installs two binaries:

  • basecamp-to-llm provides the CLI, including the mcp subcommand.
  • basecamp-mcp starts the MCP stdio server directly.

Then configure your IDE:

Create .codex/config.toml:

[mcp_servers.basecamp]
command = "npx"
args = ["-y", "@aexol-studio/basecamp-to-llm", "mcp"]
env_vars = ["BASECAMP_CLIENT_ID", "BASECAMP_CLIENT_SECRET", "BASECAMP_REDIRECT_URI", "BASECAMP_USER_AGENT"]

Create .cursor/mcp.json:

{
  "mcpServers": {
    "basecamp": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@aexol-studio/basecamp-to-llm", "mcp"],
      "env": {
        "BASECAMP_CLIENT_ID": "${env:BASECAMP_CLIENT_ID}",
        "BASECAMP_CLIENT_SECRET": "${env:BASECAMP_CLIENT_SECRET}",
        "BASECAMP_REDIRECT_URI": "${env:BASECAMP_REDIRECT_URI}",
        "BASECAMP_USER_AGENT": "${env:BASECAMP_USER_AGENT}"
      }
    }
  }
}

Add to opencode.jsonc under the top-level mcp key:

{
  "mcp": {
    "basecamp": {
      "type": "local",
      "command": ["npx", "-y", "@aexol-studio/basecamp-to-llm", "mcp"],
      "environment": {
        "BASECAMP_CLIENT_ID": "{env:BASECAMP_CLIENT_ID}",
        "BASECAMP_CLIENT_SECRET": "{env:BASECAMP_CLIENT_SECRET}",
        "BASECAMP_REDIRECT_URI": "{env:BASECAMP_REDIRECT_URI}",
        "BASECAMP_USER_AGENT": "{env:BASECAMP_USER_AGENT}",
      },
    },
  },
}

Use stdio transport with the command:

npx -y @aexol-studio/basecamp-to-llm mcp

Set the four required environment variables (BASECAMP_CLIENT_ID, BASECAMP_CLIENT_SECRET, BASECAMP_REDIRECT_URI, BASECAMP_USER_AGENT) in your client's MCP config. BASECAMP_ACCOUNT_ID is optional.

The MCP server first connects over stdio, then tries a cached token or silent refresh. If neither is available, it opens a browser and waits for the configured local OAuth callback. In a headless environment, authenticate first with basecamp-to-llm auth (copy the printed URL into a browser) or provide a reusable token cache; a failed automatic browser flow is logged to stderr and does not terminate the MCP server. All MCP logs use stderr so stdout remains reserved for the protocol.

Available MCP Tools

Required arguments are shown without ?; optional arguments include ?.

| Tool | Arguments | Description | | ------------------------------ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | authenticate | openBrowser? | Start or refresh OAuth authentication | | api_request | method, path, query?, body?, absolute? | Make a generic Basecamp API request | | sdk_projects_list | status?, page? | List projects; status may be archived or trashed | | sdk_card_tables_get | projectId, tableId | Get a raw board and its column metadata; use list_cards for cards | | sdk_card_tables_list_cards | projectId, columnId, detail?, page?, limit? | List a bounded page of compact or extended cards | | sdk_card_tables_get_card | projectId, cardId | Get one raw card | | sdk_card_tables_get_enriched | projectId, cardId, format?, downloadImages?, imageQuality? | Get card context with comments, creator data, and attachments | | sdk_card_tables_create_task | projectId, columnId, title, content, due_on?, assignee_ids?, notify?, steps? | Create a card and optional checklist steps | | sdk_card_tables_update_card | projectId, cardId, title?, content?, due_on?, assignee_ids? | Update supported card fields | | sdk_card_tables_move_card | projectId, cardId, columnId | Move a card to another column | | sdk_people_list | None | List people in the account | | sdk_comments_create | projectId, recordingId, content | Add an HTML comment to a recording | | sdk_steps_complete | projectId, stepId, completion | Set step completion to on or off | | sdk_attachments_download | url, filename?, mimeType? | Download and save an attachment; return image content or file metadata |

Compact card listing and pagination

sdk_card_tables_list_cards defaults to detail: "compact", page: 1, and limit: 25. page is one-based and limit must be from 1 through 100. Compact cards contain only:

  • id, normalized title (title, then name, then an empty string)
  • optional status, position, due_on, app_url, and updated_at
  • projected assignees with id and name only
  • normalized comments_count
  • steps counts with completed and total

Compact cards do not include card descriptions, content, or comment bodies. Use detail: "extended" when the full raw Basecamp Card objects are required for the requested window. Listing cards never fetches comment bodies; use sdk_card_tables_get_enriched when comment context is explicitly needed.

Compact request arguments:

{
  "projectId": 123,
  "columnId": 456,
  "page": 1,
  "limit": 25
}

Extended request arguments:

{
  "projectId": 123,
  "columnId": 456,
  "detail": "extended",
  "page": 2,
  "limit": 10
}

Both modes return the same envelope:

{
  "detail": "compact",
  "cards": [],
  "pagination": {
    "page": 1,
    "limit": 25,
    "returned": 0,
    "has_more": false
  }
}

When has_more is true, pagination.next_page contains the next one-based page. It is omitted otherwise. The response does not report a total because the bounded traversal does not fetch the complete collection.

Enriched card text and attachment downloads

sdk_card_tables_get_enriched with format: "text" preserves the card description as Basecamp HTML. Comment bodies are converted to plain text. This deliberate distinction keeps rich description markup and embedded attachment references available to the LLM.

With downloadImages: true, sdk_card_tables_get_enriched returns one text metadata block plus a separate MCP image block for each successfully downloaded image. Base64 is removed from the text block, and query strings/fragments are removed from Basecamp URLs in projected text so signed capabilities are not duplicated into LLM context. Automatic downloads are limited to the first four images with at most two active downloads; metadata remains available for additional images. Individual image failures remain best-effort and are logged to MCP stderr. The imageQuality option applies only to these enriched-card image downloads.

sdk_attachments_download always writes the downloaded bytes to .basecamp/images/ under the process's current working directory. If filename is omitted, it uses attachment; characters other than letters, numbers, ., _, and - become _, while . and .. are rejected. The download directory and destination must not be symlinks. Files are opened without symlink following, verified as regular files, and written with mode 0600 where supported. A download overwrites an existing regular file with the same sanitized name. The response Content-Type is authoritative; caller mimeType is only a fallback for non-image data. MCP image output requires PNG, JPEG, GIF, or WebP magic bytes plus safe response metadata, preventing HTML or unknown bytes mislabeled by a caller from becoming image blocks.

Binary downloads accept only HTTPS URLs without embedded credentials on the exact supported Basecamp API, preview, and storage hosts. Redirects are followed manually with a bounded limit, and every target is validated. OAuth authorization is sent only to 3.basecampapi.com and is never forwarded to preview or storage hosts. Each binary is limited to 10 MiB: oversized Content-Length values are rejected before reading, streamed chunk overflow is canceled, and non-streaming runtimes receive the same post-read cap.

CLI Usage

# Authenticate with Basecamp (opens browser)
basecamp-to-llm auth --open

# List available projects
basecamp-to-llm projects

# Start the MCP server
basecamp-to-llm mcp

# Or start the installed MCP binary directly
basecamp-mcp

# Call any Basecamp API endpoint
basecamp-to-llm api GET /projects.json
basecamp-to-llm api POST /todosets/123/todolists.json -d '{"name":"My List"}'

# List available SDK actions
basecamp-to-llm sdk list

# Run an SDK action
basecamp-to-llm sdk run projects.list
basecamp-to-llm sdk run card_tables.get -a '{"projectId":123,"tableId":456}'
basecamp-to-llm sdk run card_tables.list_cards -a '{"projectId":123,"columnId":456,"page":1,"limit":25}'

Programmatic Usage

import { BasecampClient, SDK } from "@aexol-studio/basecamp-to-llm";

const client = new BasecampClient();

// List projects
const projects = new SDK.ProjectsResource(client);
const list = await projects.list();

// List a bounded compact page
const cards = new SDK.CardTablesResource(client);
const page = await cards.listCardsPage(projectId, columnId, {
  detail: "compact",
  page: 1,
  limit: 25,
});

// Create a task with steps
const task = await cards.createCardWithSteps(projectId, columnId, {
  title: "Implement auth",
  content: "<p>OAuth2 support</p>",
  steps: [
    { title: "Design auth flow" },
    { title: "Implement OAuth2" },
    { title: "Write tests" },
  ],
});

For createCardWithSteps, each nested step requires title; optional due_on uses YYYY-MM-DD, and optional assignees is a comma-separated string of Basecamp person IDs. The card-level field remains assignee_ids: number[].

The enriched-card aggregation and text formatter are currently MCP-facing helpers rather than stable top-level programmatic exports. The existing public API (BasecampFetcher, BasecampAuth, BasecampClient, and the SDK resource namespace) remains available; use SDK.CardTablesResource for the stable typed card API.

Development

Prerequisites

  • Node.js 20 or newer (CI tests the Node.js 20 and 22 LTS lines)
  • npm

Setup

git clone [email protected]:aexol/basecamp-to-llm.git
cd basecamp-to-llm
npm install

Scripts

npm run build          # Compile TypeScript
npm run dev            # Watch mode
npm test               # Run tests
npm run test:watch     # Watch tests
npm run lint           # ESLint check
npm run lint:fix       # ESLint autofix
npm run format         # Format TypeScript with Prettier
npm run format:check   # Check TypeScript formatting
npm run clean          # Remove dist/

Project Structure

src/
├── cli.ts                # CLI entry point
├── index.ts              # Public exports
├── auth.ts               # OAuth2 authentication
├── basecamp-fetcher.ts   # Legacy high-level fetcher
├── basecamp-types.ts     # TypeScript type definitions
├── mcp-server.ts         # MCP server implementation
└── sdk/
    ├── index.ts           # SDK exports
    ├── client.ts          # BasecampClient
    ├── registry.ts        # Tool registry for MCP
    ├── types.ts           # SDK type definitions
    └── resources/
        ├── cardTables.ts  # Kanban boards and cards
        ├── comments.ts    # Comments
        ├── enrichedCards.ts # Enriched card context
        ├── messages.ts    # Messages
        ├── people.ts      # People
        ├── projects.ts    # Projects
        ├── steps.ts       # Card checklist steps
        └── todos.ts       # Todos

License

MIT — see LICENSE for details.

Contributions welcome — open an issue or submit a merge request.