> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asteroid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build from your coding agent

> Drive Asteroid from Claude Code, Cursor, or Codex — over MCP or with the SDK.

Your coding agent can build Asteroid workflows, run them, and read the results. Two paths lead there: the
Asteroid MCP server, or the [TypeScript](/sdks/typescript) and [Python](/sdks/python) SDK.

<Tip>
  Give your coding agent [https://docs.asteroid.ai/skill.md](https://docs.asteroid.ai/skill.md) and it can do
  most of the integration on its own. To install the docs as a skill in tooling that supports it:

  ```bash theme={null}
  npx skills add https://docs.asteroid.ai
  ```
</Tip>

## Which path

| You want to                                          | Use |
| :--------------------------------------------------- | :-- |
| Build and debug with a person watching each step     | MCP |
| Ask for things in plain language from your editor    | MCP |
| Sign in through the browser instead of holding a key | MCP |
| Keep workflow definitions in your repository         | SDK |
| Publish a workflow from CI                           | SDK |
| Call Asteroid from your own application              | SDK |

## Connect the MCP server

The MCP server signs you in through the browser. It does not take an API key.

<Tabs>
  <Tab title="Claude Code">
    <Steps>
      <Step title="Add the server">
        Run this in your terminal:

        ```bash theme={null}
        claude mcp add --transport http asteroid https://mcp.asteroid.ai/mcp
        ```
      </Step>

      <Step title="Sign in">
        Complete the browser sign-in the first time Claude Code calls an Asteroid tool. The client stores the token
        and sends it on later requests.
      </Step>

      <Step title="Check it works">
        Ask Claude Code:

        ```
        List all my Asteroid agents
        ```

        Your workflows come back in the reply.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Cursor">
    <Steps>
      <Step title="Add the server">
        Add this to `.cursor/mcp.json` in the project, or to `~/.cursor/mcp.json` for every project:

        ```json theme={null}
        {
          "mcpServers": {
            "asteroid": {
              "url": "https://mcp.asteroid.ai/mcp"
            }
          }
        }
        ```
      </Step>

      <Step title="Sign in">
        Accept the OAuth prompt on first use so Cursor gets a token for your Asteroid user.
      </Step>

      <Step title="Check it works">
        In the chat, ask:

        ```
        List all my Asteroid agents
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

<Info>
  The server speaks streamable HTTP at `/mcp`. It publishes OAuth metadata at
  `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, so clients that
  discover authentication find it on their own.
</Info>

### When it does not connect

| Problem                        | Fix                                                                                           |
| :----------------------------- | :-------------------------------------------------------------------------------------------- |
| "Tool not found"               | Check the server is registered. Run `claude mcp list`, or read `.cursor/mcp.json`.            |
| 401 or an authentication error | Remove the server, add it again, and sign in. Check your user has access to the organization. |
| Timeouts                       | Check your network reaches `mcp.asteroid.ai`.                                                 |

## What the MCP server can do

### Context and docs

| Tool                 | What it does                                                                                                                            |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
| `getContext`         | Returns your user context, including organization IDs and names. Call it first — most tools need an organization UUID.                  |
| `availableToolsList` | Lists the tools an agent node can be given, and the activity names they produce. See [What a node can do](/concepts/node-capabilities). |
| `searchDocs`         | Searches these docs.                                                                                                                    |

### Workflows and profiles

| Tool                            | What it does                                                                    |
| :------------------------------ | :------------------------------------------------------------------------------ |
| `agentList`                     | Lists the workflows in an organization.                                         |
| `agentCreate`                   | Creates a workflow with its first graph, and publishes that graph as version 1. |
| `agentRename`                   | Renames a workflow. The name takes 1 to 100 characters.                         |
| `agentDelete`                   | Deletes a workflow and its versions. Every execution must be terminal first.    |
| `agentProfilesList`             | Lists the [agent profiles](/concepts/profiles) in an organization.              |
| `agentProfilesCreate`           | Creates a profile.                                                              |
| `agentProfileGet`               | Reads one profile.                                                              |
| `agentProfileUpdate`            | Changes a profile.                                                              |
| `agentProfileDelete`            | Deletes a profile.                                                              |
| `agentProfileClearBrowserCache` | Clears the browser state a profile has kept.                                    |

### Executions

| Tool                       | What it does                                                              |
| :------------------------- | :------------------------------------------------------------------------ |
| `agentExecutePost`         | Starts an execution of the published version.                             |
| `executionsList`           | Lists runs, with filters and pagination.                                  |
| `executionGet`             | Returns everything about one execution.                                   |
| `executionView`            | Draws an execution viewer in the chat, with status, activity, and result. |
| `executionActivitiesGet`   | Returns the timeline of tool calls, messages, and events.                 |
| `executionAgentFilesGet`   | Lists the files an execution produced, grouped by directory.              |
| `executionContextFilesGet` | Lists the files attached to an execution.                                 |
| `executionStatusUpdate`    | Pauses, resumes, or cancels an execution.                                 |
| `executionUserMessagesAdd` | Sends a message to a running workflow.                                    |

[Executions and statuses](/concepts/executions) lists the statuses these tools return.
[Debug your workflows](/operate/debug) covers how to read a failure.

### Versions

| Tool              | What it does                                                                          |
| :---------------- | :------------------------------------------------------------------------------------ |
| `workflowsList`   | Lists the versions of a workflow.                                                     |
| `workflowGet`     | Loads one version: graph, rules, and settings.                                        |
| `workflowCreate`  | Creates a version. Set `parentId` to record where it came from. It stays unpublished. |
| `workflowPublish` | Publishes a version, so new executions use it.                                        |
| `workflowExecute` | Runs one specific version, published or not. Use it to test a draft.                  |

### Files and validation

Workflow files belong to a version, not to the workflow, so different versions can carry different files. Every
write below produces a new version and returns its id as `workflowId`. Publish or run that version to use the
change. See [Workflow filesystem](/concepts/filesystem) for where the files land at run time.

| Tool                           | What it does                                                       |
| :----------------------------- | :----------------------------------------------------------------- |
| `workflowAgentFilesList`       | Lists the files on a version.                                      |
| `workflowAgentFilesUpload`     | Adds a file. Pass the content as `contentText` or `contentBase64`. |
| `workflowAgentFilesReplace`    | Replaces the contents of one file.                                 |
| `workflowAgentFilesRename`     | Renames or moves one file.                                         |
| `workflowAgentFileSignedUrl`   | Returns a short-lived URL for reading a file.                      |
| `workflowAgentFilesDelete`     | Removes one file.                                                  |
| `workflowAgentFilesDeleteMany` | Removes several files in one new version.                          |
| `schemaValidate`               | Checks a JSON schema before you put it on an output node.          |
| `workflowSpecValidate`         | Checks a graph before `agentCreate` creates anything.              |
| `workflowValidate`             | Runs the same check against a workflow that already exists.        |

## Build a workflow over MCP

The loop is `agentCreate`, then `agentExecutePost`, then poll `executionGet`, then `executionActivitiesGet`
when something goes wrong. `agentCreate` publishes the first graph as version 1, so the first execution needs no
publish step. Publishing starts to matter once you make later versions with `workflowCreate`.

### A minimal workflow

Four nodes make a runnable workflow: a start node, one agent node, and two output nodes for the two endings.
Generate a fresh UUID for every node and every transition. An agent node carries `"type": "iris"` in the
payload, and so does a transition the workflow chooses at run time.

```json theme={null}
{
  "organizationId": "<organization UUID from getContext>",
  "name": "Product Price Checker",
  "workflow": {
    "rules": "",
    "settings": {
      "viewport_width": 1440,
      "viewport_height": 900,
      "max_timeout_mins": 15
    },
    "graph": {
      "nodes": [
        {
          "id": "0b8e4b1e-0000-4000-8000-000000000001",
          "name": "Start",
          "type": "start",
          "properties": { "type": "start" }
        },
        {
          "id": "0b8e4b1e-0000-4000-8000-000000000002",
          "name": "Check Price",
          "type": "iris",
          "properties": {
            "type": "iris",
            "instructions": "Go to {{.product_url}}. Find the current price and whether the product is in stock.",
            "model": "asteroid-balanced",
            "capabilities": {
              "browser_use": true,
              "computer_use": false,
              "ask_user_question": false
            }
          }
        },
        {
          "id": "0b8e4b1e-0000-4000-8000-000000000003",
          "name": "Success",
          "type": "output",
          "properties": {
            "type": "output",
            "outcomes": ["success"],
            "schema": {
              "type": "object",
              "properties": {
                "price": { "type": "number", "description": "Current price" },
                "in_stock": { "type": "boolean", "description": "Availability" }
              },
              "additionalProperties": false,
              "required": ["price", "in_stock"]
            }
          }
        },
        {
          "id": "0b8e4b1e-0000-4000-8000-000000000004",
          "name": "Failure",
          "type": "output",
          "properties": { "type": "output", "outcomes": ["failure"] }
        }
      ],
      "transitions": [
        {
          "id": "0b8e4b1e-0000-4000-8000-00000000000a",
          "from": "0b8e4b1e-0000-4000-8000-000000000001",
          "to": "0b8e4b1e-0000-4000-8000-000000000002",
          "type": "outcome_success",
          "require_confirmation": false,
          "properties": { "type": "outcome_success" }
        },
        {
          "id": "0b8e4b1e-0000-4000-8000-00000000000b",
          "from": "0b8e4b1e-0000-4000-8000-000000000002",
          "to": "0b8e4b1e-0000-4000-8000-000000000003",
          "type": "iris",
          "require_confirmation": false,
          "description": "Price and stock status were found.",
          "properties": { "type": "iris" }
        },
        {
          "id": "0b8e4b1e-0000-4000-8000-00000000000c",
          "from": "0b8e4b1e-0000-4000-8000-000000000002",
          "to": "0b8e4b1e-0000-4000-8000-000000000004",
          "type": "iris",
          "require_confirmation": false,
          "description": "The page failed to load, or the product was not found.",
          "properties": { "type": "iris" }
        }
      ],
      "sticky_notes": []
    }
  }
}
```

### Rules the API enforces

* The start node has one outgoing transition. Its type is `outcome_success`, and it points at a node that is
  not an output node.
* An agent node routes with `iris` or `selector` transitions, never with `outcome_success`.
* Every agent node needs a path to an output node for the failure case.
* The shape of the final result goes in the output node's `schema`. A `schema` on a transition is a different
  thing: it defines the data handed to the next node. See [Inputs and outputs](/concepts/inputs-and-outputs).

These rules tie a transition to the node it leaves, so no JSON schema can express them. Validation is the only
place they surface.

### Validate before you create

Check the output schema with `schemaValidate`, and the graph with `workflowSpecValidate`. Pass
`workflowSpecValidate` the same `workflow` you are about to send. It runs the check `agentCreate` runs, and
returns every issue at once instead of one per round trip.

Each issue carries a `severity` and a `path` to the field at fault. An `error` blocks creation. A `warning` is
advice. For a workflow that already exists, `workflowValidate` is the same check.

### Run it and read the result

Start the run with `agentExecutePost`. Pass the `agentId` and any `inputs` the instructions reference. Then
poll `executionGet` every 5 to 10 seconds until the status is `completed`, `failed`, or `cancelled`.

A single-node execution usually finishes in 30 to 60 seconds. A larger graph takes minutes. When an execution ends badly,
read `executionActivitiesGet` for the step-by-step timeline.

<Warning>
  Poll until the status is terminal. Do not loop while the status equals `running` — that exits the moment the
  workflow pauses to ask you something. Handle `paused_by_agent` and `awaiting_confirmation` yourself. See
  [Executions and statuses](/concepts/executions).
</Warning>

## Point your coding agent at the docs

Two URLs teach a coding agent how Asteroid works.

| URL                                           | What it is for                                                                                                                   |
| :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| [skill.md](https://docs.asteroid.ai/skill.md) | The capability contract. What Asteroid does, what each operation needs, and what the constraints are. Read this to plan actions. |
| [llms.txt](https://docs.asteroid.ai/llms.txt) | An index of every docs page with its description. Read this to find the right page.                                              |

Hand your coding agent `skill.md` and it can do most of the work on its own. Add `llms.txt` when it needs to
find a page it has not seen.

## Build with the SDK instead

The SDK holds an API key and runs anywhere — your laptop, your server, your CI job. Get a key from
[platform.asteroid.ai/keys](https://platform.asteroid.ai/keys), and set up the client with
[SDK setup](/sdks/setup).

The loop is create, validate, publish, execute.

| Step                                      | Function                 |
| :---------------------------------------- | :----------------------- |
| Create the workflow and its first version | `agentCreate`            |
| Create a later version                    | `agentWorkflowsCreate`   |
| Check a version before you save it        | `agentWorkflowsValidate` |
| Make a version the one new executions use | `agentWorkflowsPublish`  |
| Run one specific version                  | `agentWorkflowsExecute`  |

`agentCreate` publishes its first version, so a fresh workflow runs immediately. Later versions start
unpublished. Publish one, or run it directly with `agentWorkflowsExecute` to test it first.

<CodeGroup>
  ```ts create-agent.ts theme={null}
  import {
    client,
    agentCreate,
    agentWorkflowsCreate,
    agentWorkflowsValidate,
    agentWorkflowsPublish,
    agentWorkflowsExecute,
  } from 'asteroid-odyssey';
  import graph from './graph.json' with { type: 'json' };

  client.setConfig({
    headers: { 'X-Asteroid-Agents-Api-Key': process.env.ASTEROID_API_KEY! },
  });

  const settings = { viewport_width: 1440, viewport_height: 900, max_timeout_mins: 15 };

  // 1. Create the agent. Its first version is published for you.
  const { data: agent, error: createError } = await agentCreate({
    body: {
      organizationId: process.env.ASTEROID_ORG_ID!,
      name: 'Product Price Checker',
      workflow: { rules: '', graph, settings },
    },
  });
  if (createError) throw new Error(JSON.stringify(createError));
  const agentId = agent!.id;

  // 2. Validate the next version before you save it.
  const { data: validation } = await agentWorkflowsValidate({
    path: { agentId },
    body: { rules: '', graph, settings },
  });
  const blocking = validation?.issues.filter((i) => i.severity === 'error') ?? [];
  if (blocking.length > 0) throw new Error(JSON.stringify(blocking));

  // 3. Save it.
  const { data: created } = await agentWorkflowsCreate({
    path: { agentId },
    body: { rules: '', graph, settings },
  });
  const workflowId = created!.workflowId;

  // 4. Run that exact version before anyone else gets it.
  const { data: run } = await agentWorkflowsExecute({
    path: { agentId, workflowId },
    body: { inputVariables: { product_url: 'https://example.com/widget' } },
  });
  console.log('execution', run!.executionId);

  // 5. Publish it, so new runs use it.
  const { data: published } = await agentWorkflowsPublish({ path: { agentId, workflowId } });
  console.log('published version', published!.version);
  ```

  ```python create_agent.py theme={null}
  import json
  import os

  from asteroid_odyssey import ApiClient, Configuration
  from asteroid_odyssey.api.agents_api import AgentsApi
  from asteroid_odyssey.api.workflows_api import WorkflowsApi

  with open("graph.json") as f:
      graph = json.load(f)

  settings = {"viewport_width": 1440, "viewport_height": 900, "max_timeout_mins": 15}
  spec = {"rules": "", "graph": graph, "settings": settings}

  config = Configuration(api_key={"ApiKeyAuth": os.environ["ASTEROID_API_KEY"]})

  with ApiClient(config) as api_client:
      agents_api = AgentsApi(api_client)
      workflows_api = WorkflowsApi(api_client)

      # 1. Create the agent. Its first version is published for you.
      agent = agents_api.agent_create({
          "organizationId": os.environ["ASTEROID_ORG_ID"],
          "name": "Product Price Checker",
          "workflow": spec,
      })
      agent_id = agent.id

      # 2. Validate the next version before you save it.
      validation = workflows_api.agent_workflows_validate(agent_id, spec)
      blocking = [i for i in validation.issues if i.severity == "error"]
      if blocking:
          raise SystemExit(blocking)

      # 3. Save it.
      created = workflows_api.agent_workflows_create(agent_id, spec)
      workflow_id = created.workflow_id

      # 4. Run that exact version before anyone else gets it.
      run = workflows_api.agent_workflows_execute(
          agent_id,
          workflow_id,
          {"inputVariables": {"product_url": "https://example.com/widget"}},
      )
      print("execution", run.execution_id)

      # 5. Publish it, so new runs use it.
      published = workflows_api.agent_workflows_publish(agent_id, workflow_id)
      print("published version", published.version)
  ```
</CodeGroup>

Two details save you a debugging session:

* `agentExecutePost` names its variables `inputs`. `agentWorkflowsExecute` names them `inputVariables`.
* JSON and the TypeScript SDK use camelCase. The Python SDK exposes the same fields as snake\_case attributes,
  so `executionResult` reads as `execution.execution_result`.

## Next

<CardGroup cols={2}>
  <Card title="Write good instructions" icon="pen-line" href="/build/instructions">The craft inside each node</Card>
  <Card title="Test, iterate, publish" icon="check-check" href="/build/test-and-publish">Get a draft ready, then make it live</Card>
  <Card title="Call a workflow from your code" icon="code" href="/integrate/call-an-agent">Execute, poll, and read the result</Card>
  <Card title="TypeScript SDK" icon="file-code" href="/sdks/typescript">Client setup and the common functions</Card>
</CardGroup>
