Skip to main content
Glama
API_REFERENCE.md52.3 kB
# OpenDSS MCP Server - API Reference Complete API reference for all MCP tools and utility functions. **Version:** 1.0.0 **Last Updated:** October 14, 2025 --- ## Table of Contents ### MCP Tools 1. [load_feeder](#1-load_feeder) 2. [run_power_flow_analysis](#2-run_power_flow_analysis) 3. [check_voltages](#3-check_voltages) 4. [analyze_capacity](#4-analyze_capacity) 5. [optimize_der](#5-optimize_der) 6. [run_timeseries](#6-run_timeseries) 7. [create_visualization](#7-create_visualization) ### Utility Functions - [Validators](#validators) - [Formatters](#formatters) - [Harmonics Utilities](#harmonics-utilities) - [Inverter Control Utilities](#inverter-control-utilities) ### Error Codes - [Standard Error Codes](#error-codes) --- ## MCP Tools All MCP tools follow a consistent response format: ```typescript { success: boolean, // Operation success status data: object | null, // Result data (null on error) metadata: object | null, // Additional metadata (null on error) errors: string[] | null // Error messages (null on success) } ``` --- ## 1. load_feeder Load an IEEE test feeder into the OpenDSS engine. ### Function Signature ```python def load_feeder( feeder_id: str, modifications: Optional[Dict[str, Any]] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `feeder_id` | `string` | Yes | - | IEEE feeder identifier: `"IEEE13"`, `"IEEE34"`, or `"IEEE123"` | | `modifications` | `dict` | No | `{}` | Optional modifications to apply (see Modifications section) | #### Modifications Structure ```python { "loads": { "<load_name>": { "kw_multiplier": float, # Scale load real power "kvar_multiplier": float # Scale load reactive power } }, "capacitors": { "<cap_name>": { "bus": str, # Bus to connect "kvar": float, # Reactive power rating "phases": int # Number of phases } }, "lines": { "<line_name>": { "linecode": str, # Line configuration "length": float # Length in miles/km } } } ``` ### Return Format ```typescript { success: boolean, data: { feeder_id: string, // Feeder identifier num_buses: number, // Number of buses in circuit num_lines: number, // Number of lines num_transformers: number, // Number of transformers num_loads: number, // Number of loads num_capacitors: number, // Number of capacitors num_regulators: number, // Number of voltage regulators source_bus: string, // Source bus name voltage_bases: number[], // Voltage base values (kV) base_frequency: number, // System frequency (Hz) dss_file_path: string // Path to loaded DSS file }, metadata: { timestamp: string, // ISO 8601 timestamp execution_time_ms: number, // Execution time opendss_version: string // OpenDSS version }, errors: null } ``` ### Example Request ```json { "tool": "load_feeder", "parameters": { "feeder_id": "IEEE13", "modifications": { "loads": { "671": { "kw_multiplier": 1.2 } }, "capacitors": { "new_cap_675": { "bus": "675", "kvar": 300, "phases": 3 } } } } } ``` ### Example Response (Success) ```json { "success": true, "data": { "feeder_id": "IEEE13", "num_buses": 13, "num_lines": 11, "num_transformers": 1, "num_loads": 15, "num_capacitors": 1, "num_regulators": 1, "source_bus": "650", "voltage_bases": [115.0, 4.16], "base_frequency": 60.0, "dss_file_path": "/path/to/ieee_feeders/13Bus/IEEE13.dss" }, "metadata": { "timestamp": "2025-10-14T10:30:00.000Z", "execution_time_ms": 245, "opendss_version": "9.8.0.1" }, "errors": null } ``` ### Example Response (Error) ```json { "success": false, "data": null, "metadata": null, "errors": [ "Unsupported feeder ID: 'IEEE999'. Valid options are: \"IEEE13\", \"IEEE34\", \"IEEE123\"" ] } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `INVALID_FEEDER_ID` | Unsupported feeder ID: '{id}' | Feeder ID not in supported list | | `DSS_FILE_NOT_FOUND` | DSS file not found at path: '{path}' | Feeder DSS file missing | | `DSS_COMPILE_ERROR` | Failed to compile DSS file: {details} | OpenDSS compilation failure | | `MODIFICATION_ERROR` | Error applying modifications: {details} | Invalid modification specification | --- ## 2. run_power_flow_analysis Run power flow analysis on a loaded feeder. ### Function Signature ```python def run_power_flow_analysis( feeder_id: str, options: Optional[Dict[str, Any]] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `feeder_id` | `string` | Yes | - | IEEE feeder identifier | | `options` | `dict` | No | `{}` | Power flow options (see Options section) | #### Options Structure ```python { "max_iterations": int, # Maximum iterations (default: 100) "tolerance": float, # Convergence tolerance (default: 0.0001) "control_mode": str, # Solution mode: "snapshot", "daily", "yearly" (default: "snapshot") "include_harmonics": bool # Run harmonics analysis (default: false) } ``` ### Return Format ```typescript { success: boolean, data: { converged: boolean, // Did power flow converge? iterations: number, // Number of iterations to converge min_voltage: number, // Minimum voltage (pu) min_voltage_bus: string, // Bus with minimum voltage max_voltage: number, // Maximum voltage (pu) max_voltage_bus: string, // Bus with maximum voltage total_losses_kw: number, // Total real power losses (kW) total_losses_kvar: number, // Total reactive power losses (kvar) num_buses: number, // Number of buses in circuit num_nodes: number, // Number of nodes (phases) solution_time_ms: number, // OpenDSS solution time harmonics?: { // Optional harmonics data enabled: boolean, buses_analyzed: string[], max_thd_pct: number, max_thd_bus: string } }, metadata: { timestamp: string, execution_time_ms: number, feeder_id: string }, errors: null } ``` ### Example Request ```json { "tool": "run_power_flow_analysis", "parameters": { "feeder_id": "IEEE13", "options": { "max_iterations": 100, "tolerance": 0.0001, "control_mode": "snapshot", "include_harmonics": false } } } ``` ### Example Response (Success) ```json { "success": true, "data": { "converged": true, "iterations": 8, "min_voltage": 0.9542, "min_voltage_bus": "675.3", "max_voltage": 1.0500, "max_voltage_bus": "650.1", "total_losses_kw": 116.2, "total_losses_kvar": 68.3, "num_buses": 13, "num_nodes": 36, "solution_time_ms": 45, "harmonics": { "enabled": false } }, "metadata": { "timestamp": "2025-10-14T10:35:00.000Z", "execution_time_ms": 52, "feeder_id": "IEEE13" }, "errors": null } ``` ### Example Response (Convergence Failure) ```json { "success": false, "data": null, "metadata": null, "errors": [ "Power flow did not converge after 100 iterations. Check circuit for modeling errors." ] } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load a feeder first. | No feeder has been loaded | | `CONVERGENCE_FAILURE` | Power flow did not converge after {n} iterations | Solution did not converge | | `SOLUTION_ERROR` | OpenDSS solution error: {details} | OpenDSS internal error | | `INVALID_OPTION` | Invalid option: {option} | Unrecognized option parameter | --- ## 3. check_voltages Check all bus voltages against specified limits and identify violations. ### Function Signature ```python def check_voltages( min_voltage_pu: float = 0.95, max_voltage_pu: float = 1.05, phase: Optional[str] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `min_voltage_pu` | `float` | No | `0.95` | Minimum acceptable voltage (per-unit) | | `max_voltage_pu` | `float` | No | `1.05` | Maximum acceptable voltage (per-unit) | | `phase` | `string` | No | `null` | Filter by phase: `"1"`, `"2"`, `"3"`, or `null` for all | ### Return Format ```typescript { success: boolean, data: { violations: Array<{ bus: string, // Bus name phase: string, // Phase number voltage_pu: number, // Actual voltage (pu) deviation_pu: number, // Deviation from limit (pu) deviation_pct: number, // Deviation percentage type: string // "undervoltage" or "overvoltage" }>, summary: { total_violations: number, // Total violation count undervoltage_count: number,// Undervoltage violations overvoltage_count: number, // Overvoltage violations buses_checked: number, // Number of buses analyzed worst_undervoltage: number | null, // Worst undervoltage (pu) worst_overvoltage: number | null, // Worst overvoltage (pu) worst_undervoltage_bus: string | null, worst_overvoltage_bus: string | null }, limits: { min_voltage_pu: number, max_voltage_pu: number } }, metadata: { timestamp: string, execution_time_ms: number }, errors: null } ``` ### Example Request ```json { "tool": "check_voltages", "parameters": { "min_voltage_pu": 0.95, "max_voltage_pu": 1.05, "phase": null } } ``` ### Example Response (Success) ```json { "success": true, "data": { "violations": [ { "bus": "675", "phase": "3", "voltage_pu": 0.9542, "deviation_pu": -0.0458, "deviation_pct": -4.58, "type": "undervoltage" }, { "bus": "634", "phase": "3", "voltage_pu": 0.9501, "deviation_pu": -0.0499, "deviation_pct": -4.99, "type": "undervoltage" } ], "summary": { "total_violations": 2, "undervoltage_count": 2, "overvoltage_count": 0, "buses_checked": 13, "worst_undervoltage": 0.9501, "worst_overvoltage": null, "worst_undervoltage_bus": "634.3", "worst_overvoltage_bus": null }, "limits": { "min_voltage_pu": 0.95, "max_voltage_pu": 1.05 } }, "metadata": { "timestamp": "2025-10-14T10:40:00.000Z", "execution_time_ms": 23 }, "errors": null } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load and solve a feeder first. | No feeder loaded | | `NO_SOLUTION` | No power flow solution available. Run power flow first. | Power flow not solved | | `INVALID_LIMITS` | Voltage limits must satisfy {min} ≤ min_pu < max_pu ≤ {max} | Invalid limit values | | `INVALID_PHASE` | Invalid phase: {phase}. Must be "1", "2", "3", or null | Invalid phase filter | --- ## 4. analyze_capacity Analyze maximum DER hosting capacity at a specific bus. ### Function Signature ```python def analyze_capacity( bus_id: str, der_type: str = "solar", increment_kw: float = 100, max_capacity_kw: float = 10000, constraints: Optional[Dict[str, Any]] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `bus_id` | `string` | Yes | - | Bus identifier where DER will be connected | | `der_type` | `string` | No | `"solar"` | DER type: `"solar"`, `"battery"`, `"wind"` | | `increment_kw` | `float` | No | `100` | Capacity increment for each iteration (kW) | | `max_capacity_kw` | `float` | No | `10000` | Maximum capacity to test (kW) | | `constraints` | `dict` | No | `{}` | Constraint limits (see Constraints section) | #### Constraints Structure ```python { "min_voltage_pu": float, # Minimum voltage limit (default: 0.95) "max_voltage_pu": float, # Maximum voltage limit (default: 1.05) "max_line_loading_pct": float # Max line loading percent (default: 100.0) } ``` ### Return Format ```typescript { success: boolean, data: { bus_id: string, der_type: string, max_capacity_kw: number, // Maximum capacity before violation limiting_constraint: string, // "max_voltage", "min_voltage", "max_loading", or "none" capacity_curve: Array<{ capacity_kw: number, min_voltage_pu: number, max_voltage_pu: number, max_loading_pct: number, losses_kw: number, violation?: string // Present if constraint violated }>, violation_details?: { // Present if capacity limited constraint: string, limit: number, value: number, bus: string } }, metadata: { timestamp: string, execution_time_ms: number, iterations: number // Number of capacity iterations tested }, errors: null } ``` ### Example Request ```json { "tool": "analyze_capacity", "parameters": { "bus_id": "675", "der_type": "solar", "increment_kw": 500, "max_capacity_kw": 5000, "constraints": { "min_voltage_pu": 0.95, "max_voltage_pu": 1.05, "max_line_loading_pct": 100.0 } } } ``` ### Example Response (Success) ```json { "success": true, "data": { "bus_id": "675", "der_type": "solar", "max_capacity_kw": 2500, "limiting_constraint": "max_voltage", "capacity_curve": [ { "capacity_kw": 0, "min_voltage_pu": 0.9542, "max_voltage_pu": 1.0500, "max_loading_pct": 65.2, "losses_kw": 116.2 }, { "capacity_kw": 500, "min_voltage_pu": 0.9654, "max_voltage_pu": 1.0498, "max_loading_pct": 62.1, "losses_kw": 108.5 }, { "capacity_kw": 1000, "min_voltage_pu": 0.9766, "max_voltage_pu": 1.0496, "max_loading_pct": 58.9, "losses_kw": 98.3 }, { "capacity_kw": 1500, "min_voltage_pu": 0.9878, "max_voltage_pu": 1.0494, "max_loading_pct": 55.4, "losses_kw": 85.7 }, { "capacity_kw": 2000, "min_voltage_pu": 0.9990, "max_voltage_pu": 1.0492, "max_loading_pct": 51.2, "losses_kw": 70.1 }, { "capacity_kw": 2500, "min_voltage_pu": 1.0102, "max_voltage_pu": 1.0490, "max_loading_pct": 46.5, "losses_kw": 51.2 }, { "capacity_kw": 3000, "min_voltage_pu": 1.0214, "max_voltage_pu": 1.0488, "max_loading_pct": 41.3, "losses_kw": 29.1, "violation": "max_voltage" } ], "violation_details": { "constraint": "max_voltage", "limit": 1.05, "value": 1.0214, "bus": "675" } }, "metadata": { "timestamp": "2025-10-14T11:00:00.000Z", "execution_time_ms": 3420, "iterations": 7 }, "errors": null } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load a feeder first. | No feeder loaded | | `INVALID_BUS` | Bus '{bus_id}' not found in circuit | Bus doesn't exist | | `INVALID_DER_TYPE` | Invalid DER type: {type}. Valid: solar, battery, wind | Unsupported DER type | | `INVALID_INCREMENT` | Increment must be positive, got {value} | Invalid increment value | | `ANALYSIS_ERROR` | Error during capacity analysis: {details} | Analysis failure | --- ## 5. optimize_der Optimize DER placement to achieve a specified objective. ### Function Signature ```python def optimize_der( der_type: str, capacity_kw: float, battery_kwh: Optional[float] = None, objective: str = "minimize_losses", candidate_buses: Optional[list] = None, constraints: Optional[Dict[str, Any]] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `der_type` | `string` | Yes | - | DER type: `"solar"`, `"battery"`, `"solar_battery"`, `"ev_charger"`, `"wind"` | | `capacity_kw` | `float` | Yes | - | DER capacity in kW | | `battery_kwh` | `float` | No | `null` | Battery energy capacity in kWh (required for battery types) | | `objective` | `string` | No | `"minimize_losses"` | Optimization objective (see Objectives) | | `candidate_buses` | `string[]` | No | `null` | List of bus IDs to evaluate (null = all buses, max 20) | | `constraints` | `dict` | No | `{}` | Constraint limits | #### Optimization Objectives | Objective | Description | |-----------|-------------| | `"minimize_losses"` | Minimize total system real power losses | | `"maximize_capacity"` | Maximize DER capacity before constraint violations | | `"minimize_violations"` | Minimize number of voltage violations | #### Constraints Structure ```python { "min_voltage_pu": float, # Minimum voltage limit (default: 0.95) "max_voltage_pu": float, # Maximum voltage limit (default: 1.05) "max_candidates": int # Maximum buses to evaluate (default: 20) } ``` ### Return Format ```typescript { success: boolean, data: { optimal_bus: string, // Best bus for DER placement der_type: string, capacity_kw: number, objective: string, baseline: { // System without DER total_losses_kw: number, min_voltage_pu: number, max_voltage_pu: number, voltage_violations: number }, optimal_case: { // System with DER at optimal bus total_losses_kw: number, min_voltage_pu: number, max_voltage_pu: number, voltage_violations: number }, improvement_metrics: { loss_reduction_kw: number, loss_reduction_pct: number, voltage_improvement_pu: number, violations_fixed: number }, comparison_table: Array<{ // Ranked results for all candidates bus: string, losses_kw: number, min_voltage: number, violations: number, rank: number }>, candidates_evaluated: number }, metadata: { timestamp: string, execution_time_ms: number, power_flow_runs: number // Number of power flows executed }, errors: null } ``` ### Example Request ```json { "tool": "optimize_der", "parameters": { "der_type": "solar", "capacity_kw": 2000, "battery_kwh": null, "objective": "minimize_losses", "candidate_buses": null, "constraints": { "min_voltage_pu": 0.95, "max_voltage_pu": 1.05, "max_candidates": 20 } } } ``` ### Example Response (Success) ```json { "success": true, "data": { "optimal_bus": "675", "der_type": "solar", "capacity_kw": 2000, "objective": "minimize_losses", "baseline": { "total_losses_kw": 116.2, "min_voltage_pu": 0.9542, "max_voltage_pu": 1.0500, "voltage_violations": 3 }, "optimal_case": { "total_losses_kw": 78.5, "min_voltage_pu": 0.9712, "max_voltage_pu": 1.0498, "voltage_violations": 0 }, "improvement_metrics": { "loss_reduction_kw": 37.7, "loss_reduction_pct": 32.4, "voltage_improvement_pu": 0.0170, "violations_fixed": 3 }, "comparison_table": [ { "bus": "675", "losses_kw": 78.5, "min_voltage": 0.9712, "violations": 0, "rank": 1 }, { "bus": "634", "losses_kw": 82.3, "min_voltage": 0.9698, "violations": 0, "rank": 2 }, { "bus": "671", "losses_kw": 85.1, "min_voltage": 0.9680, "violations": 1, "rank": 3 } ], "candidates_evaluated": 13 }, "metadata": { "timestamp": "2025-10-14T11:15:00.000Z", "execution_time_ms": 5230, "power_flow_runs": 14 }, "errors": null } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load a feeder first. | No feeder loaded | | `INVALID_DER_TYPE` | Invalid DER type: {type} | Unsupported DER type | | `INVALID_CAPACITY` | Capacity must be positive, got {value} | Invalid capacity value | | `INVALID_OBJECTIVE` | Invalid objective: {objective} | Unsupported objective | | `NO_CANDIDATES` | No valid candidate buses found | All buses invalid/failed | | `OPTIMIZATION_ERROR` | Optimization failed: {details} | Optimization failure | --- ## 6. run_timeseries Run time-series power flow simulation with load and generation profiles. ### Function Signature ```python def run_timeseries( load_profile: Union[str, dict], generation_profile: Optional[Union[str, dict]] = None, duration_hours: int = 24, timestep_minutes: int = 60, output_variables: Optional[list] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `load_profile` | `string` or `dict` | Yes | - | Load profile name or custom profile (see Profile Format) | | `generation_profile` | `string` or `dict` | No | `null` | Generation profile name or custom profile | | `duration_hours` | `int` | No | `24` | Simulation duration in hours | | `timestep_minutes` | `int` | No | `60` | Time step resolution in minutes | | `output_variables` | `string[]` | No | `["voltages", "losses"]` | Variables to track | #### Profile Format **String (named profile):** ```python "residential_summer" ``` **Dictionary (custom profile):** ```python { "name": "custom_profile", "multipliers": [0.4, 0.35, 0.3, ...] # One value per timestep } ``` #### Output Variables | Variable | Description | Data Returned | |----------|-------------|---------------| | `"voltages"` | Bus voltages | Min/max/avg voltage per timestep | | `"losses"` | System losses | Real and reactive losses per timestep | | `"loadings"` | Line loading | Maximum line loading per timestep | | `"powers"` | Bus powers | Real and reactive power per bus | ### Return Format ```typescript { success: boolean, data: { timesteps: Array<{ hour: number, time: string, // HH:MM format converged: boolean, min_voltage_pu: number, max_voltage_pu: number, avg_voltage_pu: number, total_losses_kw: number, total_load_kw: number, max_line_loading_pct: number }>, summary: { num_timesteps: number, convergence_rate_pct: number, // % of timesteps that converged min_voltage_pu: number, // Worst voltage across all timesteps max_voltage_pu: number, // Highest voltage across all timesteps avg_losses_kw: number, peak_load_kw: number, peak_load_hour: number, total_energy_delivered_kwh: number, total_energy_losses_kwh: number, loss_percentage: number, voltage_violation_hours: number } }, metadata: { timestamp: string, execution_time_ms: number, load_profile: string, generation_profile: string | null }, errors: null } ``` ### Example Request ```json { "tool": "run_timeseries", "parameters": { "load_profile": { "name": "custom_residential", "multipliers": [0.4, 0.35, 0.3, 0.3, 0.35, 0.5, 0.7, 0.85, 0.9, 0.85, 0.8, 0.75, 0.7, 0.75, 0.8, 0.85, 0.95, 1.0, 0.95, 0.85, 0.75, 0.65, 0.55, 0.45] }, "generation_profile": { "name": "solar_clear_day", "multipliers": [0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.6, 0.8, 0.95, 1.0, 0.95, 0.9, 0.8, 0.6, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] }, "duration_hours": 24, "timestep_minutes": 60, "output_variables": ["voltages", "losses", "loadings"] } } ``` ### Example Response (Success) ```json { "success": true, "data": { "timesteps": [ { "hour": 0, "time": "00:00", "converged": true, "min_voltage_pu": 0.9623, "max_voltage_pu": 1.0500, "avg_voltage_pu": 0.9912, "total_losses_kw": 85.3, "total_load_kw": 2845.6, "max_line_loading_pct": 52.1 }, { "hour": 1, "time": "01:00", "converged": true, "min_voltage_pu": 0.9645, "max_voltage_pu": 1.0500, "avg_voltage_pu": 0.9925, "total_losses_kw": 76.8, "total_load_kw": 2567.3, "max_line_loading_pct": 48.5 } ], "summary": { "num_timesteps": 24, "convergence_rate_pct": 100.0, "min_voltage_pu": 0.9542, "max_voltage_pu": 1.0512, "avg_losses_kw": 98.5, "peak_load_kw": 3842.1, "peak_load_hour": 18, "total_energy_delivered_kwh": 78234.5, "total_energy_losses_kwh": 2364.0, "loss_percentage": 3.02, "voltage_violation_hours": 2 } }, "metadata": { "timestamp": "2025-10-14T11:30:00.000Z", "execution_time_ms": 8920, "load_profile": "custom_residential", "generation_profile": "solar_clear_day" }, "errors": null } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load a feeder first. | No feeder loaded | | `INVALID_PROFILE` | Invalid profile format: {details} | Malformed profile | | `PROFILE_LENGTH_MISMATCH` | Profile length ({n}) doesn't match timesteps ({m}) | Profile too short/long | | `INVALID_DURATION` | Duration must be positive, got {value} | Invalid duration | | `INVALID_TIMESTEP` | Timestep must be between 1 and 60 minutes | Invalid timestep | | `SIMULATION_ERROR` | Time-series simulation failed: {details} | Simulation failure | --- ## 7. create_visualization Generate professional visualizations for power system analysis results. ### Function Signature ```python def create_visualization( plot_type: str, data_source: str = "circuit", options: Optional[Dict[str, Any]] = None ) -> Dict[str, Any] ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `plot_type` | `string` | Yes | - | Type of visualization (see Plot Types) | | `data_source` | `string` | No | `"circuit"` | Data source to visualize (see Data Sources) | | `options` | `dict` | No | `{}` | Plot customization options | #### Plot Types | Plot Type | Description | |-----------|-------------| | `"voltage_profile"` | Bar chart of bus voltages with violation highlighting | | `"network_diagram"` | Network topology diagram with voltage-colored nodes | | `"timeseries"` | Multi-panel line plots for time-varying data | | `"capacity_curve"` | Scatter plot for DER hosting capacity analysis | | `"harmonics_spectrum"` | Bar chart of harmonic voltage magnitudes | #### Data Sources | Source | Compatible Plot Types | |--------|----------------------| | `"circuit"` | voltage_profile, network_diagram | | `"last_power_flow"` | voltage_profile, network_diagram | | `"last_timeseries"` | timeseries | | `"last_capacity"` | capacity_curve | | `"last_harmonics"` | harmonics_spectrum | #### Options Structure ```python { "save_path": str, # File path to save (if null, returns base64) "figsize": tuple, # Figure size (width, height) in inches "dpi": int, # Resolution in dots per inch "title": str, # Custom plot title "show_violations": bool, # Highlight voltage violations (default: true) "bus_filter": list # Specific buses to include (null = all) } ``` ### Return Format ```typescript { success: boolean, data: { plot_type: string, data_source: string, file_path: string | null, // Path if saved, null if base64 image_base64: string | null, // Base64-encoded PNG if not saved dimensions: { width_inches: number, height_inches: number, dpi: number }, content: { // Plot-specific metadata num_buses?: number, num_violations?: number, voltage_range?: number[], timesteps?: number } }, metadata: { timestamp: string, execution_time_ms: number }, errors: null } ``` ### Example Request ```json { "tool": "create_visualization", "parameters": { "plot_type": "voltage_profile", "data_source": "circuit", "options": { "save_path": null, "figsize": [14, 6], "dpi": 100, "title": "IEEE13 Voltage Profile Analysis", "show_violations": true, "bus_filter": null } } } ``` ### Example Response (Success - Base64) ```json { "success": true, "data": { "plot_type": "voltage_profile", "data_source": "circuit", "file_path": null, "image_base64": "iVBORw0KGgoAAAANSUhEUgAABLAAAASwCAYAA...", "dimensions": { "width_inches": 14, "height_inches": 6, "dpi": 100 }, "content": { "num_buses": 13, "num_violations": 3, "voltage_range": [0.9542, 1.0500] } }, "metadata": { "timestamp": "2025-10-14T12:00:00.000Z", "execution_time_ms": 234 }, "errors": null } ``` ### Example Response (Success - Saved File) ```json { "success": true, "data": { "plot_type": "voltage_profile", "data_source": "circuit", "file_path": "/path/to/voltage_profile.png", "image_base64": null, "dimensions": { "width_inches": 14, "height_inches": 6, "dpi": 300 }, "content": { "num_buses": 13, "num_violations": 3, "voltage_range": [0.9542, 1.0500] } }, "metadata": { "timestamp": "2025-10-14T12:00:00.000Z", "execution_time_ms": 412 }, "errors": null } ``` ### Error Codes | Code | Message | Description | |------|---------|-------------| | `NO_DATA` | No data available for plot type: {type} | Data source empty | | `INVALID_PLOT_TYPE` | Invalid plot type: {type} | Unsupported plot type | | `INVALID_DATA_SOURCE` | Invalid data source: {source} | Unsupported data source | | `INCOMPATIBLE_SOURCE` | Data source '{source}' not compatible with plot type '{type}' | Mismatched source/type | | `SAVE_ERROR` | Error saving file to {path}: {details} | File save failure | | `RENDER_ERROR` | Error rendering visualization: {details} | Rendering failure | --- ## Utility Functions ### Validators Module: `opendss_mcp.utils.validators` #### validate_bus_id ```python def validate_bus_id(bus_id: str, dss_circuit: DSSCircuit) -> None ``` Validate that a bus ID exists in the current circuit. **Parameters:** - `bus_id` (str): The bus ID to validate - `dss_circuit` (DSSCircuit): Instance of DSSCircuit to check against **Raises:** - `ValueError`: If the bus ID is not found in the circuit - `RuntimeError`: If there's an error accessing circuit buses **Example:** ```python from opendss_mcp.utils.validators import validate_bus_id from opendss_mcp.utils.dss_wrapper import DSSCircuit circuit = DSSCircuit() validate_bus_id("675", circuit) # Raises ValueError if not found ``` --- #### validate_positive_float ```python def validate_positive_float(value: float, name: str) -> None ``` Validate that a numeric value is positive. **Parameters:** - `value` (float): The value to validate - `name` (str): The name of the parameter (used in error message) **Raises:** - `ValueError`: If the value is not a positive number **Example:** ```python from opendss_mcp.utils.validators import validate_positive_float validate_positive_float(100.0, "capacity_kw") # OK validate_positive_float(-50.0, "capacity_kw") # Raises ValueError validate_positive_float(0.0, "capacity_kw") # Raises ValueError ``` --- #### validate_voltage_limits ```python def validate_voltage_limits(min_pu: float, max_pu: float) -> None ``` Validate voltage limits are within acceptable range and min < max. **Parameters:** - `min_pu` (float): Minimum voltage in per-unit - `max_pu` (float): Maximum voltage in per-unit **Raises:** - `ValueError`: If voltage limits are outside valid range (0.8-1.2 pu) or min >= max **Constants:** - `MIN_VOLTAGE_PU = 0.8`: Absolute minimum allowed - `MAX_VOLTAGE_PU = 1.2`: Absolute maximum allowed **Example:** ```python from opendss_mcp.utils.validators import validate_voltage_limits validate_voltage_limits(0.95, 1.05) # OK validate_voltage_limits(1.05, 0.95) # Raises ValueError (min >= max) validate_voltage_limits(0.5, 1.05) # Raises ValueError (below 0.8) ``` --- #### validate_feeder_id ```python def validate_feeder_id(feeder_id: str) -> None ``` Validate that the feeder ID is one of the supported IEEE test feeders. **Parameters:** - `feeder_id` (str): The feeder ID to validate **Raises:** - `ValueError`: If the feeder ID is not in the list of supported feeders **Constants:** - `VALID_FEEDER_IDS = ["IEEE13", "IEEE34", "IEEE123"]` **Example:** ```python from opendss_mcp.utils.validators import validate_feeder_id validate_feeder_id("IEEE13") # OK validate_feeder_id("IEEE999") # Raises ValueError ``` --- ### Formatters Module: `opendss_mcp.utils.formatters` #### format_success_response ```python def format_success_response( data: Union[Dict[str, Any], List[Any]], metadata: Optional[Dict[str, Any]] = None ) -> SuccessResponse ``` Format a successful API response. **Parameters:** - `data` (dict or list): The main response data - `metadata` (dict, optional): Additional metadata **Returns:** - `SuccessResponse`: Formatted success response with standard structure **Example:** ```python from opendss_mcp.utils.formatters import format_success_response data = {"voltage": 1.02, "bus": "675"} metadata = {"timestamp": "2025-10-14T10:00:00Z"} response = format_success_response(data, metadata) # { # "success": True, # "data": {"voltage": 1.02, "bus": "675"}, # "metadata": {"timestamp": "2025-10-14T10:00:00Z"}, # "errors": None # } ``` --- #### format_error_response ```python def format_error_response(errors: Union[str, List[str]]) -> ErrorResponse ``` Format an error response. **Parameters:** - `errors` (str or list): Single error message or list of error messages **Returns:** - `ErrorResponse`: Formatted error response with standard structure **Example:** ```python from opendss_mcp.utils.formatters import format_error_response response = format_error_response("Bus not found") # { # "success": False, # "data": None, # "metadata": None, # "errors": ["Bus not found"] # } response = format_error_response(["Error 1", "Error 2"]) # { # "success": False, # "data": None, # "metadata": None, # "errors": ["Error 1", "Error 2"] # } ``` --- #### format_voltage_results ```python def format_voltage_results(voltages: Dict[str, float]) -> Dict[str, Any] ``` Calculate and format voltage statistics. **Parameters:** - `voltages` (dict): Dictionary mapping bus names to voltage magnitudes (p.u.) **Returns:** - `dict`: Dictionary containing: - `min` (float): Minimum voltage - `max` (float): Maximum voltage - `avg` (float): Average voltage - `min_bus` (str): Bus with minimum voltage - `max_bus` (str): Bus with maximum voltage **Example:** ```python from opendss_mcp.utils.formatters import format_voltage_results voltages = {"650": 1.05, "671": 0.98, "675": 0.95} stats = format_voltage_results(voltages) # { # "min": 0.95, # "max": 1.05, # "avg": 0.9933, # "min_bus": "675", # "max_bus": "650" # } ``` --- #### format_line_flow_results ```python def format_line_flow_results(flows: Dict[str, Dict[str, float]]) -> Dict[str, Any] ``` Calculate and format line flow statistics. **Parameters:** - `flows` (dict): Dictionary mapping line names to flow information. Each flow must contain: - `P` (float): Real power flow (kW) - `Q` (float): Reactive power flow (kvar) - `loading` (float): Line loading percentage **Returns:** - `dict`: Dictionary containing: - `max_loading` (float): Maximum line loading percentage - `max_loading_line` (str): Line with maximum loading - `total_p` (float): Total real power (kW) - `total_q` (float): Total reactive power (kvar) - `line_count` (int): Number of lines **Example:** ```python from opendss_mcp.utils.formatters import format_line_flow_results flows = { "Line1": {"P": 100, "Q": 50, "loading": 65.2}, "Line2": {"P": 200, "Q": 75, "loading": 82.1} } stats = format_line_flow_results(flows) # { # "max_loading": 82.1, # "max_loading_line": "Line2", # "total_p": 300.0, # "total_q": 125.0, # "line_count": 2 # } ``` --- ### Harmonics Utilities Module: `opendss_mcp.utils.harmonics` #### run_frequency_scan ```python def run_frequency_scan(orders: list[int] | None = None) -> dict[str, Any] ``` Run frequency scan to analyze harmonic content in the circuit. **Parameters:** - `orders` (list, optional): List of harmonic orders to scan (e.g., `[3, 5, 7, 9, 11, 13]`). Default: `[3, 5, 7, 9, 11, 13]` **Returns:** - `dict`: Dictionary containing: - `success` (bool): Whether scan was successful - `harmonic_data` (dict): Mapping of harmonic order to results - `fundamental_frequency_hz` (float): Fundamental frequency (typically 60 Hz) - `errors` (list): List of error messages **Example:** ```python from opendss_mcp.utils.harmonics import run_frequency_scan result = run_frequency_scan([3, 5, 7]) if result['success']: for order, data in result['harmonic_data'].items(): print(f"Order {order}: {data['frequency_hz']} Hz, Converged: {data['converged']}") ``` --- #### calculate_thd ```python def calculate_thd(harmonics: dict[int, float]) -> float ``` Calculate Total Harmonic Distortion (THD) from harmonic magnitudes. **Formula:** ``` THD = sqrt(sum(H_n^2 for n > 1)) / H_1 * 100 ``` **Parameters:** - `harmonics` (dict): Dictionary mapping harmonic order to magnitude. Must include order 1 (fundamental). **Returns:** - `float`: THD percentage. Returns 0.0 if fundamental is missing or zero. **Example:** ```python from opendss_mcp.utils.harmonics import calculate_thd harmonics = {1: 120.0, 3: 10.0, 5: 8.0, 7: 5.0} thd = calculate_thd(harmonics) print(f"THD: {thd:.2f}%") # THD: 11.18% ``` --- #### get_harmonic_voltages ```python def get_harmonic_voltages(bus_id: str, orders: list[int] | None = None) -> dict[str, Any] ``` Get voltage magnitudes at each harmonic order for a specific bus. **Parameters:** - `bus_id` (str): Identifier of the bus to analyze - `orders` (list, optional): List of harmonic orders. Default: `[1, 3, 5, 7, 9, 11, 13]` **Returns:** - `dict`: Dictionary containing: - `success` (bool): Operation success status - `bus_id` (str): The bus identifier - `harmonic_voltages` (dict): Mapping of order to voltage data - `thd_percent` (float): Total harmonic distortion percentage - `fundamental_voltage_pu` (float): Fundamental voltage magnitude - `errors` (list): List of error messages **Example:** ```python from opendss_mcp.utils.harmonics import get_harmonic_voltages result = get_harmonic_voltages("675", [1, 3, 5, 7]) if result['success']: print(f"Bus {result['bus_id']}, THD: {result['thd_percent']:.2f}%") for order, data in result['harmonic_voltages'].items(): print(f" Order {order}: {data['avg_voltage_pu']:.4f} pu") ``` --- #### get_harmonic_currents ```python def get_harmonic_currents(line_id: str, orders: list[int] | None = None) -> dict[str, Any] ``` Get current magnitudes at each harmonic order for a specific line. **Parameters:** - `line_id` (str): Identifier of the line (with or without "Line." prefix) - `orders` (list, optional): List of harmonic orders. Default: `[1, 3, 5, 7, 9, 11, 13]` **Returns:** - `dict`: Dictionary containing: - `success` (bool): Operation success status - `line_id` (str): The line identifier - `harmonic_currents` (dict): Mapping of order to current data - `thd_percent` (float): Total harmonic distortion percentage - `fundamental_current_amps` (float): Fundamental current magnitude - `errors` (list): List of error messages **Example:** ```python from opendss_mcp.utils.harmonics import get_harmonic_currents result = get_harmonic_currents("Line.650632", [1, 3, 5, 7]) if result['success']: print(f"Line {result['line_id']}, THD: {result['thd_percent']:.2f}%") for order, data in result['harmonic_currents'].items(): print(f" Order {order}: {data['max_current_amps']:.2f} A") ``` --- ### Inverter Control Utilities Module: `opendss_mcp.utils.inverter_control` #### load_curve ```python def load_curve(curve_name: str) -> list[tuple[float, float]] ``` Load control curve points from a JSON file. **Parameters:** - `curve_name` (str): Name of standard curve (`"IEEE1547"`, `"RULE21"`) or path to custom JSON file **Returns:** - `list`: List of (x, y) tuples representing curve points **Raises:** - `FileNotFoundError`: If the specified curve file doesn't exist - `ValueError`: If the JSON file is invalid or missing required fields - `KeyError`: If the curve name is not recognized **Standard Curves:** - `"IEEE1547"`: IEEE 1547-2018 Category B volt-var curve - `"RULE21"`: California Rule 21 volt-var curve **Example:** ```python from opendss_mcp.utils.inverter_control import load_curve # Load standard curve points = load_curve("IEEE1547") print(points) # [(0.92, 0.44), (0.98, 0.0), (1.02, 0.0), (1.08, -0.44)] # Load custom curve points = load_curve("path/to/custom_curve.json") ``` --- #### configure_volt_var_control ```python def configure_volt_var_control( pv_name: str, curve_points: list[tuple[float, float]], response_time: float = 10.0, curve_name: str | None = None ) -> None ``` Configure volt-var control for a PV system or inverter in OpenDSS. **Parameters:** - `pv_name` (str): Name of the PVSystem element (without "PVSystem." prefix) - `curve_points` (list): List of (voltage_pu, var_pu) tuples defining the curve - `response_time` (float, optional): Response time in seconds. Default: 10.0 - `curve_name` (str, optional): Name for the XYCurve. Default: auto-generated **Raises:** - `ValueError`: If curve has fewer than 2 points - `RuntimeError`: If configuration fails **Example:** ```python from opendss_mcp.utils.inverter_control import load_curve, configure_volt_var_control # Load and apply IEEE 1547 curve curve = load_curve("IEEE1547") configure_volt_var_control("PV_675", curve, response_time=5.0) # Custom curve custom_curve = [(0.95, 0.44), (0.98, 0.0), (1.02, 0.0), (1.05, -0.44)] configure_volt_var_control("PV_611", custom_curve) ``` --- #### configure_volt_watt_control ```python def configure_volt_watt_control( pv_name: str, curve_points: list[tuple[float, float]], curve_name: str | None = None ) -> None ``` Configure volt-watt control for a PV system or inverter in OpenDSS. **Parameters:** - `pv_name` (str): Name of the PVSystem element (without "PVSystem." prefix) - `curve_points` (list): List of (voltage_pu, watt_pu) tuples defining the curve - `curve_name` (str, optional): Name for the XYCurve. Default: auto-generated **Raises:** - `ValueError`: If curve has fewer than 2 points - `RuntimeError`: If configuration fails **Example:** ```python from opendss_mcp.utils.inverter_control import configure_volt_watt_control # Typical volt-watt curve: curtail above 1.06 pu vw_curve = [ (0.00, 1.00), # Normal operation (1.06, 1.00), # Start curtailment (1.10, 0.20) # 80% curtailment at 1.10 pu ] configure_volt_watt_control("PV_675", vw_curve) ``` --- #### get_inverter_status ```python def get_inverter_status(pv_name: str) -> dict[str, Any] ``` Get current status of a PVSystem inverter including control outputs. **Parameters:** - `pv_name` (str): Name of the PVSystem element (without "PVSystem." prefix) **Returns:** - `dict`: Dictionary containing: - `success` (bool): Whether status was retrieved - `pv_name` (str): The PVSystem name - `kw` (float): Real power output in kW - `kvar` (float): Reactive power output in kvar - `kva` (float): Apparent power in kVA - `pf` (float): Power factor - `voltage_pu` (float): Terminal voltage in per-unit - `errors` (list): List of error messages **Example:** ```python from opendss_mcp.utils.inverter_control import get_inverter_status status = get_inverter_status("PV_675") if status["success"]: print(f"PV Output: {status['kw']:.2f} kW, {status['kvar']:.2f} kvar") print(f"Voltage: {status['voltage_pu']:.4f} pu, PF: {status['pf']:.3f}") ``` --- #### list_available_curves ```python def list_available_curves() -> list[dict[str, str]] ``` List all available standard control curves. **Returns:** - `list`: List of dictionaries containing: - `name` (str): Curve name (for use with `load_curve`) - `file` (str): JSON filename - `description` (str): Curve description - `type` (str): Control type (volt-var, volt-watt, etc.) **Example:** ```python from opendss_mcp.utils.inverter_control import list_available_curves curves = list_available_curves() for curve in curves: print(f"{curve['name']}: {curve['description']}") # IEEE1547: IEEE 1547-2018 Category B volt-var curve # RULE21: California Rule 21 volt-var curve ``` --- ## Error Codes ### Standard Error Response Format All errors follow this structure: ```json { "success": false, "data": null, "metadata": null, "errors": ["Error message 1", "Error message 2"] } ``` ### Error Code Categories #### Circuit State Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `NO_CIRCUIT_LOADED` | No circuit loaded. Load a feeder first. | 400 Bad Request | Operation requires loaded circuit | | `NO_SOLUTION` | No power flow solution available. Run power flow first. | 400 Bad Request | Operation requires solved circuit | | `CIRCUIT_STATE_ERROR` | Circuit state inconsistent: {details} | 500 Internal Server Error | Circuit in invalid state | #### Validation Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `INVALID_FEEDER_ID` | Unsupported feeder ID: '{id}' | 400 Bad Request | Invalid feeder identifier | | `INVALID_BUS` | Bus '{bus_id}' not found in circuit | 404 Not Found | Bus doesn't exist | | `INVALID_LINE` | Line '{line_id}' not found in circuit | 404 Not Found | Line doesn't exist | | `INVALID_PARAMETER` | Invalid parameter '{param}': {details} | 400 Bad Request | Parameter validation failed | | `INVALID_LIMITS` | Voltage limits must satisfy {constraints} | 400 Bad Request | Invalid voltage limits | | `INVALID_CAPACITY` | Capacity must be positive, got {value} | 400 Bad Request | Invalid capacity value | | `INVALID_INCREMENT` | Increment must be positive, got {value} | 400 Bad Request | Invalid increment value | | `INVALID_DER_TYPE` | Invalid DER type: {type} | 400 Bad Request | Unsupported DER type | | `INVALID_OBJECTIVE` | Invalid objective: {objective} | 400 Bad Request | Unsupported objective | | `INVALID_PHASE` | Invalid phase: {phase}. Must be "1", "2", "3", or null | 400 Bad Request | Invalid phase filter | #### OpenDSS Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `DSS_FILE_NOT_FOUND` | DSS file not found at path: '{path}' | 404 Not Found | DSS file missing | | `DSS_COMPILE_ERROR` | Failed to compile DSS file: {details} | 500 Internal Server Error | OpenDSS compilation failure | | `CONVERGENCE_FAILURE` | Power flow did not converge after {n} iterations | 500 Internal Server Error | Solution didn't converge | | `SOLUTION_ERROR` | OpenDSS solution error: {details} | 500 Internal Server Error | OpenDSS internal error | #### Analysis Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `ANALYSIS_ERROR` | Error during capacity analysis: {details} | 500 Internal Server Error | Capacity analysis failed | | `OPTIMIZATION_ERROR` | Optimization failed: {details} | 500 Internal Server Error | Optimization failure | | `SIMULATION_ERROR` | Time-series simulation failed: {details} | 500 Internal Server Error | Time-series failure | | `NO_CANDIDATES` | No valid candidate buses found | 400 Bad Request | All buses invalid/failed | #### Visualization Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `NO_DATA` | No data available for plot type: {type} | 400 Bad Request | Data source empty | | `INVALID_PLOT_TYPE` | Invalid plot type: {type} | 400 Bad Request | Unsupported plot type | | `INVALID_DATA_SOURCE` | Invalid data source: {source} | 400 Bad Request | Unsupported data source | | `INCOMPATIBLE_SOURCE` | Data source '{source}' not compatible with plot type '{type}' | 400 Bad Request | Mismatched source/type | | `SAVE_ERROR` | Error saving file to {path}: {details} | 500 Internal Server Error | File save failure | | `RENDER_ERROR` | Error rendering visualization: {details} | 500 Internal Server Error | Rendering failure | #### File and Profile Errors | Code | Message | HTTP Equivalent | Description | |------|---------|-----------------|-------------| | `FILE_NOT_FOUND` | File not found: {path} | 404 Not Found | File doesn't exist | | `INVALID_PROFILE` | Invalid profile format: {details} | 400 Bad Request | Malformed profile | | `PROFILE_LENGTH_MISMATCH` | Profile length ({n}) doesn't match timesteps ({m}) | 400 Bad Request | Profile too short/long | | `MODIFICATION_ERROR` | Error applying modifications: {details} | 400 Bad Request | Invalid modification | --- ## Version History ### Version 1.0.0 (October 14, 2025) - Initial release with 7 MCP tools - Complete utility function library - Harmonics analysis support - Inverter control utilities - Professional visualization capabilities --- ## Support For issues and questions: - **GitHub Issues:** https://github.com/ahmedelshazly27/opendss-mcp-server/issues - **Documentation:** See [USER_GUIDE.md](USER_GUIDE.md) for usage examples - **Installation:** See [INSTALLATION.md](INSTALLATION.md) for setup instructions --- **End of API Reference**

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/ahmedelshazly27/opendss-mcp-server1'

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