Skip to main content
Glama
ToKiDoO

Advanced Obsidian MCP Server

by ToKiDoO

obsidian_understand_vault

Analyze Obsidian vault structure by generating a directory tree and visualizing note connections through a NetworkX graph to understand file relationships.

Instructions

Get a comprehensive understanding of the vault structure. Returns: 1. directory tree representation and 2. NetworkX graph of note connections used to understand how different notes (.md) and other files (e.g. images, PDFs, referenced/attached) are connected. Combines filesystem directory structure with note relationship graph between notes (.md files).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
directory_pathNoOptional path to a subdirectory to analyze, defaults to vault root
include_attachments_in_graphNoWhether to include attachment files (images, PDFs, etc.) in the *NetworkX connections graph*, excluding attachments in Obsidian. Defaults to True
include_other_files_in_treeNoWhether to show only .md files in the *directory tree structure*, excluding other file types. Defaults to True

Implementation Reference

  • The UnderstandVaultToolHandler class provides the core implementation of the 'obsidian_understand_vault' tool. It defines the tool schema, builds a directory tree of the vault (optionally filtering files), and generates a NetworkX graph of note connections using obsidiantools.
    class UnderstandVaultToolHandler(ToolHandler):
        def __init__(self):
            super().__init__(TOOL_UNDERSTAND_VAULT)
    
        def get_tool_description(self):
            return Tool(
                name=self.name,
                description="Get a comprehensive understanding of the vault structure. Returns: 1. directory tree representation and 2. NetworkX graph of note connections used to understand how different notes (.md) and other files (e.g. images, PDFs, referenced/attached) are connected. Combines filesystem directory structure with note relationship graph between notes (.md files).",
                inputSchema={
                    'type': 'object',
                    'properties': {
                        'directory_path': {
                            'type': 'string',
                            'description': 'Optional path to a subdirectory to analyze, defaults to vault root'
                        },
                        'include_attachments_in_graph': {
                            'type': 'boolean',
                            'description': 'Whether to include attachment files (images, PDFs, etc.) in the *NetworkX connections graph*, excluding attachments in Obsidian. Defaults to True',
                            'default': True
                        },
                        'include_other_files_in_tree': {
                            'type': 'boolean',
                            'description': 'Whether to show only .md files in the *directory tree structure*, excluding other file types. Defaults to True',
                            'default': True
                        }
                    }
                }
            )
    
        def _build_directory_tree(self, vault_path: str, target_dir: str = None, include_other_files: bool = True) -> str:
            """Build a directory tree structure using anytree."""
            vault_path_obj = Path(vault_path)
            target_path = vault_path_obj / target_dir if target_dir else vault_path_obj
            
            if not target_path.exists():
                raise ValueError(f"Directory path does not exist: {target_path}")
            
            # Create root node with vault name
            vault_name = vault_path_obj.name
            root = Node(vault_name)
            
            # Dictionary to store nodes by their path for building hierarchy
            nodes = {str(target_path): root}
            
            # Walk through all files and directories
            for item_path in sorted(target_path.rglob("*")):
                if item_path == target_path:
                    continue
                
                # If include_other_files is False, only show .md files and directories
                if not include_other_files and item_path.is_file() and item_path.suffix != '.md':
                    continue
                    
                # Get relative path from vault root
                rel_path = item_path.relative_to(vault_path_obj)
                
                # Find parent node
                parent_path = str(item_path.parent)
                parent_node = nodes.get(parent_path, root)
                
                # Create display name with relative path for files
                if item_path.is_file():
                    display_name = f"{item_path.name} ({rel_path})"
                else:
                    display_name = f"{item_path.name}/"
                
                # Create node
                node = Node(display_name, parent=parent_node)
                nodes[str(item_path)] = node
            
            # Render tree
            tree_lines = []
            for pre, _, node in RenderTree(root):
                tree_lines.append(f"{pre}{node.name}")
            
            return "\n".join(tree_lines)
    
        def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
            vault_path = os.getenv("OBSIDIAN_VAULT_PATH")
            if not vault_path:
                raise ValueError("OBSIDIAN_VAULT_PATH environment variable is not set. This tool cannot run without it.")
    
            directory_path = args.get('directory_path')
            include_attachments_in_graph = args.get('include_attachments_in_graph', True)
            include_other_files_in_tree = args.get('include_other_files_in_tree', True)
    
            # Generate directory tree
            try:
                tree_structure = self._build_directory_tree(vault_path, directory_path, include_other_files_in_tree)
            except Exception as e:
                raise ValueError(f"Error building directory tree: {str(e)}")
    
            # Generate note connections graph with proper attachments parameter
            vault_path_obj = Path(vault_path)
            vault = otools.Vault(vault_path_obj).connect(attachments=include_attachments_in_graph).gather()
            graph = vault.graph
            graph_data = json_graph.node_link_data(graph)
    
            # Combine both outputs
            return [
                TextContent(
                    type="text",
                    text=f"# Vault Understanding\n\n## Vault Tree Structure:\n```\n{tree_structure}\n```\n\n## Note Connections:\n```json\n{json.dumps(graph_data, indent=2)}\n```"
                )
            ]
  • The input schema for the tool defines optional parameters: directory_path (string), include_attachments_in_graph (boolean, default True), include_other_files_in_tree (boolean, default True).
    def get_tool_description(self):
        return Tool(
            name=self.name,
            description="Get a comprehensive understanding of the vault structure. Returns: 1. directory tree representation and 2. NetworkX graph of note connections used to understand how different notes (.md) and other files (e.g. images, PDFs, referenced/attached) are connected. Combines filesystem directory structure with note relationship graph between notes (.md files).",
            inputSchema={
                'type': 'object',
                'properties': {
                    'directory_path': {
                        'type': 'string',
                        'description': 'Optional path to a subdirectory to analyze, defaults to vault root'
                    },
                    'include_attachments_in_graph': {
                        'type': 'boolean',
                        'description': 'Whether to include attachment files (images, PDFs, etc.) in the *NetworkX connections graph*, excluding attachments in Obsidian. Defaults to True',
                        'default': True
                    },
                    'include_other_files_in_tree': {
                        'type': 'boolean',
                        'description': 'Whether to show only .md files in the *directory tree structure*, excluding other file types. Defaults to True',
                        'default': True
                    }
                }
            }
        )
  • The TOOL_MAPPING dictionary registers 'obsidian_understand_vault' (TOOL_UNDERSTAND_VAULT) to the UnderstandVaultToolHandler class. This mapping is used in register_tools() to instantiate and add the handler to the server.
    TOOL_MAPPING = {
        tools.TOOL_LIST_FILES_IN_DIR: tools.ListFilesInDirToolHandler,
        tools.TOOL_SIMPLE_SEARCH: tools.SearchToolHandler,
        tools.TOOL_PATCH_CONTENT: tools.PatchContentToolHandler,
        tools.TOOL_PUT_CONTENT: tools.PutContentToolHandler,
        tools.TOOL_APPEND_CONTENT: tools.AppendContentToolHandler,
        tools.TOOL_DELETE_FILE: tools.DeleteFileToolHandler,
        tools.TOOL_COMPLEX_SEARCH: tools.ComplexSearchToolHandler,
        tools.TOOL_BATCH_GET_FILES: tools.BatchGetFilesToolHandler,
        tools.TOOL_PERIODIC_NOTES: tools.PeriodicNotesToolHandler,
        tools.TOOL_RECENT_PERIODIC_NOTES: tools.RecentPeriodicNotesToolHandler,
        tools.TOOL_RECENT_CHANGES: tools.RecentChangesToolHandler,
        tools.TOOL_UNDERSTAND_VAULT: tools.UnderstandVaultToolHandler,
        tools.TOOL_GET_ACTIVE_NOTE: tools.GetActiveNoteToolHandler,
        tools.TOOL_OPEN_FILES: tools.OpenFilesToolHandler,
        tools.TOOL_LIST_COMMANDS: tools.ListCommandsToolHandler,
        tools.TOOL_EXECUTE_COMMANDS: tools.ExecuteCommandsToolHandler,
    }
  • The register_tools() function instantiates handlers from TOOL_MAPPING (including for obsidian_understand_vault if selected) and registers them with the MCP server.
    def register_tools():
        """Register the selected tools with the server."""
        tools_to_include = parse_include_tools()
        
        registered_count = 0
        for tool_name in tools_to_include:
            if tool_name in TOOL_MAPPING:
                handler_class = TOOL_MAPPING[tool_name]
                handler_instance = handler_class()
                add_tool_handler(handler_instance)
                registered_count += 1
                logger.debug(f"Registered tool: {tool_name}")
        
        logger.info(f"Successfully registered {registered_count} tools")
    
    # Register tools based on INCLUDE_TOOLS environment variable
    register_tools()
  • Helper method within the handler to build and render a directory tree structure of the vault or subdirectory using anytree library.
    def _build_directory_tree(self, vault_path: str, target_dir: str = None, include_other_files: bool = True) -> str:
        """Build a directory tree structure using anytree."""
        vault_path_obj = Path(vault_path)
        target_path = vault_path_obj / target_dir if target_dir else vault_path_obj
        
        if not target_path.exists():
            raise ValueError(f"Directory path does not exist: {target_path}")
        
        # Create root node with vault name
        vault_name = vault_path_obj.name
        root = Node(vault_name)
        
        # Dictionary to store nodes by their path for building hierarchy
        nodes = {str(target_path): root}
        
        # Walk through all files and directories
        for item_path in sorted(target_path.rglob("*")):
            if item_path == target_path:
                continue
            
            # If include_other_files is False, only show .md files and directories
            if not include_other_files and item_path.is_file() and item_path.suffix != '.md':
                continue
                
            # Get relative path from vault root
            rel_path = item_path.relative_to(vault_path_obj)
            
            # Find parent node
            parent_path = str(item_path.parent)
            parent_node = nodes.get(parent_path, root)
            
            # Create display name with relative path for files
            if item_path.is_file():
                display_name = f"{item_path.name} ({rel_path})"
            else:
                display_name = f"{item_path.name}/"
            
            # Create node
            node = Node(display_name, parent=parent_node)
            nodes[str(item_path)] = node
        
        # Render tree
        tree_lines = []
        for pre, _, node in RenderTree(root):
            tree_lines.append(f"{pre}{node.name}")
        
        return "\n".join(tree_lines)

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/ToKiDoO/mcp-obsidian-advanced'

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