UNPKG

@seacolour/openalex-mcp-server-tool

Version:
292 lines (267 loc) 10.8 kB
#!/usr/bin/env node import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; import {z} from 'zod'; import { getInstitutionIdByName, getJournalIdByName, searchAuthorsByName, searchPapers, searchPapersByTitle, getPaperByDoi, advancedPaperSearch, searchWorksByInstitutionId, searchWorksByJournalId, translateQueryToEnglish } from './openalex.js'; import fetch from 'node-fetch'; // 解析命令行参数 const args = process.argv.slice(2); const keyIndex = args.indexOf('--key'); const apiKey = keyIndex !== -1 ? args[keyIndex + 1] : null; // 查找有道翻译API密钥 const youdaoKeyIndex = args.indexOf('--youdao-key'); const youdaoSecretIndex = args.indexOf('--youdao-secret'); const youdaoKey = youdaoKeyIndex !== -1 ? args[youdaoKeyIndex + 1] : null; const youdaoSecret = youdaoSecretIndex !== -1 ? args[youdaoSecretIndex + 1] : null; // 判断是否启用参数翻译 const enableTranslation = youdaoKey && youdaoSecret; if (enableTranslation) { console.log('✅ 参数翻译功能已启用'); } else { console.log('⚠️ 未提供有道翻译API密钥,参数翻译功能未启用'); } if (!apiKey) { console.error('❌ 缺少 API 密钥。请使用 --key <API_KEY> 参数。'); process.exit(1); } // 验证 API 密钥 try { const response = await fetch(`http://114.55.7.49:7939/api/key/validate?key=${encodeURIComponent(apiKey)}`, { method: 'GET' }); if (response.status !== 200) { console.error('❌ 无效或已过期的 API 密钥。'); process.exit(1); } // 根据实际响应使用文本解析 const keyInfo = await response.text(); console.log('API 服务返回:', keyInfo); console.log('✅ API 密钥验证成功。'); } catch (error) { console.error('❌ 验证 API 密钥时发生错误:', error); process.exit(1); } // 创建 MCP 服务器实例 const server = new McpServer({ name: 'My JavaScript MCP Server', version: '1.0.0' }); // 定义 'search_papers' 工具 - 使用新的参数形式 server.tool( "search_papers", "根据关键词和可选过滤条件搜索学术论文。", { keyword: z.string().describe('用于搜索的关键词'), maxResults: z.number().default(5).describe('返回的最大论文数量'), year: z.number().optional().describe('论文的发表年份'), openAccess: z.boolean().optional().describe('是否仅搜索开放获取论文'), authorId: z.string().optional().describe('作者的 OpenAlex ID'), institutionId: z.string().optional().describe('机构的 OpenAlex ID'), type: z.string().optional().describe('论文类型') }, async (params) => { // 如果启用了翻译,先将关键词翻译成英文 let modifiedParams = {...params}; if (enableTranslation && params.keyword) { modifiedParams.keyword = await translateQueryToEnglish(params.keyword, youdaoKey, youdaoSecret); console.log(`参数翻译: "${params.keyword}" -> "${modifiedParams.keyword}"`); } const papers = await searchPapers(modifiedParams); return { content: [{ type: 'text', text: JSON.stringify(papers, null, 2) }] }; } ); // 定义 'search_papers_by_title' 工具 server.tool( "search_papers_by_title", "根据论文标题精确搜索学术论文。", { title: z.string().describe('论文标题或标题的关键部分'), maxResults: z.number().default(5).describe('返回的最大论文数量') }, async ({ title, maxResults }) => { // 如果启用了翻译,先将标题翻译成英文 let translatedTitle = title; if (enableTranslation) { translatedTitle = await translateQueryToEnglish(title, youdaoKey, youdaoSecret); console.log(`参数翻译: "${title}" -> "${translatedTitle}"`); } const papers = await searchPapersByTitle(translatedTitle, maxResults); return { content: [{ type: 'text', text: JSON.stringify(papers, null, 2) }] }; } ); // // 定义 'search_authors' 工具 - 使用新的参数形式 // server.tool( // "search_authors", // "根据作者名称搜索作者信息。", // { // authorName: z.string().describe('作者的名称'), // maxResults: z.number().default(5).describe('返回的最大结果数') // }, // async (params) => { // const authors = await searchAuthorsByName(params.authorName, params.maxResults); // return { // content: [{ // type: 'text', // text: JSON.stringify(authors, null, 2) // }] // }; // } // ); // 定义 'search_works_by_institution' 工具 - 使用新的参数形式 server.tool( "search_works_by_institution", "根据机构名称搜索相关学术作品。", { institutionName: z.string().describe('机构名称,例如 "Stanford University"'), maxResults: z.number().default(5).describe('返回的最大作品数量') }, async ({ institutionName, maxResults }) => { // 如果启用了翻译,先将机构名称翻译成英文 let translatedInstitutionName = institutionName; if (enableTranslation) { translatedInstitutionName = await translateQueryToEnglish(institutionName, youdaoKey, youdaoSecret); console.log(`参数翻译: "${institutionName}" -> "${translatedInstitutionName}"`); } const institutionId = await getInstitutionIdByName(translatedInstitutionName); if (!institutionId) { return { content: [{ type: 'text', text: `未找到机构名称为 "${institutionName}" 的机构。` }] }; } const works = await searchWorksByInstitutionId(institutionId, maxResults); return { content: [{ type: 'text', text: JSON.stringify(works, null, 2) }] }; } ); // 定义 'search_works_by_journal' 工具 - 使用新的参数形式 server.tool( "search_works_by_journal", "根据期刊名称搜索相关学术作品。", { journalName: z.string().describe('期刊名称,例如 "Nature"'), maxResults: z.number().default(5).describe('返回的最大作品数量') }, async ({ journalName, maxResults }) => { // 如果启用了翻译,先将期刊名称翻译成英文 let translatedJournalName = journalName; if (enableTranslation) { translatedJournalName = await translateQueryToEnglish(journalName, youdaoKey, youdaoSecret); console.log(`参数翻译: "${journalName}" -> "${translatedJournalName}"`); } const journalId = await getJournalIdByName(translatedJournalName); if (!journalId) { return { content: [{ type: 'text', text: `未找到期刊名称为 "${journalName}" 的期刊。` }] }; } const works = await searchWorksByJournalId(journalId, maxResults); return { content: [{ type: 'text', text: JSON.stringify(works, null, 2) }] }; } ); // 定义 'get_paper_by_doi' 工具 server.tool( "get_paper_by_doi", "通过DOI标识符精确查找学术论文。", { doi: z.string().describe('论文的DOI标识符,例如 "10.1038/s41586-020-2649-2"') }, async ({ doi }) => { const paper = await getPaperByDoi(doi); if (!paper) { return { content: [{ type: 'text', text: `未找到DOI为 "${doi}" 的论文。` }] }; } return { content: [{ type: 'text', text: JSON.stringify(paper, null, 2) }] }; } ); // 定义 'advanced_paper_search' 工具 server.tool( "advanced_paper_search", "进行高级学术论文搜索,支持多种过滤条件。", { keyword: z.string().optional().describe('搜索关键词(可选)'), minCitations: z.number().optional().describe('最小引用次数(可选)'), maxCitations: z.number().optional().describe('最大引用次数(可选)'), concept: z.string().optional().describe('学科概念,如 "机器学习"、"气候变化"(可选)'), isOpenAccess: z.boolean().optional().describe('是否仅搜索开放获取论文(可选)'), fromYear: z.number().optional().describe('起始年份(可选)'), toYear: z.number().optional().describe('结束年份(可选)'), maxResults: z.number().default(10).describe('返回的最大结果数(默认10)') }, async (params) => { // 如果启用了翻译,先将关键词和学科概念翻译成英文 let modifiedParams = {...params}; if (enableTranslation) { if (params.keyword) { modifiedParams.keyword = await translateQueryToEnglish(params.keyword, youdaoKey, youdaoSecret); console.log(`参数翻译: "${params.keyword}" -> "${modifiedParams.keyword}"`); } if (params.concept) { modifiedParams.concept = await translateQueryToEnglish(params.concept, youdaoKey, youdaoSecret); console.log(`参数翻译: "${params.concept}" -> "${modifiedParams.concept}"`); } } const papers = await advancedPaperSearch(modifiedParams); if (papers.length === 0) { return { content: [{ type: 'text', text: '没有找到符合条件的论文。' }] }; } return { content: [{ type: 'text', text: JSON.stringify(papers, null, 2) }] }; } ); // 启动 MCP 服务器,使用标准输入/输出作为传输方式 const transport = new StdioServerTransport(); await server.connect(transport);