Skip to main content
Glama

NASA API Desktop Extension

by seoh0711
index.js12 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import fetch from 'node-fetch'; class NASAServer { constructor() { this.server = new Server( { name: 'nasa-api-extension', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.apiKey = process.env.NASA_API_KEY || 'DEMO_KEY'; this.baseUrl = 'https://api.nasa.gov'; this.setupToolHandlers(); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_apod', description: 'NASA의 오늘의 천체 사진(Astronomy Picture of the Day)을 가져옵니다', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'YYYY-MM-DD 형식의 날짜 (선택사항, 기본값: 오늘)', }, hd: { type: 'boolean', description: '고해상도 이미지 여부 (기본값: false)', default: false, }, }, }, }, { name: 'get_mars_rover_photos', description: '화성 로버의 사진을 가져옵니다', inputSchema: { type: 'object', properties: { rover: { type: 'string', enum: ['curiosity', 'opportunity', 'spirit', 'perseverance'], description: '로버 이름', default: 'curiosity', }, sol: { type: 'number', description: '화성 일수 (Sol)', default: 1000, }, camera: { type: 'string', description: '카메라 타입 (FHAZ, RHAZ, MAST, CHEMCAM, MAHLI, MARDI, NAVCAM)', }, page: { type: 'number', description: '페이지 번호', default: 1, }, }, required: ['rover'], }, }, { name: 'get_neo_feed', description: '근지구 천체(Near Earth Objects) 정보를 가져옵니다', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: '시작 날짜 (YYYY-MM-DD)', }, end_date: { type: 'string', description: '종료 날짜 (YYYY-MM-DD)', }, }, }, }, { name: 'search_nasa_images', description: 'NASA 이미지 및 비디오 라이브러리에서 검색합니다', inputSchema: { type: 'object', properties: { q: { type: 'string', description: '검색 쿼리', }, media_type: { type: 'string', enum: ['image', 'video', 'audio'], description: '미디어 타입', default: 'image', }, page: { type: 'number', description: '페이지 번호', default: 1, }, }, required: ['q'], }, }, { name: 'get_earth_imagery', description: 'NASA의 지구 이미지 API를 통해 위성 이미지를 가져옵니다', inputSchema: { type: 'object', properties: { lat: { type: 'number', description: '위도', }, lon: { type: 'number', description: '경도', }, date: { type: 'string', description: '날짜 (YYYY-MM-DD)', }, dim: { type: 'number', description: '이미지 크기 (0.03 ~ 0.5)', default: 0.15, }, }, required: ['lat', 'lon'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_apod': return await this.getAPOD(args); case 'get_mars_rover_photos': return await this.getMarsRoverPhotos(args); case 'get_neo_feed': return await this.getNEOFeed(args); case 'search_nasa_images': return await this.searchNASAImages(args); case 'get_earth_imagery': return await this.getEarthImagery(args); default: throw new McpError( ErrorCode.MethodNotFound, `알 수 없는 도구: ${name}` ); } } catch (error) { throw new McpError( ErrorCode.InternalError, `도구 실행 중 오류 발생: ${error.message}` ); } }); } async getAPOD(args) { const { date, hd = false } = args || {}; const params = new URLSearchParams({ api_key: this.apiKey, hd: hd.toString(), }); if (date) { params.append('date', date); } const response = await fetch(`${this.baseUrl}/planetary/apod?${params}`); const data = await response.json(); if (!response.ok) { throw new Error(`NASA API 오류: ${data.error?.message || '알 수 없는 오류'}`); } return { content: [ { type: 'text', text: `**${data.title}** (${data.date}) ${data.explanation} ${data.media_type === 'image' ? `이미지 URL: ${data.url}` : `비디오 URL: ${data.url}`} ${data.copyright ? `저작권: ${data.copyright}` : ''}`, }, ], }; } async getMarsRoverPhotos(args) { const { rover, sol = 1000, camera, page = 1 } = args; const params = new URLSearchParams({ api_key: this.apiKey, sol: sol.toString(), page: page.toString(), }); if (camera) { params.append('camera', camera); } const response = await fetch(`${this.baseUrl}/mars-photos/api/v1/rovers/${rover}/photos?${params}`); const data = await response.json(); if (!response.ok) { throw new Error(`NASA API 오류: ${data.error?.message || '알 수 없는 오류'}`); } const photos = data.photos.slice(0, 10); // 최대 10개 사진만 반환 return { content: [ { type: 'text', text: `**${rover.toUpperCase()} 로버 사진들** (Sol ${sol}) 총 ${data.photos.length}개의 사진을 찾았습니다. 처음 ${photos.length}개를 보여드립니다: ${photos.map((photo, index) => ` ${index + 1}. **사진 ID**: ${photo.id} - **카메라**: ${photo.camera.full_name} (${photo.camera.name}) - **촬영일**: ${photo.earth_date} - **이미지 URL**: ${photo.img_src} `).join('')}`, }, ], }; } async getNEOFeed(args) { const { start_date, end_date } = args || {}; const params = new URLSearchParams({ api_key: this.apiKey, }); if (start_date) params.append('start_date', start_date); if (end_date) params.append('end_date', end_date); const response = await fetch(`${this.baseUrl}/neo/rest/v1/feed?${params}`); const data = await response.json(); if (!response.ok) { throw new Error(`NASA API 오류: ${data.error?.message || '알 수 없는 오류'}`); } const totalCount = data.element_count; const dates = Object.keys(data.near_earth_objects); let neoList = []; dates.forEach(date => { data.near_earth_objects[date].forEach(neo => { neoList.push({ date, name: neo.name, id: neo.id, diameter: neo.estimated_diameter.kilometers, hazardous: neo.is_potentially_hazardous_asteroid, close_approach: neo.close_approach_data[0], }); }); }); return { content: [ { type: 'text', text: `**근지구 천체 정보** (${start_date || '오늘'} ~ ${end_date || '7일 후'}) 총 ${totalCount}개의 근지구 천체가 발견되었습니다: ${neoList.slice(0, 10).map((neo, index) => ` ${index + 1}. **${neo.name}** - **ID**: ${neo.id} - **날짜**: ${neo.date} - **지름**: ${neo.diameter.estimated_diameter_min.toFixed(3)} - ${neo.diameter.estimated_diameter_max.toFixed(3)} km - **위험 여부**: ${neo.hazardous ? '위험' : '안전'} - **최근접 거리**: ${parseFloat(neo.close_approach.miss_distance.kilometers).toLocaleString()} km - **속도**: ${parseFloat(neo.close_approach.relative_velocity.kilometers_per_hour).toLocaleString()} km/h `).join('')}`, }, ], }; } async searchNASAImages(args) { const { q, media_type = 'image', page = 1 } = args; const params = new URLSearchParams({ q, media_type, page: page.toString(), }); const response = await fetch(`https://images-api.nasa.gov/search?${params}`); const data = await response.json(); if (!response.ok) { throw new Error(`NASA Images API 오류: ${data.reason || '알 수 없는 오류'}`); } const items = data.collection.items.slice(0, 10); // 최대 10개 결과만 반환 return { content: [ { type: 'text', text: `**NASA 이미지 검색 결과** - "${q}" 총 ${data.collection.metadata.total_hits}개의 결과를 찾았습니다. 처음 ${items.length}개를 보여드립니다: ${items.map((item, index) => ` ${index + 1}. **${item.data[0].title}** - **설명**: ${item.data[0].description || '설명 없음'} - **날짜**: ${item.data[0].date_created} - **미디어 타입**: ${item.data[0].media_type} - **이미지 URL**: ${item.links?.[0]?.href || '이미지 없음'} - **NASA ID**: ${item.data[0].nasa_id} `).join('')}`, }, ], }; } async getEarthImagery(args) { const { lat, lon, date, dim = 0.15 } = args; const params = new URLSearchParams({ lat: lat.toString(), lon: lon.toString(), dim: dim.toString(), api_key: this.apiKey, }); if (date) { params.append('date', date); } const response = await fetch(`${this.baseUrl}/planetary/earth/imagery?${params}`); if (!response.ok) { const errorData = await response.json(); throw new Error(`NASA Earth Imagery API 오류: ${errorData.error?.message || '알 수 없는 오류'}`); } // 이미지 데이터를 직접 반환하는 대신 URL을 구성 const imageUrl = `${this.baseUrl}/planetary/earth/imagery?${params}`; return { content: [ { type: 'text', text: `**지구 위성 이미지** 위치: 위도 ${lat}, 경도 ${lon} ${date ? `날짜: ${date}` : '최신 이미지'} 이미지 크기: ${dim} 이미지 URL: ${imageUrl} 이 이미지는 NASA의 Landsat 8 위성에서 촬영된 지구 표면의 위성 이미지입니다.`, }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('NASA API MCP 서버가 시작되었습니다.'); } } const server = new NASAServer(); server.run().catch(console.error);

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/seoh0711/dxt_nasa'

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