Skip to main content
Glama
ESJavadex

REE MCP Server

by ESJavadex

get_grid_stability

Analyzes grid stability by comparing synchronous generation (provides inertia) with variable renewables (no inertia) at specified times to assess stability risks in Spain's electrical grid.

Instructions

Get grid stability metrics at a specific time.

Analyzes synchronous generation (provides inertia) vs variable renewables (no inertia) to assess grid stability risk.

Args: date: Date in YYYY-MM-DD format hour: Hour in HH format (00-23, default: 12)

Returns: JSON string with grid stability analysis.

Examples: Get grid stability at noon: >>> await get_grid_stability("2025-10-08", "12")

Check overnight stability:
>>> await get_grid_stability("2025-10-08", "02")

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dateYes
hourNo12

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Registration of the get_grid_stability tool using @mcp.tool() decorator and the wrapper handler function that handles input parameters, creates services, and formats response.
    @mcp.tool()
    async def get_grid_stability(date: str, hour: str = "12") -> str:
        """Get grid stability metrics at a specific time.
    
        Analyzes synchronous generation (provides inertia) vs variable renewables
        (no inertia) to assess grid stability risk.
    
        Args:
            date: Date in YYYY-MM-DD format
            hour: Hour in HH format (00-23, default: 12)
    
        Returns:
            JSON string with grid stability analysis.
    
        Examples:
            Get grid stability at noon:
            >>> await get_grid_stability("2025-10-08", "12")
    
            Check overnight stability:
            >>> await get_grid_stability("2025-10-08", "02")
        """
        try:
            start_datetime, end_datetime = DateTimeHelper.build_datetime_range(date, hour)
    
            async with ToolExecutor() as executor:
                use_case = executor.create_get_indicator_data_use_case()
                data_fetcher = DataFetcher(use_case)
                service = GridStabilityService(data_fetcher)
    
                result = await service.get_grid_stability(start_datetime, end_datetime)
    
            return ResponseFormatter.success(result)
    
        except Exception as e:
            return ResponseFormatter.unexpected_error(e, context="Error getting grid stability")
  • Core handler logic in GridStabilityService.get_grid_stability that fetches generation data for synchronous and variable renewables, computes stability metrics including percentages, inertia ratio, and stability level assessment.
    async def get_grid_stability(self, start_date: str, end_date: str) -> dict[str, Any]:
        """Get grid stability metrics.
    
        Args:
            start_date: Start datetime in ISO format
            end_date: End datetime in ISO format
    
        Returns:
            Grid stability analysis with synchronous/variable breakdown
        """
        synchronous = IndicatorIDs.get_synchronous_sources()
        variable_renewables = IndicatorIDs.get_variable_renewable_sources()
    
        # Fetch all data
        sync_data = await self.data_fetcher.fetch_multiple_indicators(
            synchronous, start_date, end_date, "hour"
        )
        var_data = await self.data_fetcher.fetch_multiple_indicators(
            variable_renewables, start_date, end_date, "hour"
        )
    
        result: dict[str, Any] = {
            "datetime": start_date,
            "synchronous_generation": {},
            "variable_renewables": {},
            "analysis": {},
        }
    
        # Process synchronous generation
        total_synchronous_mw = 0.0
        for source_name, response_data in sync_data.items():
            if "error" not in response_data:
                values = response_data.get("values", [])
                if values:
                    value_mw = values[0]["value"]
                    result["synchronous_generation"][source_name] = {"value_mw": value_mw}
                    total_synchronous_mw += value_mw
                else:
                    result["synchronous_generation"][source_name] = {"error": "No data"}
            else:
                result["synchronous_generation"][source_name] = response_data
    
        # Process variable renewables
        total_variable_mw = 0.0
        for source_name, response_data in var_data.items():
            if "error" not in response_data:
                values = response_data.get("values", [])
                if values:
                    value_mw = values[0]["value"]
                    result["variable_renewables"][source_name] = {"value_mw": value_mw}
                    total_variable_mw += value_mw
                else:
                    result["variable_renewables"][source_name] = {"error": "No data"}
            else:
                result["variable_renewables"][source_name] = response_data
    
        # Calculate analysis metrics
        demand_mw = await self.data_fetcher.fetch_value_at_time(
            IndicatorIDs.REAL_DEMAND_NATIONAL, start_date, end_date, "hour"
        )
    
        if demand_mw and demand_mw > 0:
            synchronous_pct = (total_synchronous_mw / demand_mw) * 100
            variable_pct = (total_variable_mw / demand_mw) * 100
            inertia_ratio = (
                (total_synchronous_mw / total_variable_mw)
                if total_variable_mw > 0
                else float("inf")
            )
    
            # Stability assessment
            if synchronous_pct >= 70:
                stability_level = "excellent"
            elif synchronous_pct >= 50:
                stability_level = "good"
            elif synchronous_pct >= 30:
                stability_level = "moderate"
            else:
                stability_level = "concerning"
    
            result["analysis"] = {
                "total_synchronous_mw": round(total_synchronous_mw, 2),
                "total_variable_renewable_mw": round(total_variable_mw, 2),
                "total_demand_mw": round(demand_mw, 2),
                "synchronous_percentage": round(synchronous_pct, 2),
                "variable_renewable_percentage": round(variable_pct, 2),
                "inertia_ratio": (
                    round(inertia_ratio, 2) if inertia_ratio != float("inf") else "infinite"
                ),
                "stability_level": stability_level,
                "interpretation": {
                    "excellent": ">=70% synchronous (high inertia)",
                    "good": "50-70% synchronous (adequate inertia)",
                    "moderate": "30-50% synchronous (requires monitoring)",
                    "concerning": "<30% synchronous (stability risk)",
                },
            }
        else:
            result["analysis"] = {"error": "Could not calculate analysis: No demand data"}
    
        return result
  • GridStabilityService class providing the data fetching and analysis capabilities used by the tool handler.
    class GridStabilityService:
Behavior3/5

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

With no annotations provided, the description carries the full burden. It discloses that the tool analyzes generation types for stability risk, which adds behavioral context beyond basic input/output. However, it lacks details on permissions, rate limits, or error handling, leaving gaps for a tool with no 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 well-structured and front-loaded with the core purpose, followed by analysis details, args, returns, and examples. Every sentence adds value without redundancy, making it efficient and easy to parse.

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 moderate complexity (2 parameters, no annotations, but has output schema), the description is mostly complete. It explains purpose, parameters, and returns, and the output schema handles return values. However, it could benefit from more behavioral context like error cases or data sources.

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?

Schema description coverage is 0%, so the description must compensate. It adds meaning by specifying the date format (YYYY-MM-DD), hour format (HH, 00-23), and default value (12), which are not in the schema. This covers both parameters adequately, though it could detail what 'grid stability metrics' entail.

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 specific action ('Get grid stability metrics') and resource ('grid stability'), distinguishing it from siblings like 'get_generation_mix' or 'get_demand_summary' by focusing on stability analysis rather than generation, demand, or price metrics. It explicitly mentions analyzing synchronous generation vs variable renewables for stability risk assessment.

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?

The description implies usage for assessing grid stability at specific times, with examples showing different hours, but does not explicitly state when to use this tool versus alternatives like 'get_generation_mix' for generation data or 'analyze_demand_volatility' for demand analysis. No exclusions or prerequisites are mentioned.

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/ESJavadex/ree-mcp'

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