# CLI and MCP Server Technical Reference

Canonical page: https://www.adaptabletools.com/docs/technical-reference-ai-cli

- Every command and its MCP tool, with a sample of what it prints, the JSON envelope and the exit codes
- Call the MCP tool when the server is configured. Otherwise run the CLI with `--json`. A script only ever has the CLI
- Install, and the choice between the MCP server and the CLI, is on the [CLI and MCP Server](https://www.adaptabletools.com/docs/getting-started-ai-cli/index.md) page

- This page is for the Agent Skills - it is the command and tool reference they follow
- Install, and the choice between the MCP server and the CLI, is on the [CLI and MCP Server](https://www.adaptabletools.com/docs/getting-started-ai-cli/index.md) page

## Which binding to call

Prefer the MCP tool when the server is configured. Otherwise run the CLI as `npx adaptable <command> --json`. A script cannot reach the MCP session, so a script only ever runs the CLI.

Both return the same envelope. The server exposes tools only: docs are read through `search_docs` and `get_doc`. A dash in the MCP column means the command is CLI-only.

The tool name is not the command name. `detect` is `detect_environment`, `docs search` is `search_docs`, `changes` is `list_changes`, `compat` is `get_compatibility`, and `aql functions` is `list_expression_functions`.

<AiCliReference part="summary"/>

CLI-only, and why:

- `scaffold`, `convert coldefs`, `anonymise` and `cache` write files or manage the machine's cache. Run them in a terminal
- `docs list` and `docs demos` are browsing aids. The tools reach the same pages through `search_docs`, `get_doc` and `get_demo`
- `list_changes` returns the change records. `changes --repo` adds the repository search, and that search is CLI-only

In a long-lived server the docs manifest is re-validated at most once a minute, and the AdapTable package is resolved per call, so one server serves projects on different majors.

## Commands by job

One command per job, with a trimmed sample of what it prints (paths shortened, long messages cut at `…`). The tool name for each is in the table above. Every flag is in the [Command reference](#command-reference).

### Know your project

<AiCli args="detect" />

```text
_AdapTable 24.0.0 (detected) · react · AG Grid 36.2.0 · code: installed · docs: https://www.adaptabletools.com_

## /work/trades-blotter

- AdapTable: 24.0.0 (declared 24.0.0), major 24
- Wrapper: @adaptabletools/adaptable-react-aggrid 24.0.0 (declared 24.0.0)
- AG Grid: ag-grid-enterprise 36.2.0 (declared 36.2.0)
- Framework: react (react 19.3.1 (declared ^19.0.0), react-dom 19.3.1 (declared ^19.0.0))
- Licence key in sources: no
- Initial State literals: `src/adaptableOptions.ts`
- columnDefs files: `src/columnDefs.ts`
```

`compat` prints the AG Grid version, wrapper packages and environment (Node, TypeScript, framework) a major needs, and which majors have change records.

### Read the docs for your version

<AiCli args='docs search "format column rule" --limit 3' />

```text
1. **FormatColumn** › Property Details > Rule — `reference/formatcolumn` (AdapTable Types Reference, ~58 tokens)
   https://www.adaptabletools.com/docs/reference/formatcolumn?framework=react
   AdapTableQL boolean expression (evaluated per row) deciding whether to apply the Format, e.g. `[price] > 100` …

3. **Format Columns** › Condition — `handbook-column-formatting` (Cell Rendering, ~110 tokens)
   https://www.adaptabletools.com/docs/handbook-column-formatting?framework=react
   By default, a Format Column applies to every cell in scope. An optional **Rule** limits application to matching rows …
```

- `docs list` — the feature index, with the demos each page shows (`--area`, `--section` narrow it)
- `docs search <query>` — the pages of your version and framework, ranked, with the section that matched
- `docs get <page>` — a page as Markdown; `--section "<heading>"` returns one section, `--max-tokens` cuts at a heading boundary, `--full` inlines demo code
- `docs demos <page>` and `docs demo <name>` — the runnable demos a page shows, and one demo's files. `docs demos` is CLI-only; one demo's files are the `get_demo` tool
- `aql functions` — the functions the engine accepts, with signatures and examples. `--module`, `--group` or `--query` narrows the list. Call `list_expression_functions` before using a function you are not certain exists for the installed version

All of it comes from this site's docs export (`/llms/manifest.json`, `pages.json`, one `.md` per page), picked for the detected major, re-validated with a conditional request and cached.

### Check what was written

The three commands the Skills run after every edit, and the ones worth wiring into CI.

<AiCli args="check state src/adaptableOptions.ts --columns src/columnDefs.ts" />

```text
## Issues (3)

| Severity | Rule | Where | Message |
| --- | --- | --- | --- |
| error | `layout/current-exists` | src/adaptableOptions.ts:8:22 | CurrentLayout "Trading" does not match any Layout Name ("Trades"). — Set `CurrentLayout` to one of "Trades", or rename that Layout to "Trading". |
| error | `layout/column-refs` | src/adaptableOptions.ts:12:25 | Column "trader" is not in the grid's columns (columnDefs.ts). — Use a column id from columnDefs (`colId`, else `field`) … |
| error | `aql/syntax` | src/adaptableOptions.ts:22:17 | FormatColumn expression `[pnl] < 0 && [status] == "Open"`: Parse error on line 1 … got '&' |

# check state — 3 errors

- Input: src/adaptableOptions.ts:6:17 (property), initial State
- Checked: shape types.d.ts@24.0.0; semantics 16 rules; column refs yes (columnDefs.ts); options visible; AdapTableQL yes
- Stats: 2 sections, 1 layouts, 1 expressions, 6 columns
```

`check state` reads the `initialState` literal out of a source file (or a JSON file, or stdin with `-`), type-checks it against the installed version's `InitialState` type, runs its semantic rules — `CurrentLayout` names a Layout, every column reference exists in `--columns`, Scopes are consistent, every embedded Expression parses for its Module — and reports what it could not verify (a spread, a function call, a computed key). `--kind persistent` checks a State exported from a running Grid (`getPersistentState()` from the [DevTools In-page API](https://www.adaptabletools.com/docs/dev-guide-support-devtools-api/index.md)); `--columns` takes a `columnDefs` source file or a `getColumns()` dump. `--major <n>` checks against a major that is not installed yet, which is how a plan checks State for the target version.

<AiCli args={`check expression "[pnl] < 0 AND [status] = 'Open'" --module FormatColumn --columns src/columnDefs.ts`} />

```text
## `[pnl] < 0 AND [status] = 'Open'`
- Valid as **boolean** in FormatColumn (accepts boolean), returns boolean
- Functions: —; columns: [pnl], [status]

_Engine 24.0.0 (installed); module rules from the engine._
```

`check expression` validates one AdapTableQL Expression for the Module it will be used in, with the column types from `--columns`. Written with JavaScript operators (`&&`, `==`), the same Expression fails with `AQL_SPLIT_BRAIN` and the AdapTableQL to write instead. `aql functions` lists the functions available, by `--module`, `--group` or `--query`.

<AiCli args="check setup" />

```text
## Issues (3)

| Severity | Rule | Where | Message |
| --- | --- | --- | --- |
| error | `setup/css-import` |  | AdapTable's stylesheet (`@adaptabletools/adaptable/index.css`) is not imported anywhere; the grid renders unstyled. — Import it once … |
| error | `setup/modules` |  | No AG Grid module registration was found (`ModuleRegistry.registerModules([...])`); AG Grid 33+ renders nothing without it. — Register `AllEnterpriseModule` once at startup … |
| info | `setup/ready-hook` |  | No ready hook was found; the Adaptable API (`adaptableApi`) is delivered through it. — Pass `onAdaptableReady={({ adaptableApi }) => …}` to `<AdaptableReact>`. |

# check setup — 2 errors
```

`check setup` audits the wiring: the wrapper package and its version against the core, AG Grid Enterprise at the paired version, the modules registered, the CSS imported, `adaptableId` and `primaryKey` set (and the key naming a real column), the Options literal against the installed `AdaptableOptions` type, the licence key not in source, the framework component used (not the grid directly), `adaptableApi` taken from the ready hook, stable references in React, `cellDataType` on every column, and `ColDef` properties that AdapTable's Layout owns (`hide`, `pinned`, `width`, `sort`, `rowGroup`, `aggFunc` …).

### Move between majors

<AiCli args="changes --from 23 --to 24 --repo ." />

```text
# Changes 23 → 24

- Environment for 24: node >=20 · typescript >=5.5.4 · react >=18 · angular >=20 · vue >=3
- Searched 7 files in /work/trades-blotter

### Adaptable Options

#### New option defaults

- **BEHAVIOUR** · auto-migrated: n/a · id `options-new-defaults`
- Batched Alerts default: `one Alert per changed cell` → `alertOptions.consolidateBatchAlerts defaults to true (set false to restore)`
- Note: Accept the new defaults or restore v.23 behaviour per row …
- Release note: https://www.adaptabletools.com/support/version-240-release-note#chg-options-new-defaults
- Matches: (applies to every project)
```

`changes` lists every change record between two majors — the tagged rows of the release notes' Upgrade Guides — and, with `--repo`, searches your repository for each record's tokens so the plan names `file:line`; `--with-docs` inlines the release-note section that holds the remedy.

<AiCli args="migrate state src/adaptableOptions.ts --from 23 --to 24 --out migrated.json" />

`migrate state` runs AdapTable's own State migration on an `initialState` literal or an exported State, writes the result with `--out`, logs every change it made, re-checks the result against the target major and lists the leftovers it could not convert. The [adaptable-upgrade](https://www.adaptabletools.com/docs/getting-started-ai-skills-upgrade/index.md) Skill drives this, `migrate expression` and `changes`.

<AiCli args='migrate expression "SUM({pnl})" --from 23 --to 24' />

`migrate expression` rewrites one Expression to the target major with AdapTable's own migration (brace aggregations, named operands, legacy observable rules) and validates the result. `--from` and `--to` match `migrate state`. The MCP tool is `migrate_expression`.

### Start and share

- `scaffold --framework <fw> --out <dir> --data <file>` overlays an AdapTable project onto a fresh framework shell from a CSV, JSON or XLSX sample: columns typed from the data, one Layout, the AGENTS.md snippet, keys read from environment variables (`--dry-run` previews it)
- `convert coldefs <file>` turns AG Grid `columnDefs` into an AdapTable Layout and Format Columns, and checks the result
- `anonymise <file>` replaces the values of a State, Options, Grid Info or row data with shape-preserving placeholders — names, column ids, Expressions and structure kept, licence keys and user names redacted — for a support case

<AiCli args="anonymise grid-info.json --out anonymised.json" />

```text
# anonymise — 3 strings and 3 numbers replaced, 1 identifier kept

- Input: grid-info.json (json)
- Redacted: `userName`, `licenseKey`
- Row data: 1 row; fields renamed: tradeId → field1, counterparty → field2, notional → field3, pnl → field4
- Written to: anonymised.json
```

### Cache

- `cache add <spec>` downloads an AdapTable package (a major such as `24`, an exact version or a dist-tag) so checks and migrations for it work offline
- `cache list` shows the packages, engine bundles and docs held, with sizes and ages
- `cache clear` removes everything, or only `--docs` or `--pkgs`

## What a run resolves

Every command resolves the project before it does anything: the AdapTable major (and exact version) from `package.json` and the lockfile, the framework, the AG Grid version and whether the pair is compatible, and which docs site serves that major. `--major <n>` and `--framework <fw>` override detection for a folder that has no project yet; the resolved context is in the Markdown header and in `resolvedContext` of the JSON envelope. In a monorepo pass `--cwd <dir>` for the project. When `detect` finds more than one project it stops; `--all-projects` lists every one.

The checks and migrations load their engine from an AdapTable package of major 24 or later:

- the one installed in the project when it is 24+ (`codeSource: installed`)
- otherwise a copy of the right version downloaded through `npm` into the cache (`codeSource: cached`), so a 22 or 23 project is checked with 22 or 23 types and migrated with the 24 engine

## Output

Markdown by default: a context line, warnings, a table of issues with `file:line` positions, then the command's own report. `--quiet` drops warnings and `info` issues.

`--json` prints the envelope, the same for every command and for the MCP tools:

```json
{
  "schemaVersion": 1,
  "ok": true,
  "resolvedContext": { "adaptableMajor": 24, "framework": "react", "source": "detected", "docsHost": "https://www.adaptabletools.com", "codeSource": "installed" },
  "warnings": [],
  "issues": [
    {
      "code": "layout/current-exists",
      "severity": "error",
      "path": "Layout.CurrentLayout",
      "message": "CurrentLayout \"Trading\" does not match any Layout Name (\"Trades\").",
      "fix": "Set `CurrentLayout` to one of \"Trades\", or rename that Layout to \"Trading\".",
      "docs": "handbook-layouts",
      "file": "src/adaptableOptions.ts",
      "line": 8,
      "column": 20
    }
  ],
  "data": { }
}
```

- `issues[]` — findings about *your* input: a rule `code`, a `severity` (`error`, `warning`, `info`), the `path` inside the checked object, the position in the source file when the input was one, a `fix` and the `docs` page for the rule
- `warnings[]` — about the run itself (a package not installed, docs served from cache, a host for another major)
- `data` — the command's payload; its shape per command is in the [command reference](#command-reference)

A failed call is `{ "ok": false, "error": { "code", "message", "hint" } }`; `hint` says what to do about it.

## Exit codes

| Code | Meaning                                                                                                                      |
| ---- | ---------------------------------------------------------------------------------------------------------------------------- |
| `0`  | ok, no findings                                                                                                              |
| `1`  | findings: at least one `error` or `warning` issue (`info` does not count) — what a pre-commit hook or CI step keys on         |
| `2`  | usage: a bad flag or argument, an input that could not be read or extracted, a command the MCP binding does not offer         |
| `3`  | environment: no AdapTable dependency, an unsupported version, a package or the docs could not be fetched, network required    |

## Offline and restricted networks

- `--offline` (or no network) uses the cached docs and packages and touches nothing; a command that needs something not cached fails with `NETWORK_REQUIRED` or `DOCS_NOT_AVAILABLE` and says so
- `cache add <spec>` downloads a package ahead of time; `cache list` and `cache clear` manage the cache (`~/.cache/adaptable-mcp`, or `ADAPTABLE_MCP_CACHE_DIR`). Copy that folder to a machine without registry access — see [Prepare a machine with no network](#prepare-a-machine-with-no-network)
- Package downloads go through the `npm` CLI, so `HTTPS_PROXY` and `.npmrc` (registry mirror, auth) apply. Docs downloads use Node's `fetch`: on Node 24+ set `NODE_USE_ENV_PROXY=1` for the proxy to apply; on Node 20–22 warm the cache from a connected machine
- `--docs-host <url>` (or `ADAPTABLE_MCP_DOCS_HOST`) reads a different docs site — an internal mirror, or a pre-release host

- `~/.cache/adaptable-mcp` holds the docs export per docs host, AdapTable packages per version and the engine bundles built from them; nothing in it is specific to a project, and none of your code, State or data is written to it
- The only commands that write files are `scaffold --out`, `migrate state --out`, `anonymise --out` and the cache

### Prepare a machine with no network

On a connected machine, with the same CLI version:

{`npx adaptable cache add 24
npx adaptable docs list > /dev/null
npx adaptable cache list`}

`cache add` stores the package. Any docs command stores the docs for the detected major. Copy `~/.cache/adaptable-mcp` to the other machine (or point `ADAPTABLE_MCP_CACHE_DIR` at a shared copy) and run with `--offline`.

## Environment variables

| Variable                      | Default                                                     | Use                                                           |
| ----------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------- |
| `ADAPTABLE_MCP_CACHE_DIR`     | `$XDG_CACHE_HOME/adaptable-mcp` or `~/.cache/adaptable-mcp` | Cache for docs, packages and bundled engine entries           |
| `ADAPTABLE_MCP_DOCS_HOST`     | the docs site matching the detected major                   | Docs site to read instead (same as `--docs-host`)             |
| `HTTPS_PROXY`, `.npmrc`       |                                                             | Honoured by the package downloads (they go through `npm`)     |
| `NODE_USE_ENV_PROXY`          |                                                             | Set to `1` on Node 24+ so docs downloads use `HTTPS_PROXY`    |

## Troubleshooting

<Troubleshoot>
<Trouble>
<Symptom>A command fails with `NETWORK_REQUIRED` or `DOCS_NOT_AVAILABLE`</Symptom>
<Fix>It needs a package or the docs that are not in the cache, and the run is `--offline` or has no network. Run it once with network access, or prepare the cache on a connected machine — see [Prepare a machine with no network](#prepare-a-machine-with-no-network)</Fix>
</Trouble>
<Trouble>
<Symptom>`check state` lists properties as "unverifiable"</Symptom>
<Fix>The literal contains something a static read cannot evaluate: a spread of another object, a function call, a computed key. The rest of the State is checked; check the listed parts by hand, or pass a JSON export of the State instead</Fix>
</Trouble>
<Trouble>
<Symptom>It checks against the wrong AdapTable major</Symptom>
<Fix>Run `detect` to see what it found. In a monorepo pass `--cwd` for the project; for a folder with no project yet, or to check against another major, pass `--major <n>`</Fix>
</Trouble>
<Trouble>
<Symptom>Docs downloads fail behind a corporate proxy</Symptom>
<Fix>On Node 24+ set `NODE_USE_ENV_PROXY=1` alongside `HTTPS_PROXY`; on Node 20–22, warm the cache from a connected machine. Package downloads already use your `npm` proxy and `.npmrc`</Fix>
</Trouble>
</Troubleshoot>

## Use it in CI and hooks

`check state` and `check setup` exit `1` on findings, so a CI step or a pre-commit hook fails on them without any parsing. Install the package as a devDependency so the version is pinned.

A GitHub Actions job:

```yaml
- uses: actions/setup-node@v4
  with:
    node-version: 22
- run: npm ci
- name: Check AdapTable setup
  run: npx adaptable check setup
- name: Check AdapTable Initial State
  run: npx adaptable check state src/adaptableOptions.ts --columns src/columnDefs.ts
```

A pre-commit hook (for example `.husky/pre-commit`):

```bash
npx adaptable check state src/adaptableOptions.ts --columns src/columnDefs.ts --quiet
```

- On an AdapTable 24+ project the checks use the installed package and need no download
- On a 21–23 project the checks also need a 24 package as the engine. If the runner has no registry access, run `npx adaptable cache add 24` when the runner image is built
- `--json` gives the [envelope](#output) instead of Markdown, for a step that posts the issues as annotations

## Command reference

<AiCliReference part="detail"/>
