feat(skills): enhance agent skills with additional tags and commands
Some checks failed
CI / build-and-test (push) Failing after -29m56s

- Added language_tags, library_tags, framework_tags, and domain_tags to agent skills.
- Introduced new commands: get_skill and get_guardrail for fetching specific skills and guardrails.
- Updated database schema and migration scripts to accommodate new fields.
- Enhanced skill listing functionality to support filtering by new tags.
- Improved error handling and response structures for skill-related operations.
This commit is contained in:
2026-05-05 09:24:58 +02:00
parent 1f671dad54
commit 1ceb317f4b
9 changed files with 364 additions and 67 deletions

View File

@@ -23,10 +23,14 @@ func NewSkillsTool(db *store.DB, sessions *session.ActiveProjects) *SkillsTool {
// add_skill
type AddSkillInput struct {
Name string `json:"name" jsonschema:"unique skill name"`
Description string `json:"description,omitempty" jsonschema:"short description of what the skill does"`
Content string `json:"content" jsonschema:"the full skill instruction or prompt content"`
Tags []string `json:"tags,omitempty" jsonschema:"optional tags for grouping or filtering"`
Name string `json:"name" jsonschema:"unique skill name"`
Description string `json:"description,omitempty" jsonschema:"short description of what the skill does"`
Content string `json:"content" jsonschema:"the full skill instruction or prompt content"`
Tags []string `json:"tags,omitempty" jsonschema:"general tags for grouping or filtering"`
LanguageTags []string `json:"language_tags,omitempty" jsonschema:"programming languages this skill applies to (e.g. go, python, typescript)"`
LibraryTags []string `json:"library_tags,omitempty" jsonschema:"libraries or packages this skill applies to (e.g. bunrouter, pgx, axios)"`
FrameworkTags []string `json:"framework_tags,omitempty" jsonschema:"frameworks this skill applies to (e.g. gin, nextjs, django)"`
DomainTags []string `json:"domain_tags,omitempty" jsonschema:"topic or problem domains this skill applies to (e.g. auth, testing, migrations)"`
}
type AddSkillOutput struct {
@@ -44,10 +48,14 @@ func (t *SkillsTool) AddSkill(ctx context.Context, _ *mcp.CallToolRequest, in Ad
in.Tags = []string{}
}
skill, err := t.store.AddSkill(ctx, ext.AgentSkill{
Name: strings.TrimSpace(in.Name),
Description: strings.TrimSpace(in.Description),
Content: strings.TrimSpace(in.Content),
Tags: in.Tags,
Name: strings.TrimSpace(in.Name),
Description: strings.TrimSpace(in.Description),
Content: strings.TrimSpace(in.Content),
Tags: in.Tags,
LanguageTags: in.LanguageTags,
LibraryTags: in.LibraryTags,
FrameworkTags: in.FrameworkTags,
DomainTags: in.DomainTags,
})
if err != nil {
return nil, AddSkillOutput{}, err
@@ -72,10 +80,41 @@ func (t *SkillsTool) RemoveSkill(ctx context.Context, _ *mcp.CallToolRequest, in
return nil, RemoveSkillOutput{Removed: true}, nil
}
// get_skill
type GetSkillInput struct {
ID int64 `json:"id,omitempty" jsonschema:"skill id (preferred; use instead of name when available)"`
Name string `json:"name,omitempty" jsonschema:"skill name (alternative to id)"`
}
type GetSkillOutput struct {
Skill ext.AgentSkill `json:"skill"`
}
func (t *SkillsTool) GetSkill(ctx context.Context, _ *mcp.CallToolRequest, in GetSkillInput) (*mcp.CallToolResult, GetSkillOutput, error) {
var (
skill ext.AgentSkill
err error
)
switch {
case in.ID != 0:
skill, err = t.store.GetSkill(ctx, in.ID)
case strings.TrimSpace(in.Name) != "":
skill, err = t.store.GetSkillByName(ctx, strings.TrimSpace(in.Name))
default:
return nil, GetSkillOutput{}, errRequiredField("id or name")
}
if err != nil {
return nil, GetSkillOutput{}, err
}
return nil, GetSkillOutput{Skill: skill}, nil
}
// list_skills
type ListSkillsInput struct {
Tag string `json:"tag,omitempty" jsonschema:"filter by tag"`
Tag string `json:"tag,omitempty" jsonschema:"filter by tag (searches all tag fields: tags, language_tags, library_tags, framework_tags, domain_tags)"`
IncludeContent bool `json:"include_content,omitempty" jsonschema:"include full skill content in results (default false — use get_skill to load content for a specific skill)"`
}
type ListSkillsOutput struct {
@@ -90,6 +129,11 @@ func (t *SkillsTool) ListSkills(ctx context.Context, _ *mcp.CallToolRequest, in
if skills == nil {
skills = []ext.AgentSkill{}
}
if !in.IncludeContent {
for i := range skills {
skills[i].Content = ""
}
}
return nil, ListSkillsOutput{Skills: skills}, nil
}
@@ -182,6 +226,36 @@ func (t *SkillsTool) ListGuardrails(ctx context.Context, _ *mcp.CallToolRequest,
return nil, ListGuardrailsOutput{Guardrails: guardrails}, nil
}
// get_guardrail
type GetGuardrailInput struct {
ID int64 `json:"id,omitempty" jsonschema:"guardrail id (preferred; use instead of name when available)"`
Name string `json:"name,omitempty" jsonschema:"guardrail name (alternative to id)"`
}
type GetGuardrailOutput struct {
Guardrail ext.AgentGuardrail `json:"guardrail"`
}
func (t *SkillsTool) GetGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in GetGuardrailInput) (*mcp.CallToolResult, GetGuardrailOutput, error) {
var (
g ext.AgentGuardrail
err error
)
switch {
case in.ID != 0:
g, err = t.store.GetGuardrail(ctx, in.ID)
case strings.TrimSpace(in.Name) != "":
g, err = t.store.GetGuardrailByName(ctx, strings.TrimSpace(in.Name))
default:
return nil, GetGuardrailOutput{}, errRequiredField("id or name")
}
if err != nil {
return nil, GetGuardrailOutput{}, err
}
return nil, GetGuardrailOutput{Guardrail: g}, nil
}
// add_project_skill
type AddProjectSkillInput struct {