Skip to main content
Glama
XD3an
by XD3an

install_local_mcp_server

Install an MCP server from a local directory by specifying the path, arguments, and environment variables for configuration.

Instructions

Install an MCP server from a local directory.

Args:
    path: The path to the MCP server code cloned on your computer
    args: The arguments to pass along
    env: The environment variables to set, delimited by =

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
argsNo
envNo

Implementation Reference

  • The @mcp.tool()-decorated handler function that implements the 'install_local_mcp_server' tool. It installs a local MCP server by checking if it's a Node.js or Python project, determining the entry point, and adding the configuration to Claude Desktop's config file using the helper install_to_claude_desktop.
    @mcp.tool()
    def install_local_mcp_server(
        path: str, 
        args: Optional[List[str]] = None,
        env: Optional[List[str]] = None,
    ) -> str:
        """
        Install an MCP server from a local directory.
        
        Args:
            path: The path to the MCP server code cloned on your computer
            args: The arguments to pass along
            env: The environment variables to set, delimited by =
        """
        if args is None:
            args = []
            
        if not os.path.exists(path):
            return f"Path '{path}' does not exist."
            
        # Check if it's a Node.js or Python project
        has_package_json = os.path.exists(os.path.join(path, "package.json"))
        has_pyproject_toml = os.path.exists(os.path.join(path, "pyproject.toml"))
        has_setup_py = os.path.exists(os.path.join(path, "setup.py"))
        
        # Determine server name from directory name
        server_name = os.path.basename(path)
        
        # Check if Node.js is installed (for npm packages)
        has_node = check_command_exists("node")
        has_npm = check_command_exists("npm")
        has_pip = check_command_exists("pip")
        has_python = check_command_exists("python")
        
        # Handle Node.js projects
        if has_package_json and has_node and has_npm:
            # For Node.js projects, use node directly to run the local script
            # Find the main entry point from package.json
            try:
                with open(os.path.join(path, "package.json"), "r") as f:
                    package_json = json.load(f)
                    
                # Try to find the main entry point
                main_file = package_json.get("main", "index.js")
                
                # Install to Claude Desktop
                install_to_claude_desktop(
                    server_name,
                    "node",
                    [os.path.join(path, main_file)] + args,
                    env,
                )
                
                return f"Successfully installed local Node.js MCP server '{server_name}'! Please tell the user to restart the application."
            except Exception as e:
                return f"Error installing Node.js MCP server: {str(e)}"
                
        # Handle Python projects
        elif (has_pyproject_toml or has_setup_py) and has_python:
            # For Python projects, use python -m to run the module
            # Try to determine the module name from directory structure
            module_name = server_name.replace("-", "_")
            
            # Check if there's a directory with the same name
            if os.path.isdir(os.path.join(path, module_name)):
                # Install to Claude Desktop
                install_to_claude_desktop(
                    server_name,
                    "python",
                    ["-m", module_name] + args,
                    env,
                    cwd=path,
                )
                
                return f"Successfully installed local Python MCP server '{server_name}'! Please tell the user to restart the application."
            else:
                # Try to find any Python files in the root directory
                py_files = [f for f in os.listdir(path) if f.endswith(".py") and f != "setup.py"]
                
                if py_files:
                    main_file = py_files[0].replace(".py", "")
                    
                    # Install to Claude Desktop
                    install_to_claude_desktop(
                        server_name,
                        "python",
                        [os.path.join(path, main_file + ".py")] + args,
                        env,
                    )
                    
                    return f"Successfully installed local Python MCP server '{server_name}'! Please tell the user to restart the application."
        
        return f"Could not determine how to install MCP server from '{path}'. Make sure it's a valid Node.js or Python project."
  • Core helper function install_to_claude_desktop used by install_local_mcp_server to write the MCP server configuration (command, args, env, cwd) to the Claude Desktop JSON config file under mcpServers.
    def install_to_claude_desktop(
        server_name: str,
        command: str,
        args: List[str],
        env: Optional[List[str]] = None,
        cwd: Optional[str] = None,
    ) -> None:
        """
        Install an MCP server to Claude Desktop.
        
        Args:
            server_name: The name of the MCP server
            command: The command to run the MCP server
            args: The arguments to pass to the command
            env: The environment variables to set, delimited by =
            cwd: The working directory for the command
        """
        # Normalize server name to be a valid identifier
        # For npm packages, make sure we use a simple name without @ or /
        if server_name.startswith("@"):
            # For @scope/package, use just "package" as the server name
            if "/" in server_name:
                server_name = server_name.split("/")[1]
        
        # Remove any invalid characters from server name
        server_name = re.sub(r'[^a-zA-Z0-9_-]', '-', server_name)
        
        # Get the Claude Desktop config file path
        config_path = get_claude_desktop_config_path()
        
        if not config_path:
            raise ValueError("Could not find Claude Desktop config file")
            
        # Read the existing config
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                config = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            config = {}
            
        # Initialize mcpServers if it doesn't exist
        if "mcpServers" not in config:
            config["mcpServers"] = {}
            
        # Prepare the server config
        server_config = {
            "command": command,
            "args": args,
        }
        
        # Add environment variables if provided
        if env and len(env) > 0:
            server_config["env"] = parse_env_vars(env)
                    
        # Add working directory if provided
        if cwd:
            server_config["cwd"] = cwd
            
        # Add the server to the config
        config["mcpServers"][server_name] = server_config
        
        # Write the config back to the file
        with open(config_path, "w", encoding="utf-8") as f:
            json.dump(config, f, indent=2)
  • Helper function check_command_exists to verify if required commands like node, npm, pip are available.
    def check_command_exists(command: str) -> bool:
        """Check if a command exists in the system PATH."""
        return shutil.which(command) is not None
  • Helper function parse_env_vars to parse list of KEY=VALUE strings into a dict for env config.
    def parse_env_vars(env_vars: Optional[List[str]]) -> Optional[Dict[str, str]]:
        """Parse environment variables from a list of KEY=VALUE strings."""
        if not env_vars:
            return None
            
        env_obj = {}
        for env_var in env_vars:
            if "=" in env_var:
                key, value = env_var.split("=", 1)
                env_obj[key] = value
                
        return env_obj if env_obj else None
  • The @mcp.tool() decorator registers the install_local_mcp_server function as an MCP tool.
    @mcp.tool()
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 mentions installing from a local directory but fails to describe critical behaviors like whether this is a destructive operation, what permissions are needed, how errors are handled, or what the expected outcome is. This leaves significant gaps for an agent to understand the tool's behavior.

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

Conciseness4/5

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

The description is appropriately sized and front-loaded, starting with a clear purpose statement followed by a structured parameter list. It avoids unnecessary verbosity, though the parameter descriptions could be slightly more informative without sacrificing conciseness.

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

Completeness2/5

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

Given the complexity of installing an MCP server, no annotations, no output schema, and minimal parameter details, the description is incomplete. It doesn't cover expected outputs, error conditions, dependencies, or behavioral nuances, making it inadequate for an agent to use the tool confidently in varied contexts.

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?

The description includes an 'Args' section that lists and briefly describes the three parameters (path, args, env), adding meaning beyond the input schema, which has 0% description coverage. However, the explanations are minimal (e.g., 'The arguments to pass along' without specifying format or examples), providing only basic semantic value without fully compensating for the schema's lack of detail.

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 ('Install') and resource ('an MCP server from a local directory'), making the purpose specific and understandable. However, it doesn't explicitly differentiate from its sibling tool 'install_repo_mcp_server', which likely installs from a repository rather than a local directory.

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, such as the sibling tool 'install_repo_mcp_server'. It lacks context about prerequisites, typical use cases, or any exclusions, leaving the agent without clear usage direction.

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/XD3an/mcp-builder'

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