feat(tools): implement CRUD operations for thoughts and projects
* Add tools for creating, retrieving, updating, and deleting thoughts. * Implement project management tools for creating and listing projects. * Introduce linking functionality between thoughts. * Add search and recall capabilities for thoughts based on semantic queries. * Implement statistics and summarization tools for thought analysis. * Create database migrations for thoughts, projects, and links. * Add helper functions for UUID parsing and project resolution.
This commit is contained in:
87
internal/tools/helpers.go
Normal file
87
internal/tools/helpers.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/session"
|
||||
"git.warky.dev/wdevs/amcs/internal/store"
|
||||
thoughttypes "git.warky.dev/wdevs/amcs/internal/types"
|
||||
)
|
||||
|
||||
func parseUUID(id string) (uuid.UUID, error) {
|
||||
parsed, err := uuid.Parse(strings.TrimSpace(id))
|
||||
if err != nil {
|
||||
return uuid.Nil, fmt.Errorf("invalid id %q: %w", id, err)
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func sessionID(req *mcp.CallToolRequest) (string, error) {
|
||||
if req == nil || req.Session == nil || req.Session.ID() == "" {
|
||||
return "", fmt.Errorf("tool requires an MCP session")
|
||||
}
|
||||
return req.Session.ID(), nil
|
||||
}
|
||||
|
||||
func resolveProject(ctx context.Context, db *store.DB, sessions *session.ActiveProjects, req *mcp.CallToolRequest, raw string, required bool) (*thoughttypes.Project, error) {
|
||||
projectRef := strings.TrimSpace(raw)
|
||||
if projectRef == "" && sessions != nil && req != nil && req.Session != nil {
|
||||
if activeID, ok := sessions.Get(req.Session.ID()); ok {
|
||||
project, err := db.GetProject(ctx, activeID.String())
|
||||
if err == nil {
|
||||
return &project, nil
|
||||
}
|
||||
if err != pgx.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if projectRef == "" {
|
||||
if required {
|
||||
return nil, fmt.Errorf("project is required")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
project, err := db.GetProject(ctx, projectRef)
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, fmt.Errorf("project %q not found", projectRef)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &project, nil
|
||||
}
|
||||
|
||||
func formatContextBlock(header string, lines []string) string {
|
||||
if len(lines) == 0 {
|
||||
return header + "\n\nNo matching thoughts."
|
||||
}
|
||||
return header + "\n\n" + strings.Join(lines, "\n\n")
|
||||
}
|
||||
|
||||
func thoughtContextLine(index int, content string, metadata thoughttypes.ThoughtMetadata, similarity float64) string {
|
||||
label := fmt.Sprintf("%d. %s", index+1, strings.TrimSpace(content))
|
||||
parts := make([]string, 0, 3)
|
||||
if len(metadata.Topics) > 0 {
|
||||
parts = append(parts, "topics="+strings.Join(metadata.Topics, ", "))
|
||||
}
|
||||
if metadata.Type != "" {
|
||||
parts = append(parts, "type="+metadata.Type)
|
||||
}
|
||||
if similarity > 0 {
|
||||
parts = append(parts, fmt.Sprintf("similarity=%.3f", similarity))
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return label
|
||||
}
|
||||
return label + "\n[" + strings.Join(parts, " | ") + "]"
|
||||
}
|
||||
Reference in New Issue
Block a user