Skip to main content
Glama
blockscout

Blockscout MCP Server

Official

inspect_contract_code

Analyze verified smart contract source code and metadata on blockchain networks to understand contract functionality and structure.

Instructions

Inspects a verified contract's source code or metadata.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chain_idYesThe ID of the blockchain.
addressYesThe address of the smart contract.
file_nameNoThe name of the source file to inspect. If omitted, returns contract metadata and the list of source files.

Implementation Reference

  • The main handler function implementing the core logic of the 'inspect_contract_code' tool. It handles metadata retrieval or specific file fetching, progress reporting, validation, and response construction using shared helpers.
    @log_tool_invocation
    async def inspect_contract_code(
        chain_id: Annotated[str, Field(description="The ID of the blockchain.")],
        address: Annotated[str, Field(description="The address of the smart contract.")],
        file_name: Annotated[
            str | None,
            Field(
                description=(
                    "The name of the source file to inspect. "
                    "If omitted, returns contract metadata and the list of source files."
                ),
            ),
        ] = None,
        *,
        ctx: Context,
    ) -> ToolResponse[ContractMetadata | ContractSourceFile]:
        """Inspects a verified contract's source code or metadata."""
        if file_name is None:
            start_msg = f"Starting to fetch contract metadata for {address} on chain {chain_id}..."
        else:
            start_msg = f"Starting to fetch source code for '{file_name}' of contract {address} on chain {chain_id}..."
        await report_and_log_progress(
            ctx,
            progress=0.0,
            total=2.0,
            message=start_msg,
        )
    
        processed = await _fetch_and_process_contract(chain_id, address, ctx)
        if file_name is None:
            metadata = ContractMetadata.model_validate(processed.metadata)
            instructions = None
            notes = None
            if metadata.constructor_args_truncated:
                notes = ["Constructor arguments were truncated to limit context size."]
            if processed.source_files:
                instructions = [
                    (
                        "To retrieve a specific file's contents, call this tool again with the "
                        "'file_name' argument using one of the values from 'source_code_tree_structure'."
                    )
                ]
            return build_tool_response(data=metadata, instructions=instructions, notes=notes)
        if file_name not in processed.source_files:
            available = ", ".join(processed.source_files.keys())
            raise ValueError(
                f"File '{file_name}' not found in the source code for this contract. Available files: {available}"
            )
        return build_tool_response(data=ContractSourceFile(file_content=processed.source_files[file_name]))
  • Key helper function that fetches contract data from Blockscout API or cache, processes source files and metadata (including truncation), and returns a CachedContract object used by the handler.
    async def _fetch_and_process_contract(chain_id: str, address: str, ctx: Context) -> CachedContract:
        """Fetch contract data from cache or Blockscout API."""
    
        normalized_address = address.lower()
        cache_key = f"{chain_id}:{normalized_address}"
        if cached := await contract_cache.get(cache_key):
            return cached
    
        base_url = await get_blockscout_base_url(chain_id)
        await report_and_log_progress(
            ctx,
            progress=1.0,
            total=2.0,
            message="Resolved Blockscout instance URL.",
        )
        api_path = f"/api/v2/smart-contracts/{normalized_address}"
        raw_data = await make_blockscout_request(base_url=base_url, api_path=api_path)
        await report_and_log_progress(
            ctx,
            progress=2.0,
            total=2.0,
            message="Successfully fetched contract data.",
        )
        raw_data.setdefault("name", normalized_address)
        for key in [
            "language",
            "compiler_version",
            "verified_at",
            "optimization_enabled",
            "optimization_runs",
            "evm_version",
            "license_type",
            "proxy_type",
            "is_fully_verified",
            "decoded_constructor_args",
        ]:
            raw_data.setdefault(key, None)
    
        source_files: dict[str, str] = {}
        if raw_data.get("source_code"):
            if raw_data.get("additional_sources"):
                main_file_path = _determine_file_path(raw_data)
                source_files[main_file_path] = raw_data.get("source_code")
                for item in raw_data.get("additional_sources", []):
                    item_path = item.get("file_path")
                    if item_path:
                        source_files[item_path] = item.get("source_code")
            else:
                file_path = _determine_file_path(raw_data)
                source_files[file_path] = raw_data.get("source_code")
    
        # Create a copy to avoid mutating the original raw_data
        metadata_copy = raw_data.copy()
    
        # Process constructor args on the copy instead of the original
        from blockscout_mcp_server.tools.common import _truncate_constructor_args  # Local import to avoid cycles
    
        processed_args, truncated_flag = _truncate_constructor_args(metadata_copy.get("constructor_args"))
        metadata_copy["constructor_args"] = processed_args
        metadata_copy["constructor_args_truncated"] = truncated_flag
        if metadata_copy["decoded_constructor_args"]:
            processed_decoded, decoded_truncated = _truncate_constructor_args(metadata_copy["decoded_constructor_args"])
            metadata_copy["decoded_constructor_args"] = processed_decoded
            if decoded_truncated:
                metadata_copy["constructor_args_truncated"] = True
        metadata_copy["source_code_tree_structure"] = list(source_files.keys())
        for field in [
            "abi",
            "deployed_bytecode",
            "creation_bytecode",
            "source_code",
            "additional_sources",
            "file_path",
        ]:
            metadata_copy.pop(field, None)
    
        cached_contract = CachedContract(metadata=metadata_copy, source_files=source_files)
        await contract_cache.set(cache_key, cached_contract)
        return cached_contract
  • Pydantic models ContractMetadata (for metadata response) and ContractSourceFile (for file content response) defining the structured output schema for the tool. Used in ToolResponse[ContractMetadata | ContractSourceFile].
    class ContractMetadata(BaseModel):
        """Detailed metadata for a verified smart contract."""
    
        # Allow extra fields to preserve language-specific and contract-specific metadata
        # from Blockscout API that varies by verification status and contract type
        model_config = ConfigDict(extra="allow")
    
        name: str = Field(description="The name of the contract.")
        language: str | None = Field(description="The programming language of the contract (e.g., Solidity, Vyper).")
        compiler_version: str | None = Field(description="The compiler version used.")
        verified_at: str | None = Field(description="The timestamp when the contract was verified.")
        source_code_tree_structure: list[str] = Field(description="A list of all source file paths for the contract.")
    
        optimization_enabled: bool | None = Field(description="Flag indicating if compiler optimization was enabled.")
        optimization_runs: int | None = Field(description="The number of optimization runs.")
        evm_version: str | None = Field(description="The EVM version target.")
        license_type: str | None = Field(description="The license of the contract code (e.g., MIT, none).")
        proxy_type: str | None = Field(
            description="The type of proxy if the contract is a proxy (e.g., basic_implementation)."
        )
        is_fully_verified: bool | None = Field(description="Flag indicating if the contract is fully verified.")
    
        constructor_args: str | None = Field(description="The raw constructor arguments, possibly truncated.")
        decoded_constructor_args: str | dict | list | None = Field(
            default=None, description="Decoded constructor arguments, if available."
        )
        constructor_args_truncated: bool = Field(
            default=False, description="Indicates if constructor_args or decoded_constructor_args was truncated."
        )
    
    
    # --- Model for inspect_contract_code File Payload ---
    class ContractSourceFile(BaseModel):
        """Container for a single contract source file."""
    
        file_content: str = Field(description="The raw source code of the file.")
  • Registration of the 'inspect_contract_code' tool with the FastMCP server instance, including annotations for title, read-only hint, etc.
        structured_output=False,
        annotations=create_tool_annotations("Inspect Contract Code"),
    )(inspect_contract_code)

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

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