Skip to content

Custom Capabilities

Capabilities are portable bundles that extend a runtime with one or more agents, Python tools, skills, and MCP servers. They are authored as directories with a capability.yaml manifest and installed either from the TUI capability manager or from a local capability directory on disk.

In the hosted platform, capabilities may come from:

  • your org inventory
  • the public catalog
  • a local machine-only capability directory when the runtime host is local
threat-hunting/
capability.yaml
agents/
triage.md
tools/
intel.py
skills/
report/
SKILL.md
.mcp.json

The v1 manifest is intentionally small:

schema: 1
name: threat-hunting
version: '0.1.0'
description: Threat hunting tools and skills for indicator triage.
agents:
- agents/triage.md
tools:
- tools/intel.py
skills:
- skills/report/
mcp:
files:
- .mcp.json
  • agents: Markdown files with YAML frontmatter and a system prompt body
  • tools: Python files containing @dreadnode.tool functions or dreadnode.Toolset classes
  • skills: directories containing SKILL.md
  • mcp: MCP server definitions from .mcp.json, mcp.json, or inline config

Manifest fields like agents, tools, and skills are arrays of file or directory paths. The loader expands scripts into shell tools automatically and can parse YAML/JSON tool definition files when you need full parameter schemas.

If agents, tools, or skills are omitted, the runtime auto-discovers the conventional directories. Setting one of those fields to [] disables auto-discovery for that component type.

These older fields are not part of the current capability contract:

  • hooks
  • scorers
  • client
  • links
  • entry_agent
  • config

Agent selection is a runtime or project decision, not a manifest field.

---
name: triage
description: Decide which tools and skills to use for indicator investigation.
model: claude-sonnet-4-5-20250929
tools:
'*': false
lookup_indicator: true
skills: [report]
---
You are a threat hunting triage agent. Decide what to investigate next and explain why.
import typing as t
import dreadnode
@dreadnode.tool
def lookup_indicator(
indicator: t.Annotated[str, "IP, domain, or hash to investigate"],
) -> dict[str, str]:
"""Look up an indicator in an intel source."""
return {
"indicator": indicator,
"verdict": "unknown",
"source": "example",
}

Use Python tools for in-process implementations. For shell commands, Node services, remote APIs, or polyglot integrations, expose them through MCP instead of inventing a separate tool runtime.

Use the Python SDK to load a capability and attach its tools + hooks to an agent.

import asyncio
import dreadnode as dn
from dreadnode.agents import Agent
async def main() -> None:
dn.configure(...)
capability = dn.load_capability("./capabilities/threat-hunting")
agent = Agent(
name="threat-hunter",
model="anthropic/claude-sonnet-4-20250514",
instructions="You are a threat hunting assistant.",
tools=capability.tools,
hooks=capability.hooks,
)
trajectory = await agent.run("Check 8.8.8.8 for suspicious activity.")
if trajectory.messages:
print(trajectory.messages[-1].content)
else:
print("No output")
asyncio.run(main())

For a locally hosted runtime, place a capability under:

~/.dreadnode/capabilities/

Then open the TUI and run:

/capabilities

The Installed tab shows capabilities bound to the active project. The Available tab shows installable capabilities from your org inventory and the public catalog.

  • local runtimes can use both Workspace and Local capability sources
  • sandbox runtimes use Workspace capabilities only
  • public catalog capabilities can be installed directly from Available
  • when a workspace capability and local capability share the same bare name, the workspace one wins

When you publish or upload a capability to the platform registry:

  • version must be strict semver such as 1.0.0
  • description is the canonical listing field surfaced by the API and catalog
  • org and public registry responses use canonical names in the form <owner>/<name>
  • SDK and CLI registry traffic uses the API-scoped OCI path under /api/v1, while the root /v2 path remains available for OCI-compatible clients
  • declared export paths must exist in the bundle or the import is rejected