$npx -y skills add butterbase-ai/butterbase-skills --skill agentsUse when designing, deploying, or debugging a Butterbase Agent (declarative LLM/tool graph), registering an MCP server for tool use, or wiring access controls and rate limits. Agents are first-class app resources defined by a graph_spec and invoked over `/v1/<app_id>/agents/<na
| 1 | # Butterbase Agents |
| 2 | |
| 3 | A Butterbase agent is a **declarative graph** of LLM and tool nodes — not a free-running chat loop. The runtime traverses the graph, calls tools (builtin / MCP / function), and resolves the `end` node's `output_template`. State, rate limits, and budgets are enforced by the control plane. |
| 4 | |
| 5 | ## When to use |
| 6 | |
| 7 | - The user wants to add a workflow that combines an LLM with tool calls (DB writes, storage reads, MCP servers, app functions). |
| 8 | - The user wants to expose an agent endpoint to end users (`visibility: public` or `authenticated`). |
| 9 | - Debugging a failing agent run (look at `list_agent_runs`, then `get_agent_run`). |
| 10 | - Registering an external MCP server for the agent to use. |
| 11 | |
| 12 | Don't use for plain LLM chat completions — use the `ai` skill (`manage_ai` / `/v1/ai/chat`). Agents are for stateful, multi-step, tool-using workflows. |
| 13 | |
| 14 | ## Concepts |
| 15 | |
| 16 | ### `graph_spec` (validated by `validate_agent_spec` before anything is persisted) |
| 17 | |
| 18 | | Field | Required | Notes | |
| 19 | |---|---|---| |
| 20 | | `spec_version` | yes | Literal `"1"`. | |
| 21 | | `entry` | yes | ID of the first node. | |
| 22 | | `nodes` | yes | Record `{ id → node }`. | |
| 23 | | `edges` | yes | `[{ from, to }]`. Both endpoints must exist in `nodes`. | |
| 24 | | `tools` | yes | `{ builtin: [], mcp_servers: [], functions: [] }` — declares what nodes can call. | |
| 25 | | `limits` | yes | `max_steps` (1–200), `max_tool_calls` (0–500), `max_parallel_tools` (1–16), `timeout_seconds` (5–3600), `human_timeout_seconds` (60–7×24×3600). | |
| 26 | |
| 27 | **Node types:** |
| 28 | |
| 29 | - **`llm`** — `model`, `system_prompt`, `input_template`, `output_key`, `tools: [toolRef]`, optional `temperature` (0–2), `max_tokens`. |
| 30 | - **`tool`** — `tool_ref`, `args_template` (record), `output_key`. |
| 31 | - **`end`** — `output_template` (string; can interpolate `{{output_key}}` values). |
| 32 | |
| 33 | **`toolRef`** is a discriminated union by `source`: |
| 34 | - `{ source: 'builtin', name }` |
| 35 | - `{ source: 'mcp', server_id, name }` |
| 36 | - `{ source: 'function', name }` |
| 37 | |
| 38 | Each may carry `mode_override` (`read_only` | `read_write`) and `exposed_to_override` (`developer_only` | `end_user`). |
| 39 | |
| 40 | ### Builtin tools (always available, no setup) |
| 41 | |
| 42 | | Name | Purpose | Args | |
| 43 | |---|---|---| |
| 44 | | `query_table` | Select rows (RLS enforced) | `table`, `filter`, `limit` (≤200) | |
| 45 | | `insert_row` | Insert | `table`, `values` | |
| 46 | | `update_row` | Update by id | `table`, `id`, `patch` | |
| 47 | | `delete_row` | Delete by id | `table`, `id` | |
| 48 | | `read_storage` | Get object (≤5 MB) | `key` | |
| 49 | | `write_storage` | Put object (≤1 MB b64) | `key`, `content_base64`, `content_type?` | |
| 50 | | `auth_user_lookup` | Find a user | `email` OR `id` | |
| 51 | |
| 52 | All builtins respect role: `end_user` runs as `butterbase_user` with their user id (RLS applies); `developer_only` runs as `butterbase_service`. |
| 53 | |
| 54 | ### MCP servers |
| 55 | |
| 56 | Register before referencing in `graph_spec.tools.mcp_servers`. Transports: `sse`, `http`, `streamable_http`. The control plane **probes** on register (calls `listTools()`), stores `status='healthy'|'unhealthy'`. Re-probe with the same endpoint after a server URL change. |
| 57 | |
| 58 | ### Access & limits |
| 59 | |
| 60 | | Field | Default | Notes | |
| 61 | |---|---|---| |
| 62 | | `visibility` | `private` | `private` (owner only), `authenticated` (any app user), `public` (anyone, with rate limits). | |
| 63 | | `max_runs_per_user_per_hour` | null | null = unlimited. | |
| 64 | | `max_runs_per_ip_per_hour` | null | Primary public-agent throttle. | |
| 65 | | `max_runs_per_app_per_hour` | null | App-wide cap. | |
| 66 | | `daily_budget_usd` | null | Hard kill once exceeded. | |
| 67 | | `max_concurrent_runs` | null | | |
| 68 | | `safety_acknowledged` | false | **Required true** if visibility ≠ private AND any node calls a write tool (`insert_row`, `update_row`, `delete_row`, `write_storage`, or a write-mode MCP/function tool). | |
| 69 | |
| 70 | ## Procedure |
| 71 | |
| 72 | ### Designing a new agent |
| 73 | |
| 74 | 1. **Sketch the graph in prose first.** "User asks X → LLM rephrases → query_table for context → LLM answers → end." Concrete node IDs. |
| 75 | 2. **Write the spec** as a JSON file in the repo (e.g. `agents/<name>.json`) — versioning it in git makes templates portable and lets `butterbase repo push` carry it to clones. |
| 76 | 3. **Validate without persisting** — call `validate_agent_spec` (MCP) or pass the file to a `validate_agent_spec` call. Surface any Zod issues to the user with field paths. |
| 77 | 4. **Register MCP servers** if used: `agent_mcp_servers` table (MCP-tool wrapper TBD; use the dashboard or POST `/v1/<app_id>/agent-mcp-servers` directly). Wait for `status: healthy`. |
| 78 | 5. **Create** — `create_agent` with name, graph_spec, default_model, access fields. If `visibility ≠ 'private'` and any write tool is reachable, require the user to explicitly say "yes, I acknowledge" and set `safety_acknowledged: true`. |
| 79 | 6. **Smoke** — `invoke_agent` with a small input. Poll `get |