Skip to main content
Glama

typecheck

Validate source code types automatically through HooksMCP to detect errors before execution.

Instructions

Typecheck the source code

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Registers the MCP list_tools and call_tool handlers. list_tools returns tool definitions for all actions including 'typecheck'. call_tool handles execution by matching tool name to config action.
    @server.list_tools() async def list_tools() -> List[Tool]: return tools @server.call_tool() async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]: # Handle get_prompt tool specially if name == "get_prompt": prompt_name = arguments.get("prompt_name") if not prompt_name: raise ExecutionError( "HooksMCP Error: 'prompt_name' argument is required for get_prompt tool" ) # Enforce get_prompt_tool_filter if present if hooks_mcp_config.get_prompt_tool_filter is not None: # If filter is empty, don't allow any prompts (shouldn't happen since tool isn't exposed) if not hooks_mcp_config.get_prompt_tool_filter: raise ExecutionError( "HooksMCP Error: No prompts are available through get_prompt tool" ) # Otherwise, check if prompt is in the filter list if prompt_name not in hooks_mcp_config.get_prompt_tool_filter: available_prompts = ", ".join( hooks_mcp_config.get_prompt_tool_filter ) raise ExecutionError( f"HooksMCP Error: Prompt '{prompt_name}' is not available through get_prompt tool. " f"Available prompts: {available_prompts}" ) # Find the prompt by name config_prompt = next( (p for p in hooks_mcp_config.prompts if p.name == prompt_name), None ) if not config_prompt: raise ExecutionError( f"HooksMCP Error: Prompt '{prompt_name}' not found" ) # Get prompt content prompt_content = get_prompt_content(config_prompt, config_path) # Return the prompt content as text return [TextContent(type="text", text=prompt_content)] # Find the action by name action = next((a for a in hooks_mcp_config.actions if a.name == name), None) if not action: raise ExecutionError(f"HooksMCP Error: Action '{name}' not found") try: # Execute the action result = executor.execute_action(action, arguments) # Format the result output = f"Command executed: {action.command}\n" output += f"Exit code: {result['status_code']}\n" if result["stdout"]: output += f"STDOUT:\n{result['stdout']}\n" if result["stderr"]: output += f"STDERR:\n{result['stderr']}\n" return [TextContent(type="text", text=output)] except ExecutionError: raise except Exception as e: raise ExecutionError( f"HooksMCP Error: Unexpected error executing action '{name}': {str(e)}" )
  • Executes the tool logic for 'typecheck' by substituting parameters into the command (e.g. 'mypy src'), validating inputs, running subprocess securely without shell, capturing and processing output.
    def execute_action( self, action: Action, parameters: Dict[str, Any] ) -> Dict[str, Any]: """ Execute an action with the given parameters. Args: action: The action to execute parameters: Parameters provided by the MCP client Returns: Dictionary containing stdout, stderr, and status code """ # Validate and prepare parameters env_vars = self._prepare_parameters(action, parameters) # Parse command and substitute parameters in command args command_args = self._substitute_parameters(action.command, env_vars) # Determine execution directory execution_dir = self.project_root if action.run_path: execution_dir = self.project_root / action.run_path # Validate run_path is within project boundaries if not validate_project_path(action.run_path, self.project_root): raise ExecutionError( f"HooksMCP Error: Invalid run_path '{action.run_path}' for action '{action.name}'. " f"Path must be within project boundaries and not contain directory traversal sequences." ) # Execute command try: # Use subprocess.run with shell=False for security # Pass environment variables separately to avoid shell injection result = subprocess.run( command_args, cwd=execution_dir, env={**os.environ, **env_vars}, capture_output=True, text=True, timeout=action.timeout, shell=False, ) return { "stdout": process_terminal_output(result.stdout), "stderr": process_terminal_output(result.stderr), "status_code": result.returncode, } except subprocess.TimeoutExpired: raise ExecutionError( f"HooksMCP Error: Command for action '{action.name}' timed out after {action.timeout} seconds" ) except Exception as e: raise ExecutionError( f"HooksMCP Error: Failed to execute command for action '{action.name}': {str(e)}" )
  • Generates the input schema for the 'typecheck' tool based on its Action parameters, excluding env vars, defining properties and required fields.
    def create_tool_definitions( config: HooksMCPConfig, disable_prompt_tool: bool = False ) -> List[Tool]: """ Create MCP tool definitions from the HooksMCP configuration. Args: config: The HooksMCP configuration disable_prompt_tool: If True, don't expose the get_prompt tool Returns: List of MCP Tool definitions """ tools = [] for action in config.actions: # Convert action parameters to MCP tool parameters parameters: List[ActionParameter] = [] for param in action.parameters: # Skip required_env_var and optional_env_var as they're not provided by the client if param.type in [ ParameterType.REQUIRED_ENV_VAR, ParameterType.OPTIONAL_ENV_VAR, ]: continue parameters.append(param) tool = Tool( name=action.name, description=action.description, inputSchema={ "type": "object", "properties": { param.name: { "type": "string", "description": param.description, } for param in parameters }, "required": [ param.name for param in parameters if param.default is None ], }, ) tools.append(tool) # Add get_prompt tool if there are prompts and it should be exposed if config.prompts and not disable_prompt_tool: # Determine which prompts to expose based on get_prompt_tool_filter exposed_prompts = config.prompts if config.get_prompt_tool_filter is not None: # If filter is empty, don't expose the tool at all if not config.get_prompt_tool_filter: return tools # Otherwise, filter prompts by name filter_set = set(config.get_prompt_tool_filter) exposed_prompts = [p for p in config.prompts if p.name in filter_set] # Only add the tool if there are prompts to expose if exposed_prompts: # Build tool description with list of prompts prompt_list_desc = "\n".join( [f"- {prompt.name}: {prompt.description}" for prompt in exposed_prompts] ) tool_description = ( "Get a prompt designed for this codebase. The prompts include:\n" f"{prompt_list_desc}" ) # Create enum of prompt names for the tool parameter prompt_names = [prompt.name for prompt in exposed_prompts] get_prompt_tool = Tool( name="get_prompt", description=tool_description, inputSchema={ "type": "object", "properties": { "prompt_name": { "type": "string", "description": "The name of the prompt to retrieve", "enum": prompt_names, } }, "required": ["prompt_name"], }, ) tools.append(get_prompt_tool) return tools
  • Helper utility to process terminal output from commands like mypy typecheck, stripping ANSI codes and handling carriage returns for clean MCP response.
    def process_terminal_output(text: str) -> str: """ Process terminal output to simulate what would actually be visible after control characters like carriage returns are handled. Args: text: Raw terminal output Returns: Processed text that represents the final visible state """ # Strip ANSI codes first clean_text = strip_ansi_codes(text) # Handle carriage returns - they overwrite the current line lines = clean_text.split("\n") processed_lines = [] for line in lines: # Split by carriage return and keep only the last part (final state) parts = line.split("\r") if parts: # The last part is what would be visible after all carriage returns processed_lines.append(parts[-1]) else: processed_lines.append("") return "\n".join(processed_lines).strip()

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/scosman/actions_mcp'

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