We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
Quickstart
From empty directory to a branded CLI, an MCP server, and a Zapier app — in about six minutes. No scaffolding graveyard, no empty stubs.
#Install
Beltline is a single binary. Pick any install path; they're interchangeable.
# No install. Runs once, caches, stays out of your PATH. $ npx create-beltline@latest acme-cli
#Scaffold a project
beltline new
asks three questions — template, auth provider, API spec — and writes a working project
with every table-stakes decision already made. No yak-shaving about
eslint
vs biome,
no vitest vs jest fork in the road.
$ beltline new acme-cli ? Template › ❯ api-client — OpenAPI import + auth + MCP export oauth-login — device + pkce login skeleton agent-cli — chat-mode + tool adapters repo-ops — release, lint, changelog, internal APIs bare — just the runtime ? Auth provider › okta, auth0, workspace, entra-id, workos, custom-oidc ? OpenAPI spec › ./openapi.yaml (press enter to skip) ✓ acme-cli/ written · beltline.yaml, handlers/, openapi.yaml, tests/ ✓ bun install · 47 deps · 2.1s ✓ ready cd acme-cli bun dev # run the CLI locally bun mcp # start the MCP server on stdio bun build # compile to a single binary
#Your manifest
Everything flows from beltline.yaml.
It's OpenAPI 3.1 with a small overlay: an
agent
block per command for MCP behavior, an
ipaas
block for Zapier/n8n shape, and a
cli
block for subcommand overrides.
name: acme description: Ship releases, manage users, inspect pipelines. auth: provider: okta issuer: https://acme.okta.com flows: [pkce-loopback, device] # try in order storage: keychain # fallback: encrypted-file scopes: [openid, profile, offline_access] api: spec: ./openapi.yaml baseUrl: https://api.acme.com retries: max: 4 jitter: decorrelated pagination: cursor commands: - name: deploy summary: Ship a release to production. handler: ./handlers/deploy.ts args: - name: service required: true description: Service slug, e.g. api-gateway. - name: sha default: HEAD agent: humanConfirmation: always # always | write | never toolName: deploy_service ipaas: kind: action # trigger | action | search
The top-level keys are intentionally small: auth, api, commands, plugins, telemetry.
Every deeper key has a sensible default, a docstring in the schema,
and IntelliSense in your editor as soon as you add the
$schema
comment.
Top-level keys
| Key | Type | Notes |
|---|---|---|
| name | string | The binary name. Used for AGENTS.md, Homebrew tap, completions. |
| auth | object | OAuth config. Omit for unauthenticated tools. |
| api | object | Typed client config. Points at OpenAPI / GraphQL / gRPC. |
| commands | Command[] | Each one generates: a CLI subcommand, an MCP tool, an iPaaS shape. |
| plugins | Plugin[] | Runtime-installable, pinned, capability-scoped. |
| telemetry | object | Opt-in, pluggable sink. Default: off. |
#Auth: PKCE, device flow, fallback
Auth is the single most-rebuilt slab of code in internal-CLI land. Beltline ships both flows with automatic fallback — so if your user is SSH'd into a headless box, they get the device flow without configuration. If they're on a laptop with a browser, they get the PKCE loopback. You don't write either path.
aws sso login
and
gh
both fell back to device flow. Beltline does the same — the kit detects TTY, SSH_CLIENT,
and the absence of xdg-open, and picks.
Storage
Tokens go in the OS credential store via @napi-rs/keyring:
Keychain on macOS, Credential Manager on Windows, libsecret on Linux. On headless Linux / WSL2 / Codespaces where
libsecret isn't available, Beltline falls back to an encrypted file keyed on machine-id.
You never touch this code.
Profiles
Named profiles work the way you'd expect from
aws
or
kubectl
— each profile has its own token, endpoint, and overrides. Switch with
--profile
or BELTLINE_PROFILE.
$ acme auth login --profile staging $ acme auth login --profile prod $ acme --profile prod deploy api-gateway
#MCP: one command, two surfaces
Every
command
in your manifest is also an MCP tool. Run
beltline mcp export
and you get a stdio server for Claude Desktop and a Streamable HTTP server for remote agents — both speaking the
2025-11-25 spec, both wired for OAuth 2.1 + PKCE and Resource Indicators (RFC 8707).
Human-in-the-loop
The
agent.humanConfirmation
field is the important one. It renders resolved
argument values at approval time — so an agent
can't silently escalate from
deploy --service staging
to
deploy --service prod
by rewriting args between consent and call.
| Value | Behavior |
|---|---|
| always | Confirm every invocation. Arg diff shown at approval time. |
| write | Confirm for non-idempotent operations. Inferred from OpenAPI verb. |
| never | No confirmation. Use for read-only tools. |
#What's next
You've scaffolded a project, walked the manifest, and shipped both a CLI and an MCP server from the same handlers. The two most useful next reads are the full manifest reference and the generators guide.