package tools // import ( // "context" // "errors" // "fmt" // "strings" // "time" // "github.com/google/uuid" // "github.com/jackc/pgx/v5" // "github.com/modelcontextprotocol/go-sdk/mcp" // "git.warky.dev/wdevs/amcs/internal/store" // ext "git.warky.dev/wdevs/amcs/internal/types" // ) // type CRMTool struct { // store *store.DB // } // func NewCRMTool(db *store.DB) *CRMTool { // return &CRMTool{store: db} // } // // add_professional_contact // type AddContactInput struct { // Name string `json:"name" jsonschema:"contact's full name"` // Company string `json:"company,omitempty"` // Title string `json:"title,omitempty" jsonschema:"job title"` // Email string `json:"email,omitempty"` // Phone string `json:"phone,omitempty"` // LinkedInURL string `json:"linkedin_url,omitempty"` // HowWeMet string `json:"how_we_met,omitempty"` // Tags []string `json:"tags,omitempty"` // Notes string `json:"notes,omitempty"` // FollowUpDate *time.Time `json:"follow_up_date,omitempty"` // } // type AddContactOutput struct { // Contact ext.ProfessionalContact `json:"contact"` // } // func (t *CRMTool) AddContact(ctx context.Context, _ *mcp.CallToolRequest, in AddContactInput) (*mcp.CallToolResult, AddContactOutput, error) { // if strings.TrimSpace(in.Name) == "" { // return nil, AddContactOutput{}, errRequiredField("name") // } // if in.Tags == nil { // in.Tags = []string{} // } // contact, err := t.store.AddProfessionalContact(ctx, ext.ProfessionalContact{ // Name: strings.TrimSpace(in.Name), // Company: strings.TrimSpace(in.Company), // Title: strings.TrimSpace(in.Title), // Email: strings.TrimSpace(in.Email), // Phone: strings.TrimSpace(in.Phone), // LinkedInURL: strings.TrimSpace(in.LinkedInURL), // HowWeMet: strings.TrimSpace(in.HowWeMet), // Tags: in.Tags, // Notes: strings.TrimSpace(in.Notes), // FollowUpDate: in.FollowUpDate, // }) // if err != nil { // return nil, AddContactOutput{}, err // } // return nil, AddContactOutput{Contact: contact}, nil // } // // search_contacts // type SearchContactsInput struct { // Query string `json:"query,omitempty" jsonschema:"search text matching name, company, title, or notes"` // Tags []string `json:"tags,omitempty" jsonschema:"filter by tags (all must match)"` // } // type SearchContactsOutput struct { // Contacts []ext.ProfessionalContact `json:"contacts"` // } // func (t *CRMTool) SearchContacts(ctx context.Context, _ *mcp.CallToolRequest, in SearchContactsInput) (*mcp.CallToolResult, SearchContactsOutput, error) { // contacts, err := t.store.SearchContacts(ctx, in.Query, in.Tags) // if err != nil { // return nil, SearchContactsOutput{}, err // } // if contacts == nil { // contacts = []ext.ProfessionalContact{} // } // return nil, SearchContactsOutput{Contacts: contacts}, nil // } // // log_interaction // type LogInteractionInput struct { // ContactID uuid.UUID `json:"contact_id" jsonschema:"id of the contact"` // InteractionType string `json:"interaction_type" jsonschema:"one of: meeting, email, call, coffee, event, linkedin, other"` // OccurredAt *time.Time `json:"occurred_at,omitempty" jsonschema:"when it happened (defaults to now)"` // Summary string `json:"summary" jsonschema:"summary of the interaction"` // FollowUpNeeded bool `json:"follow_up_needed,omitempty"` // FollowUpNotes string `json:"follow_up_notes,omitempty"` // } // type LogInteractionOutput struct { // Interaction ext.ContactInteraction `json:"interaction"` // } // func (t *CRMTool) LogInteraction(ctx context.Context, _ *mcp.CallToolRequest, in LogInteractionInput) (*mcp.CallToolResult, LogInteractionOutput, error) { // if strings.TrimSpace(in.Summary) == "" { // return nil, LogInteractionOutput{}, errRequiredField("summary") // } // occurredAt := time.Now() // if in.OccurredAt != nil { // occurredAt = *in.OccurredAt // } // interaction, err := t.store.LogInteraction(ctx, ext.ContactInteraction{ // ContactID: in.ContactID, // InteractionType: in.InteractionType, // OccurredAt: occurredAt, // Summary: strings.TrimSpace(in.Summary), // FollowUpNeeded: in.FollowUpNeeded, // FollowUpNotes: strings.TrimSpace(in.FollowUpNotes), // }) // if err != nil { // return nil, LogInteractionOutput{}, err // } // return nil, LogInteractionOutput{Interaction: interaction}, nil // } // // get_contact_history // type GetContactHistoryInput struct { // ContactID uuid.UUID `json:"contact_id" jsonschema:"id of the contact"` // } // type GetContactHistoryOutput struct { // History ext.ContactHistory `json:"history"` // } // func (t *CRMTool) GetHistory(ctx context.Context, _ *mcp.CallToolRequest, in GetContactHistoryInput) (*mcp.CallToolResult, GetContactHistoryOutput, error) { // history, err := t.store.GetContactHistory(ctx, in.ContactID) // if err != nil { // return nil, GetContactHistoryOutput{}, err // } // return nil, GetContactHistoryOutput{History: history}, nil // } // // create_opportunity // type CreateOpportunityInput struct { // ContactID *uuid.UUID `json:"contact_id,omitempty"` // Title string `json:"title" jsonschema:"opportunity title"` // Description string `json:"description,omitempty"` // Stage string `json:"stage,omitempty" jsonschema:"one of: identified, in_conversation, proposal, negotiation, won, lost (default: identified)"` // Value *float64 `json:"value,omitempty" jsonschema:"monetary value"` // ExpectedCloseDate *time.Time `json:"expected_close_date,omitempty"` // Notes string `json:"notes,omitempty"` // } // type CreateOpportunityOutput struct { // Opportunity ext.Opportunity `json:"opportunity"` // } // func (t *CRMTool) CreateOpportunity(ctx context.Context, _ *mcp.CallToolRequest, in CreateOpportunityInput) (*mcp.CallToolResult, CreateOpportunityOutput, error) { // if strings.TrimSpace(in.Title) == "" { // return nil, CreateOpportunityOutput{}, errRequiredField("title") // } // stage := strings.TrimSpace(in.Stage) // if stage == "" { // stage = "identified" // } // opp, err := t.store.CreateOpportunity(ctx, ext.Opportunity{ // ContactID: in.ContactID, // Title: strings.TrimSpace(in.Title), // Description: strings.TrimSpace(in.Description), // Stage: stage, // Value: in.Value, // ExpectedCloseDate: in.ExpectedCloseDate, // Notes: strings.TrimSpace(in.Notes), // }) // if err != nil { // return nil, CreateOpportunityOutput{}, err // } // return nil, CreateOpportunityOutput{Opportunity: opp}, nil // } // // get_follow_ups_due // type GetFollowUpsDueInput struct { // DaysAhead int `json:"days_ahead,omitempty" jsonschema:"look ahead window in days (default: 7)"` // } // type GetFollowUpsDueOutput struct { // Contacts []ext.ProfessionalContact `json:"contacts"` // } // func (t *CRMTool) GetFollowUpsDue(ctx context.Context, _ *mcp.CallToolRequest, in GetFollowUpsDueInput) (*mcp.CallToolResult, GetFollowUpsDueOutput, error) { // contacts, err := t.store.GetFollowUpsDue(ctx, in.DaysAhead) // if err != nil { // return nil, GetFollowUpsDueOutput{}, err // } // if contacts == nil { // contacts = []ext.ProfessionalContact{} // } // return nil, GetFollowUpsDueOutput{Contacts: contacts}, nil // } // // link_thought_to_contact // type LinkThoughtToContactInput struct { // ContactID uuid.UUID `json:"contact_id" jsonschema:"id of the contact"` // ThoughtID uuid.UUID `json:"thought_id" jsonschema:"id of the thought to link"` // } // type LinkThoughtToContactOutput struct { // Contact ext.ProfessionalContact `json:"contact"` // } // func (t *CRMTool) LinkThought(ctx context.Context, _ *mcp.CallToolRequest, in LinkThoughtToContactInput) (*mcp.CallToolResult, LinkThoughtToContactOutput, error) { // thought, err := t.store.GetThought(ctx, in.ThoughtID) // if err != nil { // if errors.Is(err, pgx.ErrNoRows) { // return nil, LinkThoughtToContactOutput{}, errEntityNotFound("thought", "thought_id", in.ThoughtID.String()) // } // return nil, LinkThoughtToContactOutput{}, err // } // appendText := fmt.Sprintf("\n\n[Linked thought %s]: %s", thought.ID, thought.Content) // if err := t.store.AppendThoughtToContactNotes(ctx, in.ContactID, appendText); err != nil { // return nil, LinkThoughtToContactOutput{}, err // } // contact, err := t.store.GetContact(ctx, in.ContactID) // if err != nil { // if errors.Is(err, pgx.ErrNoRows) { // return nil, LinkThoughtToContactOutput{}, errEntityNotFound("contact", "contact_id", in.ContactID.String()) // } // return nil, LinkThoughtToContactOutput{}, err // } // return nil, LinkThoughtToContactOutput{Contact: contact}, nil // }