Skip to main content
Glama

get_solar_energetic_particle

Retrieve solar energetic particle data from NASA for space weather analysis, specifying date ranges to monitor solar radiation levels.

Instructions

Get solar energetic particle (SEP) data.

Args: start_date: Start date in YYYY-MM-DD format. Defaults to 30 days before current date. end_date: End date in YYYY-MM-DD format. Defaults to current date.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
start_dateNo
end_dateNo

Implementation Reference

  • The handler function decorated with @mcp.tool() that implements the tool logic: fetches SEP data from NASA DONKI API, handles errors, and formats output using format_donki_results.
    async def get_solar_energetic_particle(start_date: str = None, end_date: str = None) -> str:
        """Get solar energetic particle (SEP) data.
        
        Args:
            start_date: Start date in YYYY-MM-DD format. Defaults to 30 days before current date.
            end_date: End date in YYYY-MM-DD format. Defaults to current date.
        """
        params = {}
        if start_date: params["startDate"] = start_date
        if end_date: params["endDate"] = end_date
        
        url = f"{NASA_API_BASE}/DONKI/SEP"
        data = await make_nasa_request(url, params)
    
        if not data: 
            return "Could not retrieve SEP data due to a connection error."
        
        # Check for error response (must be a dictionary)
        if isinstance(data, dict) and "error" in data:
            return f"API Error: {data.get('error')} - Details: {data.get('details', 'N/A')}"
        if isinstance(data, dict) and data.get("binary_content"):
            return f"Received unexpected binary content from SEP API. URL: {data.get('url')}"
    
        try:
            # Ensure data is a list for format_donki_results
            if not isinstance(data, list):
                logger.error(f"Unexpected non-list response from SEP API: {data}")
                return "Received unexpected data format from SEP API."
                
            return format_donki_results(data, "Solar Energetic Particle Events", "sepID")
        except Exception as e:
            logger.error(f"Error processing SEP data: {str(e)}")
            return f"Error processing solar energetic particle data: {str(e)}"
  • Supporting utility function to format lists of DONKI events (used by SEP, CME, GST, MPC, RBE, HSS tools). Formats key fields, limits output, handles linked events.
    def format_donki_results(data: list, title_prefix: str, id_key: str) -> str:
        if not data: 
            return f"No {title_prefix.lower()} data for the specified period."
        
        result = [f"{title_prefix} found: {len(data)}"]
        display_limit = 10
        count = 0
    
        for item in data:
            if count >= display_limit:
                result.append(f"n... and {len(data) - display_limit} more entries.")
                break
            
            result.append(f"nID: {item.get(id_key, 'Unknown')}")
            # Add common fields if they exist
            if 'startTime' in item: result.append(f"Start Time: {item.get('startTime', 'Unknown')}")
            if 'eventTime' in item: result.append(f"Event Time: {item.get('eventTime', 'Unknown')}")
            if 'sourceLocation' in item: result.append(f"Source Location: {item.get('sourceLocation', 'Unknown')}")
            if 'note' in item: result.append(f"Note: {item.get('note', 'N/A')}")
            if 'link' in item: result.append(f"Link: {item.get('link', 'N/A')}")
            
            # Specific fields for different DONKI types can be added here if needed
            # Example for CME:
            if id_key == 'activityID' and 'cmeAnalyses' in item:
                analyses = item.get('cmeAnalyses', [])
                if analyses:
                    result.append("  Analyses:")
                    for analysis in analyses[:2]: # Limit analyses shown
                        result.append(f"    - Time: {analysis.get('time21_5', 'N/A')}, Speed: {analysis.get('speed', 'N/A')} km/s, Type: {analysis.get('type', 'N/A')}")
            
            # Example for GST:
            if id_key == 'gstID' and 'allKpIndex' in item:
                 kp_indices = item.get('allKpIndex', [])
                 if kp_indices:
                     result.append("  Kp Indices (first 2):")
                     for kp in kp_indices[:2]:
                         result.append(f"    - Time: {kp.get('observedTime', 'N/A')}, Index: {kp.get('kpIndex', 'N/A')}")
    
            # Linked Events
            linked_events = item.get('linkedEvents', [])
            if linked_events:
                result.append("  Related event IDs (first 5):")
                result.append("    " + ", ".join([le.get('activityID', 'N/A') for le in linked_events[:5]]))
    
            result.append("-" * 40)
            count += 1
            
        return "n".join(result)
  • Core utility for making HTTP requests to NASA APIs, adding API key, handling JSON/binary responses, errors, and logging. Used by all tools including get_solar_energetic_particle.
    async def make_nasa_request(url: str, params: dict = None) -> Union[dict[str, Any], List[Any], None]:
        """Make a request to the NASA API with proper error handling.
        Handles both JSON and binary (image) responses.
        """
        
        logger.info(f"Making request to: {url} with params: {params}")
        
        if params is None:
            params = {}
        
        # Ensure API key is included in parameters
        if "api_key" not in params:
            params["api_key"] = API_KEY
        
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(url, params=params, timeout=30.0, follow_redirects=True)
                response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
    
                content_type = response.headers.get("Content-Type", "").lower()
                
                if "application/json" in content_type:
                    try:
                        return response.json()
                    except json.JSONDecodeError as json_err:
                        logger.error(f"JSON decode error for URL {response.url}: {json_err}")
                        logger.error(f"Response text: {response.text[:500]}") # Log beginning of text
                        return {"error": "Failed to decode JSON response", "details": str(json_err)}
                elif content_type.startswith("image/"):
                    logger.info(f"Received binary image content ({content_type}) from {response.url}")
                    # Return a dictionary indicating binary content was received
                    return {
                        "binary_content": True, 
                        "content_type": content_type,
                        "url": str(response.url) # Return the final URL after redirects
                    }
                else:
                    # Handle other unexpected content types
                    logger.warning(f"Unexpected content type '{content_type}' received from {response.url}")
                    return {"error": f"Unexpected content type: {content_type}", "content": response.text[:500]}
    
            except httpx.HTTPStatusError as http_err:
                logger.error(f"HTTP error occurred: {http_err} - {http_err.response.status_code} for URL {http_err.request.url}")
                try:
                    # Try to get more details from response body if possible
                    error_details = http_err.response.json()
                except Exception:
                    error_details = http_err.response.text[:500]
                return {"error": f"HTTP error: {http_err.response.status_code}", "details": error_details}
            except httpx.RequestError as req_err:
                logger.error(f"Request error occurred: {req_err} for URL {req_err.request.url}")
                return {"error": "Request failed", "details": str(req_err)}
            except Exception as e:
                logger.error(f"An unexpected error occurred: {str(e)}")
                return {"error": "An unexpected error occurred", "details": str(e)}
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool retrieves data (implying a read-only operation) and mentions date parameters with defaults, but lacks critical details: it doesn't specify data format, source, rate limits, authentication needs, error conditions, or whether it's a real-time or historical query. For a data retrieval tool with zero annotation coverage, this is insufficient.

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 appropriately sized and well-structured: a clear purpose statement followed by a parameter list with concise explanations. Every sentence adds value—none are redundant or vague. It could be slightly more front-loaded by integrating parameter hints into the main sentence, but overall it's efficient and readable.

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

Completeness2/5

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

Given the complexity of solar data retrieval, no annotations, and no output schema, the description is incomplete. It covers parameters well but omits essential context: output format (e.g., JSON, CSV), data granularity, source reliability, error handling, and how it differs from sibling solar tools. Without this, an agent might struggle to use the tool effectively or interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds significant value beyond the input schema, which has 0% description coverage. It explicitly defines both parameters ('start_date' and 'end_date'), specifies their format ('YYYY-MM-DD'), and provides default behaviors ('Defaults to 30 days before current date' and 'Defaults to current date'). This fully compensates for the schema's lack of descriptions, making parameter usage clear.

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

Purpose4/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: 'Get solar energetic particle (SEP) data.' This specifies the verb ('Get') and resource ('solar energetic particle data'), making the function unambiguous. However, it doesn't differentiate from sibling tools like 'get_coronal_mass_ejection' or 'get_solar_flare' that also retrieve solar event data, which prevents a perfect score.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools like 'get_coronal_mass_ejection' or 'get_solar_flare' for comparison, nor does it specify use cases, prerequisites, or exclusions. The only implicit context is the date parameters, but this doesn't constitute meaningful usage guidance.

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/AnCode666/nasa-mcp'

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