11import { z } from 'zod' ;
22import axios from 'axios' ;
3+ import { addResource } from '../../index' ;
34
45const CMR_API_BASE_URL = 'https://cmr.earthdata.nasa.gov/search' ;
56
@@ -73,8 +74,16 @@ export async function nasaCmrHandler(params: CmrParams) {
7374 ...otherParams
7475 } = params ;
7576
77+ // Determine the correct format extension for the URL
78+ let formatExtension = format ;
79+ if ( format === 'json' ) {
80+ formatExtension = 'json' ;
81+ } else if ( format === 'umm_json' ) {
82+ formatExtension = 'umm_json' ;
83+ }
84+
7685 // Determine search endpoint based on search type
77- const endpoint = `/${ search_type } .${ format } ` ;
86+ const endpoint = `/${ search_type } .${ formatExtension } ` ;
7887
7988 // Construct parameters
8089 const queryParams : Record < string , any > = {
@@ -108,35 +117,123 @@ export async function nasaCmrHandler(params: CmrParams) {
108117 queryParams . include_facets = 'v2' ;
109118 }
110119
120+ console . log ( `Searching CMR: ${ CMR_API_BASE_URL } ${ endpoint } with params:` , queryParams ) ;
121+
111122 // Make the request to CMR directly
112123 const response = await axios ( {
113124 url : `${ CMR_API_BASE_URL } ${ endpoint } ` ,
114125 params : queryParams ,
115126 headers : {
116- 'Client-Id' : 'NASA-MCP-Server'
117- }
127+ 'Client-Id' : 'NASA-MCP-Server' ,
128+ 'Accept' : format === 'json' || format === 'umm_json' ? 'application/json' : undefined
129+ } ,
130+ timeout : 30000 // 30 second timeout
118131 } ) ;
119132
120- return { result : response . data } ;
133+ // Parse the response based on format
134+ let data ;
135+ if ( format === 'json' || format === 'umm_json' ) {
136+ data = response . data ;
137+ } else {
138+ // For non-JSON formats, just return the raw text
139+ data = {
140+ raw : response . data ,
141+ format : format
142+ } ;
143+ }
144+
145+ // Format the response to match MCP expectations
146+ let summary = '' ;
147+ let formattedData ;
148+
149+ if ( search_type === 'collections' ) {
150+ const collectionsCount =
151+ format === 'json' ? ( data . feed ?. entry ?. length || 0 ) :
152+ format === 'umm_json' ? ( data . items ?. length || 0 ) :
153+ 0 ;
154+ summary = `Found ${ collectionsCount } NASA collections` ;
155+ formattedData = data ;
156+ } else {
157+ const granulesCount =
158+ format === 'json' ? ( data . feed ?. entry ?. length || 0 ) :
159+ format === 'umm_json' ? ( data . items ?. length || 0 ) :
160+ 0 ;
161+ summary = `Found ${ granulesCount } data granules` ;
162+ formattedData = data ;
163+ }
164+
165+ // Create a resource ID
166+ const resourceParams = [ ] ;
167+ if ( params . keyword ) resourceParams . push ( `keyword=${ encodeURIComponent ( params . keyword ) } ` ) ;
168+ if ( params . concept_id ) resourceParams . push ( `concept_id=${ params . concept_id } ` ) ;
169+ if ( temporal ) resourceParams . push ( `temporal=${ encodeURIComponent ( temporal ) } ` ) ;
170+
171+ const resourceId = `nasa://cmr/${ search_type } ${ resourceParams . length > 0 ? '?' + resourceParams . join ( '&' ) : '' } ` ;
172+
173+ // Register the response as a resource
174+ addResource ( resourceId , {
175+ name : `NASA CMR ${ search_type } search${ params . keyword ? ` for "${ params . keyword } "` : '' } ` ,
176+ mimeType : 'application/json' ,
177+ text : JSON . stringify ( formattedData , null , 2 )
178+ } ) ;
179+
180+ // If the response includes specific collections or granules, register those too
181+ if ( formattedData . feed ?. entry && Array . isArray ( formattedData . feed . entry ) ) {
182+ formattedData . feed . entry . forEach ( ( entry : any , index : number ) => {
183+ if ( index < 5 ) { // Limit to first 5 entries to avoid too many resources
184+ const entryId = entry . id || entry [ 'concept-id' ] || `${ search_type } -${ index } ` ;
185+ const entryTitle = entry . title || `NASA ${ search_type } Item ${ index + 1 } ` ;
186+
187+ const entryResourceId = `nasa://cmr/${ search_type } /item?id=${ entryId } ` ;
188+
189+ addResource ( entryResourceId , {
190+ name : entryTitle ,
191+ mimeType : 'application/json' ,
192+ text : JSON . stringify ( entry , null , 2 )
193+ } ) ;
194+ }
195+ } ) ;
196+ }
197+
198+ return {
199+ content : [
200+ {
201+ type : "text" ,
202+ text : summary
203+ } ,
204+ {
205+ type : "text" ,
206+ text : JSON . stringify ( formattedData , null , 2 )
207+ }
208+ ] ,
209+ isError : false
210+ } ;
121211 } catch ( error : any ) {
122212 console . error ( 'Error in CMR handler:' , error ) ;
123213
124214 if ( error . name === 'ZodError' ) {
125- throw {
126- error : {
127- type : 'invalid_request' ,
128- message : 'Invalid request parameters' ,
129- details : error . errors
130- }
215+ return {
216+ content : [
217+ {
218+ type : "text" ,
219+ text : `Invalid CMR request parameters: ${ error . message } `
220+ }
221+ ] ,
222+ isError : true
131223 } ;
132224 }
133225
134- throw {
135- error : {
136- type : 'server_error' ,
137- message : error . message || 'An unexpected error occurred' ,
138- details : error . response ?. data || null
139- }
226+ return {
227+ content : [
228+ {
229+ type : "text" ,
230+ text : `Error searching NASA Common Metadata Repository: ${ error . message || 'Unknown error' } `
231+ }
232+ ] ,
233+ isError : true
140234 } ;
141235 }
142- }
236+ }
237+
238+ // Export the handler function directly as default
239+ export default nasaCmrHandler ;
0 commit comments