UNPKG

@tan-yong-sheng/paper-search-mcp-nodejs

Version:

A Node.js MCP server for searching and downloading academic papers from multiple sources, including arXiv, PubMed, bioRxiv, Web of Science, and more.

271 lines 9.49 kB
/** * Web of Science API集成模块 * 支持 Web of Science Starter API 和 Web of Science Researcher API */ import axios from 'axios'; import { PaperFactory } from '../models/Paper.js'; import { PaperSource } from './PaperSource.js'; export class WebOfScienceSearcher extends PaperSource { apiUrl; apiVersion; constructor(apiKey, apiVersion = 'v1') { super('webofscience', 'https://api.clarivate.com/apis', apiKey); this.apiVersion = apiVersion; this.apiUrl = `${this.baseUrl}/wos-starter/${this.apiVersion}`; console.error(`🔧 WoS API URL: ${this.apiUrl}`); } getCapabilities() { return { search: true, download: false, // WoS 通常不提供直接PDF下载 fullText: false, // 通常只有元数据 citations: true, requiresApiKey: true, supportedOptions: ['maxResults', 'year', 'author', 'journal', 'sortBy', 'sortOrder'] }; } /** * 搜索Web of Science论文 */ async search(query, options = {}) { if (!this.apiKey) { throw new Error('Web of Science API key is required'); } try { const searchParams = this.buildSearchQuery(query, options); const response = await this.makeApiRequest('/documents', { method: 'GET', params: searchParams }); return this.parseSearchResponse(response.data); } catch (error) { this.handleHttpError(error, 'search'); } } /** * Web of Science 通常不支持直接PDF下载 */ async downloadPdf(paperId, options) { throw new Error('Web of Science does not support direct PDF download. Please use the DOI or URL to access the paper through the publisher.'); } /** * Web of Science 通常不提供全文内容 */ async readPaper(paperId, options) { throw new Error('Web of Science does not provide full-text content. Only bibliographic metadata and abstracts are available.'); } /** * 根据DOI获取论文详细信息 */ async getPaperByDoi(doi) { try { const query = `DO="${doi}"`; const results = await this.search(query, { maxResults: 1 }); return results.length > 0 ? results[0] : null; } catch (error) { console.error('Error getting paper by DOI from Web of Science:', error); return null; } } /** * 获取论文被引统计 */ async getCitationCount(paperId) { if (!this.apiKey) { throw new Error('Web of Science API key is required'); } try { const response = await this.makeApiRequest(`/documents/${paperId}`, { method: 'GET' }); const record = response.data?.Data?.[0]; const citationData = record?.dynamic_data?.citation_related?.tc_list?.silo_tc; return citationData ? parseInt(citationData.local_count, 10) : 0; } catch (error) { console.error('Error getting citation count:', error); return 0; } } /** * 构建搜索查询参数 */ buildSearchQuery(query, options) { // Web of Science requires tagged queries - use TS= for topic search let formattedQuery = `TS="${query}"`; const params = { q: formattedQuery, db: options.databases?.join(',') || 'WOS', limit: options.maxResults || 10, page: 1 }; // 添加年份过滤 if (options.year) { params.q += ` AND PY=${options.year}`; } // 添加作者过滤 if (options.author) { params.q += ` AND AU="${options.author}"`; } // 添加期刊过滤 if (options.journal) { params.q += ` AND SO="${options.journal}"`; } // 添加排序 if (options.sortBy) { const sortField = this.mapSortField(options.sortBy); params.sortField = sortField; } return params; } /** * 映射排序字段 */ mapSortField(sortBy) { const fieldMap = { 'relevance': 'relevance', 'date': 'PY', 'citations': 'TC' }; return fieldMap[sortBy] || 'relevance'; } /** * 解析搜索响应 */ parseSearchResponse(data) { if (!data.hits || !Array.isArray(data.hits)) { console.error('❌ WoS: No hits found in response or hits is not an array'); return []; } console.error(`📊 WoS: Found ${data.hits.length} hits out of ${data.metadata?.total || 0} total`); return data.hits.map(record => this.parseWoSRecord(record)) .filter(paper => paper !== null); } /** * 解析单个WoS记录 */ parseWoSRecord(record) { try { // 提取基本信息 const title = record.title || 'No title available'; const authors = record.names?.authors?.map(author => author.displayName) || []; const abstractText = record.abstract || ''; // 提取出版信息 const year = record.source?.publishYear; const publishedDate = year ? new Date(year, 0, 1) : null; const journal = record.source?.sourceTitle || ''; // 提取DOI const doi = record.identifiers?.doi || ''; // 提取被引次数 const citationCount = record.citations?.[0]?.citingArticlesCount || 0; // 提取关键词 const keywords = record.keywords?.authorKeywords || []; // 构建URL const wosUrl = `https://www.webofscience.com/wos/woscc/full-record/${record.uid}`; return PaperFactory.create({ paperId: record.uid, title: this.cleanText(title), authors: authors, abstract: this.cleanText(abstractText), doi: doi, publishedDate: publishedDate, pdfUrl: '', // WoS通常不提供直接PDF链接 url: wosUrl, source: 'webofscience', categories: record.types || [], keywords: keywords, citationCount: citationCount, journal: journal, volume: record.source?.volume || undefined, issue: record.source?.issue || undefined, pages: record.source?.pages || undefined, year: year, extra: { uid: record.uid, doctype: record.types?.[0], sourceTypes: record.sourceTypes } }); } catch (error) { console.error('Error parsing WoS record:', error); console.error('Record data:', record); return null; } } /** * 提取页码信息 */ extractPages(pubInfo) { if (!pubInfo?.page) return undefined; const beginPage = pubInfo.page['@begin']; const endPage = pubInfo.page['@end']; if (beginPage && endPage) { return `${beginPage}-${endPage}`; } else if (beginPage) { return beginPage; } return undefined; } /** * 发起API请求 */ async makeApiRequest(endpoint, config) { const url = `${this.apiUrl}${endpoint}`; const requestConfig = { ...config, headers: { 'X-ApiKey': this.apiKey, 'Content-Type': 'application/json', 'User-Agent': 'Paper-Search-MCP/1.0 (Academic Research Tool)', ...config.headers }, timeout: 30000 }; console.error(`🔍 WoS API Request: ${config.method} ${url}`); console.error(`📋 WoS Request params:`, config.params); try { const response = await axios(url, requestConfig); console.error(`✅ WoS API Response: ${response.status} ${response.statusText}`); console.error(`📄 WoS Response data preview:`, JSON.stringify(response.data, null, 2).substring(0, 500)); return response; } catch (error) { console.error(`❌ WoS API Error:`, { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, config: { url: error.config?.url, method: error.config?.method, params: error.config?.params } }); throw error; } } /** * 验证API密钥 */ async validateApiKey() { if (!this.apiKey) return false; try { await this.search('test', { maxResults: 1 }); return true; } catch (error) { // API密钥无效通常返回401或403 if (error.response?.status === 401 || error.response?.status === 403) { return false; } // 其他错误可能是网络问题,认为密钥可能有效 return true; } } } //# sourceMappingURL=WebOfScienceSearcher.js.map