Refs librefang/librefang#4842 — long-term replacement for the substring match that the OpenAI driver currently uses to decide how to handle `reasoning_content` on historical assistant turns. Three provider-specific behaviours that the driver must distinguish at wire time, now expressed as catalog metadata: * `strip` — DeepSeek R1 / deepseek-reasoner. The API rejects requests that carry reasoning_content on previous assistant messages. * `echo` — DeepSeek V4 Flash. Thinking mode is on by default and the API rejects multi-turn requests when assistant turns containing tool_calls don't echo back the original reasoning text. This is the bug surfaced in librefang/librefang#4842. * `empty_string` — Moonshot / Kimi K2 family. The field must be present (empty string) on tool_calls turns, with thinking disabled wire-side for multi-turn compatibility. * `none` (default) — most providers; field is omitted entirely. V4 Pro is intentionally NOT marked `echo` — librefang#4842 reports it working out-of-the-box; flip when there's an empirical reproducer. Marks affected models: providers/deepseek.toml deepseek-v4-flash → echo deepseek-reasoner → strip providers/moonshot.toml kimi-k2.6, kimi-k2.5, kimi-k2 → empty_string providers/kimi-coding.toml kimi-for-coding → empty_string providers/byteplus-coding.toml kimi-k2.5 → empty_string providers/novita.toml moonshotai/kimi-k2-thinking → empty_string Tooling: * schema.toml registers the field with the four enum options and a `none` default so existing TOML files keep parsing unchanged. * scripts/validate.py rejects unknown enum values; verified with a hand-crafted negative case (`reasoning_echo_policy = "bogus"` → validation fails with the expected message). * `python3 scripts/validate.py` passes (267 models). The librefang side that consumes this field will land in a follow-up PR — until then, registry consumers ignore the field via `#[serde(default)]` and the existing substring fallback continues to work, so this commit is safe to ship independently.
940 lines
26 KiB
TOML
940 lines
26 KiB
TOML
# LibreFang Registry Schema
|
|
# =========================
|
|
# Machine-parseable schema definition for all registry content types.
|
|
# Consumed by the RegistrySchema Rust type (librefang-types).
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# PROVIDER / MODEL SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[provider]
|
|
description = "LLM provider configuration"
|
|
file_pattern = "providers/*.toml"
|
|
|
|
[provider.fields.id]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique provider identifier (lowercase, hyphenated)"
|
|
example = "anthropic"
|
|
|
|
[provider.fields.display_name]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable display name"
|
|
example = "Anthropic"
|
|
|
|
[provider.fields.api_key_env]
|
|
type = "string"
|
|
required = true
|
|
description = "Environment variable name for the API key"
|
|
example = "PROVIDER_API_KEY"
|
|
|
|
[provider.fields.base_url]
|
|
type = "string"
|
|
required = true
|
|
description = "Default API base URL"
|
|
example = "https://api.example.com"
|
|
|
|
[provider.fields.key_required]
|
|
type = "bool"
|
|
required = true
|
|
description = "Whether an API key is needed (false for local providers)"
|
|
example = true
|
|
|
|
[provider.fields.api_key]
|
|
type = "string"
|
|
required = false
|
|
description = "API key value (stored securely in secrets.env, not in the TOML file)"
|
|
secret = true
|
|
|
|
# --- Provider: nested [[models]] section ---
|
|
|
|
[provider.sections.models]
|
|
description = "Model entries"
|
|
repeatable = true
|
|
|
|
[provider.sections.models.fields.id]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique model identifier (API model ID)"
|
|
example = "claude-sonnet-4-20250514"
|
|
|
|
[provider.sections.models.fields.display_name]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable display name"
|
|
example = "Claude Sonnet"
|
|
|
|
[provider.sections.models.fields.tier]
|
|
type = "string"
|
|
required = true
|
|
description = "Capability tier: frontier (cutting-edge, most capable), smart (cost-effective), balanced (speed/cost), fast (cheapest for simple tasks), local (Ollama, vLLM, LM Studio)"
|
|
options = ["frontier", "smart", "balanced", "fast", "local"]
|
|
example = "smart"
|
|
|
|
[provider.sections.models.fields.modality]
|
|
type = "string"
|
|
required = false
|
|
description = "Model modality. 'text' (default) is a chat/LLM; 'image' is an image-generation model; 'audio' is a speech / TTS model; 'video' is a video-generation model; 'music' is a music / lyrics generation model. For non-text modalities, context_window / max_output_tokens are optional and input_cost_per_m / output_cost_per_m may be 0 when the model is billed per call."
|
|
options = ["text", "image", "audio", "video", "music"]
|
|
default = "text"
|
|
example = "text"
|
|
|
|
[provider.sections.models.fields.context_window]
|
|
type = "number"
|
|
required = true
|
|
description = "Maximum input tokens. Required for modality='text'; optional for image/audio models where no published context gate exists."
|
|
example = 128000
|
|
|
|
[provider.sections.models.fields.max_output_tokens]
|
|
type = "number"
|
|
required = true
|
|
description = "Maximum output tokens. Required for modality='text'; optional for image/audio models."
|
|
example = 16384
|
|
|
|
[provider.sections.models.fields.input_cost_per_m]
|
|
type = "number"
|
|
required = true
|
|
description = "USD per million text input tokens (0.0 for free/local)"
|
|
example = 2.5
|
|
|
|
[provider.sections.models.fields.output_cost_per_m]
|
|
type = "number"
|
|
required = true
|
|
description = "USD per million text output tokens (0.0 for free/local)"
|
|
example = 10.0
|
|
|
|
[provider.sections.models.fields.image_input_cost_per_m]
|
|
type = "number"
|
|
required = false
|
|
description = "USD per million image input tokens (for image/multimodal models)"
|
|
example = 8.0
|
|
|
|
[provider.sections.models.fields.image_output_cost_per_m]
|
|
type = "number"
|
|
required = false
|
|
description = "USD per million image output tokens (for image-generation models)"
|
|
example = 30.0
|
|
|
|
[provider.sections.models.fields.per_call_cost]
|
|
type = "number"
|
|
required = false
|
|
description = "Flat USD cost per generation, for models billed per call rather than per token. Strongly recommended for modality='video' and modality='music' (which have no per-token pricing); may also be used for image/audio models that quote a flat per-image or per-request rate. When absent for a per-call modality, the runtime metering layer records the call as $0 and logs a warning."
|
|
example = 0.33
|
|
|
|
[provider.sections.models.fields.last_verified]
|
|
type = "string"
|
|
required = false
|
|
description = "ISO date when pricing was last verified against official sources"
|
|
example = "2025-03-15"
|
|
|
|
[provider.sections.models.fields.supports_tools]
|
|
type = "bool"
|
|
required = false
|
|
description = "Tool/function calling support"
|
|
default = false
|
|
|
|
[provider.sections.models.fields.supports_vision]
|
|
type = "bool"
|
|
required = false
|
|
description = "Vision/image input support"
|
|
default = false
|
|
|
|
[provider.sections.models.fields.supports_streaming]
|
|
type = "bool"
|
|
required = false
|
|
description = "Streaming response support"
|
|
default = true
|
|
|
|
[provider.sections.models.fields.supports_thinking]
|
|
type = "bool"
|
|
required = false
|
|
description = "Extended thinking / reasoning support"
|
|
default = false
|
|
|
|
[provider.sections.models.fields.reasoning_echo_policy]
|
|
type = "string"
|
|
required = false
|
|
description = "How the OpenAI-compatible driver must handle the reasoning_content field on historical assistant turns when this model is used. 'none' (default) omits the field entirely. 'strip' is required by DeepSeek-R1 / deepseek-reasoner — the API rejects multi-turn requests that carry reasoning_content from previous turns. 'echo' is required by DeepSeek V4 Flash and other thinking-mode-on models — the original thinking text MUST be round-tripped on assistant turns that contain tool_calls, otherwise the API returns 400. 'empty_string' is required by Moonshot / Kimi K2 — the field must be present (empty string) on tool_calls turns, with thinking disabled wire-side."
|
|
options = ["none", "strip", "echo", "empty_string"]
|
|
default = "none"
|
|
|
|
[provider.sections.models.fields.aliases]
|
|
type = "array"
|
|
required = false
|
|
description = "Alternative names for this model"
|
|
item_type = "string"
|
|
example = ["alias1", "alias2"]
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# AGENT SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[agent]
|
|
description = "Agent configuration"
|
|
file_pattern = "agents/<name>/agent.toml"
|
|
|
|
[agent.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Agent identifier, must match directory name"
|
|
example = "agent-name"
|
|
|
|
[agent.fields.version]
|
|
type = "string"
|
|
required = false
|
|
description = "Semver version string"
|
|
example = "0.1.0"
|
|
|
|
[agent.fields.description]
|
|
type = "string"
|
|
required = true
|
|
description = "One-sentence description of what this agent does"
|
|
example = "What this agent does"
|
|
|
|
[agent.fields.author]
|
|
type = "string"
|
|
required = false
|
|
description = "Author or organization"
|
|
example = "author-name"
|
|
|
|
[agent.fields.module]
|
|
type = "string"
|
|
required = true
|
|
description = "Runtime module (builtin:chat, builtin:tool, etc.)"
|
|
example = "builtin:chat"
|
|
|
|
# --- Agent: [model] section ---
|
|
|
|
[agent.sections.model]
|
|
description = "Agent's LLM configuration"
|
|
repeatable = false
|
|
|
|
[agent.sections.model.fields.provider]
|
|
type = "string"
|
|
required = false
|
|
description = "Provider ID or \"default\""
|
|
example = "default"
|
|
|
|
[agent.sections.model.fields.model]
|
|
type = "string"
|
|
required = false
|
|
description = "Model ID or \"default\""
|
|
example = "default"
|
|
|
|
[agent.sections.model.fields.max_tokens]
|
|
type = "number"
|
|
required = false
|
|
description = "Max response tokens"
|
|
example = 4096
|
|
|
|
[agent.sections.model.fields.temperature]
|
|
type = "number"
|
|
required = false
|
|
description = "Sampling temperature (0.0-2.0)"
|
|
example = 0.7
|
|
|
|
[agent.sections.model.fields.system_prompt]
|
|
type = "string"
|
|
required = true
|
|
description = "Behavioral instructions for the agent"
|
|
|
|
# --- Agent: [metadata.routing] section ---
|
|
|
|
[agent.sections.metadata]
|
|
description = "Agent metadata"
|
|
repeatable = false
|
|
|
|
[agent.sections.metadata.sections.routing]
|
|
description = "Routing configuration"
|
|
repeatable = false
|
|
|
|
[agent.sections.metadata.sections.routing.fields.aliases]
|
|
type = "array"
|
|
required = false
|
|
description = "Phrases that route directly to this agent"
|
|
item_type = "string"
|
|
example = ["exact match phrases"]
|
|
|
|
[agent.sections.metadata.sections.routing.fields.weak_aliases]
|
|
type = "array"
|
|
required = false
|
|
description = "Keywords that suggest this agent"
|
|
item_type = "string"
|
|
example = ["partial match keywords"]
|
|
|
|
# --- Agent: [resources] section ---
|
|
|
|
[agent.sections.resources]
|
|
description = "Resource limits"
|
|
repeatable = false
|
|
|
|
[agent.sections.resources.fields.max_llm_tokens_per_hour]
|
|
type = "number"
|
|
required = false
|
|
description = "Token budget per hour"
|
|
example = 100000
|
|
|
|
# --- Agent: [capabilities] section ---
|
|
|
|
[agent.sections.capabilities]
|
|
description = "Agent permissions"
|
|
repeatable = false
|
|
|
|
[agent.sections.capabilities.fields.tools]
|
|
type = "array"
|
|
required = false
|
|
description = "List of allowed tool names"
|
|
item_type = "string"
|
|
example = ["tool1", "tool2"]
|
|
|
|
[agent.sections.capabilities.fields.network]
|
|
type = "array"
|
|
required = false
|
|
description = "Network access patterns (\"*\" = unrestricted)"
|
|
item_type = "string"
|
|
example = ["*"]
|
|
|
|
[agent.sections.capabilities.fields.memory_read]
|
|
type = "array"
|
|
required = false
|
|
description = "Memory read permissions"
|
|
item_type = "string"
|
|
example = ["*"]
|
|
|
|
[agent.sections.capabilities.fields.memory_write]
|
|
type = "array"
|
|
required = false
|
|
description = "Memory write permissions"
|
|
item_type = "string"
|
|
example = ["self.*"]
|
|
|
|
[agent.sections.capabilities.fields.agent_spawn]
|
|
type = "bool"
|
|
required = false
|
|
description = "Whether this agent can spawn sub-agents"
|
|
default = false
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# HAND SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[hand]
|
|
description = "Hand configuration (a hand is a user-facing capability backed by an agent)"
|
|
file_pattern = "hands/<name>/HAND.toml"
|
|
|
|
[hand.fields.id]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique identifier, must match directory name"
|
|
example = "hand-id"
|
|
|
|
[hand.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable display name"
|
|
example = "Hand Name"
|
|
|
|
[hand.fields.description]
|
|
type = "string"
|
|
required = true
|
|
description = "One-sentence description of what this hand does"
|
|
example = "What this hand does"
|
|
|
|
[hand.fields.category]
|
|
type = "string"
|
|
required = true
|
|
description = "Hand category"
|
|
options = [
|
|
"communication",
|
|
"content",
|
|
"data",
|
|
"development",
|
|
"devops",
|
|
"finance",
|
|
"productivity",
|
|
"research",
|
|
"social",
|
|
]
|
|
example = "productivity"
|
|
|
|
[hand.fields.icon]
|
|
type = "string"
|
|
required = false
|
|
description = "Emoji icon"
|
|
|
|
[hand.fields.tools]
|
|
type = "array"
|
|
required = true
|
|
description = "List of tool names this hand uses"
|
|
item_type = "string"
|
|
example = ["tool1", "tool2"]
|
|
|
|
# --- Hand: [routing] section ---
|
|
|
|
[hand.sections.routing]
|
|
description = "Routing configuration"
|
|
repeatable = false
|
|
|
|
[hand.sections.routing.fields.aliases]
|
|
type = "array"
|
|
required = false
|
|
description = "Phrases that activate this hand"
|
|
item_type = "string"
|
|
example = ["activate phrases"]
|
|
|
|
[hand.sections.routing.fields.weak_aliases]
|
|
type = "array"
|
|
required = false
|
|
description = "Keyword hints for routing"
|
|
item_type = "string"
|
|
example = ["keyword hints"]
|
|
|
|
# --- Hand: [[requires]] section ---
|
|
|
|
[hand.sections.requires]
|
|
description = "External dependencies"
|
|
repeatable = true
|
|
|
|
[hand.sections.requires.fields.key]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique dependency key"
|
|
example = "python3"
|
|
|
|
[hand.sections.requires.fields.label]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable label"
|
|
example = "Python 3"
|
|
|
|
[hand.sections.requires.fields.requirement_type]
|
|
type = "string"
|
|
required = true
|
|
description = "Type of requirement"
|
|
options = ["binary", "package", "service"]
|
|
example = "binary"
|
|
|
|
[hand.sections.requires.fields.check_value]
|
|
type = "string"
|
|
required = true
|
|
description = "Binary name or check command"
|
|
example = "python3"
|
|
|
|
[hand.sections.requires.fields.optional]
|
|
type = "bool"
|
|
required = false
|
|
description = "Whether the dependency is optional"
|
|
default = false
|
|
|
|
[hand.sections.requires.fields.description]
|
|
type = "string"
|
|
required = false
|
|
description = "Why this dependency is needed"
|
|
|
|
# --- Hand: [[requires]] -> [install] nested section ---
|
|
|
|
[hand.sections.requires.sections.install]
|
|
description = "Platform-specific install instructions"
|
|
repeatable = false
|
|
|
|
[hand.sections.requires.sections.install.fields.macos]
|
|
type = "string"
|
|
required = false
|
|
description = "macOS install command"
|
|
example = "brew install python3"
|
|
|
|
[hand.sections.requires.sections.install.fields.windows]
|
|
type = "string"
|
|
required = false
|
|
description = "Windows install command"
|
|
example = "winget install Python.Python.3.12"
|
|
|
|
[hand.sections.requires.sections.install.fields.linux_apt]
|
|
type = "string"
|
|
required = false
|
|
description = "Linux (apt) install command"
|
|
example = "sudo apt install python3"
|
|
|
|
[hand.sections.requires.sections.install.fields.manual_url]
|
|
type = "string"
|
|
required = false
|
|
description = "URL for manual installation instructions"
|
|
example = "https://..."
|
|
|
|
# --- Hand: [[settings]] section ---
|
|
|
|
[hand.sections.settings]
|
|
description = "User-configurable settings"
|
|
repeatable = true
|
|
|
|
[hand.sections.settings.fields.key]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique setting key"
|
|
example = "setting_key"
|
|
|
|
[hand.sections.settings.fields.label]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable label"
|
|
example = "Setting Label"
|
|
|
|
[hand.sections.settings.fields.description]
|
|
type = "string"
|
|
required = false
|
|
description = "What this setting controls"
|
|
|
|
[hand.sections.settings.fields.setting_type]
|
|
type = "string"
|
|
required = true
|
|
description = "Setting input type"
|
|
options = ["toggle", "select", "text", "number"]
|
|
example = "toggle"
|
|
|
|
[hand.sections.settings.fields.default]
|
|
type = "string"
|
|
required = false
|
|
description = "Default value as string"
|
|
example = "true"
|
|
|
|
# --- Hand: [[settings]] -> [[options]] nested section ---
|
|
|
|
[hand.sections.settings.sections.options]
|
|
description = "Available options for select-type settings"
|
|
repeatable = true
|
|
|
|
[hand.sections.settings.sections.options.fields.value]
|
|
type = "string"
|
|
required = true
|
|
description = "Option value"
|
|
example = "option1"
|
|
|
|
[hand.sections.settings.sections.options.fields.label]
|
|
type = "string"
|
|
required = true
|
|
description = "Option display label"
|
|
example = "Option Label"
|
|
|
|
# --- Hand: [agent] section ---
|
|
|
|
[hand.sections.agent]
|
|
description = "The agent that powers this hand"
|
|
repeatable = false
|
|
|
|
[hand.sections.agent.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Agent name"
|
|
example = "hand-agent-name"
|
|
|
|
[hand.sections.agent.fields.description]
|
|
type = "string"
|
|
required = false
|
|
description = "Agent description"
|
|
|
|
[hand.sections.agent.fields.module]
|
|
type = "string"
|
|
required = true
|
|
description = "Runtime module"
|
|
example = "builtin:chat"
|
|
|
|
[hand.sections.agent.fields.provider]
|
|
type = "string"
|
|
required = false
|
|
description = "Provider ID"
|
|
example = "default"
|
|
|
|
[hand.sections.agent.fields.model]
|
|
type = "string"
|
|
required = false
|
|
description = "Model ID"
|
|
example = "default"
|
|
|
|
[hand.sections.agent.fields.max_tokens]
|
|
type = "number"
|
|
required = false
|
|
description = "Max response tokens"
|
|
example = 16384
|
|
|
|
[hand.sections.agent.fields.temperature]
|
|
type = "number"
|
|
required = false
|
|
description = "Sampling temperature"
|
|
example = 0.3
|
|
|
|
[hand.sections.agent.fields.max_iterations]
|
|
type = "number"
|
|
required = false
|
|
description = "Maximum tool-use iterations"
|
|
example = 60
|
|
|
|
[hand.sections.agent.fields.system_prompt]
|
|
type = "string"
|
|
required = true
|
|
description = "Detailed behavioral prompt for the agent"
|
|
|
|
# --- Hand: [dashboard] section ---
|
|
|
|
[hand.sections.dashboard]
|
|
description = "Dashboard metrics configuration"
|
|
repeatable = false
|
|
|
|
# --- Hand: [dashboard] -> [[metrics]] nested section ---
|
|
|
|
[hand.sections.dashboard.sections.metrics]
|
|
description = "Dashboard metric entries"
|
|
repeatable = true
|
|
|
|
[hand.sections.dashboard.sections.metrics.fields.label]
|
|
type = "string"
|
|
required = true
|
|
description = "Metric display name"
|
|
example = "Metric Name"
|
|
|
|
[hand.sections.dashboard.sections.metrics.fields.memory_key]
|
|
type = "string"
|
|
required = true
|
|
description = "Memory key to read the metric value from"
|
|
example = "metric_memory_key"
|
|
|
|
[hand.sections.dashboard.sections.metrics.fields.format]
|
|
type = "string"
|
|
required = true
|
|
description = "Display format for the metric value"
|
|
options = ["number", "currency", "percentage"]
|
|
example = "number"
|
|
|
|
# --- Hand: [metadata] section ---
|
|
|
|
[hand.sections.metadata]
|
|
description = "Operational metadata"
|
|
repeatable = false
|
|
|
|
[hand.sections.metadata.fields.frequency]
|
|
type = "string"
|
|
required = false
|
|
description = "How often this hand runs"
|
|
options = ["continuous", "on-demand", "scheduled"]
|
|
example = "continuous"
|
|
|
|
[hand.sections.metadata.fields.token_consumption]
|
|
type = "string"
|
|
required = false
|
|
description = "Expected token usage level"
|
|
options = ["low", "medium", "high"]
|
|
example = "low"
|
|
|
|
[hand.sections.metadata.fields.default_active]
|
|
type = "bool"
|
|
required = false
|
|
description = "Whether this hand is active by default"
|
|
default = true
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# INTEGRATION SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[integration]
|
|
description = "External service integration via MCP server"
|
|
file_pattern = "mcp/<name>.toml"
|
|
|
|
[integration.fields.id]
|
|
type = "string"
|
|
required = true
|
|
description = "Unique identifier, must match filename"
|
|
example = "integration-id"
|
|
|
|
[integration.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable display name"
|
|
example = "Service Name"
|
|
|
|
[integration.fields.description]
|
|
type = "string"
|
|
required = false
|
|
description = "One-sentence description of what this integration provides"
|
|
|
|
[integration.fields.category]
|
|
type = "string"
|
|
required = false
|
|
description = "Integration category"
|
|
options = [
|
|
"devtools",
|
|
"communication",
|
|
"storage",
|
|
"monitoring",
|
|
"data",
|
|
"productivity",
|
|
]
|
|
example = "devtools"
|
|
|
|
[integration.fields.icon]
|
|
type = "string"
|
|
required = false
|
|
description = "Emoji icon"
|
|
|
|
[integration.fields.tags]
|
|
type = "array"
|
|
required = false
|
|
description = "Searchable tags"
|
|
item_type = "string"
|
|
example = ["tag1", "tag2"]
|
|
|
|
[integration.fields.setup_instructions]
|
|
type = "string"
|
|
required = false
|
|
description = "Multi-line setup guide"
|
|
|
|
# --- Integration: [transport] section ---
|
|
|
|
[integration.sections.transport]
|
|
description = "MCP server transport configuration"
|
|
repeatable = false
|
|
|
|
[integration.sections.transport.fields.type]
|
|
type = "string"
|
|
required = true
|
|
description = "Transport type"
|
|
options = ["stdio", "sse"]
|
|
example = "stdio"
|
|
|
|
[integration.sections.transport.fields.command]
|
|
type = "string"
|
|
required = true
|
|
description = "Command to launch the MCP server"
|
|
example = "npx"
|
|
|
|
[integration.sections.transport.fields.args]
|
|
type = "array"
|
|
required = false
|
|
description = "Command arguments"
|
|
item_type = "string"
|
|
example = ["-y", "@pkg/server"]
|
|
|
|
# --- Integration: [[required_env]] section ---
|
|
|
|
[integration.sections.required_env]
|
|
description = "Required environment variables"
|
|
repeatable = true
|
|
|
|
[integration.sections.required_env.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Environment variable name"
|
|
example = "SERVICE_API_KEY"
|
|
|
|
[integration.sections.required_env.fields.label]
|
|
type = "string"
|
|
required = true
|
|
description = "Human-readable label"
|
|
example = "API Key"
|
|
|
|
[integration.sections.required_env.fields.help]
|
|
type = "string"
|
|
required = false
|
|
description = "Help text explaining how to get this value"
|
|
|
|
[integration.sections.required_env.fields.is_secret]
|
|
type = "bool"
|
|
required = false
|
|
description = "Whether this is a secret value"
|
|
default = true
|
|
|
|
[integration.sections.required_env.fields.get_url]
|
|
type = "string"
|
|
required = false
|
|
description = "URL where user can obtain the value"
|
|
example = "https://..."
|
|
|
|
# --- Integration: [oauth] section ---
|
|
|
|
[integration.sections.oauth]
|
|
description = "OAuth configuration"
|
|
repeatable = false
|
|
|
|
[integration.sections.oauth.fields.provider]
|
|
type = "string"
|
|
required = true
|
|
description = "OAuth provider identifier"
|
|
example = "github"
|
|
|
|
[integration.sections.oauth.fields.scopes]
|
|
type = "array"
|
|
required = false
|
|
description = "OAuth scopes to request"
|
|
item_type = "string"
|
|
example = ["repo", "read:org"]
|
|
|
|
[integration.sections.oauth.fields.auth_url]
|
|
type = "string"
|
|
required = false
|
|
description = "OAuth authorization URL"
|
|
example = "https://..."
|
|
|
|
[integration.sections.oauth.fields.token_url]
|
|
type = "string"
|
|
required = false
|
|
description = "OAuth token exchange URL"
|
|
example = "https://..."
|
|
|
|
# --- Integration: [health_check] section ---
|
|
|
|
[integration.sections.health_check]
|
|
description = "Health check configuration"
|
|
repeatable = false
|
|
|
|
[integration.sections.health_check.fields.interval_secs]
|
|
type = "number"
|
|
required = false
|
|
description = "Health check interval in seconds"
|
|
example = 60
|
|
|
|
[integration.sections.health_check.fields.unhealthy_threshold]
|
|
type = "number"
|
|
required = false
|
|
description = "Number of consecutive failures before marking unhealthy"
|
|
example = 3
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# SKILL SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[skill]
|
|
description = "Reusable skill definition (prompt template or script)"
|
|
file_pattern = "skills/<name>/skill.toml"
|
|
|
|
# --- Skill: [skill] section (metadata) ---
|
|
# Note: the top-level "skill" content type contains a [skill] section for metadata.
|
|
# In the TOML file, these fields live directly under [skill].
|
|
|
|
[skill.sections.skill]
|
|
description = "Skill metadata"
|
|
repeatable = false
|
|
|
|
[skill.sections.skill.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Skill identifier, must match directory name"
|
|
example = "skill-name"
|
|
|
|
[skill.sections.skill.fields.version]
|
|
type = "string"
|
|
required = false
|
|
description = "Semver version string"
|
|
example = "0.1.0"
|
|
|
|
[skill.sections.skill.fields.description]
|
|
type = "string"
|
|
required = false
|
|
description = "One-sentence description of what this skill does"
|
|
|
|
[skill.sections.skill.fields.author]
|
|
type = "string"
|
|
required = false
|
|
description = "Author or organization"
|
|
example = "author-name"
|
|
|
|
[skill.sections.skill.fields.tags]
|
|
type = "array"
|
|
required = false
|
|
description = "Searchable tags"
|
|
item_type = "string"
|
|
example = ["tag1", "tag2"]
|
|
|
|
# --- Skill: [runtime] section ---
|
|
|
|
[skill.sections.runtime]
|
|
description = "Execution runtime configuration"
|
|
repeatable = false
|
|
|
|
[skill.sections.runtime.fields.type]
|
|
type = "string"
|
|
required = true
|
|
description = "Runtime type"
|
|
options = ["promptonly", "python", "node", "shell"]
|
|
example = "promptonly"
|
|
|
|
[skill.sections.runtime.fields.entry]
|
|
type = "string"
|
|
required = false
|
|
description = "Entry point file (required for non-promptonly runtimes)"
|
|
example = "main.py"
|
|
|
|
# --- Skill: [input] section ---
|
|
|
|
[skill.sections.input]
|
|
description = "Input parameter definitions (each field is a parameter with type, description, required)"
|
|
repeatable = false
|
|
|
|
# Note: input fields are dynamic — each key is a parameter name with an object value
|
|
# containing type, description, and required. This section uses a convention where
|
|
# individual parameters are defined as fields.
|
|
|
|
# --- Skill: [prompt] section ---
|
|
|
|
[skill.sections.prompt]
|
|
description = "Prompt template (required for promptonly runtime)"
|
|
repeatable = false
|
|
|
|
[skill.sections.prompt.fields.template]
|
|
type = "string"
|
|
required = true
|
|
description = "Prompt template string, supports {{param_name}} placeholders"
|
|
example = "Use {{param_name}} in the template"
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# PLUGIN SCHEMA
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
[plugin]
|
|
description = "Plugin that extends agent behavior via lifecycle hooks"
|
|
file_pattern = "plugins/<name>/plugin.toml"
|
|
|
|
[plugin.fields.name]
|
|
type = "string"
|
|
required = true
|
|
description = "Plugin identifier, must match directory name"
|
|
example = "plugin-name"
|
|
|
|
[plugin.fields.version]
|
|
type = "string"
|
|
required = true
|
|
description = "Semver version string"
|
|
example = "0.1.0"
|
|
|
|
[plugin.fields.description]
|
|
type = "string"
|
|
required = true
|
|
description = "One-sentence description of what this plugin does"
|
|
|
|
[plugin.fields.author]
|
|
type = "string"
|
|
required = false
|
|
description = "Author or organization"
|
|
example = "author-name"
|
|
|
|
# --- Plugin: [hooks] section ---
|
|
# Hook scripts communicate via stdin/stdout JSON:
|
|
# ingest receives: {"type": "ingest", "agent_id": "...", "message": "..."}
|
|
# ingest returns: {"type": "ingest_result", "memories": [{"content": "..."}]}
|
|
# after_turn receives: {"type": "after_turn", "agent_id": "...", "messages": [...]}
|
|
# after_turn returns: {"type": "ok"}
|
|
|
|
[plugin.sections.hooks]
|
|
description = "Hook entry points — scripts invoked at specific lifecycle events"
|
|
repeatable = false
|
|
|
|
[plugin.sections.hooks.fields.ingest]
|
|
type = "string"
|
|
required = false
|
|
description = "Script called when a user message is received"
|
|
example = "hooks/ingest.py"
|
|
|
|
[plugin.sections.hooks.fields.after_turn]
|
|
type = "string"
|
|
required = false
|
|
description = "Script called after each conversation turn"
|
|
example = "hooks/after_turn.py"
|