nasa_power
Access meteorological data for energy resource prediction by specifying location coordinates, date range, and parameters through NASA's data interface.
Instructions
Prediction of Worldwide Energy Resources - meteorological data
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| parameters | Yes | Comma-separated data parameters | |
| community | Yes | User community (RE, SB, AG, etc.) | |
| longitude | Yes | Longitude coordinate | |
| latitude | Yes | Latitude coordinate | |
| start | Yes | Start date (YYYYMMDD) | |
| end | Yes | End date (YYYYMMDD) | |
| format | No | Response format (json, csv, etc.) |
Implementation Reference
- src/handlers/nasa/power.ts:120-222 (handler)The core handler function `nasaPowerHandler` that implements the `nasa_power` tool logic: validates input with Zod schema, queries NASA POWER API via axios, adds result as MCP resource, formats output as readable text table.export async function nasaPowerHandler(params: PowerParams) { try { // POWER API base URL const POWER_API_URL = 'https://power.larc.nasa.gov/api/temporal/daily/point'; // Validate and normalize parameters using the schema const validatedParams = powerParamsSchema.parse(params); // Call the NASA POWER API const response = await axios({ url: POWER_API_URL, params: validatedParams, // Use validated & normalized params method: 'GET', timeout: 30000 // Increased timeout to 30 seconds }); // Create a resource ID based on key parameters const resourceParams = []; if (validatedParams.parameters) resourceParams.push(`parameters=${validatedParams.parameters}`); if (validatedParams.latitude !== undefined) resourceParams.push(`lat=${validatedParams.latitude}`); if (validatedParams.longitude !== undefined) resourceParams.push(`lon=${validatedParams.longitude}`); if (validatedParams.start) resourceParams.push(`start=${validatedParams.start}`); if (validatedParams.end) resourceParams.push(`end=${validatedParams.end}`); const resourceId = `nasa://power/${validatedParams.community}?${resourceParams.join('&')}`; // Register the response as a resource addResource(resourceId, { name: `NASA POWER ${validatedParams.community.toUpperCase()} Data${validatedParams.latitude !== undefined ? ` at (${validatedParams.latitude}, ${validatedParams.longitude})` : ''}`, mimeType: validatedParams.format === 'json' ? 'application/json' : 'text/plain', text: validatedParams.format === 'json' ? JSON.stringify(response.data, null, 2) : response.data }); // Format the data for display const formattedText = formatPowerDataText(response.data, validatedParams); // Return the formatted result return { content: [ { type: "text", text: formattedText } ], isError: false }; } catch (error: any) { // Handle Zod validation errors separately if (error instanceof z.ZodError) { console.error('Error validating POWER parameters:', error.errors); return { isError: true, content: [{ type: "text", text: `Error: Invalid parameters for NASA POWER API. Issues: ${error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join('; ')}` }] }; } console.error('Error in POWER handler:', error); let errorMessage = 'An unexpected error occurred'; if (error.response) { // The request was made and the server responded with an error status console.error('Response status:', error.response.status); console.error('Response headers:', JSON.stringify(error.response.headers)); console.error('Response data:', JSON.stringify(error.response.data).substring(0, 200)); // Check content type before assuming JSON for error response const contentType = error.response.headers?.['content-type'] || ''; let errorDetails = 'Unknown error'; if (contentType.includes('application/json') && error.response.data) { errorDetails = error.response.data?.message || error.response.data?.errors?.join(', ') || JSON.stringify(error.response.data).substring(0, 100); } else if (typeof error.response.data === 'string') { errorDetails = error.response.data.substring(0, 150); // Show raw text if not JSON } errorMessage = `NASA POWER API error (${error.response.status}): ${errorDetails}`; } else if (error.request) { // The request was made but no response was received console.error('Request details:'); console.error('- URL:', error.config?.url || 'Not available'); // Use config for URL console.error('- Params:', error.config?.params || 'Not available'); console.error('- Method:', error.request.method || 'Not available'); console.error('- Headers:', error.request._header || 'Not available'); errorMessage = `NASA POWER API network error: No response received or request timed out. Check network connectivity and API status. URL: ${error.config?.url}`; } else { // Something happened in setting up the request errorMessage = `NASA POWER API request setup error: ${error.message}`; } return { isError: true, content: [{ type: "text", text: `Error: ${errorMessage}` }] }; } }
- src/handlers/nasa/power.ts:6-22 (schema)Zod input validation schema `powerParamsSchema` and type `PowerParams` used by the handler to parse and normalize parameters for NASA POWER API request.export const powerParamsSchema = z.object({ parameters: z.string(), community: z.enum(['re', 'sb', 'ag', 'RE', 'SB', 'AG']).transform(val => val.toLowerCase()), format: z.enum(['json', 'csv', 'ascii', 'netcdf']).optional().default('json'), // Location parameters latitude: z.number().min(-90).max(90).optional(), longitude: z.number().min(-180).max(180).optional(), // Regional parameters (alternative to lat/long) bbox: z.string().optional(), // Temporal parameters start: z.string().optional(), end: z.string().optional(), // Climatology parameters climatology_start: z.string().optional(), climatology_end: z.string().optional(), time_standard: z.enum(['utc', 'lst']).optional().default('utc') });
- src/index.ts:1657-1674 (registration)Direct MCP request handler registration for method 'nasa/power', which delegates to the generic `handleToolCall` dispatching to the power handler.// POWER Handler server.setRequestHandler( z.object({ method: z.literal("nasa/power"), params: z.object({ community: z.string(), parameters: z.union([z.string(), z.array(z.string())]), latitude: z.number(), longitude: z.number(), start: z.string(), end: z.string(), format: z.string().optional() }).optional() }), async (request) => { return await handleToolCall("nasa/power", request.params || {}); } );
- src/handlers/nasa/power.ts:29-114 (helper)Helper function `formatPowerDataText` that converts raw NASA POWER API JSON response into formatted markdown text with tables for display.function formatPowerDataText(responseData: any, params: PowerParams): string { try { const properties = responseData.properties || {}; const parameterData = properties.parameter || {}; const geometry = responseData.geometry || {}; const header = responseData.header || {}; const requestedParams = params.parameters.split(','); let locationStr = 'Global'; if (geometry.type === 'Point' && geometry.coordinates) { locationStr = `Point (${geometry.coordinates[1]}, ${geometry.coordinates[0]})`; } else if (params.bbox) { locationStr = `Region (${params.bbox})`; } let dateRangeStr = ''; if (header.start && header.end) { dateRangeStr = `${header.start} to ${header.end}`; } else if (params.start && params.end) { dateRangeStr = `${params.start} to ${params.end}`; } let text = `# NASA POWER Data\n\n`; text += `**Community:** ${params.community.toUpperCase()}\n`; text += `**Location:** ${locationStr}\n`; text += `**Date Range:** ${dateRangeStr || 'N/A'}\n\n`; text += `## Parameters\n\n`; requestedParams.forEach(paramKey => { const data = parameterData[paramKey]; const unit = header.parameter_information?.[paramKey]?.units || 'N/A'; const longName = header.parameter_information?.[paramKey]?.long_name || paramKey; text += `### ${longName} (${paramKey})\n`; text += `- **Units:** ${unit}\n`; if (data && typeof data === 'object') { const dates = Object.keys(data).sort(); if (dates.length > 0) { text += `- **Data:**\n`; text += '| Date | Value |\n'; text += '|------------|-------|\n'; // Show first 10 and last 10 dates to avoid excessive length const maxEntries = 10; const totalEntries = dates.length; let entriesShown = 0; for (let i = 0; i < Math.min(maxEntries, totalEntries); i++) { const date = dates[i]; const value = data[date] !== undefined ? data[date] : 'N/A'; text += `| ${date} | ${value} | `; entriesShown++; } if (totalEntries > maxEntries * 2) { text += `| ... | ... |\n`; // Indicate truncation } if (totalEntries > maxEntries) { const startIndex = Math.max(maxEntries, totalEntries - maxEntries); for (let i = startIndex; i < totalEntries; i++) { const date = dates[i]; const value = data[date] !== undefined ? data[date] : 'N/A'; text += `| ${date} | ${value} | `; entriesShown++; } } text += `\n*(Showing ${entriesShown} of ${totalEntries} daily values)*\n\n`; } else { text += `- Data: No data available for this period.\n\n`; } } else { text += `- Data: Not available or invalid format.\n\n`; } }); return text; } catch (formatError: any) { console.error('Error formatting POWER data:', formatError); return `Error: Failed to format POWER data. Raw data might be available in resources. Error: ${formatError.message}`; } }
- src/index.ts:1842-2221 (registration)Generic `handleToolCall` dispatcher that dynamically imports and invokes the specific handler (e.g., ./handlers/nasa/power.ts) based on tool name 'nasa_power' -> 'nasa/power'.async function handleToolCall(toolName: string, args: Record<string, any>) { try { // Convert toolName format (e.g., nasa_neo -> nasa/neo) const internalToolId = toolName.replace('_', '/'); serverInstance?.sendLoggingMessage({ level: "info", data: `Handling tool call for: ${toolName} (Internal ID: ${internalToolId}) with args: ${JSON.stringify(args)}`, }); // Use internalToolId for routing logic if (internalToolId.startsWith("nasa/")) { // Extract the NASA API endpoint name const endpoint = internalToolId.split("/")[1]; const normalizedEndpoint = endpoint.replace(/-/g, '_'); // Normalize dashes for handler lookup if needed // Log endpoint for debugging serverInstance?.sendLoggingMessage({ level: "info", data: `NASA Endpoint: ${endpoint} (Normalized: ${normalizedEndpoint})`, }); try { // Dynamic import for NASA handlers using the original slash format path const handlerModule = await import(`./handlers/nasa/${endpoint}.js`); serverInstance?.sendLoggingMessage({ level: "info", data: `Successfully imported handler module for: ./handlers/nasa/${endpoint}.js`, }); // Try different potential handler function names const handlerFunctionName = `nasa${endpoint.charAt(0).toUpperCase() + endpoint.slice(1).replace(/-/g, '_')}Handler`; // e.g. nasaMars_roverHandler const simpleHandlerName = `${endpoint.replace(/-/g, '_')}Handler`; // e.g. mars_roverHandler const handlerFunction = handlerModule.default || handlerModule[handlerFunctionName] || handlerModule[simpleHandlerName]; if (typeof handlerFunction === 'function') { serverInstance?.sendLoggingMessage({ level: "info", data: `Executing handler function for ${endpoint}`, }); return await handlerFunction(args); } else { serverInstance?.sendLoggingMessage({ level: "info", data: `No handler function found in module: ${JSON.stringify(Object.keys(handlerModule))}`, }); throw new Error(`No handler function found for NASA endpoint: ${normalizedEndpoint}`); } } catch (importError) { throw new Error(`Failed to import handler for NASA endpoint: ${normalizedEndpoint}. Error: ${importError instanceof Error ? importError.message : String(importError)}`); } } else if (internalToolId.startsWith("jpl/")) { // Extract the JPL API endpoint name const endpoint = internalToolId.split("/")[1]; serverInstance?.sendLoggingMessage({ level: "info", data: `JPL Endpoint: ${endpoint}`, }); try { // Dynamic import for JPL handlers using the original slash format path serverInstance?.sendLoggingMessage({ level: "info", data: `Importing handler module: ./handlers/jpl/${endpoint}.js`, }); const handlerModule = await import(`./handlers/jpl/${endpoint}.js`); // Try to find the handler function in various export formats const handlerFunction = handlerModule.default || handlerModule[`jpl${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}Handler`] || handlerModule[`${endpoint}Handler`]; if (typeof handlerFunction === 'function') { return await handlerFunction(args); } else { throw new Error(`Handler for ${endpoint} not found in module`); } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error executing JPL tool '${toolName}': ${errorMessage}` }], isError: true }; } } return { content: [{ type: "text", text: `Unknown tool: ${toolName}` }], isError: true }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error executing tool '${toolName}': ${errorMessage}` }], isError: true }; } } // Utility function to add a resource (can be used by handlers to store results) export function addResource(uri: string, resource: Resource) { addResourceCore(uri, resource); // Send notification about resource change if server is initialized if (serverInstance) { serverInstance.notification({ method: "notifications/resources/list_changed" }); } } // Start the server startServer().catch(error => { console.error("Error starting NASA MCP Server:", error); process.exit(1); }); // Handle stdin close for graceful shutdown process.stdin.on("close", () => { serverInstance?.sendLoggingMessage({ level: "info", data: "NASA MCP Server shutting down...", }); if (serverInstance) { serverInstance.close(); } setTimeout(() => { process.exit(0); }, 100); }); // Helper function to register MCP tools export function registerMcpTools() { try { // Define a type for MCP tool handler functions type McpToolHandler = (args: Record<string, any>) => Promise<any>; // Define a typesafe way to assign to global function registerGlobalTool(name: string, handler: McpToolHandler): void { (global as any)[name] = handler; } // Register each NASA API as an MCP tool registerGlobalTool('mcp__nasaapod', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA APOD called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/apod', args); }); registerGlobalTool('mcp__nasaneo', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA NEO called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/neo', args); }); registerGlobalTool('mcp__nasaepic', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA EPIC called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/epic', args); }); registerGlobalTool('mcp__nasagibs', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA GIBS called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/gibs', args); }); registerGlobalTool('mcp__nasacmr', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA CMR called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/cmr', args); }); registerGlobalTool('mcp__nasafirms', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA FIRMS called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/firms', args); }); registerGlobalTool('mcp__nasaimages', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA Images called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/images', args); }); registerGlobalTool('mcp__nasaexoplanet', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA Exoplanet called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/exoplanet', args); }); registerGlobalTool('mcp__nasadonki', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA DONKI called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/donki', args); }); registerGlobalTool('mcp__nasamars_rover', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA Mars Rover called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/mars-rover', args); }); registerGlobalTool('mcp__nasaeonet', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA EONET called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/eonet', args); }); registerGlobalTool('mcp__nasapower', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP NASA POWER called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('nasa/power', args); }); // Register JPL tools registerGlobalTool('mcp__jplsbdb', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL SBDB called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/sbdb', args); }); registerGlobalTool('mcp__jplfireball', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Fireball called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/fireball', args); }); registerGlobalTool('mcp__jpljd_cal', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL JD Calendar called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/jd_cal', args); }); registerGlobalTool('mcp__jplnhats', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL NHATS called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/nhats', args); }); registerGlobalTool('mcp__jplcad', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL CAD called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/cad', args); }); registerGlobalTool('mcp__jplsentry', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Sentry called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/sentry', args); }); registerGlobalTool('mcp__jplhorizons', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Horizons called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/horizons', args); }); // Register Horizons File Tool registerGlobalTool('mcp__jplhorizons_file', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Horizons File called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/horizons_file', args); }); // Register Periodic Orbits Tool registerGlobalTool('mcp__jplperiodic_orbits', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Periodic Orbits called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/periodic_orbits', args); }); // Register Earth tool - COMMENTED OUT: NASA Earth API is archived and replaced with GIBS // registerGlobalTool('mcp__nasaearth', async (args: Record<string, any>) => { // serverInstance?.sendLoggingMessage({ // level: "info", // data: `MCP NASA Earth called with args: ${JSON.stringify(args)}`, // }); // return await handleToolCall('nasa/earth', args); // }); // Register Scout tool registerGlobalTool('mcp__jplscout', async (args: Record<string, any>) => { serverInstance?.sendLoggingMessage({ level: "info", data: `MCP JPL Scout called with args: ${JSON.stringify(args)}`, }); return await handleToolCall('jpl/scout', args); }); serverInstance?.sendLoggingMessage({ level: "info", data: "All NASA MCP tools registered", }); } catch (error) { console.error('Error registering MCP tools:', error); } } // Call the registration function registerMcpTools();