Skip to main content
Glama

get_top_clients

Fetch a list of top clients sorted by total query count. Optionally, set blocked to true to view clients with the most blocked queries.

Instructions

Get top clients by query count. Set blocked=true for top clients by blocked query count.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
countNo
blockedNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The handler function for the 'get_top_clients' tool. It fetches top clients by query count from /stats/top_clients Pi-hole API endpoint. Accepts 'count' (default 10) and 'blocked' (default False) parameters.
    @mcp.tool()
    async def get_top_clients(count: int = 10, blocked: bool = False) -> dict:
        """Get top clients by query count. Set blocked=true for top clients by blocked query count."""
        params: dict = {"count": count}
        if blocked:
            params["blocked"] = "true"
        return await client.get("/stats/top_clients", params=params)
  • The 'register' function defines all stat tools including 'get_top_clients' via @mcp.tool() decorator. The decorator registers the tool with the FastMCP server.
    def register(mcp: FastMCP, client: PiholeClient) -> int:
        @mcp.tool()
        async def get_stats() -> dict:
            """Get summary statistics (total queries, blocked queries, blocking percentage, unique domains, clients)."""
            return await client.get("/stats/summary")
    
        @mcp.tool()
        async def get_top_blocked(count: int = 10) -> dict:
            """Get top blocked domains by query count."""
            return await client.get("/stats/top_domains", params={"blocked": "true", "count": count})
    
        @mcp.tool()
        async def get_top_permitted(count: int = 10) -> dict:
            """Get top allowed (permitted) domains by query count."""
            return await client.get("/stats/top_domains", params={"blocked": "false", "count": count})
    
        @mcp.tool()
        async def get_top_clients(count: int = 10, blocked: bool = False) -> dict:
            """Get top clients by query count. Set blocked=true for top clients by blocked query count."""
            params: dict = {"count": count}
            if blocked:
                params["blocked"] = "true"
            return await client.get("/stats/top_clients", params=params)
    
        @mcp.tool()
        async def get_query_types() -> dict:
            """Breakdown of DNS query types (A, AAAA, PTR, SRV, etc)."""
            return await client.get("/stats/query_types")
    
        @mcp.tool()
        async def get_forward_destinations() -> dict:
            """Upstream DNS server stats (which forwarders served how many queries)."""
            return await client.get("/stats/upstreams")
    
        @mcp.tool()
        async def get_recent_blocked(count: int = 10) -> dict:
            """Recently blocked domains."""
            return await client.get("/stats/recent_blocked", params={"count": count})
    
        @mcp.tool()
        async def get_history() -> dict:
            """Time-series activity graph: timestamps, allowed/blocked/other counts per bucket."""
            return await client.get("/history")
    
        return 8
  • The 'register_all' function calls stats.register(mcp, client) which triggers the registration of get_top_clients and all other stat tools.
    def register_all(mcp: FastMCP, client: PiholeClient) -> int:
        """Register every tool module against the FastMCP instance. Returns tool count."""
        count = 0
        for module in (stats, queries, blocking, domains, local_dns, maintenance):
            count += module.register(mcp, client)
        return count
  • The PiholeClient.get() method used by the handler to make the actual HTTP GET request to the Pi-hole API.
    async def get(self, path: str, *, params: dict[str, Any] | None = None) -> Any:
        return await self.request("GET", path, params=params)
Behavior3/5

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

The description discloses that setting blocked=true changes the metric from query count to blocked query count. However, with no annotations provided, it lacks information on authorization, rate limits, or side effects. It provides basic behavioral insight but not comprehensive.

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?

Two sentences, no redundancy. First sentence states the purpose, second clarifies the parameter. Every word earns its place.

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 simplicity (2 parameters, no required, output schema exists), the description covers the core functionality adequately. It could mention that it returns a list of clients, but the output schema presumably handles that.

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?

The description adds meaning to the 'blocked' parameter (toggles between query count and blocked query count) and implies 'count' is the number of clients. With 0% schema description coverage, this adds some value but could be more detailed.

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 verb 'Get' and the resource 'top clients', and distinguishes between two modes based on the 'blocked' parameter. It is specific and distinguishes from sibling tools like 'get_top_blocked' (which focuses on blocked domains).

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

Usage Guidelines3/5

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

No explicit guidance on when to use this tool versus alternatives. While the description implies its use for top clients, it does not mention when not to use it or suggest any sibling tools for similar tasks.

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/chris2ao/pihole-mcp'

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