Skip to main content
Glama
base_tool.pyโ€ข3.87 kB
""" Base class for MCP tools with project root enforcement. """ import os from typing import Any, Dict, Optional from server.utils.no_cwd_guard import assert_not_server_cwd from server.utils.project_resolver import ProjectRootError, resolve_project_root class BaseMCPTool: """Base class for all MCP tools with project root enforcement.""" def __init__(self, security_manager: Optional[Any] = None): """Initialize base tool.""" self.security_manager = security_manager def resolve_project_root( self, arguments: Dict[str, Any], ide_context: Optional[dict] = None ) -> str: """ Resolve project root from arguments with IDE fallback. Args: arguments: Tool arguments ide_context: IDE metadata from MCP client Returns: Absolute project root path Raises: ProjectRootError: If project root cannot be resolved """ try: project_root = resolve_project_root(arguments, ide_meta=ide_context) assert_not_server_cwd(project_root) return project_root except ProjectRootError as exc: # Re-raise with helpful context raise ProjectRootError( f"Tool {self.__class__.__name__} requires project_root. " "Pass project_root parameter or set PROJECT_PATH environment variable." ) from exc def validate_path_under_project(self, file_path: str, project_root: str) -> str: """ Validate that a file path is under the project root. Args: file_path: File path to validate project_root: Project root directory Returns: Absolute path under project root Raises: ValueError: If path is outside project root """ if os.path.isabs(file_path): abs_path = file_path else: abs_path = os.path.join(project_root, file_path) abs_path = os.path.abspath(abs_path) abs_project_root = os.path.abspath(project_root) # Check if path is under project root try: os.path.relpath(abs_path, abs_project_root) except ValueError as exc: # Happens on Windows when paths are on different drives raise ValueError(f"Path {file_path} is outside project root {project_root}") from exc if not abs_path.startswith(abs_project_root + os.sep) and abs_path != abs_project_root: raise ValueError(f"Path {file_path} is outside project root {project_root}") return abs_path def normalize_inputs(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Normalize input arguments with camelCase/snake_case synonyms. Args: arguments: Raw input arguments Returns: Normalized arguments with standard keys """ # Standard normalization for common parameters normalized = dict(arguments) # Project root if "project_root" not in normalized and "projectRoot" in normalized: normalized["project_root"] = normalized.pop("projectRoot") # File path if "file_path" not in normalized and "filePath" in normalized: normalized["file_path"] = normalized.pop("filePath") # Build tool if "build_tool" not in normalized and "buildTool" in normalized: normalized["build_tool"] = normalized.pop("buildTool") # Skip tests if "skip_tests" not in normalized and "skipTests" in normalized: normalized["skip_tests"] = normalized.pop("skipTests") # Max findings if "max_findings" not in normalized and "maxFindings" in normalized: normalized["max_findings"] = normalized.pop("maxFindings") return normalized

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/normaltusker/kotlin-mcp-server'

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