Skip to content

Agent Cards

Agent cards define specialized AI agents. Each card is a markdown file: YAML frontmatter for configuration, markdown body as the system prompt. Cards are the primary way to define delegation targets — an agent with delegation enabled can hand a task to any card by name via delegate_session, and the child runs as a real session with the card’s prompt, model, and tool policy.

What’s in an Agent Card

  • Who is this agent? — Name, description, system prompt
  • What can it do? — Per-tool permissions and MCP servers
  • What model? — Optional provider/model override (omit to inherit the spawning context’s model)
  • How long may it run?max_turns caps the tool loop

File Locations

Discovery order (later locations shadow earlier ones, by card name):

  1. ~/.config/crucible/agents/ — personal cards
  2. KILN/.crucible/agents/ — kiln-scoped cards
  3. PROJECT/.crucible/agents/ — project-scoped cards (checked into a repo)

Only .crucible/ directories. A kiln’s visible tree is not searched: KILN/agents/ and KILN/Agents/ used to be, which meant any kiln you cloned, synced or were handed could introduce an agent card — and a card names a model, a system prompt and a tool policy, so that is not something to acquire by accident. A kiln’s top level belongs to your notes.

A kiln that genuinely is a card library — an org’s shared agent and skill repo — brings itself in rather than being scanned: its Lua adds the directory at load. The component composes itself into the host; the host does not go looking.

Basic Example

Create agents/researcher.md:

---
description: Explores and synthesizes knowledge
tools:
semantic_search: true
read_note: true
create_note: ask
bash: deny
mcps:
- context7
---
You are a research assistant specializing in knowledge exploration.
## Your Approach
- Search thoroughly before answering
- Cite sources using [[wikilinks]]
- Synthesize information from multiple notes
- Acknowledge gaps in knowledge

Only description is required. The card’s name defaults to its file stem (researcher above); version defaults to 0.1.0.

Frontmatter Fields

FieldRequiredDescription
descriptionYesBrief description (shown in delegation target listings)
nameNoCard name (default: file stem)
versionNoSemantic version (default: 0.1.0)
toolsNoPer-tool permissions (true/false/allow/ask/deny)
mcpsNoMCP servers this agent can use (alias: mcp_servers)
providerNoProvider override (ollama, anthropic, …); omit to inherit
modelNoModel override; omit to inherit (better portability)
temperatureNoSampling temperature override
max_tokensNoMax output tokens override
max_turnsNoMax tool-loop turns per message
modeNoInitial mode (auto/plan)
specialtyNoModel category resolved via [llm.models] (see below)
tagsNoTags for discovery

Model Resolution

A card’s model resolves through one explicit chain, most specific first:

  1. Card-explicit provider: / model: — always wins.
  2. specialty: mapped through your [llm.models] config table.
  3. Inherit from the spawning context — the delegating parent’s provider/model, or the configured default for session.create.

The specialty layer keeps cards portable: the card says what kind of model it wants, and each machine maps that to its own preferred model:

[llm.models]
reasoning = "openai/o1" # provider/model — switches both
coder = "qwen2.5-coder" # bare model — provider inherited
writing = "anthropic/claude-haiku"

An unmapped specialty simply falls through to inheritance, so sharing a card with a specialty the recipient hasn’t configured still works.

Tool Permissions

tools:
semantic_search: true # Always allowed, never prompts
write_file: ask # Always prompts (even in permissive contexts)
bash: deny # Not advertised, refused if called

Permission values:

  • true or allow — auto-approve; the permission gate is skipped
  • ask — force a prompt for every use, even for read-only tools
  • false or deny — the tool is removed from the agent’s toolset AND refused at dispatch if requested anyway

Tools not listed use the default behavior (safe read-only tools run freely; mutating tools go through the permission gate). Note: delegated child sessions run non-interactively — for them, ask is effectively deny unless a permission pattern or Lua hook answers the prompt.

Trust note: allow skips the interactive prompt, so only install cards from sources you trust — a kiln-shipped card granting bash: allow runs shell commands unattended when delegated to. The operator’s [permissions] deny rules are still evaluated for a card-allowed tool, so a card cannot sidestep them: deny = ["bash:*"] in your permissions config outranks any card.

It outranks cards, not everything. Three things sit outside it, and a deny rule is a backstop only against what it can actually see:

  • --permissions allow discards the rule lists entirely, deny included, for the session launched with it. Only the hardcoded denies survive. If you rely on a deny line, do not pair it with that flag.
  • A rule names one tool. bash:* covers calls checked under the name bash — and, for external ACP agents, the bash execute kind. It says nothing about an MCP or plugin tool that shells out under its own name; gate those by their own names as cru tools lists them.
  • A command pattern like bash:rm * is matched per statement, which is stronger than it looks but not airtight. Permission Precedence states exactly what the split guarantees and the three edges it does not reach. bash:* is unaffected — it matches every statement whatever the line does.

Delegating to a Card

An agent whose session has delegation enabled (delegation_config.enabled) sees a delegate_session tool listing the available cards. Delegation resolves targets in this order: agent card → ACP profile (external agents like claude, opencode).

// the parent agent calls:
delegate_session { "prompt": "Survey what we know about X", "target": "researcher" }

The child runs as a real (hidden) session: the card’s system prompt, tool policy, and model, with Precognition and session persistence. See Delegation.

Using Cards from the CLI

Terminal window
# List / inspect / validate cards
cru agents list
cru agents show researcher
cru agents validate
# Create a session with a card-configured internal agent
cru session create --agent researcher
# via RPC: session.create { configure_agent: true, agent_card: "researcher" }

--agent is not --acp: --acp names an ACP profile and launches an external agent subprocess. They are refused together rather than ranked, because claude, gemini, codex, cursor and opencode always exist as profiles — guessing would make a card of that name permanently unreachable, and cru agents show "Claude Code" is a documented example of exactly such a card.

agent_name still selects a card on an internal session, but it is deprecated there: on an ACP session the same field names a subprocess profile, so one field meant two things depending on agent_type. Use agent_card. Setting both is rejected with INVALID_PARAMS.

Writing Good Prompts

The markdown body becomes the system prompt. Write it like instructions:

Be specific about role:

You are a code reviewer focused on Rust best practices.
You catch common mistakes and suggest idiomatic improvements.

Define behavior:

## How You Work
1. Read the code carefully before commenting
2. Prioritize correctness over style
3. Explain the "why" behind suggestions
4. Acknowledge good patterns too

Set boundaries:

## What You Don't Do
- Don't rewrite entire files
- Don't suggest unrelated refactors
- Don't ignore the user's stated goals

See Also