← Back to Blog

OE Runtime File Reference: SKILL.md, agent.yaml & oe-config.json

Every OE Runtime project uses three files. This is the complete field reference — what each file does, every field explained, and a full working example that ties them all together.

📄
File 0
SKILL.md
The portable skill — agentskills.io format. Frontmatter + system instructions + ## Step N: headings. Runs on OE Runtime, Claude, Cursor, or Windsurf unchanged.
File 1
agent.yaml
The orchestrator — lists all skill paths, trigger types, connectors, and params. No inline instructions here.
File 2
oe-config.json
Holds all secrets — LLM API keys, connector credentials, and server settings.

💡 The split is intentional. SKILL.md and agent.yaml describe what an agent does — they are safe to commit to git. oe-config.json holds credentials — keep it out of version control with .gitignore.

What You Need


File 0 — SKILL.md

SKILL.md

The portable skill file — defined by the agentskills.io open spec. A plain Markdown file split into three zones:

  1. YAML frontmatter (between the --- fences) — identity and metadata that every compatible runtime reads the same way: name, description, license, metadata
  2. System instructions (free text after the closing ---) — the LLM's system prompt: role, rules, tone, constraints
  3. Steps (## Step N: Title headings) — sequential tasks the runtime executes in order via tool calls

The same SKILL.md runs unmodified on OE Runtime, Claude Code, Cursor, and Windsurf — no vendor lock-in. Params and connectors that the skill needs are declared in agent.yaml, not in SKILL.md — that keeps the skill file clean and truly portable.

Annotated Example — agentskills.io format

SKILL.md — the agentskills.io format: frontmatter · system instructions · steps
---
# ═══════════════════════════════════════════════════════════════
# ZONE 1 — YAML frontmatter (between the --- fences)
# agentskills.io standard fields — recognised by every compatible runtime.
# ═══════════════════════════════════════════════════════════════

name: daily-sales-report           # kebab-case slug, no spaces — used in /run daily-sales-report
description: Queries the CRM and sends a daily pipeline summary to Telegram
license: Apache-2.0               # SPDX identifier — required for agentskills.io publishing
metadata:
  author: Open Enthrium            # your name, org, or GitHub handle
  version: "1.0"                   # semver — increment when the skill behaviour changes
---

# ═══════════════════════════════════════════════════════════════
# ZONE 2 — System instructions (free text, after the closing ---)
# Becomes the LLM's system prompt: role, rules, tone, constraints.
# ═══════════════════════════════════════════════════════════════

You are a sales reporting agent. Your job is to query the database
for today's pipeline metrics, summarise them clearly, and send the
report to Telegram. Be concise — use bullet points and bold numbers.
Never include raw SQL in the Telegram message.

# ═══════════════════════════════════════════════════════════════
# ZONE 3 — Steps (## Step N: Title headings)
# The runtime executes each ## Step heading in order via LLM tool calls.
# Each step is one discrete task. Use {{param}} substitution freely.
# Params are declared in agent.yaml — the runtime injects them here.
# ═══════════════════════════════════════════════════════════════

## Step 1: Fetch pipeline data

Query the Sales DB connector for today's ({{date}}) deals in region {{region}}.

SELECT stage, COUNT(*) AS count, SUM(value) AS total
FROM deals
WHERE region = '{{region}}' AND date = '{{date}}'
GROUP BY stage;

## Step 2: Format and send report

Format the query results as a clean, readable Telegram message using
bullet points and bold totals. Send it to the Sales Telegram Bot
via POST /sendMessage.

⚠️ Keep SKILL.md portable. Do not put params or connectors inside SKILL.md frontmatter — those are OE Runtime-specific extensions and break portability on other runtimes. Declare params and connectors in agent.yaml instead. The skill file stays clean and runs on any agentskills.io-compatible tool unchanged.

SKILL.md Frontmatter Fields

Standard agentskills.io fields — recognised by OE Runtime, Claude Code, Cursor, Windsurf, and any other agentskills.io-compatible runtime.

FieldRequiredDescription
namerequiredKebab-case slug — no spaces. Used to invoke the skill (/run daily-sales-report), shown in listings, and matched by Telegram, Slack, HTTP, and MCP. Must be unique within a project.
descriptionrequiredOne-line summary. Shown in /skills listings, Postman, Claude Code tool descriptions, and the agentskills.io marketplace card.
licenseoptional*SPDX identifier — e.g. Apache-2.0, MIT, CC-BY-4.0. Required to publish to agentskills.io.
metadata.authoroptionalAuthor name, GitHub handle, or org. Shown on the agentskills.io marketplace card and in /skills output.
metadata.versionoptionalSemver string (e.g. "1.0", "2.3.1"). Increment when you change the skill's behaviour. Used for marketplace versioning and change tracking.

📚 agentskills.io is the open community registry for SKILL.md files — browse, fork, and publish skills that work on any compatible runtime. Every skill in the OE Runtime skills library is also listed there. Browse the registry →

File 1 — agent.yaml

agent.yaml

The orchestrator file. Lists every skill in the project with its path and trigger type. Also sets project-level params and connector references that apply across skills. No inline instructions or steps — those live in each skill's SKILL.md. No secrets here either.

Annotated Example

agent.yaml — orchestrator with multiple skills
# ── Identity ─────────────────────────────────────────────────
name: Sales Automation             # project name shown in terminal
description: Daily reporting and outreach pipeline

# ── Skills ───────────────────────────────────────────────────
# Each path points to a folder containing a SKILL.md file.
# The runtime auto-appends /SKILL.md — write path: ./daily-report, not ./daily-report/SKILL.md.
skills:
  # Auto skills fire immediately, in order, passing output as context.
  - path: ./daily-report             # SKILL.md name: daily-sales-report
    trigger_type: auto

  # Manual skills pause and wait for approval before running.
  - path: ./notify-manager           # SKILL.md name: notify-manager
    trigger_type: manual

  # Default skill: runs when a user sends a plain message (no /run command).
  - path: ./chat-bot                  # SKILL.md name: chat-bot
    trigger_type: auto
    default: true

# ── Params ───────────────────────────────────────────────────
# Project-level params substituted as {{name}} in all SKILL.md files.
params:
  - name: date                        # required — caller must supply
  - name: region
    default: Global                  # optional — falls back to this value

# ── Connectors ───────────────────────────────────────────────
# Project-level connector references. Credentials live in oe-config.json.
connectors:
  - connection_name: Sales DB
    connection_type: postgresql
  - connection_name: Sales Telegram Bot
    connection_type: telegram

# ── Config ───────────────────────────────────────────────────
maxRounds: 25                        # max LLM tool-call iterations (default: 25)

agent.yaml Field Reference

FieldRequiredDescription
nameoptionalDisplay name shown in the terminal and server startup logs.
descriptionoptionalOne-line summary of what this project does.
skillsrequiredList of skill references. Each entry has a path (directory containing SKILL.md), a trigger_type (auto or manual), and an optional default: true flag. The runtime auto-appends /SKILL.md.
skills[].pathrequiredRelative path to the folder containing the skill's SKILL.md. Write path: ./daily-report, not path: ./daily-report/SKILL.md.
skills[].trigger_typerequiredauto — fires immediately, passing output as context to the next skill. manual — pauses and waits for human approval (via Telegram, Slack, HTTP, or MCP) before running.
skills[].defaultoptionaltrue — this skill runs when a user sends any plain message (not a /run command). Only one skill should be default: true.
paramsoptionalProject-level parameters. Each has a name and an optional default. Referenced as {{name}} in any SKILL.md in the project.
connectorsoptionalProject-level connector references — connection_name and connection_type only. No credentials here. Matched against oe-config.json at runtime.
maxRoundsoptionalMaximum number of LLM tool-call iterations before the agent stops. Prevents infinite loops. Default: 25.

⚠️ SKILL.md vs agent.yaml: SKILL.md is the portable skill — it holds the system instructions and workflow steps and runs on any compatible runtime unchanged. agent.yaml is the wiring and orchestration layer — it lists all skill paths, trigger types, and project-level params and connectors. Never put credentials in either file; those belong in oe-config.json.

File 2 — oe-config.json

oe-config.json

The secrets file. Holds the LLM provider and API key, full connector credentials, and server settings. Never commit this to git — add it to .gitignore.

Annotated Example

oe-config.json — complete example with every section
{
  // ── LLM ──────────────────────────────────────────────────────
  "llm": {
    "provider": "openai",           // see provider list below
    "model":    "gpt-4o",           // any model name the provider accepts
    "apiKey":   "sk-...",           // provider API key
    "baseURL":  "https://..."       // optional — override the default endpoint
  },

  // ── Connectors ───────────────────────────────────────────────
  // Array of connector credentials. connection_name must match the
  // connection_name used in agent.yaml or SKILL.md frontmatter.
  "connectors": [
    {
      "connection_name": "Sales DB",        // matches agent.yaml / SKILL.md frontmatter
      "connection_type": "postgresql",      // connector type
      "host":     "localhost",
      "port":     5432,
      "database": "salesdb",
      "user":     "postgres",
      "password": "your-password"
    },
    {
      "connection_name": "Sales Telegram Bot",
      "connection_type": "telegram",
      "baseUrl": "https://api.telegram.org/botYOUR_BOT_TOKEN"
    }
  ],

  // ── Server (optional) ─────────────────────────────────────────
  // Enable to expose agents via HTTP API.
  "server": {
    "enabled":   false,             // true = start as HTTP server on launch
    "port":      3333,              // port to listen on
    "apiKey":    "your-secret",     // x-api-key header required on all requests
    "publicUrl": "https://...",     // optional — used to auto-register Telegram webhook
    "webhook":   true               // enable /webhook/telegram, /webhook/slack etc.
  }
}

LLM Providers

Set "provider" to any of these — the runtime handles the API client automatically:

Provider valueDefault endpoint
openaiapi.openai.com
anthropicapi.anthropic.com
azureSet baseURL to your Azure OpenAI endpoint
groqapi.groq.com/openai/v1
geminigenerativelanguage.googleapis.com
ollamahttp://localhost:11434/v1 — runs fully offline
lmstudiohttp://localhost:1234/v1
mistralapi.mistral.ai/v1
deepseekapi.deepseek.com/v1
xaiapi.x.ai/v1
openrouteropenrouter.ai/api/v1
fireworksapi.fireworks.ai/inference/v1
togetheraiapi.together.xyz/v1
sambanovaapi.sambanova.ai/v1
bedrockAWS Bedrock — set region via baseURL
generic-openaiAny OpenAI-compatible endpoint — set baseURL manually

Connector Credentials Format

connectors is an array. Each entry has connection_name (matched to agent.yaml or SKILL.md frontmatter), connection_type, and connector-specific credential fields:

"connectors": [
  {
    "connection_name": "My Database",
    "connection_type": "postgresql",
    "host": "localhost", "port": 5432, "database": "mydb", "user": "postgres", "password": "..."
  },
  {
    "connection_name": "My Slack Bot",
    "connection_type": "slack",
    "botToken": "xoxb-..."
  }
]

Field Reference

FieldRequiredDescription
llm.providerrequiredLLM provider name. See table above.
llm.apiKeyrequiredAPI key for the LLM provider. Use "ollama" for local Ollama.
llm.modeloptionalModel name. Defaults vary by provider (e.g. gpt-4o for OpenAI).
llm.baseURLoptionalOverride the provider's default API endpoint. Required for Azure and generic-openai.
connectorsoptionalArray of connector credentials. Each entry has connection_name, connection_type, and provider-specific fields (host, password, token, etc.).
server.enabledoptionaltrue — auto-start in server mode without the --serve flag.
server.portoptionalHTTP port. Default: 3333.
server.apiKeyoptionalProtects all server endpoints with an x-api-key header. Omit to run without auth (internal use only).
server.publicUrloptionalYour public HTTPS URL. Used to auto-register Telegram's webhook on startup.
server.webhookoptionaltrue — enable /webhook/telegram, /webhook/slack, /webhook/whatsapp endpoints.

Complete Working Example

All three files together — a daily sales report project with two skills and a Telegram bot.

sales-project/
├── agent.yaml                # orchestrates both skills
├── oe-config.json            # LLM key + DB + Telegram credentials (gitignored)
├── daily-report/
│   └── SKILL.md              # skill: queries DB and posts to Telegram
└── chat-bot/
    └── SKILL.md              # skill: conversational assistant (default)
agent.yaml
name: Sales Automation
description: Daily reporting and conversational assistant

skills:
  - path: ./daily-report
    trigger_type: auto

  - path: ./chat-bot
    trigger_type: auto
    default: true

connectors:
  - connection_name: Sales DB
    connection_type: postgresql
  - connection_name: My Telegram Bot
    connection_type: telegram
daily-report/SKILL.md
---
name: daily-report
description: Queries the CRM and posts a summary to Telegram
license: Apache-2.0
metadata:
  author: Open Enthrium
  version: "1.0"
---

You are a sales reporting agent. Query the database, summarise
today's pipeline, and send the result to Telegram.

## Step 1: Query pipeline
SELECT stage, COUNT(*) AS count, SUM(value) AS total
FROM deals WHERE date = '{{date}}' GROUP BY stage;

## Step 2: Send to Telegram
Get the chat_id from My Telegram Bot via GET /getUpdates.
Send a formatted summary via POST /sendMessage.
chat-bot/SKILL.md
---
name: chat-bot
description: Conversational assistant — answers questions about deals
license: Apache-2.0
metadata:
  author: Open Enthrium
  version: "1.0"
params:
  - name: message
  - name: user
---

You are a helpful AI assistant. Respond concisely and clearly.
Keep replies under 300 words.

## Step 1: Reply

{{user}} says: {{message}}

Write a helpful, friendly reply.
oe-config.json
{
  "llm": {
    "provider": "openai",
    "model": "gpt-4o",
    "apiKey": "sk-..."
  },
  "connectors": [
    {
      "connection_name": "Sales DB",
      "connection_type": "postgresql",
      "host": "localhost", "port": 5432,
      "database": "salesdb", "user": "postgres", "password": "..."
    },
    {
      "connection_name": "My Telegram Bot",
      "connection_type": "telegram",
      "baseUrl": "https://api.telegram.org/botYOUR_BOT_TOKEN",
      "auto_reply": true
    }
  ],
  "server": {
    "enabled": true,
    "port": 3333,
    "publicUrl": "https://purple-mouse-1234.trycloudflare.com",
    "webhook": true
  }
}

Run it

# Start a Cloudflare tunnel first (so Telegram can call your server)
npx cloudflared tunnel --url http://localhost:3333

# Then start the server
npx -y @openenthrium/oe-runtime@latest --serve --config oe-config.json

From Telegram: /run daily-report runs the reporting skill. Any plain message is handled by the default chat-bot skill.

Download samples: 📁 Skills Library 🔗 GitHub

Three files. One agent. Any interface.

Download OE Runtime and connect your first agent to Telegram, Slack, or an HTTP API in under 10 minutes.

Get OE Runtime →