Skip to main content
Glama

jpl_horizons_file

Retrieve ephemeris data for solar system objects using JPL Horizons data. Specify target objects, time ranges, and observation parameters to generate position and motion information.

Instructions

JPL Horizons - Solar system objects ephemeris data (File Input)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
formatNoResponse format (json, text)
COMMANDYesTarget object identifier (e.g., '499' for Mars, '1' for Ceres, 'C/2020 F3' for Comet NEOWISE)
OBJ_DATANoInclude object data
MAKE_EPHEMNoGenerate ephemeris
EPHEM_TYPENoType of ephemeris (OBSERVER, VECTORS, ELEMENTS)
CENTERNoCoordinate center (e.g., '500@399' for Earth)
START_TIMENoStart time for ephemeris (e.g., '2023-01-01')
STOP_TIMENoStop time for ephemeris (e.g., '2023-01-02')
STEP_SIZENoStep size for ephemeris points (e.g., '1d' for daily, '1h' for hourly)
QUANTITIESNoObservable quantities to include (e.g., 'A' for all, or '1,2,20,23' for specific ones)
OUT_UNITSNoOutput units for vector tables

Implementation Reference

  • The core handler function `horizonsFileHandler` that executes the `jpl_horizons_file` tool. It formats input parameters into a Horizons-compatible text file, submits via multipart POST to the JPL API, processes the response, adds it as a resource, and returns formatted output.
    export async function horizonsFileHandler(args: Record<string, any>) {
      try {
        // Base URL for the Horizons File API (POST)
        const baseUrl = 'https://ssd.jpl.nasa.gov/api/horizons_file.api';
        
        // Format arguments into the key='value' text format for the input file
        // DO NOT include format here, it's a separate form field.
        const formattedArgs = { ...args };
        delete formattedArgs.format; // Remove format if present
    
        let fileContent = '!$$SOF\n'; // Add !SOF marker
        for (const [key, value] of Object.entries(formattedArgs)) {
          let formattedValue: string | number;
          const upperKey = key.toUpperCase();
    
          // Leave numbers unquoted
          if (typeof value === 'number') {
            formattedValue = value;
          } 
          // Quote ALL other values (strings, including YES/NO)
          else {
            formattedValue = `'${String(value).replace(/'/g, "\'")}'`;
          }
          
          fileContent += `${upperKey}=${formattedValue}\n`;
        }
        fileContent += '!$$EOF\n'; // Correct !EOF marker
    
        // Create FormData payload
        const form = new FormData();
        // Add format as a separate field
        form.append('format', args.format || 'json'); 
        // Add the file content under the 'input' field name
        form.append('input', fileContent, {
          filename: 'horizons_input.txt', // Required filename, content doesn't matter
          contentType: 'text/plain',
        });
    
        // Make the API request using POST with multipart/form-data
        const response = await axios.post(baseUrl, form, {
          headers: {
            ...form.getHeaders(), // Important for correct boundary
          },
        });
        const data = response.data; // Assume response is JSON based on 'format=json'
    
        // Create a resource URI that represents this query (similar to GET handler)
        let resourceUri = 'jpl://horizons-file'; // Distinguish from GET
        let resourceName = 'JPL Horizons file-based ephemeris data';
    
        if (args.COMMAND) {
          resourceUri += `/object/${encodeURIComponent(args.COMMAND)}`;
          resourceName = `${args.COMMAND} ephemeris data (file input)`;
          if (args.START_TIME && args.STOP_TIME) {
            resourceName += ` (${args.START_TIME} to ${args.STOP_TIME})`;
          }
        }
        
        // Add response to resources
        addResource(resourceUri, {
          name: resourceName,
          mimeType: "application/json", // Assuming JSON response
          text: JSON.stringify(data, null, 2)
        });
        
        // Format the response
        return {
          content: [{
            type: "text",
            text: JSON.stringify(data, null, 2)
          }]
        };
      } catch (error: any) {
        let errorMessage = `Error accessing JPL Horizons File API: ${error.message}`;
        if (error.response) {
          // Include more detail from the API response if available
          errorMessage += `\nStatus: ${error.response.status}\nData: ${JSON.stringify(error.response.data)}`;
        }
        return {
          content: [{
            type: "text",
            text: errorMessage
          }],
          isError: true
        };
      }
    }
  • Input schema and metadata definition for the `jpl_horizons_file` tool provided in the `tools/list` MCP endpoint response.
      name: "jpl_horizons_file",
      description: "JPL Horizons - Solar system objects ephemeris data (File Input)",
      inputSchema: {
        type: "object",
        properties: {
          format: {
            type: "string",
            description: "Response format (json, text)",
            enum: ["json", "text"]
          },
          COMMAND: {
            type: "string",
            description: "Target object identifier (e.g., '499' for Mars, '1' for Ceres, 'C/2020 F3' for Comet NEOWISE)"
          },
          OBJ_DATA: {
            type: "string",
            description: "Include object data",
            enum: ["YES", "NO"]
          },
          MAKE_EPHEM: {
            type: "string",
            description: "Generate ephemeris",
            enum: ["YES", "NO"]
          },
          EPHEM_TYPE: {
            type: "string",
            description: "Type of ephemeris (OBSERVER, VECTORS, ELEMENTS)",
            enum: ["OBSERVER", "VECTORS", "ELEMENTS"]
          },
          CENTER: {
            type: "string",
            description: "Coordinate center (e.g., '500@399' for Earth)"
          },
          START_TIME: {
            type: "string",
            description: "Start time for ephemeris (e.g., '2023-01-01')"
          },
          STOP_TIME: {
            type: "string",
            description: "Stop time for ephemeris (e.g., '2023-01-02')"
          },
          STEP_SIZE: {
            type: "string",
            description: "Step size for ephemeris points (e.g., '1d' for daily, '1h' for hourly)"
          },
          QUANTITIES: {
            type: "string",
            description: "Observable quantities to include (e.g., 'A' for all, or '1,2,20,23' for specific ones)"
          },
          OUT_UNITS: {
            type: "string",
            description: "Output units for vector tables",
            enum: ["KM-S", "AU-D", "KM-D"]
          }
        },
        required: ["COMMAND"]
      }
    },
  • src/index.ts:1896-1932 (registration)
    Dynamic registration and dispatch logic in `handleToolCall` for JPL tools. Dynamically imports `./handlers/jpl/horizons_file.js` (endpoint='horizons_file') and executes its default export handler function.
    } 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
        };
      }
    }
  • src/index.ts:2151-2157 (registration)
    Global MCP tool registration for `mcp__jplhorizons_file` that routes calls to the `jpl/horizons_file` handler via `handleToolCall`.
    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);
    });

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