package tools import ( "context" "time" "github.com/modelcontextprotocol/go-sdk/mcp" "isphere-ai-bridge/internal/isphere" ) const ToolNameSearchContacts = "isphere_search_contacts" type SearchContactsArgs struct { Query string `json:"query" jsonschema:"contact search query; currently matches bare JIDs from local readonly message logs"` Limit int `json:"limit,omitempty" jsonschema:"maximum number of contacts to return; zero or negative returns all matches"` } func RegisterISphereContactTools(server *mcp.Server, source ReceiveMessagesSource) { if source == nil { source = isphere.EncryptedPacketLogSource{} } mcp.AddTool[SearchContactsArgs, map[string]any](server, &mcp.Tool{ Name: ToolNameSearchContacts, Description: "Search iSphere contact candidates from the configured read-only message source.", }, func(ctx context.Context, _ *mcp.CallToolRequest, input SearchContactsArgs) (*mcp.CallToolResult, map[string]any, error) { started := time.Now().UTC() messages, err := source.ReceiveMessages(ctx, isphere.ReceiveMessagesQuery{Limit: 0}) if err != nil { return nil, nil, err } contacts := isphere.SearchContactsFromMessages(messages.Messages, isphere.SearchContactsQuery{Query: input.Query, Limit: input.Limit}) return nil, searchContactsResultToMap(contacts, started, time.Now().UTC()), nil }) } func searchContactsResultToMap(result isphere.SearchContactsResult, started time.Time, finished time.Time) map[string]any { contacts := make([]map[string]any, 0, len(result.Contacts)) for _, contact := range result.Contacts { contacts = append(contacts, map[string]any{ "contact_id": contact.ContactID, "display_name": contact.DisplayName, "account": contact.Account, "department": nil, "title": nil, "source": contact.Source, "confidence": contact.Confidence, "raw_ref": contact.RawRef, }) } return map[string]any{ "ok": true, "contacts": contacts, "next_cursor": nil, "audit": map[string]any{ "tool": ToolNameSearchContacts, "source": "local_readonly", "execution_mode": "read", "started_at": started.Format(time.RFC3339Nano), "finished_at": finished.Format(time.RFC3339Nano), "result": "ok", }, } }