Docs / Start here / Quickstart

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.

! Opinionated by design Beltline ships one path. TypeScript on Node 22-compatible Bun, YAML manifest, one command per file. If you'd prefer four choices per decision, use oclif — we mean that sincerely.

#Install

Beltline is a single binary. Pick any install path; they're interchangeable.

v0.3.0 · Apr 2026
# 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.

interactive
$ 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.

# $schema: https://beltline.dev/schema/v1.json
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.

beltline auth login --provider okta --profile prod
i Why both flows Claude Code shipped PKCE-only and broke for SSH users. 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.