package tools import ( "context" "strings" "github.com/modelcontextprotocol/go-sdk/mcp" "git.warky.dev/wdevs/amcs/internal/store" ext "git.warky.dev/wdevs/amcs/internal/types" ) type AgentPersonasTool struct { store *store.DB } func NewAgentPersonasTool(db *store.DB) *AgentPersonasTool { return &AgentPersonasTool{store: db} } // ────────────────────────────────────────────── // Personas // ────────────────────────────────────────────── // create_agent_persona type CreatePersonaInput struct { Name string `json:"name" jsonschema:"unique persona name — used as the load key"` Description string `json:"description,omitempty" jsonschema:"short description of the persona"` Summary string `json:"summary" jsonschema:"concise behaviour summary, returned by default on load"` Detail string `json:"detail,omitempty" jsonschema:"full behaviour detail, returned only when detail=true"` Tags []string `json:"tags,omitempty" jsonschema:"optional tags for grouping or filtering"` } type CreatePersonaOutput struct { Persona ext.Persona `json:"persona"` } func (t *AgentPersonasTool) CreatePersona(ctx context.Context, _ *mcp.CallToolRequest, in CreatePersonaInput) (*mcp.CallToolResult, CreatePersonaOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, CreatePersonaOutput{}, errRequiredField("name") } if strings.TrimSpace(in.Summary) == "" { return nil, CreatePersonaOutput{}, errRequiredField("summary") } if in.Tags == nil { in.Tags = []string{} } persona, err := t.store.CreatePersona(ctx, ext.Persona{ Name: strings.TrimSpace(in.Name), Description: strings.TrimSpace(in.Description), Summary: strings.TrimSpace(in.Summary), Detail: strings.TrimSpace(in.Detail), Tags: in.Tags, }) if err != nil { return nil, CreatePersonaOutput{}, err } return nil, CreatePersonaOutput{Persona: persona}, nil } // update_agent_persona type UpdatePersonaInput struct { Name string `json:"name" jsonschema:"name of the persona to update"` NewName string `json:"new_name,omitempty" jsonschema:"rename the persona"` Description *string `json:"description,omitempty" jsonschema:"update description"` Summary *string `json:"summary,omitempty" jsonschema:"update summary"` Detail *string `json:"detail,omitempty" jsonschema:"update detail"` Tags []string `json:"tags,omitempty" jsonschema:"replace tags"` } type UpdatePersonaOutput struct { Persona ext.Persona `json:"persona"` } func (t *AgentPersonasTool) UpdatePersona(ctx context.Context, _ *mcp.CallToolRequest, in UpdatePersonaInput) (*mcp.CallToolResult, UpdatePersonaOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, UpdatePersonaOutput{}, errRequiredField("name") } updates := map[string]any{} if n := strings.TrimSpace(in.NewName); n != "" { updates["name"] = n } if in.Description != nil { updates["description"] = strings.TrimSpace(*in.Description) } if in.Summary != nil { updates["summary"] = strings.TrimSpace(*in.Summary) } if in.Detail != nil { updates["detail"] = strings.TrimSpace(*in.Detail) } if in.Tags != nil { updates["tags"] = in.Tags } persona, err := t.store.UpdatePersona(ctx, strings.TrimSpace(in.Name), updates) if err != nil { return nil, UpdatePersonaOutput{}, err } return nil, UpdatePersonaOutput{Persona: persona}, nil } // delete_agent_persona type DeletePersonaInput struct { Name string `json:"name" jsonschema:"name of the persona to delete"` } type DeletePersonaOutput struct { Deleted bool `json:"deleted"` } func (t *AgentPersonasTool) DeletePersona(ctx context.Context, _ *mcp.CallToolRequest, in DeletePersonaInput) (*mcp.CallToolResult, DeletePersonaOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, DeletePersonaOutput{}, errRequiredField("name") } if err := t.store.DeletePersona(ctx, strings.TrimSpace(in.Name)); err != nil { return nil, DeletePersonaOutput{}, err } return nil, DeletePersonaOutput{Deleted: true}, nil } // list_agent_personas type ListPersonasInput struct { Tag string `json:"tag,omitempty" jsonschema:"filter by tag"` } type ListPersonasOutput struct { Personas []ext.Persona `json:"personas"` } func (t *AgentPersonasTool) ListPersonas(ctx context.Context, _ *mcp.CallToolRequest, in ListPersonasInput) (*mcp.CallToolResult, ListPersonasOutput, error) { personas, err := t.store.ListPersonas(ctx, in.Tag) if err != nil { return nil, ListPersonasOutput{}, err } if personas == nil { personas = []ext.Persona{} } return nil, ListPersonasOutput{Personas: personas}, nil } // get_agent_persona type GetPersonaInput struct { Name string `json:"name" jsonschema:"persona name to load"` Detail bool `json:"detail,omitempty" jsonschema:"when true, return full part content and persona detail instead of summaries"` Overrides map[string]string `json:"overrides,omitempty" jsonschema:"runtime part substitutions: {part_type: part_name}. Replaces all parts of that type without modifying the persona."` } type GetPersonaOutput struct { Persona ext.PersonaFull `json:"persona"` } func (t *AgentPersonasTool) GetPersona(ctx context.Context, _ *mcp.CallToolRequest, in GetPersonaInput) (*mcp.CallToolResult, GetPersonaOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, GetPersonaOutput{}, errRequiredField("name") } persona, err := t.store.GetPersona(ctx, strings.TrimSpace(in.Name), in.Detail, in.Overrides) if err != nil { return nil, GetPersonaOutput{}, err } return nil, GetPersonaOutput{Persona: persona}, nil } // get_persona_manifest type GetPersonaManifestInput struct { Name string `json:"name" jsonschema:"persona name"` } type GetPersonaManifestOutput struct { Manifest ext.PersonaManifest `json:"manifest"` } func (t *AgentPersonasTool) GetPersonaManifest(ctx context.Context, _ *mcp.CallToolRequest, in GetPersonaManifestInput) (*mcp.CallToolResult, GetPersonaManifestOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, GetPersonaManifestOutput{}, errRequiredField("name") } manifest, err := t.store.GetPersonaManifest(ctx, strings.TrimSpace(in.Name)) if err != nil { return nil, GetPersonaManifestOutput{}, err } return nil, GetPersonaManifestOutput{Manifest: manifest}, nil } // compile_persona type CompilePersonaInput struct { Name string `json:"name" jsonschema:"persona name to compile"` } type CompilePersonaOutput struct { Persona ext.Persona `json:"persona"` } func (t *AgentPersonasTool) CompilePersona(ctx context.Context, _ *mcp.CallToolRequest, in CompilePersonaInput) (*mcp.CallToolResult, CompilePersonaOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, CompilePersonaOutput{}, errRequiredField("name") } persona, err := t.store.CompilePersona(ctx, strings.TrimSpace(in.Name)) if err != nil { return nil, CompilePersonaOutput{}, err } return nil, CompilePersonaOutput{Persona: persona}, nil } // ────────────────────────────────────────────── // Parts // ────────────────────────────────────────────── // create_agent_part type CreatePartInput struct { Name string `json:"name" jsonschema:"globally unique part name — used as the override key"` PartType string `json:"part_type" jsonschema:"one of: system, agent, soul, identity, skill, specialization, tone, goal, context, protocol, backstory, motivation, voice, archetype, flaw, relationship"` Description string `json:"description,omitempty" jsonschema:"short description of what this part does"` Summary string `json:"summary" jsonschema:"concise version, used in summary mode"` Content string `json:"content,omitempty" jsonschema:"full content, used in detail mode"` Tags []string `json:"tags,omitempty" jsonschema:"optional tags"` } type CreatePartOutput struct { Part ext.Part `json:"part"` } func (t *AgentPersonasTool) CreatePart(ctx context.Context, _ *mcp.CallToolRequest, in CreatePartInput) (*mcp.CallToolResult, CreatePartOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, CreatePartOutput{}, errRequiredField("name") } if strings.TrimSpace(in.PartType) == "" { return nil, CreatePartOutput{}, errRequiredField("part_type") } if strings.TrimSpace(in.Summary) == "" { return nil, CreatePartOutput{}, errRequiredField("summary") } if in.Tags == nil { in.Tags = []string{} } part, err := t.store.CreatePart(ctx, ext.Part{ Name: strings.TrimSpace(in.Name), PartType: strings.TrimSpace(in.PartType), Description: strings.TrimSpace(in.Description), Summary: strings.TrimSpace(in.Summary), Content: strings.TrimSpace(in.Content), Tags: in.Tags, }) if err != nil { return nil, CreatePartOutput{}, err } return nil, CreatePartOutput{Part: part}, nil } // update_agent_part type UpdatePartInput struct { Name string `json:"name" jsonschema:"name of the part to update"` NewName string `json:"new_name,omitempty" jsonschema:"rename the part"` PartType *string `json:"part_type,omitempty" jsonschema:"update part type"` Description *string `json:"description,omitempty" jsonschema:"update description"` Summary *string `json:"summary,omitempty" jsonschema:"update summary"` Content *string `json:"content,omitempty" jsonschema:"update content"` Tags []string `json:"tags,omitempty" jsonschema:"replace tags"` } type UpdatePartOutput struct { Part ext.Part `json:"part"` } func (t *AgentPersonasTool) UpdatePart(ctx context.Context, _ *mcp.CallToolRequest, in UpdatePartInput) (*mcp.CallToolResult, UpdatePartOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, UpdatePartOutput{}, errRequiredField("name") } updates := map[string]any{} if n := strings.TrimSpace(in.NewName); n != "" { updates["name"] = n } if in.PartType != nil { updates["part_type"] = strings.TrimSpace(*in.PartType) } if in.Description != nil { updates["description"] = strings.TrimSpace(*in.Description) } if in.Summary != nil { updates["summary"] = strings.TrimSpace(*in.Summary) } if in.Content != nil { updates["content"] = strings.TrimSpace(*in.Content) } if in.Tags != nil { updates["tags"] = in.Tags } part, err := t.store.UpdatePart(ctx, strings.TrimSpace(in.Name), updates) if err != nil { return nil, UpdatePartOutput{}, err } return nil, UpdatePartOutput{Part: part}, nil } // delete_agent_part type DeletePartInput struct { Name string `json:"name" jsonschema:"name of the part to delete"` } type DeletePartOutput struct { Deleted bool `json:"deleted"` } func (t *AgentPersonasTool) DeletePart(ctx context.Context, _ *mcp.CallToolRequest, in DeletePartInput) (*mcp.CallToolResult, DeletePartOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, DeletePartOutput{}, errRequiredField("name") } if err := t.store.DeletePart(ctx, strings.TrimSpace(in.Name)); err != nil { return nil, DeletePartOutput{}, err } return nil, DeletePartOutput{Deleted: true}, nil } // list_agent_parts type ListPartsInput struct { PartType string `json:"part_type,omitempty" jsonschema:"filter by part type"` Tag string `json:"tag,omitempty" jsonschema:"filter by tag"` } type ListPartsOutput struct { Parts []ext.Part `json:"parts"` } func (t *AgentPersonasTool) ListParts(ctx context.Context, _ *mcp.CallToolRequest, in ListPartsInput) (*mcp.CallToolResult, ListPartsOutput, error) { parts, err := t.store.ListParts(ctx, in.PartType, in.Tag) if err != nil { return nil, ListPartsOutput{}, err } if parts == nil { parts = []ext.Part{} } return nil, ListPartsOutput{Parts: parts}, nil } // get_agent_part type GetPartInput struct { Name string `json:"name" jsonschema:"part name"` } type GetPartOutput struct { Part ext.Part `json:"part"` } func (t *AgentPersonasTool) GetPart(ctx context.Context, _ *mcp.CallToolRequest, in GetPartInput) (*mcp.CallToolResult, GetPartOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, GetPartOutput{}, errRequiredField("name") } part, err := t.store.GetPartByName(ctx, strings.TrimSpace(in.Name)) if err != nil { return nil, GetPartOutput{}, err } return nil, GetPartOutput{Part: part}, nil } // ────────────────────────────────────────────── // Persona-Part / Skill / Guardrail links // ────────────────────────────────────────────── // add_persona_part type AddPersonaPartInput struct { Persona string `json:"persona" jsonschema:"persona name"` Part string `json:"part" jsonschema:"part name to link"` Order int `json:"order,omitempty" jsonschema:"assembly order (lower first, default 0)"` Priority int `json:"priority,omitempty" jsonschema:"context-budget priority (higher loads first when trimming)"` } type AddPersonaPartOutput struct { Persona string `json:"persona"` Part string `json:"part"` } func (t *AgentPersonasTool) AddPersonaPart(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaPartInput) (*mcp.CallToolResult, AddPersonaPartOutput, error) { if strings.TrimSpace(in.Persona) == "" { return nil, AddPersonaPartOutput{}, errRequiredField("persona") } if strings.TrimSpace(in.Part) == "" { return nil, AddPersonaPartOutput{}, errRequiredField("part") } if err := t.store.AddPersonaPart(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Part), in.Order, in.Priority); err != nil { return nil, AddPersonaPartOutput{}, err } return nil, AddPersonaPartOutput{Persona: in.Persona, Part: in.Part}, nil } // remove_persona_part type RemovePersonaPartInput struct { Persona string `json:"persona" jsonschema:"persona name"` Part string `json:"part" jsonschema:"part name to unlink"` } type RemovePersonaPartOutput struct { Removed bool `json:"removed"` } func (t *AgentPersonasTool) RemovePersonaPart(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaPartInput) (*mcp.CallToolResult, RemovePersonaPartOutput, error) { if strings.TrimSpace(in.Persona) == "" { return nil, RemovePersonaPartOutput{}, errRequiredField("persona") } if strings.TrimSpace(in.Part) == "" { return nil, RemovePersonaPartOutput{}, errRequiredField("part") } if err := t.store.RemovePersonaPart(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Part)); err != nil { return nil, RemovePersonaPartOutput{}, err } return nil, RemovePersonaPartOutput{Removed: true}, nil } // add_persona_skill type AddPersonaSkillInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` SkillID int64 `json:"skill_id" jsonschema:"agent skill id to link"` } type AddPersonaSkillOutput struct { PersonaID int64 `json:"persona_id"` SkillID int64 `json:"skill_id"` } func (t *AgentPersonasTool) AddPersonaSkill(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaSkillInput) (*mcp.CallToolResult, AddPersonaSkillOutput, error) { if err := t.store.AddPersonaSkill(ctx, in.PersonaID, in.SkillID); err != nil { return nil, AddPersonaSkillOutput{}, err } return nil, AddPersonaSkillOutput{PersonaID: in.PersonaID, SkillID: in.SkillID}, nil } // remove_persona_skill type RemovePersonaSkillInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` SkillID int64 `json:"skill_id" jsonschema:"agent skill id to unlink"` } type RemovePersonaSkillOutput struct { Removed bool `json:"removed"` } func (t *AgentPersonasTool) RemovePersonaSkill(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaSkillInput) (*mcp.CallToolResult, RemovePersonaSkillOutput, error) { if err := t.store.RemovePersonaSkill(ctx, in.PersonaID, in.SkillID); err != nil { return nil, RemovePersonaSkillOutput{}, err } return nil, RemovePersonaSkillOutput{Removed: true}, nil } // add_persona_guardrail type AddPersonaGuardrailInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` GuardrailID int64 `json:"guardrail_id" jsonschema:"agent guardrail id to link"` } type AddPersonaGuardrailOutput struct { PersonaID int64 `json:"persona_id"` GuardrailID int64 `json:"guardrail_id"` } func (t *AgentPersonasTool) AddPersonaGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaGuardrailInput) (*mcp.CallToolResult, AddPersonaGuardrailOutput, error) { if err := t.store.AddPersonaGuardrail(ctx, in.PersonaID, in.GuardrailID); err != nil { return nil, AddPersonaGuardrailOutput{}, err } return nil, AddPersonaGuardrailOutput{PersonaID: in.PersonaID, GuardrailID: in.GuardrailID}, nil } // remove_persona_guardrail type RemovePersonaGuardrailInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` GuardrailID int64 `json:"guardrail_id" jsonschema:"agent guardrail id to unlink"` } type RemovePersonaGuardrailOutput struct { Removed bool `json:"removed"` } func (t *AgentPersonasTool) RemovePersonaGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaGuardrailInput) (*mcp.CallToolResult, RemovePersonaGuardrailOutput, error) { if err := t.store.RemovePersonaGuardrail(ctx, in.PersonaID, in.GuardrailID); err != nil { return nil, RemovePersonaGuardrailOutput{}, err } return nil, RemovePersonaGuardrailOutput{Removed: true}, nil } // ────────────────────────────────────────────── // Traits // ────────────────────────────────────────────── // create_agent_trait type CreateTraitInput struct { Name string `json:"name" jsonschema:"globally unique trait name"` TraitType string `json:"trait_type" jsonschema:"one of: personality, cognitive, emotional, social, behavioral"` Description string `json:"description,omitempty" jsonschema:"short description of this trait"` Instruction string `json:"instruction,omitempty" jsonschema:"how to apply this trait in practice"` Tags []string `json:"tags,omitempty" jsonschema:"optional tags"` } type CreateTraitOutput struct { Trait ext.Trait `json:"trait"` } func (t *AgentPersonasTool) CreateTrait(ctx context.Context, _ *mcp.CallToolRequest, in CreateTraitInput) (*mcp.CallToolResult, CreateTraitOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, CreateTraitOutput{}, errRequiredField("name") } if strings.TrimSpace(in.TraitType) == "" { return nil, CreateTraitOutput{}, errRequiredField("trait_type") } if in.Tags == nil { in.Tags = []string{} } trait, err := t.store.CreateTrait(ctx, ext.Trait{ Name: strings.TrimSpace(in.Name), TraitType: strings.TrimSpace(in.TraitType), Description: strings.TrimSpace(in.Description), Instruction: strings.TrimSpace(in.Instruction), Tags: in.Tags, }) if err != nil { return nil, CreateTraitOutput{}, err } return nil, CreateTraitOutput{Trait: trait}, nil } // update_agent_trait type UpdateTraitInput struct { Name string `json:"name" jsonschema:"name of the trait to update"` NewName string `json:"new_name,omitempty" jsonschema:"rename the trait"` TraitType *string `json:"trait_type,omitempty" jsonschema:"update trait type"` Description *string `json:"description,omitempty" jsonschema:"update description"` Instruction *string `json:"instruction,omitempty" jsonschema:"update instruction"` Tags []string `json:"tags,omitempty" jsonschema:"replace tags"` } type UpdateTraitOutput struct { Trait ext.Trait `json:"trait"` } func (t *AgentPersonasTool) UpdateTrait(ctx context.Context, _ *mcp.CallToolRequest, in UpdateTraitInput) (*mcp.CallToolResult, UpdateTraitOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, UpdateTraitOutput{}, errRequiredField("name") } updates := map[string]any{} if n := strings.TrimSpace(in.NewName); n != "" { updates["name"] = n } if in.TraitType != nil { updates["trait_type"] = strings.TrimSpace(*in.TraitType) } if in.Description != nil { updates["description"] = strings.TrimSpace(*in.Description) } if in.Instruction != nil { updates["instruction"] = strings.TrimSpace(*in.Instruction) } if in.Tags != nil { updates["tags"] = in.Tags } trait, err := t.store.UpdateTrait(ctx, strings.TrimSpace(in.Name), updates) if err != nil { return nil, UpdateTraitOutput{}, err } return nil, UpdateTraitOutput{Trait: trait}, nil } // delete_agent_trait type DeleteTraitInput struct { Name string `json:"name" jsonschema:"name of the trait to delete"` } type DeleteTraitOutput struct { Deleted bool `json:"deleted"` } func (t *AgentPersonasTool) DeleteTrait(ctx context.Context, _ *mcp.CallToolRequest, in DeleteTraitInput) (*mcp.CallToolResult, DeleteTraitOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, DeleteTraitOutput{}, errRequiredField("name") } if err := t.store.DeleteTrait(ctx, strings.TrimSpace(in.Name)); err != nil { return nil, DeleteTraitOutput{}, err } return nil, DeleteTraitOutput{Deleted: true}, nil } // list_agent_traits type ListTraitsInput struct { TraitType string `json:"trait_type,omitempty" jsonschema:"filter by trait type"` Tag string `json:"tag,omitempty" jsonschema:"filter by tag"` } type ListTraitsOutput struct { Traits []ext.Trait `json:"traits"` } func (t *AgentPersonasTool) ListTraits(ctx context.Context, _ *mcp.CallToolRequest, in ListTraitsInput) (*mcp.CallToolResult, ListTraitsOutput, error) { traits, err := t.store.ListTraits(ctx, in.TraitType, in.Tag) if err != nil { return nil, ListTraitsOutput{}, err } if traits == nil { traits = []ext.Trait{} } return nil, ListTraitsOutput{Traits: traits}, nil } // get_agent_trait type GetTraitInput struct { Name string `json:"name" jsonschema:"trait name"` } type GetTraitOutput struct { Trait ext.Trait `json:"trait"` } func (t *AgentPersonasTool) GetTrait(ctx context.Context, _ *mcp.CallToolRequest, in GetTraitInput) (*mcp.CallToolResult, GetTraitOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, GetTraitOutput{}, errRequiredField("name") } trait, err := t.store.GetTraitByName(ctx, strings.TrimSpace(in.Name)) if err != nil { return nil, GetTraitOutput{}, err } return nil, GetTraitOutput{Trait: trait}, nil } // add_persona_trait type AddPersonaTraitInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` TraitID int64 `json:"trait_id" jsonschema:"agent trait id to link"` } type AddPersonaTraitOutput struct { PersonaID int64 `json:"persona_id"` TraitID int64 `json:"trait_id"` } func (t *AgentPersonasTool) AddPersonaTrait(ctx context.Context, _ *mcp.CallToolRequest, in AddPersonaTraitInput) (*mcp.CallToolResult, AddPersonaTraitOutput, error) { if err := t.store.AddPersonaTrait(ctx, in.PersonaID, in.TraitID); err != nil { return nil, AddPersonaTraitOutput{}, err } return nil, AddPersonaTraitOutput{PersonaID: in.PersonaID, TraitID: in.TraitID}, nil } // remove_persona_trait type RemovePersonaTraitInput struct { PersonaID int64 `json:"persona_id" jsonschema:"persona id"` TraitID int64 `json:"trait_id" jsonschema:"agent trait id to unlink"` } type RemovePersonaTraitOutput struct { Removed bool `json:"removed"` } func (t *AgentPersonasTool) RemovePersonaTrait(ctx context.Context, _ *mcp.CallToolRequest, in RemovePersonaTraitInput) (*mcp.CallToolResult, RemovePersonaTraitOutput, error) { if err := t.store.RemovePersonaTrait(ctx, in.PersonaID, in.TraitID); err != nil { return nil, RemovePersonaTraitOutput{}, err } return nil, RemovePersonaTraitOutput{Removed: true}, nil } // ────────────────────────────────────────────── // Character Arcs // ────────────────────────────────────────────── // create_character_arc type CreateCharacterArcInput struct { Name string `json:"name" jsonschema:"unique arc name"` Description string `json:"description,omitempty" jsonschema:"description of the arc"` Summary string `json:"summary,omitempty" jsonschema:"brief arc summary"` } type CreateCharacterArcOutput struct { Arc ext.CharacterArc `json:"arc"` } func (t *AgentPersonasTool) CreateCharacterArc(ctx context.Context, _ *mcp.CallToolRequest, in CreateCharacterArcInput) (*mcp.CallToolResult, CreateCharacterArcOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, CreateCharacterArcOutput{}, errRequiredField("name") } arc, err := t.store.CreateCharacterArc(ctx, ext.CharacterArc{ Name: strings.TrimSpace(in.Name), Description: strings.TrimSpace(in.Description), Summary: strings.TrimSpace(in.Summary), }) if err != nil { return nil, CreateCharacterArcOutput{}, err } return nil, CreateCharacterArcOutput{Arc: arc}, nil } // list_character_arcs type ListCharacterArcsInput struct{} type ListCharacterArcsOutput struct { Arcs []ext.CharacterArc `json:"arcs"` } func (t *AgentPersonasTool) ListCharacterArcs(ctx context.Context, _ *mcp.CallToolRequest, _ ListCharacterArcsInput) (*mcp.CallToolResult, ListCharacterArcsOutput, error) { arcs, err := t.store.ListCharacterArcs(ctx) if err != nil { return nil, ListCharacterArcsOutput{}, err } if arcs == nil { arcs = []ext.CharacterArc{} } return nil, ListCharacterArcsOutput{Arcs: arcs}, nil } // add_arc_stage type AddArcStageInput struct { Arc string `json:"arc" jsonschema:"arc name"` Name string `json:"name" jsonschema:"stage name"` StageOrder int `json:"stage_order,omitempty" jsonschema:"position in arc sequence (lower first)"` Description string `json:"description,omitempty" jsonschema:"what happens at this stage"` Condition string `json:"condition,omitempty" jsonschema:"trigger condition description (evaluated externally)"` } type AddArcStageOutput struct { Stage ext.ArcStage `json:"stage"` } func (t *AgentPersonasTool) AddArcStage(ctx context.Context, _ *mcp.CallToolRequest, in AddArcStageInput) (*mcp.CallToolResult, AddArcStageOutput, error) { if strings.TrimSpace(in.Arc) == "" { return nil, AddArcStageOutput{}, errRequiredField("arc") } if strings.TrimSpace(in.Name) == "" { return nil, AddArcStageOutput{}, errRequiredField("name") } stage, err := t.store.AddArcStage(ctx, strings.TrimSpace(in.Arc), ext.ArcStage{ Name: strings.TrimSpace(in.Name), StageOrder: in.StageOrder, Description: strings.TrimSpace(in.Description), Condition: strings.TrimSpace(in.Condition), }) if err != nil { return nil, AddArcStageOutput{}, err } return nil, AddArcStageOutput{Stage: stage}, nil } // add_stage_part type AddStagePartInput struct { StageID int64 `json:"stage_id" jsonschema:"arc stage id"` PartName string `json:"part_name" jsonschema:"part name to link"` } type AddStagePartOutput struct { StageID int64 `json:"stage_id"` PartName string `json:"part_name"` } func (t *AgentPersonasTool) AddStagePart(ctx context.Context, _ *mcp.CallToolRequest, in AddStagePartInput) (*mcp.CallToolResult, AddStagePartOutput, error) { if strings.TrimSpace(in.PartName) == "" { return nil, AddStagePartOutput{}, errRequiredField("part_name") } if err := t.store.AddStagePart(ctx, in.StageID, strings.TrimSpace(in.PartName)); err != nil { return nil, AddStagePartOutput{}, err } return nil, AddStagePartOutput{StageID: in.StageID, PartName: in.PartName}, nil } // remove_stage_part type RemoveStagePartInput struct { StageID int64 `json:"stage_id" jsonschema:"arc stage id"` PartName string `json:"part_name" jsonschema:"part name to unlink"` } type RemoveStagePartOutput struct { Removed bool `json:"removed"` } func (t *AgentPersonasTool) RemoveStagePart(ctx context.Context, _ *mcp.CallToolRequest, in RemoveStagePartInput) (*mcp.CallToolResult, RemoveStagePartOutput, error) { if strings.TrimSpace(in.PartName) == "" { return nil, RemoveStagePartOutput{}, errRequiredField("part_name") } if err := t.store.RemoveStagePart(ctx, in.StageID, strings.TrimSpace(in.PartName)); err != nil { return nil, RemoveStagePartOutput{}, err } return nil, RemoveStagePartOutput{Removed: true}, nil } // assign_persona_arc type AssignPersonaArcInput struct { Persona string `json:"persona" jsonschema:"persona name"` Arc string `json:"arc" jsonschema:"arc name to assign"` StartStage string `json:"start_stage" jsonschema:"name of the starting stage"` } type AssignPersonaArcOutput struct { Persona string `json:"persona"` Arc string `json:"arc"` Stage string `json:"stage"` } func (t *AgentPersonasTool) AssignPersonaArc(ctx context.Context, _ *mcp.CallToolRequest, in AssignPersonaArcInput) (*mcp.CallToolResult, AssignPersonaArcOutput, error) { if strings.TrimSpace(in.Persona) == "" { return nil, AssignPersonaArcOutput{}, errRequiredField("persona") } if strings.TrimSpace(in.Arc) == "" { return nil, AssignPersonaArcOutput{}, errRequiredField("arc") } if strings.TrimSpace(in.StartStage) == "" { return nil, AssignPersonaArcOutput{}, errRequiredField("start_stage") } if err := t.store.AssignPersonaArc(ctx, strings.TrimSpace(in.Persona), strings.TrimSpace(in.Arc), strings.TrimSpace(in.StartStage)); err != nil { return nil, AssignPersonaArcOutput{}, err } return nil, AssignPersonaArcOutput{Persona: in.Persona, Arc: in.Arc, Stage: in.StartStage}, nil } // advance_persona_stage type AdvancePersonaStageInput struct { Persona string `json:"persona" jsonschema:"persona name"` } type AdvancePersonaStageOutput struct { Stage ext.ArcStage `json:"stage"` } func (t *AgentPersonasTool) AdvancePersonaStage(ctx context.Context, _ *mcp.CallToolRequest, in AdvancePersonaStageInput) (*mcp.CallToolResult, AdvancePersonaStageOutput, error) { if strings.TrimSpace(in.Persona) == "" { return nil, AdvancePersonaStageOutput{}, errRequiredField("persona") } stage, err := t.store.AdvancePersonaStage(ctx, strings.TrimSpace(in.Persona)) if err != nil { return nil, AdvancePersonaStageOutput{}, err } return nil, AdvancePersonaStageOutput{Stage: stage}, nil } // reset_persona_stage type ResetPersonaStageInput struct { Persona string `json:"persona" jsonschema:"persona name"` } type ResetPersonaStageOutput struct { Stage ext.ArcStage `json:"stage"` } func (t *AgentPersonasTool) ResetPersonaStage(ctx context.Context, _ *mcp.CallToolRequest, in ResetPersonaStageInput) (*mcp.CallToolResult, ResetPersonaStageOutput, error) { if strings.TrimSpace(in.Persona) == "" { return nil, ResetPersonaStageOutput{}, errRequiredField("persona") } stage, err := t.store.ResetPersonaStage(ctx, strings.TrimSpace(in.Persona)) if err != nil { return nil, ResetPersonaStageOutput{}, err } return nil, ResetPersonaStageOutput{Stage: stage}, nil }