Skip to main content
Glama

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
NameRequiredDescriptionDefault
parametersYesComma-separated data parameters
communityYesUser community (RE, SB, AG, etc.)
longitudeYesLongitude coordinate
latitudeYesLatitude coordinate
startYesStart date (YYYYMMDD)
endYesEnd date (YYYYMMDD)
formatNoResponse format (json, csv, etc.)

Implementation Reference

  • 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}`
          }]
        };
      }
    }
  • 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 || {});
      }
    );
  • 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(); 

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/ProgramComputer/NASA-MCP-server'

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