Registry¶
The Registry provides central skill registration and discovery. It manages the lifecycle of skills and enables tool lookup across all registered skills.
Registry Interface¶
type Registry interface {
Register(s skill.Skill) error
Unregister(name string) error
Get(name string) (skill.Skill, error)
List() []skill.Skill
ListTools() []skill.Tool
GetTool(fullName string) (skill.Tool, error)
Init(ctx context.Context) error
Close() error
Count() int
}
Creating a Registry¶
Registering Skills¶
// Register a skill
err := reg.Register(&skill.BaseSkill{
SkillName: "math",
SkillDescription: "Mathematical operations",
SkillTools: []skill.Tool{addTool, multiplyTool},
})
if errors.Is(err, registry.ErrSkillExists) {
// Skill with this name already registered
}
Retrieving Skills¶
// Get by name
mathSkill, err := reg.Get("math")
if errors.Is(err, registry.ErrSkillNotFound) {
// Skill not found
}
// List all skills
for _, s := range reg.List() {
fmt.Printf("Skill: %s - %s\n", s.Name(), s.Description())
}
// Count registered skills
count := reg.Count()
Tool Discovery¶
The registry enables tool discovery across all skills:
// List all tools from all skills
tools := reg.ListTools()
for _, t := range tools {
fmt.Printf("Tool: %s\n", t.Name())
}
// Get tool by full name (skill.tool)
tool, err := reg.GetTool("math.add")
// Get tool by short name (searches all skills)
tool, err := reg.GetTool("add")
Lifecycle Management¶
The registry manages skill initialization and cleanup:
// Initialize all registered skills
if err := reg.Init(ctx); err != nil {
log.Fatal("Failed to initialize skills:", err)
}
// Later, clean up all skills
if err := reg.Close(); err != nil {
log.Println("Error closing skills:", err)
}
Init stops at the first error. Close continues through all skills and returns a joined error if any fail.
Auto-Registration with MCP Server¶
Skills registered with an MCP server runtime can automatically register with a registry:
reg := registry.New()
rt := server.New(impl, &server.Options{
Registry: reg, // Enable auto-registration
})
// This registers with BOTH the runtime AND the registry
rt.RegisterSkill(mySkill)
// Verify it's in the registry
s, _ := reg.Get("myskill")
fmt.Println(s.Name()) // "myskill"
Unregistering Skills¶
err := reg.Unregister("math")
if errors.Is(err, registry.ErrSkillNotFound) {
// Skill was not registered
}
Thread Safety¶
The in-memory registry is thread-safe. Multiple goroutines can safely register, unregister, and query skills concurrently.
// Safe to call from multiple goroutines
go func() { reg.Register(skill1) }()
go func() { reg.Register(skill2) }()
go func() { reg.List() }()
Error Handling¶
var (
ErrSkillNotFound = errors.New("skill not found")
ErrSkillExists = errors.New("skill already registered")
)
Use errors.Is() to check error types:
err := reg.Register(skill)
if errors.Is(err, registry.ErrSkillExists) {
// Handle duplicate registration
}
Use Cases¶
1. Agent Tool Discovery¶
// Agent discovers all available tools
tools := reg.ListTools()
toolDescriptions := make([]string, len(tools))
for i, t := range tools {
toolDescriptions[i] = fmt.Sprintf("%s: %s", t.Name(), t.Description())
}
// Send to LLM as available tools
2. Dynamic Skill Loading¶
// Load skills based on configuration
for _, cfg := range config.Skills {
skill := createSkillFromConfig(cfg)
reg.Register(skill)
}
// Initialize all at once
reg.Init(ctx)
3. Graceful Shutdown¶
// On shutdown signal
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
// Close all skills
if err := reg.Close(); err != nil {
log.Printf("Warning: error closing skills: %v", err)
}