search_springer
Search academic papers from Springer Nature database using metadata or open access filters to find relevant research publications.
Instructions
Search academic papers from Springer Nature database. Uses Metadata API by default (all content) or OpenAccess API when openAccess=true (full text available). Same API key works for both.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query string | |
| maxResults | No | Maximum number of results to return | |
| year | No | Year filter (e.g., "2023", "2020-2023") | |
| author | No | Author name filter | |
| journal | No | Journal name filter | |
| subject | No | Subject area filter | |
| openAccess | No | Search only open access content | |
| type | No | Publication type filter |
Implementation Reference
- src/mcp/handleToolCall.ts:347-370 (handler)MCP tool handler case for 'search_springer'. Validates API key, calls SpringerSearcher.search with parameters, formats and returns JSON response with paper results.case 'search_springer': { const { query, maxResults, year, author, journal, subject, openAccess, type } = args; if (!process.env.SPRINGER_API_KEY) { throw new Error('Springer API key not configured. Please set SPRINGER_API_KEY environment variable.'); } const results = await searchers.springer.search(query, { maxResults, year, author, journal, subject, openAccess, type } as any); return jsonTextResponse( `Found ${results.length} Springer papers.\n\n${JSON.stringify( results.map((paper: Paper) => PaperFactory.toDict(paper)), null, 2 )}` ); }
- src/mcp/schemas.ts:159-170 (schema)Zod input schema validation for search_springer tool parameters including query, filters, and pagination.export const SearchSpringerSchema = z .object({ query: z.string().min(1), maxResults: z.number().int().min(1).max(100).optional().default(10), year: z.string().optional(), author: z.string().optional(), journal: z.string().optional(), subject: z.string().optional(), openAccess: z.boolean().optional(), type: z.enum(['Journal', 'Book', 'Chapter']).optional() }) .strip();
- src/mcp/tools.ts:390-420 (registration)MCP tool registration entry for 'search_springer' including name, description, and input schema definition.{ name: 'search_springer', description: 'Search academic papers from Springer Nature database. Uses Metadata API by default (all content) or OpenAccess API when openAccess=true (full text available). Same API key works for both.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query string' }, maxResults: { type: 'number', minimum: 1, maximum: 100, description: 'Maximum number of results to return' }, year: { type: 'string', description: 'Year filter (e.g., "2023", "2020-2023")' }, author: { type: 'string', description: 'Author name filter' }, journal: { type: 'string', description: 'Journal name filter' }, subject: { type: 'string', description: 'Subject area filter' }, openAccess: { type: 'boolean', description: 'Search only open access content' }, type: { type: 'string', enum: ['Journal', 'Book', 'Chapter'], description: 'Publication type filter' } }, required: ['query'] } },
- Core search implementation in SpringerSearcher class. Handles API selection (Meta v2 or OpenAccess), builds query with filters, calls Springer API, parses responses, and returns Paper objects. Includes rate limiting, error handling, and fallback logic.async search(query: string, options: SearchOptions = {}): Promise<Paper[]> { const customOptions = options as any; if (!this.apiKey) { throw new Error('Springer API key is required'); } const maxResults = Math.min(options.maxResults || 10, 100); const papers: Paper[] = []; try { // Decide which API to use let useOpenAccess = customOptions.openAccess === true; // If openAccess is requested and we haven't tested the API yet, test it if (useOpenAccess && this.hasOpenAccessAPI === undefined) { await this.testOpenAccessAPI(); } // Fall back to Meta API if OpenAccess API is not available if (useOpenAccess && !this.hasOpenAccessAPI) { logDebug('OpenAccess API not available, using Meta API with filtering'); useOpenAccess = false; } // Build query parameters const params: any = { q: query, api_key: useOpenAccess ? this.openAccessApiKey : this.apiKey, s: 1, // start index p: maxResults // page size }; // Add filters - Note: Some filters may require premium access if (options.author) { const sanitizedAuthor = this.sanitizeQueryValue(options.author); params.q += ` name:"${sanitizedAuthor}"`; } if (options.journal) { const sanitizedJournal = this.sanitizeQueryValue(options.journal); params.q += ` pub:"${sanitizedJournal}"`; } if (options.year) { // Year filter may cause 403 for some API keys if (options.year.includes('-')) { const [startYear, endYear] = options.year.split('-'); params.q += ` year:${startYear} TO ${endYear || '*'}`; } else { params.q += ` year:${options.year}`; } } if (customOptions.subject) { // Subject filter may cause 403 for some API keys const sanitizedSubject = this.sanitizeQueryValue(customOptions.subject); params.q += ` subject:"${sanitizedSubject}"`; } if (customOptions.type) { // Type filter generally works params.q += ` type:${customOptions.type}`; } await this.rateLimiter.waitForPermission(); // Choose the appropriate API let response: any; if (useOpenAccess) { // Use OpenAccess API (if available) response = await this.openAccessClient.get<SpringerResponse>('/json', { params }); } else { // Use Meta v2 API response = await this.metadataClient.get<SpringerResponse>('/json', { params }); } // Handle different response structures // Meta v2 API: records contains the actual papers, result contains metadata // OpenAccess API: might use either records or result for the actual papers let results: SpringerResult[] = []; // For Meta v2 API, records is always the array of papers if (response.data.records && Array.isArray(response.data.records)) { results = response.data.records; } // For older API versions or different response format else if (response.data.result && Array.isArray(response.data.result) && response.data.result.length > 0 && response.data.result[0].title) { // If result contains actual papers (has title field), use it results = response.data.result as SpringerResult[]; } if (results && results.length > 0) { for (const result of results) { const paper = this.parseResult(result); if (paper) { // If openAccess filter was requested but using Meta API, filter results if (customOptions.openAccess && !useOpenAccess && result.openaccess !== 'true') { continue; } papers.push(paper); } } } return papers; } catch (error: any) { if (error.response?.status === 401) { this.handleHttpError(error, 'search'); } if (error.response?.status === 403) { // Some filters require premium access logWarn('Springer API returned 403 - some filters may require premium access'); // Try a simpler query without advanced filters if (options.year || customOptions.subject) { logDebug('Retrying without year/subject filters...'); const simpleOptions = { ...options }; delete simpleOptions.year; delete (simpleOptions as any).subject; return this.search(query, simpleOptions); } this.handleHttpError(error, 'search'); } if (error.response?.status === 429) { this.handleHttpError(error, 'search'); } this.handleHttpError(error, 'search'); } }
- src/mcp/searchers.ts:37-81 (helper)Initializes all searchers including SpringerSearcher instance using environment variables, making it available as searchers.springer for tool handlers.export function initializeSearchers(): Searchers { if (searchers) return searchers; logDebug('Initializing searchers...'); const arxivSearcher = new ArxivSearcher(); const wosSearcher = new WebOfScienceSearcher(process.env.WOS_API_KEY, process.env.WOS_API_VERSION); const pubmedSearcher = new PubMedSearcher(process.env.PUBMED_API_KEY); const biorxivSearcher = new BioRxivSearcher('biorxiv'); const medrxivSearcher = new MedRxivSearcher(); const semanticSearcher = new SemanticScholarSearcher(process.env.SEMANTIC_SCHOLAR_API_KEY); const iacrSearcher = new IACRSearcher(); const googleScholarSearcher = new GoogleScholarSearcher(); const sciHubSearcher = new SciHubSearcher(); const scienceDirectSearcher = new ScienceDirectSearcher(process.env.ELSEVIER_API_KEY); const springerSearcher = new SpringerSearcher( process.env.SPRINGER_API_KEY, process.env.SPRINGER_OPENACCESS_API_KEY ); const wileySearcher = new WileySearcher(process.env.WILEY_TDM_TOKEN); const scopusSearcher = new ScopusSearcher(process.env.ELSEVIER_API_KEY); const crossrefSearcher = new CrossrefSearcher(process.env.CROSSREF_MAILTO); searchers = { arxiv: arxivSearcher, webofscience: wosSearcher, pubmed: pubmedSearcher, wos: wosSearcher, biorxiv: biorxivSearcher, medrxiv: medrxivSearcher, semantic: semanticSearcher, iacr: iacrSearcher, googlescholar: googleScholarSearcher, scholar: googleScholarSearcher, scihub: sciHubSearcher, sciencedirect: scienceDirectSearcher, springer: springerSearcher, wiley: wileySearcher, scopus: scopusSearcher, crossref: crossrefSearcher }; logDebug('Searchers initialized successfully'); return searchers; }