tap-capability-authoring
SkillGuide for creating Octomind capabilities: the config.toml triggers file, provider files and the default.toml symlink, MCP server wiring with env and headers, version pins, setup-symlinks.sh registration, and the validation checklist. Activate when creating or editing capabilities/<name>/ files.
Instructions
Overview
A capability is the layer between agents and MCP servers or toolchains: agents declare what they need (capabilities = ["websearch"]), the capability supplies the wiring. This skill covers the two files every capability needs, provider wiring, registration, and validation. Use it when an agent needs a tool no existing capability covers, or when adding a provider to an existing one.
Mental model
One capability, two kinds of file:
config.toml— routing metadata: requiredtriggers(how users phrase the need) and an optionaldomainsgate. It drives auto-activation,capability discover, and the tap's embedding model.<provider>.toml— how one tool fulfils the capability:[deps],[roles.mcp],[[mcp.servers]].default.tomlis a symlink to the active provider; users switch with[capabilities] <name> = "<provider>"in their config.
Agents never see providers, so implementations swap without touching agents.
Rules
Directory layout
capabilities/<name>/
config.toml triggers (+ optional domains)
<provider-a>.toml provider wiring
<provider-b>.toml optional alternative
default.toml → <provider-a>.tomldefault.toml is always a symlink, except for the built-ins core, agent, and orchestration, which have real files and no link entries.
config.toml
# capabilities/<name>/config.toml
triggers = [
"search the web for recent news on this",
"look up the latest docs online",
"find sources about this topic",
]
# domains = ["developer"] # omit = available in every domainAuthor 5–15 short, concrete phrasings in the user's voice, not abstract descriptions. Use vocabulary specific to this capability; sibling capabilities that share generic verbs (all messaging-*, all legal-*) end in near-ties and the runtime abstains. A capability without config.toml or with empty triggers fails to resolve. Changing triggers changes the embedding model's training data — see the tap-model-training skill.
Provider file
# capabilities/<name>/<provider>.toml
# Capability: <name>
# Provider: <provider>
# Title: Short Capability Title (5–60 chars)
# Description: What this provider supplies (20–160 chars). Requires FOO_API_KEY.
[deps]
require = ["org/tool"] # each needs deps/<org>/<tool>.sh + .md
[roles.mcp]
server_refs = ["servername"]
allowed_tools = ["servername:*"]
[[mcp.servers]]
name = "servername"
type = "stdio" # stdio | http | builtin
command = "npx" # spawned directly, no shell expansion
args = ["-y", "[email protected]"] # exact version pin
timeout_seconds = 60 # 1–3600, idle deadline reset by MCP progress
tools = [] # required; [] = all
env = { FOO_API_KEY = "{{ENV:FOO_API_KEY}}" }- Sections are optional per kind: deps-only (toolchains like
programming-python), MCP-only, or both. - The
# Title:/# Description:header is whatcapability listanddiscovershow; mention required env vars there. allowed_tools:server:*exposes everything;server:tooland trailing-*prefixes narrow it.- Built-in server refs (
core,octofs,agent,octocode) need no[[mcp.servers]]block; every otherserver_refsentry must have one with the samename. - stdio servers take
env(passed to the child) and optionalcwd; http servers takeurland optionalheaders(e.g.Authorization = "Bearer {{ENV:TOKEN}}"). {{ENV:KEY}}inenvorheadersresolves from the environment at spawn time, and the capability is withheld from auto-activation (and refusesenable) while any such key is unset. Use the exact variable name the server reads. Literal values inenvare passed through as-is.{{INPUT:KEY}}is a text substitution from the user's persistent input store at manifest load; prefer{{ENV:KEY}}for anything a server reads from its environment.bin/loadparses capability files with a simple regex TOML subset: keep arrays on one line and avoid inline nesting beyond what the examples show.
Version pins
Every registry-launched server (npx, uvx, including inside sh -c) pins an exact version ([email protected], pkg==X.Y.Z), so a registry publish never changes what users run. lint-capabilities.sh enforces presence offline; scripts/mcp-versions.sh check|update|test handles drift and live MCP handshakes.
Registration
Add both to scripts/setup-symlinks.sh:
link "my-capability" "provider.toml"
DECLARED=( ... "my-capability" )Then run bash scripts/setup-symlinks.sh — it force-creates links, exits 1 on a missing provider file, and warns about undeclared directories.
Dep scripts
Every require = ["<org>/<tool>"] needs deps/<org>/<tool>.sh plus a companion .md. type: mcp makes a server launchable (ensures npx/uvx), type: dep installs a CLI used directly. Load the tap-deps-authoring skill to write them.
Creation checklist
capabilities/<name>/config.tomlwith triggers (anddomainsif domain-specific).capabilities/<name>/<provider>.tomlwith header, wiring, pins, env.- Dep scripts and companion docs for every
requireentry. link+DECLAREDentries, thenbash scripts/setup-symlinks.sh.bash scripts/lint-capabilities.sh capabilities/<name>,bash scripts/lint-deps.sh deps/<org>/<tool>.sh,bash scripts/validate-capabilities.sh.scripts/mcp-versions.sh test <name>for npx/uvx servers (missing-credential errors are fine if the server speaks MCP).- Reference
"<name>"from agents; retrain the embedding model when triggers changed.
Examples
Example 1: MCP capability with an API key
# capabilities/websearch/tavily.toml
# Capability: websearch
# Provider: tavily
# Title: Web Search (Tavily)
# Description: Web search, content extraction, crawling, and site mapping via the Tavily API.
[deps]
require = ["tavily-ai/tavily"]
[roles.mcp]
server_refs = ["tavily"]
allowed_tools = ["tavily_search", "tavily_extract", "tavily_crawl", "tavily_map", "tavily_research"]
[[mcp.servers]]
name = "tavily"
type = "stdio"
command = "npx"
args = ["-y", "[email protected]"]
timeout_seconds = 60
env = { TAVILY_API_KEY = "{{ENV:TAVILY_API_KEY}}" }
tools = []Bare tool names in allowed_tools narrow the surface to exactly those tools.
Example 2: deps-only toolchain
# capabilities/programming-python/uv.toml
# Capability: programming-python
# Provider: uv
# Title: Python Runtime via uv
# Description: Python runtime and package management via uv and uvx.
[deps]
require = ["astral-sh/uv"]Example 3: common mistake — secret as an argument
❌ args = ["-y", "[email protected]", "--api-key", "{{INPUT:KEY}}"] — the secret shows up in process listings and the capability is never env-gated.
✅ env = { SOME_API_KEY = "{{ENV:SOME_API_KEY}}" } — delivered to the child, and auto-activation waits until the key exists.
Checklist
config.tomlhas 5–15 concrete, capability-specific triggers?- Provider header has Title, Description, and required env vars?
- Every non-built-in
server_refsentry has a matching[[mcp.servers]]block withtimeout_secondsandtools? - npx/uvx servers pinned to an exact version?
- Secrets passed via
env/headerswith{{ENV:KEY}}using the server's exact variable name? linkandDECLAREDupdated;setup-symlinks.sh,lint-capabilities.sh,lint-deps.sh,validate-capabilities.shall pass?
References
templates/capability.toml,templates/capability-config.toml— canonical templatesscripts/setup-symlinks.sh— default provider linksscripts/mcp-versions.sh— pin drift, updates, smoke teststap-deps-authoringskill — dep scripts