package tools import ( "context" "time" "github.com/modelcontextprotocol/go-sdk/mcp" "isphere-ai-bridge/internal/isphere" ) const ToolNameSearchGroups = "isphere_search_groups" type SearchGroupsArgs struct { Query string `json:"query" jsonschema:"group search query; currently matches bare group JIDs from local readonly message logs"` Limit int `json:"limit,omitempty" jsonschema:"maximum number of groups to return; zero or negative returns all matches"` Cursor string `json:"cursor,omitempty" jsonschema:"pagination cursor; currently only empty cursor is supported"` SourcePreference string `json:"source_preference,omitempty" jsonschema:"source selector; currently supports auto or local_readonly only"` IncludeArchived bool `json:"include_archived,omitempty" jsonschema:"accepted for contract compatibility; local readonly message logs do not expose archived state"` } func RegisterISphereGroupTools(server *mcp.Server, source ReceiveMessagesSource) { if source == nil { source = isphere.EncryptedPacketLogSource{} } mcp.AddTool[SearchGroupsArgs, map[string]any](server, &mcp.Tool{ Name: ToolNameSearchGroups, Description: "Search iSphere group candidates from the configured read-only message source.", }, func(ctx context.Context, _ *mcp.CallToolRequest, input SearchGroupsArgs) (*mcp.CallToolResult, map[string]any, error) { if err := validateLocalReadonlySourceAndCursor(ToolNameSearchGroups, input.SourcePreference, input.Cursor); err != nil { return nil, nil, err } started := time.Now().UTC() messages, err := source.ReceiveMessages(ctx, isphere.ReceiveMessagesQuery{Limit: 0}) if err != nil { return nil, nil, err } groups := isphere.SearchGroupsFromMessages(messages.Messages, isphere.SearchGroupsQuery{Query: input.Query, Limit: input.Limit}) return nil, searchGroupsResultToMap(groups, started, time.Now().UTC()), nil }) } func searchGroupsResultToMap(result isphere.SearchGroupsResult, started time.Time, finished time.Time) map[string]any { groups := make([]map[string]any, 0, len(result.Groups)) for _, group := range result.Groups { groups = append(groups, map[string]any{ "group_id": group.GroupID, "display_name": group.DisplayName, "member_count": nil, "owner_ref": nil, "source": group.Source, "confidence": group.Confidence, "raw_ref": group.RawRef, }) } return map[string]any{ "ok": true, "groups": groups, "next_cursor": nil, "audit": map[string]any{ "tool": ToolNameSearchGroups, "source": "local_readonly", "execution_mode": "read", "started_at": started.Format(time.RFC3339Nano), "finished_at": finished.Format(time.RFC3339Nano), "result": "ok", }, } }