Skill

Skill Middleware provides Skill support for the Eino ADK Agent, enabling the Agent to dynamically discover and use predefined skills to complete tasks.

What is a Skill

A Skill is a folder containing instructions, scripts, and resources. Agents can discover and use these Skills on demand to extend their capabilities. The core is the SKILL.md file, which includes metadata (at least name and description) and instructions to guide the Agent in executing tasks.

my-skill/
├── SKILL.md          # Required: instructions + metadata
├── scripts/          # Optional: executable code
├── references/       # Optional: reference docs
└── assets/           # Optional: templates, resources

Skills use Progressive Disclosure to efficiently manage context:

  1. Discovery: The Agent only loads each available Skill’s name and description — enough to determine when the Skill might be needed
  2. Activation: When a task matches a Skill, the Agent loads the full SKILL.md content into context
  3. Execution: The Agent follows the instructions to execute the task, loading other files or executing bundled code as needed

💡 Ref: https://agentskills.io/home

Interface Reference

FrontMatter

Skill metadata structure, parsed from the YAML frontmatter of SKILL.md. Used for quickly displaying Skill information during the discovery phase:

type FrontMatter struct {
    Name        string      `yaml:"name"`
    Description string      `yaml:"description"`
    Context     ContextMode `yaml:"context"`
    Agent       string      `yaml:"agent"`
    Model       string      `yaml:"model"`
}
FieldTypeDescription
Name
string
Unique identifier for the Skill. Short, meaningful names are recommended (e.g.,
pdf-processing
,
web-research
)
Description
string
Functional description of the Skill. The key basis for the Agent to decide whether to use the Skill; should clearly describe applicable scenarios and capabilities
Context
ContextMode
Context mode. Optional values:
fork
(isolated context),
fork_with_context
(copy history messages). Empty means inline mode
Agent
string
Specifies the Agent name to use, used with
Context
, resolved via
AgentHub
. Empty uses the default Agent
Model
string
Specifies the model name to use, resolved via
ModelHub
to obtain the model instance

ContextMode

const (
    ContextModeFork            ContextMode = "fork"              // Isolated context
    ContextModeForkWithContext ContextMode = "fork_with_context" // Copy history messages
)
ModeDescription
Inline (default)Skill content is returned directly as the tool result, and the current Agent continues processing
fork_with_context
Creates a new Agent, copies the current conversation history, independently executes the Skill task, then returns the result
fork
Creates a new Agent with isolated context (containing only the Skill content), independently executes, then returns the result

Skill

The complete Skill structure, including metadata and instruction content:

type Skill struct {
    FrontMatter
    Content       string
    BaseDirectory string
}
FieldTypeDescription
FrontMatter
FrontMatter
Embedded metadata structure
Content
string
The body content after frontmatter in SKILL.md, including detailed instructions, workflows, examples, etc.
BaseDirectory
string
Absolute path to the Skill directory; the Agent can use this path to access other resource files in the directory

Backend

The Skill backend interface, decoupling skill storage from usage:

type Backend interface {
    List(ctx context.Context) ([]FrontMatter, error)
    Get(ctx context.Context, name string) (Skill, error)
}
MethodDescription
List
Lists metadata of all available skills. Called when the Agent starts to build the skill tool description
Get
Gets the full skill content by name. Called when the Agent decides to use a skill

NewBackendFromFilesystem

A backend implementation based on the filesystem.Backend interface that scans first-level subdirectories under the specified directory to read skills:

type BackendFromFilesystemConfig struct {
    Backend filesystem.Backend
    BaseDir string
}

func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystemConfig) (Backend, error)
FieldTypeRequiredDescription
Backend
filesystem.Backend
YesFilesystem backend implementation used for file operations
BaseDir
string
YesSkill root directory path. Scans first-level subdirectories under this directory to find directories containing
SKILL.md
files

How it works:

  • Scans first-level subdirectories under BaseDir
  • Looks for SKILL.md files in each subdirectory
  • Parses YAML frontmatter to obtain metadata
  • Deeply nested SKILL.md files are ignored

filesystem.Backend has two available implementations — see the FileSystem Backend documentation for details.

AgentHub and ModelHub

When Skills use context mode (fork / fork_with_context), AgentHub and ModelHub are needed to provide Agent instances and model instances.

💡 The following shows non-generic alias types (i.e., *schema.Message specializations). Generic versions TypedAgentHub[M] and TypedModelHub[M] are available for *schema.AgenticMessage scenarios with identical interface signatures, differing only in the message type parameter.

// AgentHubOptions passed to AgentHub.Get
type AgentHubOptions = TypedAgentHubOptions[*schema.Message]

type TypedAgentHubOptions[M adk.MessageType] struct {
    // Model is the model instance specified in the skill's frontmatter (resolved via ModelHub).
    // nil means the skill did not specify a model override; implementations should use the default model.
    Model model.BaseModel[M]
}

// AgentHub provides Agent instances for context mode
type AgentHub = TypedAgentHub[*schema.Message]

type TypedAgentHub[M adk.MessageType] interface {
    // Get returns an Agent by name. Should return the default Agent when name is empty.
    Get(ctx context.Context, name string, opts *TypedAgentHubOptions[M]) (adk.TypedAgent[M], error)
}

// ModelHub resolves model instances by name
type ModelHub = TypedModelHub[*schema.Message]

type TypedModelHub[M adk.MessageType] interface {
    Get(ctx context.Context, name string) (model.BaseModel[M], error)
}

💡 Note: The return type of AgentHubOptions.Model and ModelHub.Get is model.BaseModel[M], not the model.ToolCallingChatModel from older documentation.

SubAgentInput and SubAgentOutput

These two structs are used when customizing fork mode behavior:

type SubAgentInput = TypedSubAgentInput[*schema.Message]

type TypedSubAgentInput[M adk.MessageType] struct {
    Skill        Skill
    Mode         ContextMode
    RawArguments string   // Raw JSON arguments
    SkillContent string   // Constructed Skill content
    History      []M      // Conversation history (fork_with_context mode only)
    ToolCallID   string   // Tool call ID (fork_with_context mode only)
}

type SubAgentOutput = TypedSubAgentOutput[*schema.Message]

type TypedSubAgentOutput[M adk.MessageType] struct {
    Skill        Skill
    Mode         ContextMode
    RawArguments string
    Messages     []M      // All messages produced by the sub-Agent
    Results      []string // Extracted assistant message text content
}

Initialization

Config

type Config = TypedConfig[*schema.Message]

type TypedConfig[M adk.MessageType] struct {
    Backend           Backend
    SkillToolName     *string
    AgentHub          TypedAgentHub[M]
    ModelHub          TypedModelHub[M]

    CustomSystemPrompt    SystemPromptFunc
    CustomToolDescription ToolDescriptionFunc
    CustomToolParams      func(ctx context.Context, defaults map[string]*schema.ParameterInfo) (map[string]*schema.ParameterInfo, error)
    BuildContent          func(ctx context.Context, skill Skill, rawArgs string) (string, error)
    BuildForkMessages     func(ctx context.Context, in TypedSubAgentInput[M]) ([]M, error)
    FormatForkResult      func(ctx context.Context, in TypedSubAgentOutput[M]) (string, error)
}
FieldTypeRequiredDefaultDescription
Backend
Backend
Yes-Skill backend implementation responsible for skill storage and retrieval
SkillToolName
*string
No
"skill"
Skill tool name. Can be customized to avoid conflicts if a tool with the same name already exists
AgentHub
TypedAgentHub[M]
No-Provides Agent instances. Required when using
context: fork
or
fork_with_context
ModelHub
TypedModelHub[M]
No-Provides model instances. In context mode, passed to AgentHub; in inline mode, switches the model used by subsequent ChatModel calls via WrapModel
CustomSystemPrompt
SystemPromptFunc
NoBuilt-in promptCustom system prompt. Signature:
func(ctx, toolName) string
CustomToolDescription
ToolDescriptionFunc
NoBuilt-in descriptionCustom tool description. Signature:
func(ctx, skills []FrontMatter) string
CustomToolParams
func
NoOnly the
skill
parameter
Custom tool parameter schema. Receives default parameters, returns custom parameters;
skill
is always kept as required
BuildContent
func
NoDefault formattingCustom Skill content generation; can inject additional context into the content
BuildForkMessages
func
NoSee belowCustom initial messages passed to the sub-Agent in fork mode. Default:
fork
[UserMessage(content)]
,
fork_with_context
[history..., ToolMessage(content, callID)]
FormatForkResult
func
NoConcatenated contentCustom sub-Agent result formatting. By default, assistant message content is concatenated and returned

NewMiddleware

func NewMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error)

Creates the Skill Middleware, returning adk.ChatModelAgentMiddleware for use in ChatModelAgentConfig.Handlers.

💡 The generic version NewTyped[M](ctx, config) returns adk.TypedChatModelAgentMiddleware[M], which can be used with *schema.AgenticMessage type Agents.

Usage Example

// 1. Create Backend
backend, err := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{
    Backend: fsBackend,
    BaseDir: "/path/to/skills",
})
if err != nil {
    return err
}

// 2. Create Middleware
handler, err := skill.NewMiddleware(ctx, &skill.Config{
    Backend:  backend,
    AgentHub: myAgentHub, // Optional, only needed for fork mode
    ModelHub: myModelHub, // Optional, only needed when using the model field
})
if err != nil {
    return err
}

// 3. Pass to Agent's Handlers
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    // ... other configuration
    Handlers: []adk.ChatModelAgentMiddleware{handler},
})