Customer.io CLI vs MCP: The Decision Rule for Engineering Teams

Customer.io CLI vs MCP: The Decision Rule for Engineering Teams

Paul Senechko, Customer.io's VP of Engineering, spent thirty minutes earlier this month trying to automate the same task he does every Friday. Pull data out of his project management tool, drop it into a tab in a Google Sheet, run some calculations. He'd brought along one of his engineers for what Customer.io calls a pairing-and-sharing session. The plan was to show what a manager looks like when AI does the boring parts.

It didn't work. The MCP for the project management tool couldn't see his saved reports. They tried recreating them by sharing filters... filters weren't supported. They ended up paginating 800 projects and getting the wrong answer. The Google Sheets MCP could make a new spreadsheet but couldn't write to a tab in the existing one. Thirty minutes up, nothing accomplished.

What did work was the thing Senechko had running quietly in another terminal. Claude Code, pointed at Sentry's CLI, had cleaned up a backlog of issues during the same half-hour and produced two PRs ready for review. Same engineer, same AI assistant, two different outcomes. The difference wasn't the vendor. It was the shape of the integration. Sentry's CLI was something an AI agent could drive the way an engineer drives it on the command line. The project management MCP was a catalogue of endpoints pretending to be tools.

A week later, Senechko wrote that experience up under the title "MCP sucks, mostly". The essay is worth reading if you operate Customer.io, because Customer.io shipped both MCP v2 and a properly available CLI within the same week of May 2026. This post is the operator's view of what that means, and which surface you should pick for which job.

The VP of Engineering says MCP sucks, mostly. You should care why.

The argument from inside Customer.io is unusually candid. Wrapping an API as a tool catalogue feels like the obvious move. Customer.io did it themselves. Their first MCP shipped in April 2025 on a one-person, five-day sprint and supported read-only queries against your workspace. Then they added tools, one per endpoint, and the experience failed spectacularly. Every capability needed its own tool, and the more they shipped, the worse the model got at picking the right one. One customer hit a wall trying to set up a newsletter and showed them a 90-plus back-and-forth Claude Cowork transcript that went nowhere.

That's the inside-the-house version of what your operators have been telling you for a year. If you've watched a marketer prompt Claude to fix a Customer.io workflow and produce six steps of confident nonsense, you've already met the failure mode.

What you should care about is the conclusion Customer.io drew, because they didn't deprecate the MCP. They rebuilt it. And in the same fortnight, they made the CLI properly available. Same engineering primitives, two different surfaces, designed for two different operators. The decision in front of you isn't whether to let AI touch your workspace. It's which AI surface fits which job.

What changed on 4 May 2026: MCP v2, one agent, four primitives

On 4 May 2026 Customer.io shipped MCP v2 on a one-agent architecture. The new design throws out the per-endpoint tool catalogue and replaces it with a single intelligent agent built on four primitives: read, write, search, execute.

Matthew Newhook, Customer.io's CTO, wrote up the architecture. The argument: complexity scales through depth, not proliferation. Give an LLM 80 tools and it picks the wrong one. Give it four primitives and a stable identity, and it figures the rest out by iterating. The same loop that lets a coding assistant compose arbitrary work out of read/grep/edit/bash lets the Customer.io agent compose arbitrary marketing work out of its four primitives.

The MCP v2 inherits these directly. So does the in-product agent. So does the CLI, except the CLI exposes them as direct commands rather than wrapping them in an agentic loop.

How this changes the MCP enablement playbook

On the operator side, the MCP enablement playbook still holds. What changes is the failure mode it's protecting against. With MCP v1, you were worried about Claude calling 17 endpoints in the wrong order. With MCP v2, you're worried about a competent agent doing the right thing on the wrong segment. Different problem, different controls, same enablement framework. The addendum at the end of this post extends that framework to cover service account tokens for the CLI.

What the CLI actually is, and why it lands differently

The Customer.io CLI is a command-line tool that covers the Journeys UI API and the CDP Data Pipelines API through a single cio api command. No per-endpoint wrapper code, no SDK boilerplate. Every call returns structured JSON and every endpoint is self-describing via cio schema.

You install it once. You authenticate once. After that, every Customer.io capability exposed by either API is available as a structured call. A script can make it. A CI/CD pipeline can run it. A coding assistant can drive it the way Claude Code drives Sentry's CLI.

Authentication uses service account tokens, prefixed sa_live_. The CLI exchanges them for a short-lived JWT against the OAuth endpoint, then sends the JWT as the bearer credential on every API call. The long-lived token only ever touches the token endpoint. If a JWT leaks, it expires in an hour. If the sa_live_ token leaks, it has to be rotated by hand. The exchange exists for exactly that reason.

Two further controls matter for the role-tier discussion below. A token can be permanently read-only—check the box when you create it and every session it ever mints is GET-only, regardless of the creator's permissions. A session can be temporarily read-only via the --read-only flag on the CLI (or scope=read_only on the token exchange), which gives you a write-capable token with a read-only one-off session. Useful for one-off scripts or pipeline branches that should never write, without rotating the underlying credential.

The CLI runs against the EU region too. Use https://eu.fly.customer.io for the OAuth endpoint if your account is EU-hosted. Same primitives, same auth, different base URL.

The decision rule: marketer-led or engineering-led?

The rule is short: MCP for reasoning-shaped work, CLI for script-shaped work. Both surfaces can do most things. The job's shape decides which one earns its keep.

Use the MCP for marketer-led, conversational work. "Build me a re-engagement campaign for dormant Pro users in DACH" is a planning task. The marketer doesn't know the right segment query in advance. They want an agent that can search, propose, refine, and ship. The MCP v2 agent is designed for exactly this. Same for "audit the conversion rate of my onboarding sequence and tell me where it's leaking"... the work is reasoning-shaped, not script-shaped.

Use the CLI for engineering-led, script-shaped work. Rotating a service account token across three environments is not a conversation. It's a five-line script that needs to be auditable, repeatable, and version-controlled. So is backfilling event payloads for a schema migration. So is dumping a campaign config to a JSON file so you can put it in git. These are the tasks where determinism beats intelligence.

The boundary follows three properties. Auditability: can you read the diff line by line and explain what changed? Idempotency: can you run it twice without breaking anything? Deterministic shape: do you know in advance what the output should look like? When all three are yes, the CLI is the right surface. When any one is no, the MCP earns its keep.

This decision also affects cost. The MCP v2 burns LLM tokens to plan, search, and execute. For exploratory work that's fine. For "run this same backfill script every night at 3am", you don't want an agent reasoning about the job for ten seconds and consuming credits to do it. Use the CLI. The same logic applies one level up to LLM Actions inside Customer.io itself—use intelligence where intelligence earns its credits, and deterministic primitives everywhere else.

Role-tier addendum to the MCP enablement playbook

The original playbook had three tiers. Here's how each tier extends to the CLI and service account tokens.

Marketer. MCP v2 only, scoped to their workspace. No service account token. They drive Customer.io through Claude or the in-product agent. If they need data extracted to a spreadsheet, a lifecycle engineer runs the CLI for them.

Lifecycle engineer. CLI with a read-only service account token, plus MCP v2 with the full agent. The read-only token covers 80% of their work: audits, exports, segment-state checks, deliverability investigations, campaign config dumps for source control. When they need to write, they switch to the MCP and use the agent's confirmation step before any destructive change. They don't get a write-capable CLI token by default.

Marketing engineer / CTO. CLI with a write-capable service account token, plus MCP v2 with the full agent. The write token lives in a secrets store, not in a shell config. They use --read-only on the CLI for anything exploratory and keep the write capability for explicit, scripted operations. They review the audit log monthly and rotate tokens quarterly.

One token per integration. Separate tokens for the marketer-facing dashboard, the nightly backfill cron, the CI/CD pipeline, the analytics warehouse sync. If any one leaks, you revoke that one without disrupting the others. The Customer.io docs spell this out, but most teams skip it until they have to rotate everything at once during an incident.

Five jobs the CLI is now the right answer for

Rotate a service account token. Create a new token in the UI, set CIO_TOKEN in your secret store, run cio auth status to confirm, revoke the old one. Five minutes. No conversational overhead.

Backfill event payloads after a schema change. When you add a property to a tracked event and need every historical record updated, you don't want an agent reasoning about whether to do it. You want a script that reads the CSV, calls the Data Pipelines API for each row, logs every response, and stops cleanly on the first 4xx. See event schema for marketing teams for the upstream design problem this solves.

Import a CDP source. A new product line, a new app, a new analytics warehouse—adding a Data Pipelines source is a config-shaped task with known fields and known relationships. Wire it through the CLI and the source's setup lives in your infrastructure repo, not in someone's browser history. The pattern is the same regardless of which source you're connecting, which is why the complete guide to integrating data sources with Customer.io is the right primer before you script the CLI calls.

Batch create segments from a CSV. You have a customer-success export of 200 accounts that need their own segments for an ABM campaign. The MCP could do it, but you'd be paying tokens to describe each segment in natural language. The CLI loops through the CSV and creates each segment with the same idempotent payload. Faster, cheaper, version-controllable.

Dump a campaign config for source control. You ship a campaign in the UI, then cio api GET it as JSON and commit the result. When the campaign breaks in production six weeks later and you need to know what changed, the answer is a git diff away. The CLI makes this routine. The MCP makes it possible but never convenient.

Three jobs where the MCP v2 is still the better answer

Build a campaign from a brief. "Take this onboarding doc and ship me a five-email sequence targeted at new Pro signups" is reasoning-shaped. The agent has to read, decide on structure, draft, propose segments, configure send conditions, and present the result for approval. A CLI script can't compose that. The MCP can.

Audit a segment by description, not by ID. "Find me the active segments that target dormant users and tell me which ones haven't been used in 90 days" needs search and reasoning. The CLI gives you the raw segment data; the MCP turns the description into a query, runs it, and explains the result.

Debug a deliverability complaint. When a customer reports their re-engagement email landed in spam, you want an agent that can pull the campaign config, check the suppression list, and look at the recent deliverability stats. From there it cross-references DMARC alignment and proposes three hypotheses with evidence. The MCP does this. The CLI gives you the underlying data, but the synthesis is the work. Just make sure you've shipped the AI agent guardrails before you let it touch the suppression list.

How to store the token without putting it in your .zshrc

The Customer.io docs are explicit on this: don't put sa_live_ tokens in ~/.zshrc or ~/.bashrc. Shell configs get synced to cloud backups, committed to dotfiles repos, and exposed through environment leaks. Once a token is in one of those files, it's in places you can't audit.

Local interactive development

Run cio auth login and let the CLI store the token in ~/.cio/config.json. Scoped to your user account, built-in, and never goes near a shell config.

Local scripts and non-interactive runs

A project-scoped .env file loaded by direnv or dotenv. Git-ignore the .env, commit a .env.example with placeholder values. Different projects get different tokens, which means you rotate or revoke them independently.

CI/CD pipelines

Your CI provider's secrets store. GitHub Actions secrets, GitLab CI variables, CircleCI contexts. Whichever you use. Expose the token as CIO_TOKEN to the job. Encrypted at rest, masked in logs, auditable per run.

Shared machines

The OS keychain. macOS Keychain, secret-tool on Linux. Encrypted, per-user, no plaintext on disk. Slower to set up than a .env file but unavoidable when more than one person uses the machine.

Frequently asked questions

Do I need both the Customer.io MCP and CLI?

For most engineering-owned Customer.io accounts, yes. The MCP serves marketers and exploratory work. The CLI serves scripted operations and CI/CD. They share authentication (service account tokens) and primitives (read, write, search, execute), but they expose them differently. Pick one and you'll end up using the other within a quarter.

Is the CLI safer than the MCP because it's not an agent?

It depends on the threat model. The CLI does what you tell it to do, deterministically. The MCP plans and reasons, which can produce useful outcomes but also unexpected ones. If your threat model is "an agent does something I didn't intend", the CLI reduces that surface. If your threat model is "a marketer types rm -rf by accident", the MCP's confirmation steps and reasoning layer help. They're complements, not substitutes.

Can a service account token be scoped to one campaign or one workspace?

Tokens inherit the permissions of the user who created them. A service account scoped to a single workspace produces tokens that only work for that workspace. There's no campaign-level scoping today; that's controlled at the API level, not the token level. If you need per-campaign restrictions, the right control is the workspace boundary plus an audit trail.

How long does a Customer.io service account JWT last?

One hour. The sa_live_ token is exchanged for a JWT that expires in 3600 seconds, after which the CLI re-exchanges automatically. Cache the JWT between calls inside a single run because the OAuth endpoint is rate-limited per IP. Don't request a fresh JWT on every API call.

Does the Customer.io CLI run in the EU region?

Yes. Point the token exchange at https://eu.fly.customer.io instead of https://us.fly.customer.io. Same flow, different base URL. Your data residency stays in-region.

Can I revoke a Customer.io service account token without invalidating others on the same account?

Yes. Each token is independent. Revoking one immediately invalidates that token and any JWTs minted from it, with no effect on other tokens on the same service account. This is why one-token-per-integration matters: rotation isn't all-or-nothing.

Does MCP v2 use the same authentication as the CLI?

Both surfaces use the four-primitive agent architecture, but their auth flows differ. The MCP runs as a hosted server you authenticate against from your AI assistant. The CLI uses service account tokens directly. If you've enabled MCP v2 for a workspace, that doesn't automatically give a CLI script access—you still need a service account token for the CLI.

What's the difference between MCP v1 and MCP v2?

MCP v1 (April 2025) was a per-endpoint tool catalogue. Each Customer.io capability was a separate tool, and the LLM had to pick the right one. It scaled badly. MCP v2 (4 May 2026) replaces the catalogue with a single agent built on four primitives, the same architecture as the in-product Customer.io Agent. It scales by iterating the loop, not by adding tools.

Can I use the CLI in CI/CD pipelines?

Yes. The non-interactive login is echo "$CIO_TOKEN" | cio auth login --with-token. Pull CIO_TOKEN from your CI provider's secret store, run the command at the start of the job, and the CLI is authenticated for the rest of the run. Don't put the token in the pipeline YAML.

Does the CLI work with Cursor, Codex, and other AI assistants?

Yes. The fastest path is the Customer.io skill, installed via npx skills add customerio/cli. The skill teaches the assistant how to use cio api and cio schema. If your AI tool doesn't support skills, run cio prime and paste the output into the assistant's context—that gives it the full CLI reference in one block.

What does cio prime do?

It outputs a single block of CLI reference documentation that you can paste into an AI assistant's context window. Use it when your assistant doesn't support installable skills but you still want it to know how to drive the CLI. The output covers commands, schemas, and common patterns.

What's the difference between a service account and a personal API token?

A service account is account-level and lives independently of any user. Its tokens stay valid even if the creator leaves the team (until they're rotated). A personal API token is tied to a user. When the user is removed, the token stops working. For automation, scripts, and CI/CD, always use a service account. Personal tokens are for ad-hoc human use.

Sources

David Crowther
Book a free consultation →

On our call, you'll be speaking with David Crowther, founder of NerveCentral.

Our initial consultation is not a sales call—you'll talk, I'll listen and ask questions—then I'll come back to you within 48 hours with our best ideas on how to grow your business.