Skip to main content
Glama
blockscout

Blockscout MCP Server

Official

direct_api_call

Read-only

Call raw Blockscout API endpoints to retrieve blockchain data for specific chains, supporting pagination and query parameters for advanced data access.

Instructions

Call a raw Blockscout API endpoint for advanced or chain-specific data.

Do not include query strings in ``endpoint_path``; pass all query parameters via
``query_params`` to avoid double-encoding.

**SUPPORTS PAGINATION**: If response includes 'pagination' field,
use the provided next_call to get additional pages.

Returns:
    ToolResponse[Any]: Must return ToolResponse[Any] (not ToolResponse[BaseModel])
    because specialized handlers can return lists or other types that don't inherit
    from BaseModel. The dispatcher system supports flexible data structures.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chain_idYesThe ID of the blockchain
endpoint_pathYesThe Blockscout API path to call (e.g., '/api/v2/stats'); do not include query strings.
query_paramsNoOptional query parameters forwarded to the Blockscout API.
cursorNoThe pagination cursor from a previous response to get the next page of results.

Implementation Reference

  • Main execution logic for the 'direct_api_call' tool: resolves Blockscout URL, makes API request with pagination support, dispatches to specialized handlers if applicable, and returns structured ToolResponse.
    @log_tool_invocation
    async def direct_api_call(
        chain_id: Annotated[str, Field(description="The ID of the blockchain")],
        endpoint_path: Annotated[
            str,
            Field(
                description="The Blockscout API path to call (e.g., '/api/v2/stats'); do not include query strings.",
            ),
        ],
        ctx: Context,
        query_params: Annotated[
            dict[str, Any] | None,
            Field(description="Optional query parameters forwarded to the Blockscout API."),
        ] = None,
        cursor: Annotated[
            str | None,
            Field(description="The pagination cursor from a previous response to get the next page of results."),
        ] = None,
    ) -> ToolResponse[Any]:
        """Call a raw Blockscout API endpoint for advanced or chain-specific data.
    
        Do not include query strings in ``endpoint_path``; pass all query parameters via
        ``query_params`` to avoid double-encoding.
    
        **SUPPORTS PAGINATION**: If response includes 'pagination' field,
        use the provided next_call to get additional pages.
    
        Returns:
            ToolResponse[Any]: Must return ToolResponse[Any] (not ToolResponse[BaseModel])
            because specialized handlers can return lists or other types that don't inherit
            from BaseModel. The dispatcher system supports flexible data structures.
        """
        await report_and_log_progress(
            ctx,
            progress=0.0,
            total=2.0,
            message=f"Resolving Blockscout URL for chain {chain_id}...",
        )
        base_url = await get_blockscout_base_url(chain_id)
        if endpoint_path != "/" and endpoint_path.endswith("/"):
            endpoint_path = endpoint_path.rstrip("/")
        if "?" in endpoint_path:
            raise ValueError("Do not include query parameters in endpoint_path. Use query_params instead.")
    
        params = dict(query_params) if query_params else {}
        apply_cursor_to_params(cursor, params)
    
        await report_and_log_progress(
            ctx,
            progress=1.0,
            total=2.0,
            message="Fetching data from Blockscout API...",
        )
        response_json = await make_blockscout_request(base_url=base_url, api_path=endpoint_path, params=params)
    
        handler_response = await dispatcher.dispatch(
            endpoint_path=endpoint_path,
            query_params=query_params,
            response_json=response_json,
            chain_id=chain_id,
            base_url=base_url,
            ctx=ctx,
        )
        if handler_response is not None:
            await report_and_log_progress(
                ctx,
                progress=2.0,
                total=2.0,
                message="Successfully fetched data.",
            )
            return handler_response
    
        pagination = None
        next_page_params = response_json.get("next_page_params")
        if next_page_params:
            next_cursor = encode_cursor(next_page_params)
            next_call_params = {
                "chain_id": chain_id,
                "endpoint_path": endpoint_path,
                "cursor": next_cursor,
            }
            if query_params:
                next_call_params["query_params"] = query_params
            pagination = PaginationInfo(next_call=NextCallInfo(tool_name="direct_api_call", params=next_call_params))
    
        await report_and_log_progress(
            ctx,
            progress=2.0,
            total=2.0,
            message="Successfully fetched data.",
        )
    
        data = DirectApiData.model_validate(response_json)
        return build_tool_response(data=data, pagination=pagination)
  • Registers the 'direct_api_call' tool with the FastMCP server instance.
    mcp.tool(
        structured_output=False,
        annotations=create_tool_annotations("Direct Blockscout API Call"),
    )(direct_api_call)
  • Dispatcher utility that routes API responses to registered specialized handlers based on endpoint path regex matching.
    async def dispatch(
        endpoint_path: str,
        **kwargs: Any,
    ) -> Any | None:
        """Find and execute the first matching handler for the given endpoint path.
    
        Args:
            endpoint_path: The API path that was requested.
            **kwargs: Additional context forwarded to the handler.
    
        Note: precedence follows registration order. Keep regex patterns disjoint or
        register the most specific handler first when overlap is unavoidable.
    
        Returns:
            Any | None: Must return Any (not ToolResponse[BaseModel]) because some handlers
            can return lists or other types that don't inherit from BaseModel. The handler
            is responsible for returning properly structured ToolResponse objects.
        """
        for path_regex, handler in HANDLER_REGISTRY:
            match = path_regex.fullmatch(endpoint_path)
            if match:
                return await handler(match=match, **kwargs)
        return None
  • Generic output schema used by direct_api_call and other tools for standardized responses including data, notes, instructions, and pagination.
    class ToolResponse(BaseModel, Generic[T]):
        """A standardized, structured response for all MCP tools, generic over the data payload type."""
    
        data: T = Field(description="The main data payload of the tool's response.")
    
        data_description: list[str] | None = Field(
            None,
            description="A list of notes explaining the structure, fields, or conventions of the 'data' payload.",
        )
    
        notes: list[str] | None = Field(
            None,
            description=(
                "A list of important contextual notes, such as warnings about data truncation or data quality issues."
            ),
        )
    
        instructions: list[str] | None = Field(
            None,
            description="A list of suggested follow-up actions or instructions for the LLM to plan its next steps.",
        )
    
        pagination: PaginationInfo | None = Field(
            None,
            description="Pagination information, present only if the 'data' is a single page of a larger result set.",
        )
  • Pydantic model for validating and wrapping raw direct API response data, allowing extra fields.
    class DirectApiData(BaseModel):
        """Generic container for direct API responses."""
    
        model_config = ConfigDict(extra="allow")
Behavior4/5

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

Annotations provide readOnlyHint=true, destructiveHint=false, and openWorldHint=true, indicating a safe, read-only operation with flexible endpoints. The description adds valuable context beyond this: it explains pagination support ('If response includes 'pagination' field, use the provided next_call'), clarifies return type handling ('Must return ToolResponse[Any]'), and warns about query parameter encoding. This enhances behavioral understanding without contradicting annotations.

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 well-structured and front-loaded with the core purpose, followed by specific instructions and return details. It uses bold for key points like 'SUPPORTS PAGINATION' and code formatting for parameters. While slightly verbose in the return type explanation, most sentences earn their place by providing critical guidance.

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

Completeness4/5

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

Given the tool's complexity (open-world API calls) and lack of output schema, the description is reasonably complete: it covers purpose, usage rules, pagination, and return type nuances. However, it could benefit from more examples or error-handling context. Annotations provide safety cues, but the description adequately supplements them for agent use.

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?

Schema description coverage is 100%, so the schema fully documents all four parameters. The description adds minimal param-specific semantics: it reiterates not to include query strings in 'endpoint_path' and mentions pagination via 'cursor', but these are largely covered in schema descriptions. This meets the baseline of 3 for high schema coverage.

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

Purpose5/5

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

The description clearly states the tool's purpose: 'Call a raw Blockscout API endpoint for advanced or chain-specific data.' It specifies the verb ('Call'), resource ('Blockscout API endpoint'), and scope ('advanced or chain-specific data'), distinguishing it from sibling tools that perform specific, predefined queries like 'get_address_info' or 'get_transaction_info'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage guidance: it instructs to use this tool for 'advanced or chain-specific data' (implying when sibling tools are insufficient), warns against including query strings in 'endpoint_path', and details pagination handling. This clearly defines when and how to use it versus the more specialized sibling tools.

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

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