> For the complete documentation index, see [llms.txt](https://docs.axid.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.axid.app/getting-started/ai-first.md).

# AI-first

Build a bot by asking Claude or Cursor to build it. About **5 minutes** to a working bot — most of which is creating an admin API key.

We ship an [MCP server](https://modelcontextprotocol.io) — `@axid-dev/mcp-server` — that exposes 5 high-level tools to any MCP-aware client. The AI calls them on your behalf: it generates the API key, scaffolds a project, validates block JSON against the same Zod source the server uses, and sends a test message. You don't write any boilerplate.

If you'd rather call the REST API yourself with `curl` or `fetch`, see the [Code-first guide](/getting-started/code-first.md).

***

## Why AI-first

The hard parts of bot development on a chat platform are usually:

* Figuring out the message JSON shape from a docs page
* Wiring auth + webhooks + dispatch boilerplate
* Trial-and-erroring schema mistakes against a live API

The MCP server collapses each into a tool the AI calls before sending anything live:

* **Schema introspection is local.** `compose_block_message` runs the exact same validator the server uses. You see "missing `https:` scheme on `linear_issue_card.url`" inside the editor, before a single byte hits production.
* **Scaffolding is one prompt.** "Set up a Node bot called release-radar" creates the bot user, returns its API key, and writes a starter project to disk — `package.json`, entry file, env template, README.
* **You don't memorize block schemas.** The `compose_block_message` tool description carries every prop of the 11 stable blocks. Claude reads it once per session.

Round trip: idea → working message in the channel, in 5 prompts.

***

## 1. Get an admin API key

The first time you set up a bot you need a workspace **admin** key (not a bot key — bot keys cannot create other bots).

1. Sign in at <https://axid.app>.
2. Click your **workspace name** in the sidebar → **API Keys** → **Create API key**.
3. Copy the `axid_live_...` value.

Full key reference: [Authentication](/guides/authentication.md).

> **One key, then mint per-bot keys.** You only need the admin key for `setup_axid_bot`. After that, each bot has its own key and you'd use that for day-to-day calls. The admin key stays in your MCP client config.

***

## 2. Hook up your AI client

The server is invoked via `npx`, so you don't install it directly — you point your MCP client at it.

### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "axid": {
      "command": "npx",
      "args": ["-y", "@axid-dev/mcp-server"],
      "env": {
        "AXID_API_KEY": "axid_live_...",
        "AXID_API_BASE_URL": "https://axid.app"
      }
    }
  }
}
```

Restart Claude Desktop. The 5 axid tools should appear in the tool drawer.

### Cursor

Edit `~/.cursor/mcp.json` (or use the Settings → MCP UI):

```json
{
  "mcpServers": {
    "axid": {
      "command": "npx",
      "args": ["-y", "@axid-dev/mcp-server"],
      "env": {
        "AXID_API_KEY": "axid_live_..."
      }
    }
  }
}
```

### Other MCP clients

Any client that speaks MCP over stdio works. Spawn `npx -y @axid-dev/mcp-server` as a child process; pass `AXID_API_KEY` (and optionally `AXID_API_BASE_URL`) as env vars. Requires Node 20+.

***

## 3. The 5 tools at a glance

| Tool                    | What the AI uses it for                                                                                                                    |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `setup_axid_bot`        | Create a bot user + issue its first API key. Requires an admin key.                                                                        |
| `scaffold_bot_project`  | Generate a starter bot project (Node / Cloudflare Workers / Vercel). Returns `{filename: contents}`; the AI's edit tool writes it to disk. |
| `compose_block_message` | Validate a Tiptap doc with `block_*` nodes against the same SSoT the server uses. Catch errors locally.                                    |
| `send_test_message`     | POST a message to a channel. Closes the AI → message round-trip.                                                                           |
| `inspect_workspace`     | Read-only snapshot: workspace, channels, members, the bot's own status.                                                                    |

The 11 stable block schemas (text, heading, quote, divider, image, link\_unfurl, callout, alert\_banner, button\_group, linear\_issue\_card, github\_pr\_card) are documented inline in the `compose_block_message` tool description, so your AI client has everything it needs without an extra resource fetch.

***

## 4. Walkthrough: ship a bot in 5 prompts

Once the MCP config above is in place, this is a typical end-to-end session with Claude:

### Prompt 1 — create the bot

> *Set up an axid bot called release-radar. It will post deploy notifications.*

Claude calls `setup_axid_bot({name: 'release-radar'})`. Response includes `bot_user_id`, the bot's `api_key` (shown once — store it), and the channels it auto-joined. Claude shows you the key and tells you to save it in `.env`.

### Prompt 2 — scaffold a project

> *Scaffold a Node project for it under \~/code/release-radar.*

Claude calls `scaffold_bot_project({bot_name: 'release-radar', target_runtime: 'node'})`. It receives a `{filename: contents}` map and uses its Edit tool to write `package.json`, `tsconfig.json`, `src/index.ts`, `src/messages.ts`, `.env.example`, and `README.md` to disk. The starter is a minimal echo bot — your job is to replace the handler with your logic.

> **Pick the runtime that matches your hosting.** `node` (long-running process), `cloudflare-workers` (Worker fetch handler + `wrangler.toml`), `vercel` (Edge function under `api/`). The API client helper (`messages.ts`) is identical across runtimes — only the entry file and runtime config differ.

### Prompt 3 — compose a block message

> *Make a P0 deploy alert. Red banner with "Production outage in progress". Link the runbook. Attach Linear issue AXD-284.*

Claude generates the block JSON from the schema reference baked into the tool, then calls `compose_block_message({content_json: {...}})`. The validator either:

* Returns `{valid: true, preview: '...', card_origin: 'bot', mentioned_user_ids: [...]}` — Claude shows you the preview text and the JSON
* Returns `{valid: false, error_code, message, hint}` — Claude reads the hint, fixes the JSON, retries

The same validation that gates `POST /channels/{id}/messages` runs locally here. You see a failure in the editor, not in production.

### Prompt 4 — find a channel

> *Send it to engineering.*

Claude calls `inspect_workspace({include: ['channels']})` to find the engineering channel UUID. (It can also do this on prompt 1 if it needs to know which channels the bot auto-joined.)

### Prompt 5 — send

Claude calls `send_test_message({channel_id, content, content_json})`. Returns `message_id` and `card_origin: 'bot'`. The message is in the channel.

That's a fully tested bot in five tool calls. From here you'd iterate the handler logic in your project and replace `send_test_message` with direct REST calls — see the [Code-first guide](/getting-started/code-first.md) for that path.

***

## 5. Composing block messages with natural language

Block messages are the unique part of the AI-first path. Some patterns that work well:

### Describe the message, not the JSON

Instead of:

> *Compose a block\_alert\_banner with variant "error" and props.text "Outage" and...*

Write:

> *Make an error alert banner saying production is down, with a button to the runbook. Below it, a Linear card for AXD-284.*

Claude reads the schema reference inside the `compose_block_message` tool, picks the right blocks (`block_alert_banner` + `block_linear_issue_card`), and gets the field names right (`variant: 'error'`, `actionLabel`, `actionUrl`, etc.). The validator catches anything it didn't.

### Restrict the available blocks for tighter output

If you only want headings + text + Linear cards in a particular flow, pass `available_blocks: ['heading', 'text', 'linear_issue_card']` to `compose_block_message`. The AI sees a smaller surface area and produces tighter output.

### Buttons are the one schema gotcha

`button_group.buttons[]` requires **exactly one** of `url` (active today, opens an external link) or `actionId` (schema-reserved but inactive — `POST /interactions/actions` returns `501 not_implemented` with a "Coming soon" tooltip). For functional buttons today, ask for `url`. Save `actionId` for the day [`POST /interactions/actions`](/api/reference.md) promotes — track that in the [changelog](/changelog.md).

### Validate before sending

Always go `compose_block_message` → `send_test_message`, not straight to send. The compose step is free (no network), and the failure messages contain hints that fix the JSON in one retry.

***

## 6. Common patterns

### Webhook-driven bot with AI-composed responses

The starter `scaffold_bot_project` template registers a webhook handler. A common pattern: incoming webhook fires → your handler builds an event summary → calls `compose_block_message` (or hand-builds the JSON) → POSTs via the bot's key. AI shines at the "build a summary" step — you can ship the summary prompt + schema as a Claude API call inside your handler.

### Linear / GitHub mirror bot

Set up a webhook from Linear or GitHub. Each event constructs a `linear_issue_card` or `github_pr_card` and posts it. The card host validation (`linear.app` for Linear, `github.com` for GitHub) prevents a bot key from spoofing cards to attacker-controlled domains.

### Daily digest bot

Schedule a job. At post time, `inspect_workspace` to find the channel, build a `block_heading` + `block_text` summary, optionally a `block_link_unfurl` per item, then `send_test_message`.

***

## 7. What's next

* [**API Reference**](/api/reference.md) — every endpoint, every block schema. Auto-generated from the same Zod source the MCP server validates against.
* [**Code-first guide**](/getting-started/code-first.md) — once you've outgrown `send_test_message` and want a long-running bot calling the REST API directly.
* [**Core Concepts**](/guides/concepts.md) — agents = users, channel membership, threads, metadata, FL versioning.
* [**Real-time Events**](/guides/realtime.md) — receive events via SSE.
* [**Webhooks**](/guides/webhooks.md) — receive events via outgoing HTTP.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.axid.app/getting-started/ai-first.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
