PromptisePromptise
Docs
GitHub
Promptise - AI Framework LogoPromptise

The foundation layer for agentic intelligence. Build, secure and operate autonomous AI systems with Promptise Foundry.

pip install promptise

[01] Foundry

  • MCPcast
  • The Promptise Agent
  • Reasoning Engine
  • MCP
  • Agent Runtime
  • Prompt Engineering
  • Execution Engine
  • Agent Identity

[02] Resources

  • Documentation
  • GitHub
  • Guides
  • Learning Paths
  • Questions

[03] Company

  • About
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Subprocessors

© 2026 Promptise by Manser Ventures. All rights reserved.

Open source · Python

← Learning paths> Agentic Engineering with Promptise Foundry
Lesson 06 / 71
8%

AI Agent YAML: Define and Run an Agent From a Config File

Define an AI agent in a YAML file: model, instructions and MCP servers. Validate it, run it from the CLI or Python, keep secrets out, review it in Git.

Intermediate18 minAgents as Code (YAML)Model Context Protocol (MCP)
All 71 lessons↓
  1. ✓What Is an AI Agent? Definition, How It Works, Examples
  2. ✓How to Build an AI Agent in Python (2026 Guide)
  3. ✓What Is an Agent Harness? Parts and How to Build One
  4. ✓System Prompt Best Practices for AI Agents in Python
  5. ✓Best LLM for AI Agents: Test and Switch Models in Python
  6. 06AI Agent YAML: Define and Run an Agent From a Config File
  7. 07Streaming LLM Responses with FastAPI: Tokens and Tool Calls
  8. 08Claude AI Agent with MCP: Use Anthropic Models in Promptise
  9. 09Gemini AI Agent with MCP: Use Google Models in Promptise
  10. 10Azure OpenAI Agent with MCP: Use Deployments in Promptise
  11. 11OpenRouter AI Agent with MCP: Many Models in Promptise
  12. 12Ollama AI Agent with MCP: Run Local Models in Promptise
  13. 13LLM Semantic Cache and Model Fallback in Python
  14. 14What Is MCP (Model Context Protocol)? A Developer's Guide
  15. 15What Is Tool Calling? LLM Function Calling Explained
  16. 16MCP Tools vs Resources vs Prompts (With Python Examples)
  17. 17How to Connect MCP Servers to Your AI Agent in Python
  18. 18Build a Production MCP Server in Python (Step by Step)
  19. 19MCP Transports: stdio vs Streamable HTTP vs SSE (Python)
  20. 20FastAPI MCP Server: Turn Your FastAPI App into MCP Tools
  21. 21OpenAPI to MCP: Turn Any REST API into an MCP Server
  22. 22MCP Authentication in Python: API Keys, JWT and OAuth
  23. 23Build an Authenticated MCP Server for Your AI Agent (Python)
  24. 24MCP Security Best Practices: A Checklist for MCP Servers
  25. 25Testing MCP Servers in Python: pytest Without a Live Server
  26. 26Too Many MCP Tools? LLM Tool Selection, Measured
  27. 27MCP Server Caching, Timeouts and Circuit Breakers in Python
  28. 28MCP vs CLI for AI Agents: Tokens, Security, When to Use Each
  29. 29AI Agent Audit Logging: Tamper-Evident Logs in Python
  30. 30AI Agent Human in the Loop: Approve Tool Calls in Python
  31. 31AI Agent Prompt Injection: How to Defend Against It
  32. 32Build a Multi-Agent System in Python (and When Not To)
  33. 33Context Engineering for AI Agents: Context Window Management
  34. 34How to Deploy an AI Agent with Docker (2026 Guide)
  35. 35LLM Tool Rate Limiting: Per-Client Limits for MCP Servers
  36. 36MCP Long-Running Tasks: Background Jobs in Python
  37. 37MCP Server Monitoring: Prometheus, OpenTelemetry and Logs
  38. 38Python AI Agent Frameworks Compared: Which to Choose (2026)
  39. 39Self-Improving AI Agent: Learn From Tool Failures in Python
  40. 40AI Agent Identity: Authenticate Agents to MCP Servers
  41. 41Code-Action Agents in Python: One Program vs 37 Tool Calls
  42. 42Multi-Tenant AI Agents: Keep Each Customer's Data Apart
  43. 43What Is Agentic RAG? How It Differs from Classic RAG
  44. 44AI Agent Memory in Python: Short-Term and Long-Term
  45. 45Agentic RAG in Python: Give Your AI Agent a Search Tool
  46. 46AI Agent Reasoning Patterns in Python: 9 Patterns Tested
  47. 47Plan-and-Execute Agents in Python: Beyond the ReAct Loop
  48. 48Self-Modifying AI Agent in Python: Open Mode & Limits
  49. 49CrewAI Alternatives: 7 Options and How to Migrate (2026)
  50. 50Migrate from AutoGen: Agent Framework, Promptise and More
  51. 51What Is an LLM Guardrail? Types, Examples and Limits
  52. 52AI Agent Guardrails in Python: Block Injection, Redact PII
  53. 53AI Agent Code Execution Sandbox: Run LLM Code Safely
  54. 54AI Agent Production Checklist: What to Check Before Launch
  55. 55Approval Fatigue: Auto-Approve Safe AI Agent Actions
  56. 56What Is Human in the Loop AI? Approvals for AI Agents
  57. 57AI Agent Notifications: Webhook and Slack Alerts in Python
  58. 58AI Agent Observability: Trace Tool Calls, Tokens and Cost
  59. 59AI Agent Stuck in a Loop? How to Detect and Stop It
  60. 60How to Run, Debug and Deploy an AI Agent in Python
  61. 61AI Agent Cost Tracking: Tokens and Cost per Run and User
  62. 62How to Evaluate AI Agents: Evals, LLM Judges and pass^k
  63. 63AI Agent Cost Control: Budgets That Stop Runaway Spend
  64. 64How to Test LLM Prompts and AI Agents in Python
  65. 65AI Agent Evals in CI: Run Them on Every Pull Request
  66. 66Autonomous AI Agent Runtime: Budgets, Crash Recovery, API
  67. 67Event-Driven AI Agents: Webhook, Cron and File Triggers
  68. 68Durable AI Agents: Crash Recovery and Safe Resume
  69. 69Deploy AI Agents in Production: Long-Running Python Agents
  70. 70What Is an AI Agent Framework? When You Need One
  71. 71LangGraph Alternatives: 7 Options and How to Migrate (2026)

Lessons in this path

  1. ✓What Is an AI Agent? Definition, How It Works, Examples
  2. ✓How to Build an AI Agent in Python (2026 Guide)
  3. ✓What Is an Agent Harness? Parts and How to Build One
  4. ✓System Prompt Best Practices for AI Agents in Python
  5. ✓Best LLM for AI Agents: Test and Switch Models in Python
  6. 06AI Agent YAML: Define and Run an Agent From a Config File
  7. 07Streaming LLM Responses with FastAPI: Tokens and Tool Calls
  8. 08Claude AI Agent with MCP: Use Anthropic Models in Promptise
  9. 09Gemini AI Agent with MCP: Use Google Models in Promptise
  10. 10Azure OpenAI Agent with MCP: Use Deployments in Promptise
  11. 11OpenRouter AI Agent with MCP: Many Models in Promptise
  12. 12Ollama AI Agent with MCP: Run Local Models in Promptise
  13. 13LLM Semantic Cache and Model Fallback in Python
  14. 14What Is MCP (Model Context Protocol)? A Developer's Guide
  15. 15What Is Tool Calling? LLM Function Calling Explained
  16. 16MCP Tools vs Resources vs Prompts (With Python Examples)
  17. 17How to Connect MCP Servers to Your AI Agent in Python
  18. 18Build a Production MCP Server in Python (Step by Step)
  19. 19MCP Transports: stdio vs Streamable HTTP vs SSE (Python)
  20. 20FastAPI MCP Server: Turn Your FastAPI App into MCP Tools
  21. 21OpenAPI to MCP: Turn Any REST API into an MCP Server
  22. 22MCP Authentication in Python: API Keys, JWT and OAuth
  23. 23Build an Authenticated MCP Server for Your AI Agent (Python)
  24. 24MCP Security Best Practices: A Checklist for MCP Servers
  25. 25Testing MCP Servers in Python: pytest Without a Live Server
  26. 26Too Many MCP Tools? LLM Tool Selection, Measured
  27. 27MCP Server Caching, Timeouts and Circuit Breakers in Python
  28. 28MCP vs CLI for AI Agents: Tokens, Security, When to Use Each
  29. 29AI Agent Audit Logging: Tamper-Evident Logs in Python
  30. 30AI Agent Human in the Loop: Approve Tool Calls in Python
  31. 31AI Agent Prompt Injection: How to Defend Against It
  32. 32Build a Multi-Agent System in Python (and When Not To)
  33. 33Context Engineering for AI Agents: Context Window Management
  34. 34How to Deploy an AI Agent with Docker (2026 Guide)
  35. 35LLM Tool Rate Limiting: Per-Client Limits for MCP Servers
  36. 36MCP Long-Running Tasks: Background Jobs in Python
  37. 37MCP Server Monitoring: Prometheus, OpenTelemetry and Logs
  38. 38Python AI Agent Frameworks Compared: Which to Choose (2026)
  39. 39Self-Improving AI Agent: Learn From Tool Failures in Python
  40. 40AI Agent Identity: Authenticate Agents to MCP Servers
  41. 41Code-Action Agents in Python: One Program vs 37 Tool Calls
  42. 42Multi-Tenant AI Agents: Keep Each Customer's Data Apart
  43. 43What Is Agentic RAG? How It Differs from Classic RAG
  44. 44AI Agent Memory in Python: Short-Term and Long-Term
  45. 45Agentic RAG in Python: Give Your AI Agent a Search Tool
  46. 46AI Agent Reasoning Patterns in Python: 9 Patterns Tested
  47. 47Plan-and-Execute Agents in Python: Beyond the ReAct Loop
  48. 48Self-Modifying AI Agent in Python: Open Mode & Limits
  49. 49CrewAI Alternatives: 7 Options and How to Migrate (2026)
  50. 50Migrate from AutoGen: Agent Framework, Promptise and More
  51. 51What Is an LLM Guardrail? Types, Examples and Limits
  52. 52AI Agent Guardrails in Python: Block Injection, Redact PII
  53. 53AI Agent Code Execution Sandbox: Run LLM Code Safely
  54. 54AI Agent Production Checklist: What to Check Before Launch
  55. 55Approval Fatigue: Auto-Approve Safe AI Agent Actions
  56. 56What Is Human in the Loop AI? Approvals for AI Agents
  57. 57AI Agent Notifications: Webhook and Slack Alerts in Python
  58. 58AI Agent Observability: Trace Tool Calls, Tokens and Cost
  59. 59AI Agent Stuck in a Loop? How to Detect and Stop It
  60. 60How to Run, Debug and Deploy an AI Agent in Python
  61. 61AI Agent Cost Tracking: Tokens and Cost per Run and User
  62. 62How to Evaluate AI Agents: Evals, LLM Judges and pass^k
  63. 63AI Agent Cost Control: Budgets That Stop Runaway Spend
  64. 64How to Test LLM Prompts and AI Agents in Python
  65. 65AI Agent Evals in CI: Run Them on Every Pull Request
  66. 66Autonomous AI Agent Runtime: Budgets, Crash Recovery, API
  67. 67Event-Driven AI Agents: Webhook, Cron and File Triggers
  68. 68Durable AI Agents: Crash Recovery and Safe Resume
  69. 69Deploy AI Agents in Production: Long-Running Python Agents
  70. 70What Is an AI Agent Framework? When You Need One
  71. 71LangGraph Alternatives: 7 Options and How to Migrate (2026)
← Path overview

An agent defined in Python lives inside someone's script. To change its model or its instructions, you need that person, a code review of a Python file, and a redeploy. Defining the agent in a YAML file fixes that: the agent becomes a short config file that sits in Git, gets reviewed like any other change, and runs from the command line without code. In this guide you'll write an on-call assistant for a platform team as a YAML file, validate it, run it from the CLI and from Python, keep its secrets out of the file, and set up a check that catches broken agent files before they merge. Every snippet was run against Promptise Foundry 1.3 with OpenAI's gpt-5-mini, and every output is what it printed.

[01]

How do you define an AI agent in a YAML file?

You write the model, the instructions and the MCP servers the agent may use into one file, then point a loader at it. In Promptise Foundry that file is a SuperAgent file, a plain YAML document with the .superagent extension:

YAMLoncall.superagent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: "1.0"

agent:
  model: "openai:gpt-5-mini"
  instructions: |
    You are the on-call assistant for the platform team.
    Use your tools to answer questions about incidents and runbooks.
    Quote runbook steps exactly. Never invent incidents.
  trace: true

servers:
  incidents:
    type: stdio
    command: python
    args: ["incidents_server.py"]
    env:
      PAGER_API_TOKEN: "${PAGER_API_TOKEN}"

Check it, then run it:

>_Terminal
promptise validate oncall.superagent
promptise agent oncall.superagent

That's the whole agent. There's no Python in it, ${PAGER_API_TOKEN} is filled in from the environment when the file loads, and a misspelled key is rejected instead of ignored. The same file also loads from Python with load_superagent_file, so the CLI and your services run exactly the same definition.


[02]

How it works

The file is a contract. Promptise reads it, checks it against a strict schema, fills in the environment variables, and turns it into the same arguments you'd otherwise pass to build_agent by hand:

Rendering diagram…

Every section in the schema forbids unknown keys, so instuctions is an error rather than an agent that quietly runs without its instructions. These are the top-level sections a SuperAgent file can have in Promptise 1.3:

Section

What it sets

version

The schema version. Only "1.0" exists, and it's the default, so you can leave it out.

agent

Required. The model, the instructions and trace, which prints every tool call.

servers

Named MCP servers, each type: stdio or type: http.

cross_agents

Other .superagent files this agent can delegate to.

identity

Who the agent is: a local name for attribution, or a verifiable identity from Entra, AWS, GCP, SPIFFE or OIDC.

memory

Long-term memory: in_memory, chroma or mem0.

sandbox

A container the agent can run code in.

approval

Tools that need a person's approval before they run.

guardrails, cache, observability, optimize_tools, events, adaptive

Guardrails, semantic caching, observability, tool optimization, event notifications and adaptive strategy, the same features build_agent offers.

max_invocation_time

A time limit per request, in seconds. 0, the default, means no limit.

max_delegation_depth, delegation_timeout, include_broadcast

Limits for delegating to cross_agents: how deep a chain may go, how long a peer may take, and whether the agent gets the fan-out tool.

A file needs at least one of servers, cross_agents or sandbox, because an agent with nothing to work with isn't much use. The SuperAgent files reference lists every field in every section.


[03]

What you need

  • Python 3.10 or newer.

  • Promptise Foundry 1.3.1 or newer from PyPI. It includes the promptise command.

  • An API key for a model provider. This guide uses OpenAI's gpt-5-mini; other providers work by changing the model string, as listed in Models & Providers.

>_Terminal
pip install "promptise>=1.3.1"
export OPENAI_API_KEY="sk-..."

[04]

Build a YAML-defined agent, step by step

The agent answers on-call questions: what's broken, and what the runbook says to do about it. A small MCP server gives it the data.

Step 01

Write the MCP server

The tools live in an MCP server, not in the YAML file. That's what keeps the file short: it says which tools the agent may use, and the server decides what they do.

Pythonincidents_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import os
import sys

from promptise.mcp.server import MCPServer

# The token this server would use to call your paging system.
# It comes from the environment, never from code or config files.
if not os.environ.get("PAGER_API_TOKEN"):
    sys.exit("PAGER_API_TOKEN is not set")

server = MCPServer("incidents")

# A stand-in for your incident tracker.
INCIDENTS = [
    {"id": "INC-311", "service": "checkout", "severity": "high", "status": "open",
     "summary": "Payment confirmations delayed by up to 4 minutes"},
    {"id": "INC-309", "service": "search", "severity": "low", "status": "open",
     "summary": "Autocomplete returns stale results for new products"},
    {"id": "INC-302", "service": "checkout", "severity": "medium", "status": "resolved",
     "summary": "Coupon codes rejected for EU customers"},
]

RUNBOOKS = {
    "checkout": "1. Check the payments queue depth. 2. If above 5,000, scale the "
                "payment-workers deployment to 12 replicas. 3. Page #payments if it keeps growing.",
    "search": "1. Check the last index build. 2. If older than 1 hour, trigger a reindex.",
}


@server.tool()
async def list_incidents(status: str = "open") -> list[dict]:
    """List incidents by status.

    Args:
        status: "open" or "resolved". Defaults to "open".
    """
    return [i for i in INCIDENTS if i["status"] == status]


@server.tool()
async def get_runbook(service: str) -> str:
    """Get the on-call runbook for a service, for example "checkout".

    Args:
        service: The service name.
    """
    return RUNBOOKS.get(service.strip().lower(), f"No runbook for {service}.")


if __name__ == "__main__":
    server.run()

The server refuses to start without PAGER_API_TOKEN, like a real integration would. You'll see why that matters in a moment. If MCP servers are new to you, How to Connect MCP Servers to Your AI Agent in Python walks through one line by line.

Step 02

Describe the agent in YAML

Save the file from the top of this guide as oncall.superagent, next to the server. Here's what each part does:

  • `agent.model` is the same provider:model string build_agent takes. For a temperature, a token limit or an Azure deployment, model also accepts a mapping with provider, name and those settings.

  • `agent.instructions` is the system prompt. YAML's | keeps the line breaks, so a long prompt stays readable, and every edit shows up as a clean line in a diff.

  • `servers.incidents` starts incidents_server.py as a child process over stdio, in the folder that holds the agent file, so relative paths work wherever you run the command from. command: python is looked up on your PATH, so run the agent with your virtual environment active.

  • `env` passes PAGER_API_TOKEN to that child process. The value is a placeholder; the real token never appears in the file.

Step 03

Validate the file

promptise validate checks the file without starting anything: the YAML syntax, the schema, and whether every variable the file mentions is set.

>_Terminal
promptise validate oncall.superagent
Output
Validating oncall.superagent...
✓ File format and schema valid
✓ All environment variables available
✓ Validation complete!

Run it in a shell where PAGER_API_TOKEN isn't set, and it tells you:

Output
Validating oncall.superagent...
✓ File format and schema valid
✗ Missing environment variables:
  - PAGER_API_TOKEN
Set these variables (or put them in .env), or use defaults (${VAR:-default}).
Pass --allow-missing-env to only warn, or --no-check-env to skip this check.

That's an error, and the command exits with status 1, so a CI job stops there. Pass --allow-missing-env to report missing variables as a warning instead, or --no-check-env to skip the check. The agent is just as strict: promptise agent refuses to start until the variable is set.

Output
Failed to load configuration: Missing required environment variables in …/oncall-agent/oncall.superagent: PAGER_API_TOKEN
Step 04

Run it from the CLI

promptise agent loads the file, builds the agent and opens a chat in your terminal. Type exit to leave.

>_Terminal
export PAGER_API_TOKEN="..."
promptise agent oncall.superagent
Output
Promptise Foundry loaded from oncall.superagent. Type 'exit' to quit.
> → Invoking tool: list_incidents with {'status': 'open'}
→ Invoking tool: get_runbook with {'service': 'checkout'}
✔ Tool result from list_incidents: [{"id": "INC-311", "service": "checkout", "severity": "high", "status": "open", "summary": "Payment confirmations delayed by up to 4 minutes"}, {"id": "INC-309", "service": "search", "severity": "low", "status": "open", "summary": "Autocomplete returns stale results for new products"}]
✔ Tool result from get_runbook: 1. Check the payments queue depth. 2. If above 5,000, scale the payment-workers deployment to 12 replicas. 3. Page #payments if it keeps growing.
╭────────────────────────────── Final LLM Answer ──────────────────────────────╮
│ Yes. There is an open checkout incident:                                     │
│                                                                              │
│ - INC-311 — service: checkout — severity: high — summary: Payment            │
│ confirmations delayed by up to 4 minutes                                     │
│                                                                              │
│ Runbook (quoted exactly):                                                    │
│ "1. Check the payments queue depth. 2. If above 5,000, scale the             │
│ payment-workers deployment to 12 replicas. 3. Page #payments if it keeps     │
│ growing."                                                                    │
╰──────────────────────────────────────────────────────────────────────────────╯

This run asked "Is anything open on checkout, and what does the runbook say to do?" at the > prompt; the question was piped in, so it isn't echoed above. The agent called both tools at once, picked the checkout incident out of the list, and quoted the runbook word for word, as its instructions asked. The only Python involved is the server; the agent itself is the 17-line file.

The CLI can also change a setting for one run without touching the file. --model-id, --instructions and --trace/--no-trace override the file, and --stdio or --http add a server next to the ones it declares. That's handy for trying a new model before you propose the change.

Step 05

Run the same file from Python

Your services can load the exact file the CLI runs. load_superagent_file validates it and fills in the variables, and to_build_kwargs() hands the result to build_agent:

Pythonrun_agent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import asyncio
import sys

from promptise import build_agent
from promptise.exceptions import SuperAgentError, SuperAgentValidationError
from promptise.superagent import load_superagent_file


async def main(path: str, question: str) -> None:
    try:
        loader, _ = load_superagent_file(path)
    except SuperAgentValidationError as exc:
        for error in exc.errors:
            print(" -> ".join(map(str, error["loc"])), ":", error["msg"])
        raise SystemExit(1)
    except SuperAgentError as exc:
        raise SystemExit(str(exc))

    config = loader.to_agent_config()
    print("Model:", config.model)
    print("Servers:", list(config.servers))

    agent = await build_agent(**config.to_build_kwargs())
    try:
        result = await agent.ainvoke({"messages": [{"role": "user", "content": question}]})
        print(">>>", result["messages"][-1].content)
    finally:
        await agent.shutdown()


asyncio.run(main(sys.argv[1], sys.argv[2]))
>_Terminal
python run_agent.py oncall.superagent "Which incidents are open right now, most severe first?"
Output
Model: openai:gpt-5-mini
Servers: ['incidents']
→ Invoking tool: list_incidents with {'status': 'open'}
✔ Tool result from list_incidents: [{"id": "INC-311", "service": "checkout", "severity": "high", "status": "open", "summary": "Payment confirmations delayed by up to 4 minutes"}, {"id": "INC-309", "service": "search", "severity": "low", "status": "open", "summary": "Autocomplete returns stale results for new products"}]
>>> There are 2 open incidents. Most severe first:

- INC-311 — service: checkout — severity: high — Payment confirmations delayed by up to 4 minutes
- INC-309 — service: search — severity: low — Autocomplete returns stale results for new products

Would you like me to open the runbook for either service (checkout or search)?

Two details in the script are worth copying:

  • No `.env` step. load_superagent_file loads the nearest .env file itself, the way the CLI does on every start, and never overwrites a variable that already has a value. PROMPTISE_NO_DOTENV=1 turns that off.

  • `SuperAgentValidationError` carries the problems as a list in exc.errors, each with a loc path and a msg. That's what you'd log, or show in an internal tool where people edit agent files.

load_superagent_file returns a second value too: the loaded files from cross_agents, which this agent doesn't use. If yours does, build_superagent(path) builds the agent and every agent it delegates to, each from its whole file.

Step 06

Catch mistakes before they ship

Here's the same agent with two typos, the kind that slip through a quick edit:

YAMLbroken.superagent
1
2
3
4
5
6
7
8
9
10
11
12
13
version: "1.0"

agent:
  model: "openai:gpt-5-mini"
  instuctions: |
    You are the on-call assistant for the platform team.
  trace: true

servers:
  incidents:
    type: stdio
    comand: python
    args: ["incidents_server.py"]
>_Terminal
promptise validate broken.superagent
Output
Validating broken.superagent...
✗ Schema validation failed:
Schema validation failed for …/oncall-agent/broken.superagent
File: …/oncall-agent/broken.superagent

Validation errors:
  agent -> instuctions: Extra inputs are not permitted
  servers -> incidents -> stdio -> command: Field required
  servers -> incidents -> stdio -> comand: Extra inputs are not permitted

This time the exit status is 1. Each line points at the exact key: the misspelled instuctions, and comand, which is both an unknown key and the reason command is missing. Loading the file from Python stops with the same errors, which run_agent.py prints from exc.errors:

Output
agent -> instuctions : Extra inputs are not permitted
servers -> incidents -> stdio -> command : Field required
servers -> incidents -> stdio -> comand : Extra inputs are not permitted

Without the strict schema, the first typo would give you an agent that runs fine and ignores its instructions, which is much harder to notice than an error.


[05]

Keep secrets out of the file

The agent file belongs in Git, so nothing secret goes in it. Every string in the file can reference an environment variable instead:

You write

You get

Text
"${PAGER_API_TOKEN}"

The variable's value. Loading stops if it isn't set.

Text
"${INCIDENTS_URL:-http://127.0.0.1:8270/mcp}"

The variable's value, or the default after :- when it isn't set.

Text
"Bearer ${API_TOKEN}"

Placeholders work inside longer strings too.

The variables come from wherever you already keep them: your shell, your CI secrets, your container platform or a secret manager that exports them. The Environment Resolver page covers the syntax in full.

Pass secrets to stdio servers explicitly

A stdio server doesn't see your whole environment. It inherits only a short list of safe variables, HOME, LOGNAME, PATH, SHELL, TERM and USER on macOS and Linux, plus whatever you list under env. Here's the same agent file with the env block removed, run with promptise agent in a shell where PAGER_API_TOKEN is exported:

Output
PAGER_API_TOKEN is not set
Failed to start the agent: Failed to connect to server 'incidents': Failed to connect to python incidents_server.py (McpError: Connection closed)

The first line is the server's own message: it never received the token. That's a good default. Each server gets only the secrets the file names, and a reviewer can see exactly which ones those are.

Use .env for local development

The CLI and load_superagent_file both load a .env file from the folder you run them in, or the nearest parent up to your project root, and never overwrite a variable that already has a value. Keep .env out of the repository:

Output
.gitignore:1:.env	.env

That's git check-ignore -v .env confirming the rule. Model Setup explains how .env and exported variables work together.

Let the schema warn you about pasted keys

If someone pastes a model key into the file, validation warns them. Here model uses the detailed form with an api_key:

Output
Validating hardcoded-key.superagent...
…/pydantic/main.py:790: UserWarning: Direct API key detected in config. Consider using ${ENV_VAR} syntax for security.
  return cls.__pydantic_validator__.validate_python(
✓ File format and schema valid
✓ All environment variables available
✓ Validation complete!

It's only a warning, and it only checks model.api_key. A token pasted into headers or env gets no warning, so keep a secret scanner in CI and an eye on diffs.


[06]

Connect to a remote MCP server with an API key

When the incidents server runs as a shared service, the agent connects over HTTP instead of starting it. Here the server requires an API key on every call:

Pythonremote_incidents_server.py
1
2
3
4
5
6
7
8
9
10
11
import os

from promptise.mcp.server import APIKeyAuth, AuthMiddleware, MCPServer

server = MCPServer("incidents", require_auth=True)
server.add_middleware(
    AuthMiddleware(APIKeyAuth(keys={os.environ["INCIDENTS_API_KEY"]: "oncall-agent"}))
)

if __name__ == "__main__":
    server.run(transport="http", host="127.0.0.1", port=8270)

The tools are the same as before. The agent file sends the key in the x-api-key header, read from the environment, and takes the URL from a variable with a local default:

YAMLoncall-remote.superagent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: "1.0"

agent:
  model: "openai:gpt-5-mini"
  instructions: |
    You are the on-call assistant for the platform team.
    Use your tools to answer questions about incidents and runbooks.
    Quote runbook steps exactly. Never invent incidents.

servers:
  incidents:
    type: http
    url: "${INCIDENTS_URL:-http://127.0.0.1:8270/mcp}"
    headers:
      x-api-key: "${INCIDENTS_API_KEY}"
>_Terminal
python run_agent.py oncall-remote.superagent "What should I do about the checkout incident?"
Output
Model: openai:gpt-5-mini
Servers: ['incidents']
→ Invoking tool: list_incidents with {'status': 'open'}
✔ Tool result from list_incidents: [{"id": "INC-311", "service": "checkout", "severity": "high", "status": "open", "summary": "Payment confirmations delayed by up to 4 minutes"}, {"id": "INC-309", "service": "search", "severity": "low", "status": "open", "summary": "Autocomplete returns stale results for new products"}]
→ Invoking tool: get_runbook with {'service': 'checkout'}
✔ Tool result from get_runbook: 1. Check the payments queue depth. 2. If above 5,000, scale the payment-workers deployment to 12 replicas. 3. Page #payments if it keeps growing.
>>> The checkout incident is INC-311 (service: checkout, severity: high) — "Payment confirmations delayed by up to 4 minutes."
…

There's no trace: true in this file, and the tool calls still print: trace defaults to on. Set trace: false for agents that run unattended. The server entry also has two shorthands for credentials: api_key: "${INCIDENTS_API_KEY}" sends the same x-api-key header, and bearer_token: "${API_TOKEN}" sends Authorization: Bearer … for a server that expects a JSON Web Token. Authentication & Security covers the server side.

Note

HTTP servers don't take an auth: field. A file that uses one fails validation: servers -> incidents -> http: Value error, 'auth' is not supported: it was never sent to the server. Use 'bearer_token:' (sent as 'Authorization: Bearer <token>') or 'api_key:' (sent as 'x-api-key: <key>'), e.g. api_key: "${MY_API_KEY}"


[07]

Review agent changes in Git

Once the agent is a file, changing it is a pull request. Here a teammate tightens the instructions and adds a time limit per request:

Output
diff --git a/oncall.superagent b/oncall.superagent
index 4435c14..c69752b 100644
--- a/oncall.superagent
+++ b/oncall.superagent
@@ -6,6 +6,7 @@ agent:
     You are the on-call assistant for the platform team.
     Use your tools to answer questions about incidents and runbooks.
     Quote runbook steps exactly. Never invent incidents.
+    Never suggest scaling a service without naming the incident it fixes.
   trace: true
 
 servers:
@@ -15,3 +16,5 @@ servers:
     args: ["incidents_server.py"]
     env:
       PAGER_API_TOKEN: "${PAGER_API_TOKEN}"
+
+max_invocation_time: 60

A reviewer sees the full behaviour change in two hunks, without reading any Python. The same goes for the changes that matter most: a new server, a different model, a new secret in an env block, or a removed approval rule each show up as a line in the diff.

Make the review safe to approve by validating every agent file on each pull request. This script finds them with git ls-files and stops at the first one that fails:

>_Terminalcheck_agents.sh
#!/usr/bin/env bash
# Validate every agent file in the repository. Run it in CI on each pull request.
set -euo pipefail

git ls-files '*.superagent' | while read -r file; do
  promptise validate "$file" --no-check-env
done

--no-check-env skips the variable check, because CI doesn't hold your production secrets and a missing variable would otherwise fail the job. On the change above, it passes:

Output
Validating oncall-remote.superagent...
✓ File format and schema valid
✓ Validation complete!
Validating oncall.superagent...
✓ File format and schema valid
✓ Validation complete!

On a branch that sets transport: websocket on the remote server, it fails with status 1 and names the field and the allowed values:

Output
Validating oncall-remote.superagent...
✗ Schema validation failed:
…
Validation errors:
  servers -> incidents -> http -> transport: Input should be 'http', 
'streamable-http' or 'sse'

To decide who may approve agent changes, put the agent files in their own folder and give it an owner with a CODEOWNERS file, so a platform lead signs off on every change to what the agents can reach.


[08]

Honest limits

These are true of Promptise Foundry 1.3, and worth knowing before you hand agent files to a team:

  • `validate` checks the file, not whether the agent can start. It doesn't contact the model or start servers. It does catch settings that can't work, such as approval.handler: callback, which needs a Python function, or handler: webhook without a webhook_url. Still, start each agent once in a staging environment before you rely on a change.

  • Approval from YAML is limited. queue asks at the terminal when you run promptise agent interactively. With piped input, nobody can answer, and the CLI says so: approval.handler is 'queue' but input is not a terminal: approval requests cannot be answered here and time out after 5s (on_timeout: deny). webhook needs your approvals service, with webhook_secret for its signature and webhook_allow_private_networks: true for one on your own network. For approvals handled in your own code, build the agent in Python with an ApprovalPolicy, as in the Human-in-the-Loop Approval docs.

  • The CLI is an interactive chat. There's no single-question mode. For jobs and services, load the file from Python as in Step 5.

  • `to_build_kwargs()` builds one agent. It leaves cross_agents out, with a warning; use build_superagent() for an agent that delegates, or build those agents yourself, as the Cross-Agent Delegation docs show.


[09]

Frequently asked questions

What is an AI agent configuration file?

A file that describes an agent instead of building it in code: which model it uses, its instructions, which tools or MCP servers it may call, and settings like memory or approval. A runtime reads the file and builds the agent from it. In Promptise Foundry that's a .superagent YAML file, run with promptise agent or loaded with load_superagent_file.

What are declarative AI agents?

Agents you describe rather than program. You say what the agent is and what it may use, and the framework turns that into a running agent. The behaviour still comes from the model and the tools; the declaration just moves the wiring out of code, so people who don't write Python can read, review and change it.

What are AI agents as code?

Treating agent definitions like infrastructure as code: plain text files in Git, changed through pull requests, checked automatically in CI and deployed from the repository. You get history, review and rollback for free, because a bad change is one git revert away.

Can I use a .yaml extension instead of .superagent?

Yes, as long as the name still says what it is: oncall.superagent.yaml and oncall.superagent.yml both load. A plain oncall.yaml is refused with Invalid file extension. Expected .superagent, .superagent.yaml, or .superagent.yml. The double extension also lets editors apply YAML highlighting.

Can I start a new agent file from a template?

Yes. promptise init writes a starter file, and --template picks one of basic, http, stdio, cross-agent or advanced. The templates use openai:gpt-4.1 as the model, so change that line to the model you use.


[10]

Where to go next

  • SuperAgent files: every section and field, with examples.

  • SuperAgent API reference: SuperAgentLoader, load_superagent_file and SuperAgentConfig.

  • CLI reference: promptise agent, validate and init with all their options.

  • Environment Resolver: the ${VAR} syntax and its helpers.

  • Server configuration: what the stdio and http server entries become in code.

  • How to Connect MCP Servers to Your AI Agent in Python: the same agent built in Python.

  • OpenAPI to MCP: Turn Any REST API into an MCP Server: generate a server your agent file can point at.

← PreviousBest LLM for AI Agents: Test and Switch Models in PythonNext lesson →Streaming LLM Responses with FastAPI: Tokens and Tool Calls