How to Use the Astryx JSON API Programmatically for AI Agent Integration

The Astryx JSON API provides a typed, machine-readable interface for every CLI command, enabling AI agents to parse responses, handle errors deterministically, and discover capabilities through a self-describing manifest.

The facebook/astryx repository ships a sophisticated JSON API that transforms the Astryx CLI into a programmable interface for automation tools and AI agents. By appending the --json flag to any command, the CLI emits structured envelopes instead of human-readable text, complete with TypeScript definitions that ensure type safety across the boundary between shell execution and application logic.

Understanding the JSON API Architecture

The Astryx JSON API is implemented as a thin serialization layer over the existing CLI infrastructure. Every command checks for the --json flag and, when present, outputs responses using typed helper functions rather than console formatting.

The Envelope Structure

Every JSON response follows a strict envelope pattern defined in packages/cli/src/types/base.d.ts. Successful responses return:

{ "type": "<discriminator>", "data": {  } }

Error responses use a complementary shape:

{ "error": "...", "code": "<ERR_…>", "suggestions": [] }

The type discriminators (such as component.detail, search, or manifest) function as tagged unions that allow TypeScript to narrow response types automatically.

Core Type Definitions

The type system lives in packages/cli/src/types/ and provides compile-time guarantees for AI agent integrations:

  • base.d.ts – Defines the generic CLIResult<T>, CLIError, and the union CLIAnyResponse. It also exports helper functions including jsonOut, jsonError, parseResponse, isError, and assertResponse.
  • component.d.ts – Describes component-related responses such as ComponentDetailResponse, specifying fields like name, props, and source.
  • manifest.d.ts – Exposes the self-describing capability manifest via ManifestResponse, which agents use to discover commands and their JSON support.

Consuming the API at Three Levels

Because the API is pure data, you can integrate it at three distinct abstraction levels depending on your runtime constraints.

Shell-Level Integration

Spawn a child process with the --json flag and parse the stdout stream. This approach works across any language or runtime that can execute shell commands, making it ideal for polyglot AI agent systems.

Node-Level Integration

Import the helper functions directly from @astryxdesign/cli/json to reuse the CLI's internal serialization logic in custom scripts or libraries. This eliminates string parsing and provides immediate access to the type definitions.

Agent-Level Discovery

Execute astryx manifest --json to retrieve a "mini-OpenAPI" description of every command, its arguments, flags, and JSON support status. AI agents can fetch this manifest once at startup and generate calling code dynamically without hard-coding response shapes.

Implementation Examples

Calling Astryx via Child Process

For generic AI agents running in isolated environments, spawn the CLI and parse the response using the built-in helpers:

import { execFile } from 'node:child_process';
import { parseResponse, isError } from '@astryxdesign/cli/json';

async function runAstryx(command: string[], cwd = process.cwd()) {
  return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
    execFile('npx', ['astryx', ...command, '--json'], { cwd }, (err, stdout, stderr) => {
      if (err) reject(err);
      else resolve({ stdout, stderr });
    });
  });
}

// Example: fetch details for the Button component
const { stdout } = await runAstryx(['component', 'Button']);
const result = parseResponse(JSON.parse(stdout));

if (isError(result)) {
  console.error('Astryx error:', result.code, result.error);
} else {
  // `result` is now narrowed to ComponentDetailResponse
  console.log('Component docs URL:', result.data.docUrl);
}

This pattern uses the parseResponse and isError helpers defined in packages/cli/src/types/base.d.ts to handle both success and error envelopes safely.

Direct Programmatic Usage

When running within a Node.js context where you can import the CLI library, emit responses using the same primitives the CLI uses internally:

import { jsonOut, jsonError } from '@astryxdesign/cli/json';
import type { ComponentDetailResponse } from '@astryxdesign/cli/json';

// Simulate a command implementation inside a custom script
function getButtonInfo(): ComponentDetailResponse {
  return {
    type: 'component.detail',
    data: {
      name: 'Button',
      props: [],
      source: 'packages/core/src/Button/Button.tsx',
    },
  };
}

// Emit a proper JSON envelope
jsonOut('component.detail', getButtonInfo().data);

The jsonOut function guarantees that the payload matches the declared shape according to the contracts in base.d.ts.

Discovering Capabilities via Manifest

AI agents can introspect the CLI's capabilities before executing commands:

import { execFileSync } from 'node:child_process';
import { parseResponse } from '@astryxdesign/cli/json';

const raw = execFileSync('npx', ['astryx', 'manifest', '--json'], { encoding: 'utf8' });
const manifest = parseResponse(JSON.parse(raw)); // type = ManifestResponse

if (!('commands' in manifest.data)) {
  throw new Error('Unexpected manifest shape');
}

// Find all commands that support JSON output
const jsonCommands = manifest.data.commands.filter(c => 
  c.flags.some(f => f.flag === '--json')
);
console.log('Commands with JSON support:', jsonCommands.map(c => c.name));

The manifest schema resides in packages/cli/src/types/manifest.d.ts and is exposed via the ManifestResponse type.

Asserting Specific Response Types

For type-safe narrowing when you expect a specific discriminator, use the assertion helper:

import { assertResponse } from '@astryxdesign/cli/json';
import type { SearchResponse } from '@astryxdesign/cli/json';

const raw = execFileSync('npx', ['astryx', 'search', 'button', '--json'], { encoding: 'utf8' });
const response = assertResponse(JSON.parse(raw), 'search'); // narrows to SearchResponse

console.log('Top result:', response.data.results[0].name);

The assertResponse function throws if the envelope does not match the expected discriminator, providing runtime validation that complements the static types in component.d.ts and related files.

Error Handling and Type Safety

Always use the type guard isError(result) to differentiate success from failure before accessing response data. This function checks for the presence of the error field and narrows the union type to CLIError, exposing the stable code field for programmatic error handling.

For stricter validation, the assertResponse helper validates that the type discriminator matches your expected string, throwing immediately if an unexpected response shape arrives. This prevents type confusion when integrating with third-party AI agents that may misparse outputs.

Summary

  • Astryx JSON API responses follow a strict envelope pattern with type discriminators and data payloads, or error objects with stable codes.
  • Type definitions in packages/cli/src/types/base.d.ts provide CLIResult<T>, CLIError, and helper functions like isError and assertResponse.
  • Three integration levels support shell-level parsing, direct Node.js imports from @astryxdesign/cli/json, and agent-level discovery via astryx manifest --json.
  • Error handling relies on the isError type guard to safely narrow response unions before accessing data properties.

Frequently Asked Questions

What is the envelope format for Astryx JSON API responses?

Successful responses return a JSON object with type and data fields, where type acts as a discriminator (e.g., component.detail, search). Error responses return an object with error, code, and optional suggestions fields. This format is defined in packages/cli/src/types/base.d.ts as CLIResult<T> and CLIError.

How do I handle errors when calling Astryx commands programmatically?

Import the isError type guard from @astryxdesign/cli/json to narrow the response type. If isError(result) returns true, the response contains error and code fields that provide stable identifiers for programmatic error handling. Alternatively, use assertResponse to throw immediately if the response type does not match expectations.

Can AI agents discover available Astryx commands automatically?

Yes. By running astryx manifest --json, agents retrieve a ManifestResponse containing descriptions of every command, including available flags and JSON support status. Agents can parse this manifest to determine which commands support JSON output and what arguments they require, eliminating the need for hard-coded command lists.

What package should I import for TypeScript type definitions?

Install and import from @astryxdesign/cli/json to access helper functions (parseResponse, jsonOut, isError, assertResponse) and response types (ComponentDetailResponse, SearchResponse, ManifestResponse). These definitions mirror the source files in packages/cli/src/types/ and ensure your AI agent code remains synchronized with the CLI implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →