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:
- Discovery: The Agent only loads each available Skill’s name and description — enough to determine when the Skill might be needed
- Activation: When a task matches a Skill, the Agent loads the full
SKILL.mdcontent into context - 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"`
}
| Field | Type | Description |
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 ModelHubto obtain the model instance |
ContextMode
const (
ContextModeFork ContextMode = "fork" // Isolated context
ContextModeForkWithContext ContextMode = "fork_with_context" // Copy history messages
)
| Mode | Description |
| 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
}
| Field | Type | Description |
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)
}
| Method | Description |
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)
| Field | Type | Required | Description |
Backend | filesystem.Backend | Yes | Filesystem backend implementation used for file operations |
BaseDir | string | Yes | Skill root directory path. Scans first-level subdirectories under this directory to find directories containing SKILL.mdfiles |
How it works:
- Scans first-level subdirectories under
BaseDir - Looks for
SKILL.mdfiles in each subdirectory - Parses YAML frontmatter to obtain metadata
- Deeply nested
SKILL.mdfiles 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.Messagespecializations). Generic versionsTypedAgentHub[M]andTypedModelHub[M]are available for*schema.AgenticMessagescenarios 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.ModelandModelHub.Getismodel.BaseModel[M], not themodel.ToolCallingChatModelfrom 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)
}
| Field | Type | Required | Default | Description |
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: forkor 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 | No | Built-in prompt | Custom system prompt. Signature: func(ctx, toolName) string |
CustomToolDescription | ToolDescriptionFunc | No | Built-in description | Custom tool description. Signature: func(ctx, skills []FrontMatter) string |
CustomToolParams | func | No | Only the skillparameter | Custom tool parameter schema. Receives default parameters, returns custom parameters; skillis always kept as required |
BuildContent | func | No | Default formatting | Custom Skill content generation; can inject additional context into the content |
BuildForkMessages | func | No | See below | Custom initial messages passed to the sub-Agent in fork mode. Default: fork→ [UserMessage(content)], fork_with_context→ [history..., ToolMessage(content, callID)] |
FormatForkResult | func | No | Concatenated content | Custom 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)returnsadk.TypedChatModelAgentMiddleware[M], which can be used with*schema.AgenticMessagetype 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},
})
