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.
💡 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.
The portable skill file — defined by the agentskills.io open spec. A plain Markdown file split into three zones:
--- fences) — identity and metadata that every compatible runtime reads the same way: name, description, license, metadata---) — the LLM's system prompt: role, rules, tone, constraints## Step N: Title headings) — sequential tasks the runtime executes in order via tool callsThe 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.
---
# ═══════════════════════════════════════════════════════════════
# 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.
Standard agentskills.io fields — recognised by OE Runtime, Claude Code, Cursor, Windsurf, and any other agentskills.io-compatible runtime.
| Field | Required | Description |
|---|---|---|
| name | required | Kebab-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. |
| description | required | One-line summary. Shown in /skills listings, Postman, Claude Code tool descriptions, and the agentskills.io marketplace card. |
| license | optional* | SPDX identifier — e.g. Apache-2.0, MIT, CC-BY-4.0. Required to publish to agentskills.io. |
| metadata.author | optional | Author name, GitHub handle, or org. Shown on the agentskills.io marketplace card and in /skills output. |
| metadata.version | optional | Semver 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 →
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.
# ── 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)
| Field | Required | Description |
|---|---|---|
| name | optional | Display name shown in the terminal and server startup logs. |
| description | optional | One-line summary of what this project does. |
| skills | required | List 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[].path | required | Relative path to the folder containing the skill's SKILL.md. Write path: ./daily-report, not path: ./daily-report/SKILL.md. |
| skills[].trigger_type | required | auto — 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[].default | optional | true — this skill runs when a user sends any plain message (not a /run command). Only one skill should be default: true. |
| params | optional | Project-level parameters. Each has a name and an optional default. Referenced as {{name}} in any SKILL.md in the project. |
| connectors | optional | Project-level connector references — connection_name and connection_type only. No credentials here. Matched against oe-config.json at runtime. |
| maxRounds | optional | Maximum 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.
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.
{
// ── 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.
}
}
Set "provider" to any of these — the runtime handles the API client automatically:
| Provider value | Default endpoint |
|---|---|
| openai | api.openai.com |
| anthropic | api.anthropic.com |
| azure | Set baseURL to your Azure OpenAI endpoint |
| groq | api.groq.com/openai/v1 |
| gemini | generativelanguage.googleapis.com |
| ollama | http://localhost:11434/v1 — runs fully offline |
| lmstudio | http://localhost:1234/v1 |
| mistral | api.mistral.ai/v1 |
| deepseek | api.deepseek.com/v1 |
| xai | api.x.ai/v1 |
| openrouter | openrouter.ai/api/v1 |
| fireworks | api.fireworks.ai/inference/v1 |
| togetherai | api.together.xyz/v1 |
| sambanova | api.sambanova.ai/v1 |
| bedrock | AWS Bedrock — set region via baseURL |
| generic-openai | Any OpenAI-compatible endpoint — set baseURL manually |
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 | Required | Description |
|---|---|---|
| llm.provider | required | LLM provider name. See table above. |
| llm.apiKey | required | API key for the LLM provider. Use "ollama" for local Ollama. |
| llm.model | optional | Model name. Defaults vary by provider (e.g. gpt-4o for OpenAI). |
| llm.baseURL | optional | Override the provider's default API endpoint. Required for Azure and generic-openai. |
| connectors | optional | Array of connector credentials. Each entry has connection_name, connection_type, and provider-specific fields (host, password, token, etc.). |
| server.enabled | optional | true — auto-start in server mode without the --serve flag. |
| server.port | optional | HTTP port. Default: 3333. |
| server.apiKey | optional | Protects all server endpoints with an x-api-key header. Omit to run without auth (internal use only). |
| server.publicUrl | optional | Your public HTTPS URL. Used to auto-register Telegram's webhook on startup. |
| server.webhook | optional | true — enable /webhook/telegram, /webhook/slack, /webhook/whatsapp endpoints. |
All three files together — a daily sales report project with two skills and a Telegram bot.
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
---
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.
---
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.
{
"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
}
}
# 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 OE Runtime and connect your first agent to Telegram, Slack, or an HTTP API in under 10 minutes.
Get OE Runtime →