A Clojure-based Model Context Protocol (MCP) server powered by the plumcp framework. It aggregates data from academic research databases (OpenAlex, Crossref, Semantic Scholar, Springer Nature, arXiv, and PubMed) to provide high-level research tools for LLM clients. Available on Clojars as org.clojars.punit-naik/scholarly-mcp.
The server exposes the following MCP tools to help AI assistants search, analyze, and synthesize literature:
search_papers: Aggregates and deduplicates searches across all 6 databases.get_citations: Fetches works citing a specific paper by DOI (via OpenAlex).retrieve_full_text: Looks up metadata and retrieves Open Access (OA) download links.compare_papers: Generates a side-by-side Markdown comparison table and abstracts summary for multiple DOIs.build_literature_review: Synthesizes search results, grouping them by publication venue.export_citations: Retrieves formatted references in BibTeX or RIS formats.find_nature_papers_by_abstract: Recommends related Nature journal publications based on keywords extracted from an abstract.This MCP server enables AI assistants (like Claude, ChatGPT, or custom agents) to act as autonomous research partners. Here are some key real-world use cases:
Automated Literature Reviews: Instead of manually searching, copying, and organizing papers from multiple index portals, users can ask an AI agent to:
"Find the top 15 papers on 'RAG evaluation' published since 2023, deduplicate them, and organize them into a literature review grouped by journal/venue." The agent will use
build_literature_reviewto compile a clean, structured synthesis report.
Quick Cross-Study Comparisons: Researchers often need to compare findings, methodologies, or sample sizes across multiple papers. You can tell the AI:
"Compare the papers with DOIs 10.1038/s41586-024-01641-0 and 10.1145/3703155 side-by-side." The agent will use
compare_papersto generate a structured markdown matrix comparing authors, year, citation counts, venues, and abstracts side-by-side.
Citation Traceability & Impact Mapping: When reviewing a breakthrough paper, researchers need to find how it was built upon. You can ask:
"What are the most cited papers that cite the original LLM hallucination survey (DOI: 10.1145/3703155)?" The agent will use
get_citationsto fetch citing works, allowing the AI to trace the academic lineage and construct a citation tree.
Frictionless Open Access PDF Retrieval: Instead of hitting paywalls or hunting for free PDFs manually, the AI can resolve access options instantly:
"Retrieve the metadata and check if there is an open access PDF download link available for DOI 10.1038/s41746-025-01670-7." The agent uses
retrieve_full_textto locate valid public-access URLs and PDF downloads immediately.
Auto-Generating Reference Bibliographies: When drafting a paper, users can ask:
"Give me the BibTeX citations for the following three DOIs so I can add them to my LaTeX bibliography." The agent uses
export_citationsto fetch clean, registry-validated RIS or BibTeX records directly from Crossref/DOI resolvers, preventing formatting errors.
Abstract-Based Journal Recommendations: Authors looking for a publication venue can feed a draft abstract to the AI:
"Based on my draft abstract, find similar papers published in Nature journals to help me decide where to submit." The agent uses
find_nature_papers_by_abstract(powered by local Apache Lucene keyword extraction) to recommend matching publications.
To ensure the server is fast, cheap, and lightweight for LLM clients, we adhere to the following principles:
find_nature_papers_by_abstract is done entirely in Clojure using word-frequency analysis rather than asking the LLM to identify keywords).To get the most out of the Research MCP Server, you should configure API keys for the academic indexes that require or benefit from authentication.
SPRINGER_API_KEY)SPRINGER_API_KEYwarning
Springer Nature API rate limits for the free/developer tier are very low (typically around 1–2 requests per second). Please use this key sparingly during development and testing to avoid receiving rate-limiting errors (HTTP 403 or 429).
SEMANTIC_SCHOLAR_API_KEY)SEMANTIC_SCHOLAR_API_KEYThere are two ways to test the server: automated unit tests and manual/interactive testing.
To run the automated Clojure unit tests (which do not require network access or API keys):
lein test
The easiest way to test the MCP server tools manually is using the official MCP Inspector. This launches a web-based UI where you can interactively invoke tools, see the JSON-RPC traffic, and inspect tool outputs.
Make sure you have Node.js/npx installed, then run:
SPRINGER_API_KEY="your_key" SEMANTIC_SCHOLAR_API_KEY="your_key" npx @modelcontextprotocol/inspector lein run
lein uberjar
SPRINGER_API_KEY="your_key" SEMANTIC_SCHOLAR_API_KEY="your_key" npx @modelcontextprotocol/inspector java -jar target/scholarly-mcp.jar
Open the URL printed in the console (usually http://localhost:5173) in your browser to test the tools (search_papers, get_citations, etc.).
To maintain a consistent and clean codebase, this project uses cljstyle for formatting.
cljstyle check on every commit. If any Clojure file does not comply with the formatting rules, the commit will be blocked until the formatting is resolved.The server communicates via standard input/output (stdio). You can install it in any MCP-compatible IDE or client.
Add the server configuration to your claude_desktop_config.json file.
~/.config/Claude/claude_desktop_config.json~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json{
"mcpServers": {
"scholarly-mcp": {
"command": "lein",
"args": ["run"],
"env": {
"SPRINGER_API_KEY": "YOUR_SPRINGER_API_KEY",
"SEMANTIC_SCHOLAR_API_KEY": "YOUR_SEMANTIC_SCHOLAR_API_KEY"
},
"cwd": "/home/punit/Documents/work/personal/research-mcp"
}
}
}
Ensure you run lein uberjar first to compile the JAR.
{
"mcpServers": {
"scholarly-mcp": {
"command": "java",
"args": [
"-jar",
"/home/punit/Documents/work/personal/research-mcp/target/scholarly-mcp.jar"
],
"env": {
"SPRINGER_API_KEY": "YOUR_SPRINGER_API_KEY",
"SEMANTIC_SCHOLAR_API_KEY": "YOUR_SEMANTIC_SCHOLAR_API_KEY"
}
}
}
}
To configure the server in Cursor:
ScholarlyMCPcommandjava -jar /home/punit/Documents/work/personal/research-mcp/target/scholarly-mcp.jar
export SPRINGER_API_KEY="your_key"
export SEMANTIC_SCHOLAR_API_KEY="your_key"
In addition to using the server inside interactive AI chats (like Claude Desktop or Cursor), you can build wrapper scripts or automated workflows that talk to this MCP server over stdio.
This script launches the standalone JAR, performs the JSON-RPC initialization handshake, and retrieves search results programmatically:
import subprocess
import json
import os
# Ensure the standalone JAR is compiled using `lein uberjar`
jar_path = "./target/scholarly-mcp.jar"
# Spawn the MCP server process
proc = subprocess.Popen(
["java", "-jar", jar_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
env=os.environ
)
def send_rpc(method, params, request_id):
payload = {
"jsonrpc": "2.0",
"id": request_id,
"method": method,
"params": params
}
proc.stdin.write(json.dumps(payload) + "\n")
proc.stdin.flush()
# Read the single line JSON-RPC response
line = proc.stdout.readline()
return json.loads(line)
try:
# Step 1: Send protocol handshake (initialize)
init_res = send_rpc("initialize", {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {"name": "PythonClient", "version": "1.0"}
}, 1)
print(f"Connected to: {init_res['result']['serverInfo']['name']} v{init_res['result']['serverInfo']['version']}\n")
# Step 2: Query for papers
query = "llm hallucination"
response = send_rpc("tools/call", {
"name": "search_papers",
"arguments": {"query": query}
}, 2)
# Print the resulting markdown report
markdown_report = response["result"]["content"][0]["text"]
print(markdown_report)
finally:
# Step 3: Clean up and close process
proc.terminate()
This Node.js script launches the server and prints the list of available tools:
const { spawn } = require('child_process');
const jarPath = './target/scholarly-mcp.jar';
const server = spawn('java', ['-jar', jarPath]);
let buffer = '';
server.stdout.on('data', (data) => {
buffer += data.toString();
const lines = buffer.split('\n');
buffer = lines.pop(); // Hold incomplete lines
for (const line of lines) {
if (line.trim()) {
const response = JSON.parse(line);
if (response.id === 1) {
// Initialized! Now request the tools list.
sendRpc('tools/list', {}, 2);
} else if (response.id === 2) {
// Print available tools
console.log('Registered Tools:');
response.result.tools.forEach(tool => {
console.log(`- ${tool.name}: ${tool.description}`);
});
server.kill();
process.exit(0);
}
}
}
});
function sendRpc(method, params, id) {
const req = { jsonrpc: '2.0', id, method, params };
server.stdin.write(JSON.stringify(req) + '\n');
}
// Handshake
sendRpc('initialize', {
protocolVersion: '2025-11-25',
capabilities: {},
clientInfo: { name: 'NodeClient', version: '1.0' }
}, 1);
Special thanks to the creators of the plumcp library, which made building this Model Context Protocol server in Clojure simple, lightweight, and robust.
Copyright © 2026 Punit Naik
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |