Skip to main content
Glama

get_server_tools

Discover available tools on downstream MCP servers with permission-based filtering. Use to find specific tools you can access and execute through the gateway.

Instructions

Discover tools available on a downstream MCP server accessed through this gateway. Returns only tools you have permission to use (filtered by policy rules). Use the returned tool definitions to call execute_tool.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idNoYour agent identifier (leave empty if not provided to you)
serverNoServer name from list_servers
namesNoFilter: comma-separated tool names
patternNoFilter: wildcard pattern (e.g., 'get_*')
max_schema_tokensNoLimit total tokens in returned schemas

Implementation Reference

  • The main handler function for the 'get_server_tools' MCP tool. Retrieves tools from the specified downstream server via ProxyManager, applies policy-based filtering using PolicyEngine, supports filtering by explicit names, wildcard patterns, and token budgets, and returns a structured response with available tools.
    @gateway.tool
    async def get_server_tools(
        agent_id: Annotated[Optional[str], "Your agent identifier (leave empty if not provided to you)"] = None,
        server: Annotated[str, "Server name from list_servers"] = "",
        names: Annotated[Optional[str], "Filter: comma-separated tool names"] = None,
        pattern: Annotated[Optional[str], "Filter: wildcard pattern (e.g., 'get_*')"] = None,
        max_schema_tokens: Annotated[Optional[int], "Limit total tokens in returned schemas"] = None
    ) -> dict:
        """Discover tools available on a downstream MCP server accessed through this gateway. Returns only tools you have permission to use (filtered by policy rules). Use the returned tool definitions to call execute_tool."""
        # Defensive check (middleware should have resolved agent_id)
        if agent_id is None:
            raise ToolError("Internal error: agent_id not resolved by middleware")
    
        # Check for config changes (fallback mechanism for when file watching doesn't work)
        if _check_config_changes_fn:
            try:
                _check_config_changes_fn()
            except Exception:
                pass  # Don't let config check errors break tool execution
    
        # Parse comma-separated names string into list
        names_list: Optional[list[str]] = None
        if names is not None and names.strip():
            # Split by comma and trim whitespace from each name
            names_list = [name.strip() for name in names.split(",") if name.strip()]
            # If we ended up with an empty list after filtering, treat as None
            if not names_list:
                names_list = None
    
        # Get configurations from module-level storage
        policy_engine = _policy_engine
        proxy_manager = _proxy_manager
    
        if not policy_engine:
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error="PolicyEngine not initialized in gateway state"
            ).model_dump()
    
        if not proxy_manager:
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error="ProxyManager not initialized in gateway state"
            ).model_dump()
    
        # Validate agent can access server
        if not policy_engine.can_access_server(agent_id, server):
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error=f"Access denied: Agent '{agent_id}' cannot access server '{server}'"
            ).model_dump()
    
        # Get tools from downstream server
        try:
            all_tools = await proxy_manager.list_tools(server)
        except KeyError:
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error=f"Server '{server}' not found in configured servers"
            ).model_dump()
        except RuntimeError as e:
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error=f"Server unavailable: {str(e)}"
            ).model_dump()
        except Exception as e:
            return GetServerToolsResponse(
                tools=[],
                server=server,
                total_available=0,
                returned=0,
                tokens_used=None,
                error=f"Failed to retrieve tools: {str(e)}"
            ).model_dump()
    
        total_available = len(all_tools)
    
        # Filter tools based on criteria
        filtered_tools = []
        token_count = 0
    
        for tool in all_tools:
            tool_name = tool.name if hasattr(tool, 'name') else str(tool)
    
            # Filter by explicit names list
            if names_list is not None and tool_name not in names_list:
                continue
    
            # Filter by wildcard pattern
            if pattern is not None and not _matches_pattern(tool_name, pattern):
                continue
    
            # Filter by policy permissions
            if not policy_engine.can_access_tool(agent_id, server, tool_name):
                continue
    
            # Check token budget limit
            if max_schema_tokens is not None:
                tool_tokens = _estimate_tool_tokens(tool)
                if token_count + tool_tokens > max_schema_tokens:
                    # Stop adding tools - budget exceeded
                    break
                token_count += tool_tokens
    
            # Convert tool to ToolDefinition
            tool_definition = ToolDefinition(
                name=tool_name,
                description=tool.description if hasattr(tool, 'description') and tool.description else "",
                inputSchema=tool.inputSchema if hasattr(tool, 'inputSchema') else {}
            )
    
            filtered_tools.append(tool_definition)
    
        return GetServerToolsResponse(
            tools=filtered_tools,
            server=server,
            total_available=total_available,
            returned=len(filtered_tools),
            tokens_used=token_count if max_schema_tokens is not None else None
        ).model_dump()
  • Pydantic models defining the structure of tool definitions (ToolDefinition) and the response format (GetServerToolsResponse) for the get_server_tools tool. Input parameters are defined via Annotated types in the handler function signature.
    class ToolDefinition(BaseModel):
        """Tool definition from downstream server."""
        name: Annotated[str, Field(description="Tool name (use in execute_tool)")]
        description: Annotated[str, Field(description="What this tool does")]
        inputSchema: Annotated[dict, Field(description="JSON Schema defining required/optional parameters for execute_tool args")]
    
    
    class GetServerToolsResponse(BaseModel):
        """Response from get_server_tools."""
        tools: Annotated[list[ToolDefinition], Field(description="Tool definitions you can access")]
        server: Annotated[str, Field(description="Queried server name")]
        total_available: Annotated[int, Field(description="Total tools on server (may exceed returned if filtered by permissions/criteria)")]
        returned: Annotated[int, Field(description="Count of tools returned (less than total_available is normal due to filtering)")]
        tokens_used: Annotated[Optional[int], Field(description="Tokens used in schemas (if max_schema_tokens was set)")] = None
        error: Annotated[Optional[str], Field(description="Error message if request failed")] = None
  • src/gateway.py:291-291 (registration)
    FastMCP decorator that registers the get_server_tools function as a tool on the gateway server.
    @gateway.tool
  • Helper function used by get_server_tools to match tool names against wildcard patterns (e.g., 'get_*') using fnmatch.
    def _matches_pattern(tool_name: str, pattern: str) -> bool:
        """Check if tool name matches wildcard pattern.
    
        Uses glob-style pattern matching:
        - * matches any sequence of characters
        - ? matches any single character
        - [seq] matches any character in seq
        - [!seq] matches any character not in seq
    
        Args:
            tool_name: Name of the tool to match
            pattern: Pattern with wildcards (e.g., "get_*", "*_user")
    
        Returns:
            True if tool_name matches pattern, False otherwise
    
        Example:
            >>> _matches_pattern("get_user", "get_*")
            True
            >>> _matches_pattern("delete_user", "get_*")
            False
            >>> _matches_pattern("list_items", "*_items")
            True
        """
        return fnmatch.fnmatch(tool_name, pattern)
  • Helper function used by get_server_tools to estimate the token count of a tool definition for enforcing max_schema_tokens budget limits.
    def _estimate_tool_tokens(tool: Any) -> int:
        """Estimate token count for a tool definition.
    
        Estimates tokens based on name, description, and input schema JSON length.
        Uses rough approximation: characters / 4 = tokens (typical for English text).
    
        Args:
            tool: Tool object with name, description, and inputSchema attributes
    
        Returns:
            Estimated token count for the tool definition
    
        Example:
            >>> tool = Tool(name="get_user", description="Get user by ID", inputSchema={...})
            >>> _estimate_tool_tokens(tool)
            42
        """
        # Count name length
        name_len = len(tool.name) if hasattr(tool, 'name') and tool.name else 0
    
        # Count description length
        desc_len = len(tool.description) if hasattr(tool, 'description') and tool.description else 0
    
        # Count input schema length (convert to string for estimation)
        schema_len = 0
        if hasattr(tool, 'inputSchema') and tool.inputSchema:
            # Convert schema dict to string for rough character count
            import json
            try:
                schema_len = len(json.dumps(tool.inputSchema))
            except Exception:
                # If serialization fails, use a default estimate
                schema_len = 100
    
        # Total characters / 4 = rough token estimate
        total_chars = name_len + desc_len + schema_len
        return max(1, total_chars // 4)

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/roddutra/agent-mcp-gateway'

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