Skip to main content
Glama
ToKiDoO

Advanced Obsidian MCP Server

by ToKiDoO

obsidian_open_files

Open multiple files in Obsidian vault simultaneously to view content in separate tabs for efficient reference and editing.

Instructions

Open one or more files in the vault in a new leaf.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filepathsYesList of file paths to open

Implementation Reference

  • The OpenFilesToolHandler class provides the core implementation of the 'obsidian_open_files' tool. It defines the tool schema and the run_tool method that iterates over the provided filepaths and calls the Obsidian API to open each file in a new leaf, handling errors and returning appropriate responses.
    class OpenFilesToolHandler(ToolHandler):
        def __init__(self):
            super().__init__(TOOL_OPEN_FILES)
    
        def get_tool_description(self):
            return Tool(
                name=self.name,
                description="Open one or more files in the vault in a new leaf.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "filepaths": {
                            "type": "array",
                            "items": {
                                "type": "string",
                                "description": "Path to a file (relative to your vault root)",
                                "format": "path"
                            },
                            "description": "List of file paths to open"
                        },
                    },
                    "required": ["filepaths"]
                }
            )
        
        def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
            filepaths = args.get("filepaths")
            if not filepaths:
                raise ValueError("One or more filepaths are required")
            
            successful_opens = []
            failed_opens = []
            
            for file in filepaths:
                try:
                    api.open_file(file)
                    successful_opens.append(file)
                except Exception as e:
                    failed_opens.append((file, str(e)))
            
            if failed_opens:
                error_messages = [f"Failed to open '{file}': {error}" for file, error in failed_opens]
                if successful_opens:
                    return [
                        TextContent(
                            type="text",
                            text=f"Opened {len(successful_opens)} file(s) successfully. Errors: {'; '.join(error_messages)}"
                        )
                    ]
                else:
                    raise ValueError(f"Failed to open any files: {'; '.join(error_messages)}")
            
            return [
                TextContent(
                    type="text",
                    text="File(s) opened successfully!"
                )
            ]
  • TOOL_MAPPING dictionary registers 'obsidian_open_files' (via tools.TOOL_OPEN_FILES) to the OpenFilesToolHandler class. This mapping is used in register_tools() to instantiate and add the handler to the MCP 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 Obsidian API client's open_file method makes a POST request to '/open/{filepath}' to open the specified file in Obsidian, which is called by the tool handler.
    def open_file(self, filepath: str) -> Any:
        """Open a file in the vault.
        
        Args:
            filepath: Path to the file to open (relative to vault root)
            
        Returns:
            None on success. Opens the file in the default editor in a new leaf.
        """
        url = f"{self.get_base_url()}/open/{filepath}"
        
        def call_fn():
            response = requests.post(url, headers=self._get_headers(), verify=self.verify_ssl, timeout=self.timeout)
            response.raise_for_status()
            return None
            
        return self._safe_call(call_fn)
  • register_tools function instantiates handlers from TOOL_MAPPING (including OpenFilesToolHandler for obsidian_open_files) and registers them with the MCP server via add_tool_handler.
    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")
  • Constant defining the tool name 'obsidian_open_files' used throughout for registration and handler initialization.
    TOOL_OPEN_FILES = "obsidian_open_files"
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the action ('Open') and outcome ('in a new leaf'), implying a read operation that may involve UI changes, but doesn't specify permissions, side effects (e.g., if files must exist), error handling, or what 'new leaf' entails (e.g., tab, window). This is a significant gap for a tool with potential user interface impacts.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core action and outcome. Every word earns its place, with no redundancy or unnecessary details. It's appropriately sized for a straightforward tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (opening files with UI implications), no annotations, and no output schema, the description is minimally adequate. It covers the basic purpose but lacks details on behavior, error cases, or return values. It meets the minimum viable threshold but has clear gaps in contextual information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with the parameter 'filepaths' fully documented in the schema as an array of file paths relative to the vault root. The description adds no additional meaning beyond the schema, such as format examples or constraints. Baseline 3 is appropriate since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Open') and resource ('one or more files in the vault'), specifying the outcome ('in a new leaf'). It distinguishes from siblings like obsidian_get_active_note (retrieves content) or obsidian_list_files_in_dir (lists files), but doesn't explicitly differentiate from all siblings. The purpose is specific but not fully contrasted with every alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., file existence), exclusions, or compare to siblings like obsidian_get_active_note for viewing current content or obsidian_simple_search for finding files first. Usage is implied but not explicitly stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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