# Tutorial

# Build your first blueprint — tutorial

> **Audience:** a tenant employee or external Claude session that wants to ship a custom recipe in 30 minutes.
> **Prerequisites:** an Amdahl API key with `artifact:write` scope; the Amdahl MCP server connected to your client.
> **End state:** a working blueprint forked from `content-calendar`, customized to your team's pillars and pacing, ready to read and walk from MCP.

Blueprints are recipes an LLM reads and walks itself — on the MCP tool surface (this tutorial's audience) there is no one-shot run tool, so you walk the recipe yourself. A headless server-side SDK runner also exists for unattended/scheduled/backtest runs (see [blueprint-runner-sdk.md](../../blueprint-runner-sdk.md)), and you can register such a run from MCP via `blueprints.create_schedule`; but you cannot one-shot-run a blueprint from MCP. They're written in a typed DSL ([reference](../api-reference/blueprint-dsl.md)) and stored as `agent_blueprint` artifacts. The platform ships several forkable starters; this tutorial walks through forking the `content-calendar` starter, customizing it for your team, and walking the customized recipe yourself.

## Step 1 — Look at the starter

The Amdahl-shipped starters are public. Read the calendar starter to see what you're starting from:

```text
read_resource artifact://<content-calendar-blueprint-id>
```

If you don't know the id off-hand, list public agent_blueprint artifacts:

```text
read_resource agent_blueprint://list?slug=content-calendar
```

You'll see the validated DSL. Pay attention to:

- **`identity.slug` + `version`** — you'll bump these when you fork.
- **`inputs`** — the surface customers fill in to run the blueprint.
- **`policy.tool_allowlist`** — the operations the blueprint's `tool` steps will call.
- **`steps`** — the recipe body. The steps run in order; the planning blueprint emits placeholder `content_piece` rows, then `plan_and_draft_window` (the composition starter) fans out `draft_piece` per planned piece in parallel.

## Step 2 — Decide what to change

Common forks for `plan_and_draft_window`:

1. **Lock the channel** — your team only publishes to LinkedIn; remove the `channel` input + hardcode `'linkedin'` everywhere.
2. **Pin the audience lens** — your ICP is fixed; remove the input + hardcode the audience filter.
3. **Tighten the cadence** — replace `prompt://content_writer/cadence_rhythm` with your own fragment if your audience has unusual posting times.
4. **Pillar-locked** — bake the team's pillar set into the blueprint and remove the optional input for fixed-pillar teams.

For this tutorial we'll do #1 — lock to LinkedIn.

## Step 3 — Fork the starter

Forking is a single op: `agents.fork_blueprint` reads the starter, creates a tenant-owned copy, and returns the new artifact id. The fork starts at version 1.0.0, status `draft`, visibility `private` — you customize from there.

```text
agents.fork_blueprint
  source: 'content-calendar'           # starter slug, OR a starter UUID, OR a tenant blueprint UUID
  new_slug: 'team-x-linkedin-calendar' # optional; auto-generates `<source>-fork` when omitted
  new_name: 'Team X LinkedIn Calendar' # optional; defaults to `Copy of <source name>`
```

Returns:

```jsonc
{
  "artifact_id": "<new-uuid>",
  "slug": "team-x-linkedin-calendar",
  "name": "Team X LinkedIn Calendar",
  "source_kind": "starter",
}
```

Once the fork lands, customize it via `artifacts.update` with the patches you want — drop the `channel` input, hardcode `'linkedin'` in the steps that referenced `$inputs.channel`, etc. The fork's `content_json` is a full copy of the starter; you patch only the fields you care about.

```text
artifacts.update
  id: '<new-uuid>'
  content_json:
    inputs:
      # Drop the `channel` input entirely; keep the rest.
      - { name: 'voice_profile_id', type: 'artifact_ref', required: true, validate: { artifact_type: 'voice_profile' } }
      - { name: 'period_start', type: 'date', required: true }
      - { name: 'period_days', type: 'integer', default: 7, validate: { min: 3, max: 14 } }
      - { name: 'piece_count', type: 'integer', default: 5 }
      - { name: 'audience', type: 'string' }
      - { name: 'pillar_ids', type: 'artifact_ref_list', validate: { artifact_type: 'content_pillar' } }
    steps:
      # Replace `$inputs.channel` with the literal `'linkedin'`
      # everywhere the starter referenced it.
      # ... (omitted for brevity; copy from the starter and edit)
```

The fork now reflects your customization. Promote when ready via another `artifacts.update` with `status: 'published'`.

## Step 4 — Walk your fork

There is no one-shot run tool on the MCP surface. A blueprint is a recipe you read and walk yourself. Read the recipe back:

```text
read_resource agent_blueprint://<your-fork-id>
```

You get the inputs, the policy, and the step tree. Gather the inputs you'd supply:

```text
voice_profile_id: '<your voice profile uuid>'
period_start: '2026-04-29'
period_days: 7
piece_count: 5
audience: 'B2B operators at Series B+ companies'
pillar_ids: ['<pillar-1>', '<pillar-2>', '<pillar-3>']
```

Then perform each step in order with your own primitive tools — substituting `$inputs.*` with the values above and each `$step_id` with the output of the step you already ran. A `tool` step names the operation to call (`data.cluster_search`, `artifacts.create`, etc.); an `llm` step is reasoning you do yourself; `loop` / `branch` / `parallel` steps tell you how to fan out. You ARE the runner on this surface: nothing on the MCP tool surface one-shot-executes the recipe for you. (Platform-initiated runs — manual "Run now" via REST, schedule/event/webhook triggers, replay, backtest — go through a separate headless SDK runner, not your MCP session; you can register a scheduled run from MCP with `blueprints.create_schedule`, but it still executes on that runner, not in your session.)

## Step 5 — You're done when the steps are done

There's no run row to close — when you've walked every step and written the artifacts the recipe produces (the calendar, the pieces, the drafts), the work is complete. Capture the artifact ids you created as you go; the recipe's `outputs_mapping` tells you which step output maps to which named output.

## Step 6 — Iterate

Once you've walked it once and the output matches your taste, you're done. Common iteration patterns:

- **Bump version to 1.1.0** when you tweak the prompt language or add a step. The version is authoring metadata on `identity.version`; the next agent that reads the recipe picks up the new body.
- **Re-read the recipe** (`read_resource agent_blueprint://<your-fork-id>`) any time to confirm a change landed before walking it again.
- **Note on unattended use** — the `trigger.schedule` / `trigger.event` / `trigger.webhook` fields declare intent in the DSL. A headless SDK runner now fires these for platform-initiated runs (see [blueprint-runner-sdk.md](../../blueprint-runner-sdk.md)). From your MCP session there is still no one-shot run tool, but you CAN register an unattended run with `blueprints.create_schedule` (which executes on that runner); for ad-hoc recurring use you can also just walk the recipe yourself (or wrap it in your own scheduled job that does).

## What this recipe does and doesn't constrain

The recipe's `policy.tool_allowlist` is GUIDANCE you should respect, not a server-enforced contract. The API key's scope grammar is the real safety boundary. You may adapt mid-walk when an unanticipated case calls for a different tool.

This means: if a blueprint declares a tight allowlist, follow it; if the API key has broader scope, you have the headroom to adapt. The blueprint declares intent; the API key enforces it. The headless SDK runner does not enforce the allowlist for you either — it runs against a least-privilege scoped token, so the API key's scope grammar, not the allowlist, is the real boundary on every surface.

## What's next

- [DSL reference](../api-reference/blueprint-dsl.md) — every field, every type, every kind.
- [bootstrap_workspace reference](./bootstrap_workspace.md), [draft_piece reference](./draft_piece.md), [plan_and_draft_window reference](./plan_and_draft_window.md), [research_report reference](./research_report.md) — per-starter usage docs.
