Skip to main content
Glama
navigator.py13.9 kB
""" HiveMind MCP Server - Navigator Navigate the spider-web of documentation using anchor points. """ import os from pathlib import Path from typing import Dict, List, Optional, Any from config import HIVEMIND_FILENAME, ANCHOR_PREFIX from utils import parse_anchor, create_anchor, normalize_path, extract_first_paragraph async def load_hivemind(anchor: str) -> Optional[str]: """ Load hivemind.md content from an anchor point. Args: anchor: Anchor string in format "anchor://path/to/directory" Returns: Content of hivemind.md or None if not found """ try: dir_path = parse_anchor(anchor) hivemind_path = dir_path / HIVEMIND_FILENAME if not hivemind_path.exists(): return None return hivemind_path.read_text(encoding='utf-8', errors='ignore') except Exception: return None async def load_with_context( anchor: str, include_parent: bool = True, include_children_summary: bool = True, ) -> Dict[str, Any]: """ Load target hivemind with optional parent and children context. Args: anchor: Target anchor point include_parent: Include parent directory context include_children_summary: Include summaries of child directories Returns: Dictionary with current, parent, and children content """ result = { 'current': None, 'parent': None, 'children': [], 'anchor': anchor, } try: dir_path = parse_anchor(anchor) # Load current hivemind current_path = dir_path / HIVEMIND_FILENAME if current_path.exists(): result['current'] = current_path.read_text(encoding='utf-8', errors='ignore') # Load parent hivemind if include_parent: parent_path = dir_path.parent / HIVEMIND_FILENAME if parent_path.exists(): content = parent_path.read_text(encoding='utf-8', errors='ignore') # Extract just the header and What This Does section result['parent'] = { 'anchor': create_anchor(dir_path.parent), 'name': dir_path.parent.name, 'summary': extract_first_paragraph(content), } # Load children summaries if include_children_summary: for child in dir_path.iterdir(): if child.is_dir(): child_hivemind = child / HIVEMIND_FILENAME if child_hivemind.exists(): content = child_hivemind.read_text(encoding='utf-8', errors='ignore') result['children'].append({ 'anchor': create_anchor(child), 'name': child.name, 'summary': extract_first_paragraph(content), }) except Exception as e: result['error'] = str(e) return result async def search_hive( root_path: Path, query: str, search_type: str = 'function' ) -> List[Dict[str, Any]]: """ Search all hivemind.md files for a query. Args: root_path: Root directory to start search from query: Search query (function name, module name, etc.) search_type: Type of search ('function', 'import', 'export', 'text') Returns: List of matching results with anchor points and context """ results = [] root_path = normalize_path(str(root_path)) query_lower = query.lower() def search_in_file(hivemind_path: Path): try: content = hivemind_path.read_text(encoding='utf-8', errors='ignore') lines = content.split('\n') matches = [] for i, line in enumerate(lines): line_lower = line.lower() # Check if query appears in line if query_lower in line_lower: # Determine match type based on context match_type = 'text' if search_type == 'function' and ('###' in line and '`' in line): match_type = 'function' elif search_type == 'import' and 'dependencies' in lines[max(0, i-5):i]: match_type = 'import' elif search_type == 'export' and 'exports' in lines[max(0, i-5):i]: match_type = 'export' if search_type == 'text' or match_type == search_type: matches.append({ 'line': i + 1, 'content': line.strip(), 'type': match_type, }) if matches: dir_path = hivemind_path.parent results.append({ 'anchor': create_anchor(dir_path), 'path': str(dir_path), 'name': dir_path.name, 'matches': matches, }) except Exception: pass # Walk all directories for root, dirs, files in os.walk(root_path): # Skip ignored directories dirs[:] = [d for d in dirs if not d.startswith('.') and d not in { 'node_modules', '.next', 'dist', 'build', '__pycache__', '.venv', 'venv' }] if HIVEMIND_FILENAME in files: search_in_file(Path(root) / HIVEMIND_FILENAME) return results async def find_function( root_path: Path, function_name: str, return_context: bool = False, ) -> List[Dict[str, Any]]: """ Find all definitions of a function by name. Args: root_path: Root directory to search from function_name: Name of function to find return_context: Include full context (signature, location, etc.) Returns: List of locations where function is defined """ results = await search_hive(root_path, function_name, search_type='function') if not return_context: # Return just anchor points return [{'anchor': r['anchor'], 'name': r['name']} for r in results] # Extract more context for each match enriched_results = [] for result in results: for match in result.get('matches', []): if 'function' in match.get('type', ''): enriched_results.append({ 'anchor': result['anchor'], 'name': result['name'], 'signature': match.get('content', ''), 'line': match.get('line', 0), }) return enriched_results async def trace_connections( anchor: str, direction: str = 'both', max_depth: int = 2, root_path: Optional[Path] = None, ) -> Dict[str, Any]: """ Trace dependencies and dependents of a module. Args: anchor: Starting anchor point direction: 'upstream' (dependencies), 'downstream' (dependents), or 'both' max_depth: Maximum depth to trace root_path: Optional root path for searching Returns: Dependency tree structure """ result = { 'anchor': anchor, 'upstream': [], # Things this module imports 'downstream': [], # Things that import this module 'depth': 0, } try: dir_path = parse_anchor(anchor) hivemind_path = dir_path / HIVEMIND_FILENAME if not hivemind_path.exists(): result['error'] = f"No hivemind.md found at {anchor}" return result content = hivemind_path.read_text(encoding='utf-8', errors='ignore') # Parse Connections section in_connections = False in_upstream = False in_downstream = False for line in content.split('\n'): line_stripped = line.strip() if '## 🔗 Connections' in line: in_connections = True elif line_stripped.startswith('## ') and in_connections: break elif 'Uses (Upstream)' in line or 'Uses' in line: in_upstream = True in_downstream = False elif 'Used By (Downstream)' in line or 'Used By' in line: in_downstream = True in_upstream = False elif in_connections and line_stripped.startswith('- '): # Extract anchor from line if '`anchor://' in line: import re match = re.search(r'`(anchor://[^`]+)`', line) if match: dep_anchor = match.group(1) if in_upstream and direction in ('upstream', 'both'): result['upstream'].append(dep_anchor) elif in_downstream and direction in ('downstream', 'both'): result['downstream'].append(dep_anchor) # Recursive tracing (if depth allows) if max_depth > 1: # Trace upstream if direction in ('upstream', 'both'): traced_upstream = [] for dep in result['upstream'][:5]: # Limit to prevent explosion try: child_trace = await trace_connections( dep, direction='upstream', max_depth=max_depth - 1, root_path=root_path, ) traced_upstream.append(child_trace) except Exception: traced_upstream.append({'anchor': dep, 'error': 'Failed to trace'}) result['upstream_trace'] = traced_upstream # Trace downstream if direction in ('downstream', 'both'): traced_downstream = [] for dep in result['downstream'][:5]: # Limit to prevent explosion try: child_trace = await trace_connections( dep, direction='downstream', max_depth=max_depth - 1, root_path=root_path, ) traced_downstream.append(child_trace) except Exception: traced_downstream.append({'anchor': dep, 'error': 'Failed to trace'}) result['downstream_trace'] = traced_downstream except Exception as e: result['error'] = str(e) return result def format_trace_result(trace: Dict[str, Any], indent: int = 0) -> str: """ Format trace result as readable text. Args: trace: Trace result dictionary indent: Indentation level Returns: Formatted string """ prefix = " " * indent lines = [] anchor = trace.get('anchor', 'Unknown') lines.append(f"{prefix}📍 {anchor}") # Upstream upstream = trace.get('upstream', []) if upstream: lines.append(f"{prefix} ⬆️ Uses ({len(upstream)}):") for dep in upstream: lines.append(f"{prefix} - {dep}") # Upstream trace upstream_trace = trace.get('upstream_trace', []) if upstream_trace: for child in upstream_trace: lines.append(format_trace_result(child, indent + 2)) # Downstream downstream = trace.get('downstream', []) if downstream: lines.append(f"{prefix} ⬇️ Used By ({len(downstream)}):") for dep in downstream: lines.append(f"{prefix} - {dep}") # Downstream trace downstream_trace = trace.get('downstream_trace', []) if downstream_trace: for child in downstream_trace: lines.append(format_trace_result(child, indent + 2)) return '\n'.join(lines) async def get_navigation_context( anchor: str, context_size: str = 'medium' ) -> str: """ Get navigation context for AI to understand location in codebase. Args: anchor: Current anchor point context_size: 'small', 'medium', or 'large' Returns: Formatted context string """ result = await load_with_context( anchor, include_parent=(context_size != 'small'), include_children_summary=(context_size != 'small'), ) sections = [] # Current context if result.get('current'): sections.append("=== CURRENT CONTEXT ===") if context_size == 'small': # Just the header and first section lines = result['current'].split('\n') sections.append('\n'.join(lines[:30])) elif context_size == 'medium': # Everything above the separator parts = result['current'].split('---') sections.append(parts[0] if parts else result['current']) else: # Full content sections.append(result['current']) # Parent context if result.get('parent'): sections.append("\n=== PARENT CONTEXT ===") parent = result['parent'] sections.append(f"# {parent['name']}") sections.append(parent.get('summary', '')) # Children if result.get('children'): sections.append("\n=== CHILDREN ===") for child in result['children']: sections.append(f"- **{child['name']}**: {child.get('summary', '')[:100]}...") return '\n'.join(sections)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jahanzaib-Kaleem/hive-mind-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server