DeclarAgent
Declarative runbook executor for AI agents. Validate, dry-run, and safely run real CLI workflows.
Ask AI about DeclarAgent
Powered by Claude Β· Grounded in docs
I know everything about DeclarAgent. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
DeclarAgent

Declarative runbook executor for AI agents. Validate, dry-run, and safely execute multi-step YAML workflows from any LLM tool-use loop.
Why DeclarAgent?
LLM agents are great at reasoning but dangerous when executing. DeclarAgent gives agents a structured, auditable, and safe way to run real workflows:
- YAML plans β human-readable, version-controllable runbooks
- Three step types β shell commands (
run), built-in actions (action), and HTTP requests (http) - Dry-run & explain β inspect exactly what will happen before it does
- Destructive-step gating β steps marked
destructive: truerequire explicit--approve - Structured JSON results β every run returns machine-readable output with typed errors
- Built-in actions β file I/O, JSON manipulation, and env access without shell gymnastics
- Template engine β reference outputs from prior steps with
${{steps.<id>.outputs.<key>}} - MCP server β expose plans as directly callable tools over the Model Context Protocol
- Plan-as-tool β drop YAML files in a directory, each becomes an MCP tool automatically
Installation
Requires Go. This project is currently only tested on macOS.
go install github.com/shiehn/declaragent@latest
# Verify
declaragent --help
Platform note: Linux should work out of the box (the codebase uses
sh -cwith no OS-specific code). Windows requires WSL or a similar POSIX-compatible shell.
Quick Start
# Validate a plan
declaragent validate plan.yaml
# See what it would do
declaragent explain plan.yaml --input branch=main
# Dry-run (resolves templates, shows commands)
declaragent dry-run plan.yaml --input branch=main
# Execute for real
declaragent run plan.yaml --input branch=main
# Execute with destructive steps allowed
declaragent run plan.yaml --input branch=main --approve
# Start MCP server with a plans directory
declaragent mcp --plans ./plans
Plan Schema

Plans are YAML files with a simple structure. Each step does exactly one thing: run a shell command, call a built-in action, or send an HTTP request.
name: deploy-service
description: Build, test, and deploy the service
inputs:
env:
required: true
description: Target environment
tag:
default: latest
steps:
- id: test
run: go test ./...
outputs:
result: stdout
- id: build
run: docker build -t myapp:${{inputs.tag}} .
- id: check_health
http:
url: "https://${{inputs.env}}.example.com/health"
method: GET
outputs:
status: stdout
- id: deploy
run: kubectl apply -f k8s/${{inputs.env}}.yaml
destructive: true
Step Types
| Type | Field | Description |
|---|---|---|
| Shell | run | Runs a shell command via sh -c. Captures stdout/stderr. |
| Action | action | Calls a built-in action (file I/O, JSON, env). |
| HTTP | http | Sends an HTTP request. Response body captured as stdout. |
Each step must have exactly one of run, action, or http.
HTTP Step Fields
- id: call_api
http:
url: "https://api.example.com/data" # required
method: POST # default: GET
headers:
Authorization: "Bearer ${{inputs.token}}"
body: '{"key": "value"}' # template-resolved string
outputs:
response: stdout # response body
Key Fields
| Field | Description |
|---|---|
name | Plan identifier |
inputs | Named parameters with required, description, and default |
steps[].id | Unique step identifier |
steps[].run | Shell command to execute |
steps[].action | Built-in action (alternative to run) |
steps[].http | HTTP request (alternative to run and action) |
steps[].with | Parameters passed to built-in actions |
steps[].outputs | Capture step output (e.g., stdout) |
steps[].destructive | If true, blocked unless --approve is passed |
CLI Commands
| Command | Description |
|---|---|
validate <plan.yaml> | Check plan structure and references |
explain <plan.yaml> | Show resolved steps without executing |
dry-run <plan.yaml> | Simulate execution, resolve templates |
run <plan.yaml> | Execute the plan |
mcp [--plans DIR] | Start MCP stdio server |
skill [--plans DIR] | Generate a Claude Code Skill (SKILL.md) |
All commands accept --json for machine-readable output and --input key=value for plan inputs.
Built-in Actions
| Action | Params | Description |
|---|---|---|
file.write | path, content | Write content to a file |
file.append | path, content | Append content to a file |
json.get | file, path | Read a value from a JSON file |
json.set | file, path, value | Set a value in a JSON file |
env.get | name | Read an environment variable |
Structured Results

Every execution returns a JSON result:
{
"run_id": "a1b2c3",
"success": true,
"steps": [
{"id": "test", "status": "success", "exit_code": 0, "duration": "1.2s"}
],
"outputs": {"test.result": "ok"},
"artifacts": [".declaragent/a1b2c3/test/stdout"],
"errors": []
}
Errors are typed for agent decision-making:
| Error Type | Retryable | Meaning |
|---|---|---|
VALIDATION_ERROR | No | Bad plan or missing inputs |
STEP_FAILED | No | A command returned non-zero |
PERMISSION_DENIED | No | Destructive step without --approve |
SIDE_EFFECT_BLOCKED | No | Destructive step blocked in dry-run |
TRANSIENT | Yes | Temporary failure, safe to retry |
TIMEOUT | Yes | Step exceeded time limit |
TOOL_NOT_FOUND | No | Unknown action referenced in step |
PRECONDITION_FAILED | No | Step precondition not met |
CANCELLED | No | Execution was cancelled |
MCP Integration

Run declaragent mcp to start a Model Context Protocol stdio server.
Plan-as-Tool
When you pass --plans <directory>, every YAML plan in that directory becomes a directly callable MCP tool:
declaragent mcp --plans ./plans
- Tool
name= planname - Tool
description= plandescription - Tool
inputSchema= derived from planinputs - Calling the tool = executing the plan with the provided inputs
This means an LLM agent can discover and invoke your plans without knowing anything about DeclarAgent's internal plan format.
Integrations
Claude Code
Add to .claude/settings.json:
{
"mcpServers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
Cursor
Add to .cursor/mcp.json in your project root:
{
"mcpServers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
GitHub Copilot (VS Code)
Add to .vscode/mcp.json in your project root. Note: Copilot uses "servers" instead of "mcpServers":
{
"servers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
Amazon Q Developer
Add to ~/.aws/amazonq/mcp.json:
{
"mcpServers": {
"declaragent": {
"command": "/path/to/declaragent",
"args": ["mcp", "--plans", "/path/to/your/plans"]
}
}
}
ChatGPT
ChatGPT requires a remotely accessible SSE endpoint. See SSE Transport below, then add the public URL in ChatGPT's MCP settings.
SSE Transport
For remote hosting or HTTP-based clients (e.g., ChatGPT), start the MCP server with SSE transport:
declaragent mcp --transport sse --port 19100 --plans ./plans
The server will be available at http://localhost:19100/sse. Expose this URL publicly (e.g., via a reverse proxy or tunnel) for clients that require a remote endpoint.
Built-in MCP Tools
These meta-tools are always available regardless of --plans:
| Tool | Description |
|---|---|
plan.validate | Validate a plan YAML file |
plan.explain | Explain a plan without executing |
plan.dry_run | Dry-run a plan |
plan.run | Execute a plan |
plan.schema | Return the plan YAML schema |
Claude Code Skills
As an alternative to MCP, you can generate a Claude Code Skill that teaches Claude Code how to use the DeclarAgent CLI directly. Skills are simpler and more token-efficient than MCP since they work through the CLI rather than a server.
# Generate a skill with a plan catalog
declaragent skill --plans ./plans
# Custom output directory and binary name
declaragent skill --plans ./plans --output .claude/skills/declaragent --binary declaragent
This creates .claude/skills/declaragent/SKILL.md containing CLI usage instructions and a catalog of your available plans. Claude Code automatically discovers skills in .claude/skills/, so once generated, any user (or agent) can invoke it via /declaragent.
| Flag | Default | Description |
|---|---|---|
--plans | (none) | Directory containing plan YAML files to catalog |
--output | .claude/skills/declaragent | Output directory for SKILL.md |
--binary | declaragent | Binary name used in generated instructions |
Docs: 3 Example Plans You Can Copy and Run
Copy these into a plans/ directory and start the MCP server:
mkdir -p plans
# (paste each YAML below into the corresponding file)
declaragent mcp --plans ./plans
Or run any plan directly:
declaragent run plans/hello.yaml --input name=Alice
Plan 1: plans/hello.yaml β Simple Shell Command
The simplest possible plan. Runs echo and captures the output.
name: hello
description: Say hello to someone
inputs:
name:
description: Who to greet
default: World
steps:
- id: greet
run: echo "Hello, ${{inputs.name}}!"
outputs:
message: stdout
Run it:
declaragent run plans/hello.yaml --input name=Alice
Result:
Plan "hello" completed successfully.
Run ID: <uuid>
With --json:
{
"run_id": "...",
"success": true,
"steps": [{"id": "greet", "status": "success", "stdout_ref": "Hello, Alice!\n"}]
}
Plan 2: plans/ip_lookup.yaml β HTTP Request
Fetches your public IP address from a free API.
name: ip-lookup
description: Look up your public IP address
steps:
- id: fetch_ip
http:
url: "https://httpbin.org/ip"
method: GET
outputs:
response: stdout
Run it:
declaragent run plans/ip_lookup.yaml --json
Result:
{
"success": true,
"steps": [{
"id": "fetch_ip",
"status": "success",
"stdout_ref": "{\"origin\": \"203.0.113.42\"}"
}]
}
Plan 3: plans/build_and_notify.yaml β Multi-Step with Chaining
Runs a build, then POSTs the result to a webhook. Demonstrates step chaining β the HTTP step uses the shell step's output via ${{steps.build.outputs.result}}.
name: build-and-notify
description: Run a build command and POST the result to a webhook
inputs:
webhook_url:
required: true
description: URL to POST the build result to
steps:
- id: build
run: echo "build-ok"
outputs:
result: stdout
- id: notify
http:
url: "${{inputs.webhook_url}}"
method: POST
headers:
Content-Type: application/json
body: '{"event": "build_complete", "output": "${{steps.build.outputs.result}}"}'
outputs:
response: stdout
Run it:
declaragent run plans/build_and_notify.yaml \
--input webhook_url=https://httpbin.org/post --json
This runs echo "build-ok", captures the output, then POSTs it to the webhook URL.
Testing
go test ./...
CI / Releases
All pushes to main and pull requests run tests automatically via GitHub Actions.
To create a new release, tag a commit and push the tag:
git tag v0.2.0
git push origin v0.2.0
This triggers the release workflow which:
- Runs the full test suite
- Builds binaries for macOS (arm64, amd64) and Linux (arm64, amd64)
- Creates a GitHub Release with the binaries attached and auto-generated release notes
License
See LICENSE.
