@seacolour/openalex-mcp-server-tool
Version:
MCP server for querying OpenAlex papers
292 lines (267 loc) • 10.8 kB
JavaScript
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);