Skip to main content
Glama

jpl_horizons_file

Access ephemeris data for solar system objects using JPL Horizons. Input target object, time range, and parameters to generate precise positional data in JSON or text format.

Instructions

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

Input Schema

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

Implementation Reference

  • Handler function that executes the tool logic: formats parameters into a Horizons input file, sends multipart POST to https://ssd.jpl.nasa.gov/api/horizons_file.api, adds result as resource, returns JSON response.
    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 description 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: imports and executes handler from ./handlers/jpl/horizons_file.js (matches endpoint 'horizons_file') for jpl_horizons_file tool calls.
    } 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' which delegates to the main handleToolCall with 'jpl/horizons_file'.
    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