get_astronomy_picture_of_day
Retrieve NASA's Astronomy Picture of the Day to access daily space images with options for specific dates, random selections, or video thumbnails.
Instructions
Get NASA's astronomy picture of the day.
Args: date: Date of the image in YYYY-MM-DD format. If not specified, the current date is used. count: If specified, returns 'count' random images. Cannot be used with 'date'. thumbs: If True, returns the thumbnail URL for videos. If APOD is not a video, this parameter is ignored.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| count | No | ||
| thumbs | No |
Implementation Reference
- src/nasa_mcp/server.py:86-149 (handler)The handler function that implements the 'get_astronomy_picture_of_day' tool. It fetches APOD data from the NASA API, handles single or multiple images, formats the response with title, explanation, URL, etc., and includes error handling.@mcp.tool() async def get_astronomy_picture_of_day(date: str = None, count: int = None, thumbs: bool = False) -> str: """Get NASA's astronomy picture of the day. Args: date: Date of the image in YYYY-MM-DD format. If not specified, the current date is used. count: If specified, returns 'count' random images. Cannot be used with 'date'. thumbs: If True, returns the thumbnail URL for videos. If APOD is not a video, this parameter is ignored. """ params = {} if date: params["date"] = date if count: params["count"] = count if thumbs: params["thumbs"] = "true" url = f"{NASA_API_BASE}/planetary/apod" data = await make_nasa_request(url, params) if not data: return "Could not retrieve astronomy picture of the day 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"): # APOD URL itself might point to an image, but the API response should be JSON return f"Received unexpected binary content from APOD API. URL: {data.get('url')}" try: # If count is specified, data will be a list if isinstance(data, list): result = [] for item in data: result.append(f"Date: {item.get('date', 'Unknown')}") result.append(f"Title: {item.get('title', 'No title')}") result.append(f"Explanation: {item.get('explanation', 'No explanation')}") result.append(f"URL: {item.get('url', 'Not available')}") if 'copyright' in item: result.append(f"Copyright: {item.get('copyright', 'Unknown')}") if thumbs and 'thumbnail_url' in item: result.append(f"Thumbnail URL: {item.get('thumbnail_url', 'Not available')}") result.append("-" * 40) return "n".join(result) else: # If it's a single image result = f""" Date: {data.get('date', 'Unknown')} Title: {data.get('title', 'No title')} Explanation: {data.get('explanation', 'No explanation')} URL: {data.get('url', 'Not available')} """ if 'copyright' in data: result += f"Copyright: {data.get('copyright', 'Unknown')}n" if thumbs and 'thumbnail_url' in data: result += f"Thumbnail URL: {data.get('thumbnail_url', 'Not available')}n" return result except Exception as e: logger.error(f"Error processing APOD data: {str(e)}") return f"Error processing astronomy picture data: {str(e)}"
- src/nasa_mcp/server.py:28-82 (helper)Shared helper function used by the tool (and others) to perform HTTP requests to NASA APIs, automatically adding the API key, handling JSON/binary responses, timeouts, redirects, and various errors.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)}