get_hive_overview
Retrieve a project overview showing total entries, category breakdown, and recent additions from the Hivemind MCP knowledge base.
Instructions
Get overview of project hive including total entries, category breakdown, and recent additions. Use when user says 'show me my hive' or 'hive overview'.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| user_id | No | Optional: User ID (auto-detected from .user_id in cwd if not provided) | |
| project_id | Yes | Project identifier | |
| project_path | No | Optional: Project directory path (required for local storage) |
Implementation Reference
- src/api.ts:734-823 (handler)Core handler function for get_hive_overview tool. Supports local (.hive.json) and cloud (Supabase) storage. Computes total entries, category breakdown with keyword previews, and recent entries.export async function getHiveOverview( userId: string | null, projectId: string, projectPath?: string ): Promise<GetHiveOverviewResult> { // Auto-detect user_id if not provided if (!userId) { userId = await getUserId(projectPath); if (!userId) { throw new Error('No .user_id file found. Run init_hive first.'); } } // Check if local storage if (userId.startsWith('local-') && projectPath) { const hive = await readLocalHive(projectPath); if (!hive) { throw new Error('Local hive not found'); } // Aggregate by category with previews const categoryMap: Record<string, { count: number; samples: string[] }> = {}; hive.entries.forEach(entry => { const cat = entry.category || 'uncategorized'; if (!categoryMap[cat]) { categoryMap[cat] = { count: 0, samples: [] }; } categoryMap[cat].count++; // Collect first 5 entry keywords for preview if (categoryMap[cat].samples.length < 5) { // Extract key keyword const keyword = extractKeyword(entry.query); if (keyword) { categoryMap[cat].samples.push(keyword); } } }); // Build final category breakdown with previews (sorted alphabetically) const categoryBreakdown: Record<string, { count: number; preview: string }> = {}; Object.entries(categoryMap) .sort(([a], [b]) => a.localeCompare(b)) .forEach(([cat, data]) => { categoryBreakdown[cat] = { count: data.count, preview: data.samples.join(', ') + (data.count > data.samples.length ? '...' : '') }; }); // Get recent entries (last 10) const recentEntries = hive.entries .slice(-10) .reverse() .map(e => ({ category: e.category, query: e.query, created_at: e.created_at })); return { success: true, project_name: hive.project_name, project_id: hive.project_id, user_id: hive.user_id, storage_type: 'local', rate_limit: 100, total_entries: hive.entries.length, category_breakdown: categoryBreakdown, recent_entries: recentEntries }; } // Cloud storage - use API const response = await fetch(`${API_BASE}/get-hive-overview`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ user_id: userId, project_id: projectId }), }); if (!response.ok) { throw new Error(`Get hive overview failed: ${response.statusText}`); } return response.json(); }
- src/index.ts:273-295 (schema)Input schema and description for the get_hive_overview tool, defined in the static tools list returned by ListToolsRequestSchema handler.{ name: "get_hive_overview", description: "Get overview of project hive including total entries, category breakdown, and recent additions. Use when user says 'show me my hive' or 'hive overview'.", inputSchema: { type: "object", properties: { user_id: { type: "string", description: "Optional: User ID (auto-detected from .user_id in cwd if not provided)", }, project_id: { type: "string", description: "Project identifier", }, project_path: { type: "string", description: "Optional: Project directory path (required for local storage)", }, }, required: ["project_id"], }, },
- src/index.ts:471-480 (registration)Registration and dispatch in the MCP CallToolRequestSchema switch statement. Calls the getHiveOverview function from api.ts and returns JSON response.case "get_hive_overview": { const result = await getHiveOverview( args?.user_id as string, args?.project_id as string, args?.project_path as string | undefined ); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; }
- src/api.ts:688-729 (helper)Helper function used in local hive overview to generate keyword previews for category breakdowns from entry queries.function extractKeyword(query: string): string { // Look for quoted terms first (e.g., "Tenant not found") const quotedMatch = query.match(/"([^"]+)"/); if (quotedMatch) { const words = quotedMatch[1].split(' '); return words.slice(0, 2).join(' '); } // Remove question words FIRST let text = query .replace(/^(How (did|do|does|can|should|to)|What (is|are|was|were|does|'s)|Why (is|are|was|were|did|does)|Where (is|are)|When (is|are|was|did))\s+/i, '') .replace(/\?$/, '') .replace(/^(we |the |a |an |our |your |my |I )/i, '') .trim(); // Now look for capitalized technical terms in the remaining text (Supabase, RLS, FTS, Claude, etc.) // Match all-caps acronyms (2+ letters) OR capitalized words const capitalMatch = text.match(/\b[A-Z]{2,}\b|\b[A-Z][a-z]+\b/); if (capitalMatch) { return capitalMatch[0]; } // Look for common technical patterns const techTerms = ['supabase', 'migration', 'storage', 'scanner', 'onboarding', 'preview', 'category', 'deployment', 'testing', 'setup', 'error', 'policy', 'function', 'column', 'search', 'workflow', 'endpoint', 'authentication', 'rls', 'fts', 'mcp', 'npm', 'api', 'database', 'schema']; const words = text.toLowerCase().split(/\s+/); for (const term of techTerms) { if (words.includes(term)) { return term.toUpperCase() === term ? term : term.charAt(0).toUpperCase() + term.slice(1); } } // Fallback: first meaningful word (not stop words) const stopWords = ['fix', 'work', 'does', 'use', 'make', 'create', 'add', 'added', 'get', 'set', 'in', 'on', 'for', 'to', 'from', 'with', 'by', 'what', 'how', 'why', 'when', 'where', 'this', 'that', 'there']; for (const word of words) { if (word.length > 2 && !stopWords.includes(word.toLowerCase())) { return word.charAt(0).toUpperCase() + word.slice(1); } } // Last resort: "Other" return 'Other'; }
- Cloud backend handler for /get-hive-overview endpoint, called by MCP client for remote storage. Mirrors local logic using Supabase queries.async function handleGetHiveOverview(supabase: any, body: any, corsHeaders: any) { const { user_id, project_id } = body; if (!user_id || !project_id) { return new Response(JSON.stringify({ error: 'user_id and project_id required' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Get all entries for this project const { data: entries, error } = await supabase .from('knowledge_entries') .select('id, query, category, created_at, project_name') .eq('user_id', user_id) .eq('project_id', project_id) .order('id', { ascending: false }); if (error) { console.error('Get entries error:', error); return new Response(JSON.stringify({ success: false, error: 'Failed to get hive entries' }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Aggregate by category with previews const categoryMap: Record<string, { count: number; samples: string[] }> = {}; entries.forEach((entry: any) => { const cat = entry.category || 'uncategorized'; if (!categoryMap[cat]) { categoryMap[cat] = { count: 0, samples: [] }; } categoryMap[cat].count++; // Collect first 5 entry keywords for preview if (categoryMap[cat].samples.length < 5) { // Extract key keyword let keyword = extractKeyword(entry.query); if (keyword) { categoryMap[cat].samples.push(keyword); } } }); // Build final category breakdown with previews (sorted alphabetically) const categoryBreakdown: Record<string, { count: number; preview: string }> = {}; Object.entries(categoryMap) .sort(([a], [b]) => a.localeCompare(b)) .forEach(([cat, data]) => { categoryBreakdown[cat] = { count: data.count, preview: data.samples.join(', ') + (data.count > data.samples.length ? '...' : '') }; }); // Get user tier info const { data: tierData } = await supabase .from('contributor_tiers') .select('tier, rate_limit') .eq('user_id', user_id) .single(); const storageType = tierData?.tier === 'cloud' ? 'cloud' : 'local'; const rateLimit = tierData?.rate_limit || 100; const projectName = entries.length > 0 ? entries[0].project_name : project_id; return new Response(JSON.stringify({ success: true, project_name: projectName, project_id, user_id, storage_type: storageType, rate_limit: rateLimit, total_entries: entries.length, category_breakdown: categoryBreakdown, recent_entries: entries.slice(0, 10).map((e: any) => ({ category: e.category, query: e.query, created_at: e.created_at })), _ctx: "FOLLOW PATTERNS: These are your project's proven approaches. Use them - don't reinvent." }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); }