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()
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It only states the action without disclosing behavioral traits such as whether it modifies files, requires specific permissions, has side effects, or provides output details. This is inadequate for a tool with zero annotation coverage.

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 with no wasted words. It's appropriately sized and front-loaded, making it easy to parse quickly.

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 (a code analysis tool with no annotations and no output schema), the description is incomplete. It doesn't explain what 'typecheck' entails, what the output might be, or how it fits with siblings, leaving significant gaps for the agent.

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

Parameters4/5

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

The input schema has 0 parameters with 100% coverage, so no parameter documentation is needed. The description doesn't add param info, but this is acceptable given the lack of parameters, aligning with the baseline for 0 params.

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

Purpose3/5

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

The description 'Typecheck the source code' states the action (typecheck) and target (source code), which is clear but vague. It doesn't specify what 'source code' refers to (current file, project, etc.) or how it differs from sibling tools like 'lint' or 'check_format', so it lacks sibling differentiation.

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?

No guidance is provided on when to use this tool versus alternatives. With siblings like 'lint', 'check_format', and 'test_file', the description doesn't indicate if this is for static analysis, pre-compilation checks, or other contexts, leaving the agent without 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/scosman/actions_mcp'

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