Skip to content

SDK CLI and MCP

Theodor Dimitriou edited this page Sep 24, 2026 · 2 revisions

The tools that talk to an AstroBaaS install from the outside: the astrobaas npm package (a typed client, the core types and the CLI) and the astrobaas-mcp server that lets an AI agent work with your content. This page is for developers building a frontend or automation, and for anyone connecting an AI client.

Everything here is a convenience over the REST API described in Headless and REST API. Nothing in the API requires these tools.

What gets installed

Package Install What it is for
astrobaas npm install astrobaas@alpha The typed client (astrobaas/client), the public types and helpers (astrobaas/core), the plugin registry (astrobaas/plugins), and the astrobaas CLI.
astrobaas-mcp npx -y astrobaas-mcp A zero-dependency Model Context Protocol server.

Pre-1.0 releases are published under the alpha dist-tag, so ask for astrobaas@alpha explicitly. The reasoning is in PUBLISHING.md. Both packages need Node 22.12 or later.

The npm package does not give you a running site. To run the CMS itself, clone the repository (see Installation).

The typed client: astrobaas/client

A small, dependency-free TypeScript client. It uses the global fetch, so it runs in Node, Deno, edge runtimes and browsers. Every method returns the unwrapped data from the response envelope and throws AstroBaasError on any failure.

import { createClient, AstroBaasError } from 'astrobaas/client';

const baas = createClient('https://cms.example.com', {
  apiKey: process.env.ASTROBAAS_KEY,   // omit for anonymous, published-only reads
  timeoutMs: 10_000,                   // abort slow requests
  retries: 2,                          // retry 429, 5xx and network errors
});

const posts = await baas.posts.list({ status: 'published', limit: 10 });
const post  = await baas.posts.get('hello-world');

try {
  await baas.posts.create({ title: 'From the SDK', status: 'draft' });
} catch (e) {
  if (e instanceof AstroBaasError) console.error(e.status, e.code, e.reason, e.message);
}

Options

Option Default Meaning
apiKey none Sent as Authorization: Bearer <key>. Change it later with setApiKey(key).
fetch global fetch Inject your own, for SSR frameworks or tests.
headers {} Extra headers on every request.
timeoutMs none Abort a request after this many milliseconds.
retries 0 Retries on 429, 5xx and network errors.
retryBackoffMs 250 Base of the exponential backoff. A 429 waits for Retry-After instead.

Errors

AstroBaasError carries:

Property Meaning
status HTTP status, or 0 for a network error or timeout.
code The envelope's error.code (NOT_FOUND, VALIDATION_ERROR, …), or TIMEOUT / NETWORK.
reason The specific cause, for example checkout.too_many_unpaid or IDEMPOTENCY_IN_PROGRESS.
params Values that go with reason, for example { max: 5 }.
details Per-field validation messages.

Methods

Namespace Methods Endpoint group
posts list, page, listAll, get, related, create, update, remove /api/posts
content(type) list, page, listAll, get, create, update, remove /api/content/{type}
media list, upload, update, remove /api/media/* (staff)
products list, page, listAll, get, create, update, remove, notifyWhenBack /api/products
orders list, page, get, quote, place, update, ship, refund /api/orders
customers list, page /api/customers (staff)
coupons list, create, update, remove /api/coupons (staff)
keys list, create, rotate, revoke /api/keys (admin)
webhooks list, register, remove, deliveries, redeliver /api/webhooks (admin)
audit list /api/audit (admin)
auth me, login, logout, forgotPassword, resetPassword, twoFactor.setup/enable/disable /api/auth/*, /api/2fa/*
(root) locales(), request(method, path, body?) anything else

request() is the escape hatch for endpoints without a typed helper. It still unwraps the envelope and throws on errors.

Paging helpers

page() returns one page with its metadata; listAll() walks every page for you.

const p = await baas.posts.page({ limit: 20, page: 2 });
// { items, total, count, limit, offset, page, hasMore }

const everything = await baas.content('event').listAll();

Checkout with an idempotency key

orders.place() always sends an Idempotency-Key, generating one if you do not pass it. Pass your own when your code retries a failed call, so the retry is recognised as the same attempt:

const key = crypto.randomUUID();                // once per "Place order" click
const quote = await baas.orders.quote({ items: [{ product_id, qty: 1 }] });
const order = await baas.orders.place(
  { email: 'buyer@example.com', items: [{ product_id, qty: 1 }] },
  { idempotencyKey: key },
);

Prices are always computed on the server. See Commerce.

Verifying webhooks

The client also exports verifyWebhookSignature(secret, rawBody, header), a constant-time HMAC check that runs anywhere WebCrypto exists. Usage is on the Webhooks page.

Core types and helpers: astrobaas/core

astrobaas/core is the public surface that plugins and themes build against. It exports:

  • the domain types (Post, User, Product, Order, MediaFile, CustomEntity, …) and the Storage interface type;
  • definePlugin, defineTheme, PluginManager, pluginManager and the PLUGIN_HOOKS catalogue;
  • registerContentType and the content-type definitions;
  • utilities: sanitizeHtml, validate, slugify, ApiResponseBuilder, fireEvent, webhookMatches, signWebhook, sendEmail, recordAudit;
  • LocalDB, for code that runs inside the AstroBaaS process.

Anything exported here is covered by the stability promise in STABILITY.md: pre-1.0 it may change, but breaking changes are always listed under Breaking in the changelog. See Plugins and Themes.

astrobaas/plugins exposes the plugin registry the server boots (BUNDLED_PLUGINS, ensurePluginsBootstrapped, reloadPlugins, pluginBootstrapReport). Most projects will not need it.

The CLI

The package installs an astrobaas binary. In a clone of the repository it is also available as node bin/astrobaas.mjs.

npx astrobaas@alpha --help

Project commands

These work on the files in the current directory. They need no running site.

Command What it does
astrobaas init [--force] Write a .env (from .env.example) with a freshly generated AUTH_SECRET. Refuses to overwrite an existing .env without --force.
astrobaas secret Print a new 32-byte hex secret to stdout, and nothing else.
astrobaas setup Create or replace the admin account, interactively. Works on the JSON store only; it refuses when DATABASE_URL is set.
astrobaas plugin new <id> Scaffold a code plugin in src/plugins/<id>/.
astrobaas plugin manifest <id> Scaffold a declarative plugin manifest (<id>.manifest.json), installable at runtime from the admin.
astrobaas plugin package <id> Scaffold a standalone plugin package with its own build, for loading through ASTROBAAS_PLUGINS.
astrobaas plugin validate <file> Validate a declarative manifest with the same rules the installer uses. Needs a repository checkout.
astrobaas theme new <id> Scaffold a theme in src/themes/<id>/.
astrobaas version Print the version.

A typical first run in a fresh clone:

npx astrobaas init && npm install && npm run setup && npm run dev

Commands against a running site

These go through the REST API, never the database directly, so every rule the routes enforce still applies. Set the site and a key first:

export ASTROBAAS_URL=https://cms.example.com
export ASTROBAAS_KEY=abk_…
Command What it does Key needs
astrobaas content types List the registered content types, with read and write policy. any key without scopes
astrobaas content list <type> List up to 200 entries. read access to the type
astrobaas content export <type> Print every entry as JSON, paging to the end. read access to the type
astrobaas backup export > site-backup Download a full archive to stdout. Refuses to write binary to a terminal. admin
astrobaas backup run Run the configured off-site backup now. admin
astrobaas clone <src> <dst> --yes Download an archive from one site and restore it into another. Overwrites the target. admin on both

Add --json to content types and content list for machine-readable output, and --base <url> to override ASTROBAAS_URL for one call. clone uses ASTROBAAS_SOURCE_KEY and ASTROBAAS_TARGET_KEY when the two sites need different keys, refuses when source and target are the same URL, and does nothing without --yes. A restore has driver rules of its own; read Backups and storage before cloning between installs.

The repository also has npm run scripts for work that is not part of the published CLI, such as npm run reset-password and the importers (import:md, import:wp, import:woo). See Importing.

The MCP server: astrobaas-mcp

astrobaas-mcp exposes a running AstroBaaS install as Model Context Protocol tools, so an AI client such as Claude Desktop or Claude Code can read and write its content. It speaks newline-delimited JSON-RPC over stdio, has no dependencies, and talks to your site over the same REST API as everything else. Nothing of AstroBaaS needs to be installed on the machine that runs it.

The same server also ships inside the astrobaas package as its astrobaas-mcp binary.

Configuration

Variable Default Meaning
ASTROBAAS_URL http://localhost:4321 Base URL of your install.
ASTROBAAS_KEY none An API key. Without one, only public reads work.

The key's role and scopes decide what the agent can do. Give it the least that works: a key scoped to posts:* and content:read cannot touch orders, customers or settings however the agent is prompted. See Headless and REST API.

Claude Desktop (claude_desktop_config.json), and any client that uses the same mcpServers shape:

{
  "mcpServers": {
    "astrobaas": {
      "command": "npx",
      "args": ["-y", "astrobaas-mcp"],
      "env": {
        "ASTROBAAS_URL": "https://cms.example.com",
        "ASTROBAAS_KEY": "abk_..."
      }
    }
  }
}

Claude Code:

claude mcp add astrobaas \
  -e ASTROBAAS_URL=https://cms.example.com \
  -e ASTROBAAS_KEY=abk_... \
  -- npx -y astrobaas-mcp

On startup the server writes to stderr (never stdout, which is the protocol channel): a ready line, then either the role the key resolved to or a warning that the key check failed. Check your client's MCP log if tools fail.

Tools

There are 33 tools. Ask the agent to call whoami first to confirm the connection and the key's role.

Area Tool Endpoint Access
Identity whoami GET /api/auth/me any
Posts list_posts GET /api/posts public (published only without a key)
get_post GET /api/posts/{slug} public
create_post POST /api/posts author+
update_post PUT /api/posts/{ref} author+ (authors: own posts only)
delete_post DELETE /api/posts/{ref} author+ (authors: own posts only)
list_revisions GET /api/posts/{ref}/revisions key required
Custom content list_content GET /api/content/{type} depends on the type
create_content POST /api/content/{type} editor+
update_content PUT /api/content/{type}/{id} editor+
delete_content DELETE /api/content/{type}/{id} editor+
Catalogue list_products GET /api/products public
get_product GET /api/products/{ref} public
create_product POST /api/products products:write
update_product PUT /api/products/{id} products:write
delete_product DELETE /api/products/{id} products:write
import_products POST /api/commerce/import products:write
list_product_categories GET /api/product-categories public
list_brands GET /api/brands public
Orders list_orders GET /api/orders staff, orders:read
get_order GET /api/orders/{id} staff
set_order_status PUT /api/orders/{id} staff
list_customers GET /api/customers staff, customers:read
list_payment_methods GET /api/payments public
Media list_media GET /api/media/get staff key
Editorial search GET /api/search public
list_categories GET /api/categories/get public
list_locales GET /api/locales public
Site get_settings GET /api/settings/get public keys only, all for staff
update_settings POST /api/settings/update admin
list_plugins GET /api/plugins admin
set_plugin_active POST /api/plugins/toggle admin
get_theme GET /api/themes/get public

The catalogue, order and customer tools only work while commerce is switched on. A few things the tool descriptions tell the agent, and that are worth knowing yourself:

  • Money is integer minor units: price_cents: 1999 means 19.99. Floats are refused.
  • set_order_status changes fulfilment status and moves stock; it never moves money.
  • import_products reports rejected rows rather than skipping them silently. With replace: true it empties the catalogue first, and that is not transactional, so take a backup first.
  • Settings have a public read path. Never ask an agent to store credentials in them; use environment variables.

Resources

Published posts are also exposed as MCP resources, astrobaas://post/<slug>, so a client can attach an article as context without a tool call. The list holds up to 100 recent posts, and reading one returns its HTML.

AI coding agents without MCP

An agent that writes a frontend does not need the MCP server. Point it at https://cms.example.com/llms.txt (a plain-text brief of the API) and /openapi.json. Both are public.

See also

Clone this wiki locally