search_semantic_scholar
Search academic papers on Semantic Scholar with citation data, year filters, and field-of-study options to find relevant research publications.
Instructions
Search Semantic Scholar for academic papers with citation data
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") | |
| fieldsOfStudy | No | Fields of study filter (e.g., ["Computer Science", "Biology"]) |
Implementation Reference
- Core implementation of the search functionality using Semantic Scholar Graph API v1, including query parameterization, API rate limiting, error handling, and result parsing into Paper objects.async search(query: string, options: SemanticSearchOptions = {}): Promise<Paper[]> { await this.rateLimiter.waitForPermission(); try { const params: Record<string, any> = { query: query, limit: Math.min(options.maxResults || 10, 100), // API限制最大100 fields: [ 'paperId', 'title', 'abstract', 'venue', 'year', 'referenceCount', 'citationCount', 'influentialCitationCount', 'isOpenAccess', 'openAccessPdf', 'fieldsOfStudy', 's2FieldsOfStudy', 'publicationTypes', 'publicationDate', 'journal', 'authors', 'externalIds', 'url' ].join(',') }; // 添加年份过滤 if (options.year) { params.year = options.year; } // 添加研究领域过滤 if (options.fieldsOfStudy && options.fieldsOfStudy.length > 0) { params.fieldsOfStudy = options.fieldsOfStudy.join(','); } const url = `${this.baseApiUrl}/paper/search`; const headers: Record<string, string> = { 'User-Agent': USER_AGENT, 'Accept': 'application/json', 'Accept-Language': 'en-US,en;q=0.9' }; // 添加API密钥(如果有)- 根据官方文档推荐的方式 if (this.apiKey) { headers['x-api-key'] = this.apiKey; } logDebug(`Semantic Scholar API Request: GET ${url}`); logDebug('Semantic Scholar Request params:', params); const response = await axios.get(url, { params, headers, timeout: TIMEOUTS.DEFAULT, // 改善请求可靠性 maxRedirects: 5, validateStatus: (status) => status < 500 // allow 4xx through so we can provide consistent messaging }); logDebug(`Semantic Scholar API Response: ${response.status} ${response.statusText}`); // 处理可能的错误响应 if (response.status >= 400) { // Convert non-throwing 4xx response to unified error handling this.handleHttpError({ response, config: response.config }, 'search'); } const papers = this.parseSearchResponse(response.data); logDebug(`Semantic Scholar Parsed ${papers.length} papers`); return papers; } catch (error: any) { logDebug('Semantic Scholar Search Error:', error.message); // 处理速率限制错误 if (error.response?.status === 429) { const retryAfter = error.response.headers['retry-after']; logDebug( `Rate limited by Semantic Scholar API. ${retryAfter ? `Retry after ${retryAfter} seconds.` : 'Please wait before making more requests.'}` ); } // 处理API限制错误 if (error.response?.status === 403) { logDebug('Access denied. Please check your API key or ensure you are within the free tier limits.'); } this.handleHttpError(error, 'search'); } }
- src/mcp/handleToolCall.ts:187-208 (handler)MCP tool call handler case that delegates to the SemanticScholarSearcher instance and formats the response with rate limit information.case 'search_semantic_scholar': { const { query, maxResults, year, fieldsOfStudy } = args; const results = await searchers.semantic.search(query, { maxResults, year, fieldsOfStudy }); const rateStatus = searchers.semantic.getRateLimiterStatus(); const apiKeyStatus = searchers.semantic.hasApiKey() ? 'configured' : 'not configured (using free tier)'; const rateLimit = searchers.semantic.hasApiKey() ? '200 requests/minute' : '20 requests/minute'; return jsonTextResponse( `Found ${results.length} Semantic Scholar papers.\n\nAPI Status: ${apiKeyStatus} (${rateLimit})\nRate Limiter: ${rateStatus.availableTokens}/${rateStatus.maxTokens} tokens available\n\n${JSON.stringify( results.map((paper: Paper) => PaperFactory.toDict(paper)), null, 2 )}` ); }
- src/mcp/schemas.ts:92-99 (schema)Zod schema for validating input arguments to the search_semantic_scholar tool.export const SearchSemanticScholarSchema = z .object({ query: z.string().min(1), maxResults: z.number().int().min(1).max(100).optional().default(10), year: z.string().optional(), fieldsOfStudy: z.array(z.string()).optional() }) .strip();
- src/mcp/tools.ts:205-227 (registration)Tool registration definition including name, description, and JSON input schema, added to the TOOLS array for MCP.{ name: 'search_semantic_scholar', description: 'Search Semantic Scholar for academic papers with citation data', 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")' }, fieldsOfStudy: { type: 'array', items: { type: 'string' }, description: 'Fields of study filter (e.g., ["Computer Science", "Biology"])' } }, required: ['query'] } },
- src/mcp/searchers.ts:47-47 (registration)Instantiation of the SemanticScholarSearcher instance, configured with optional API key from environment, and added to the searchers object.const semanticSearcher = new SemanticScholarSearcher(process.env.SEMANTIC_SCHOLAR_API_KEY);