package tools import ( "context" "log/slog" "strings" "time" "github.com/modelcontextprotocol/go-sdk/mcp" "git.warky.dev/wdevs/amcs/internal/ai" "git.warky.dev/wdevs/amcs/internal/config" "git.warky.dev/wdevs/amcs/internal/metadata" "git.warky.dev/wdevs/amcs/internal/store" thoughttypes "git.warky.dev/wdevs/amcs/internal/types" ) type UpdateTool struct { store *store.DB provider ai.Provider capture config.CaptureConfig log *slog.Logger } type UpdateInput struct { ID string `json:"id" jsonschema:"the thought id"` Content *string `json:"content,omitempty" jsonschema:"replacement content for the thought"` Metadata thoughttypes.ThoughtMetadata `json:"metadata,omitempty" jsonschema:"metadata fields to merge into the thought"` Project string `json:"project,omitempty" jsonschema:"optional project name or id to move the thought into"` } type UpdateOutput struct { Thought thoughttypes.Thought `json:"thought"` } func NewUpdateTool(db *store.DB, provider ai.Provider, capture config.CaptureConfig, log *slog.Logger) *UpdateTool { return &UpdateTool{store: db, provider: provider, capture: capture, log: log} } func (t *UpdateTool) Handle(ctx context.Context, _ *mcp.CallToolRequest, in UpdateInput) (*mcp.CallToolResult, UpdateOutput, error) { id, err := parseUUID(in.ID) if err != nil { return nil, UpdateOutput{}, err } current, err := t.store.GetThought(ctx, id) if err != nil { return nil, UpdateOutput{}, err } content := current.Content var embedding []float32 mergedMetadata := current.Metadata projectID := current.ProjectID if in.Content != nil { content = strings.TrimSpace(*in.Content) if content == "" { return nil, UpdateOutput{}, errInvalidInput("content must not be empty") } embedding, err = t.provider.Embed(ctx, content) if err != nil { return nil, UpdateOutput{}, err } extracted, extractErr := t.provider.ExtractMetadata(ctx, content) if extractErr != nil { t.log.Warn("metadata extraction failed during update, keeping current metadata", slog.String("error", extractErr.Error())) mergedMetadata = metadata.MarkMetadataFailed(mergedMetadata, t.capture, time.Now().UTC(), extractErr) } else { mergedMetadata = metadata.MarkMetadataComplete(extracted, t.capture, time.Now().UTC()) mergedMetadata.Attachments = current.Metadata.Attachments } } mergedMetadata = metadata.Merge(mergedMetadata, in.Metadata, t.capture) if rawProject := strings.TrimSpace(in.Project); rawProject != "" { project, err := t.store.GetProject(ctx, rawProject) if err != nil { return nil, UpdateOutput{}, err } projectID = &project.ID } updated, err := t.store.UpdateThought(ctx, id, content, embedding, t.provider.EmbeddingModel(), mergedMetadata, projectID) if err != nil { return nil, UpdateOutput{}, err } return nil, UpdateOutput{Thought: updated}, nil }