Skip to main content
Glama
afrise

Academic Paper Search MCP Server

search_papers

Search for academic papers using a specific query across multiple sources. Retrieve metadata, abstracts, and full-text content, with results limited to a specified number for structured, real-time access.

Instructions

Search for papers across multiple sources.

args: query: the search query limit: the maximum number of results to return (default 10)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes

Implementation Reference

  • The core handler function for the 'search_papers' tool. Decorated with @mcp.tool() which handles both implementation and registration. Searches Semantic Scholar and Crossref APIs for papers matching the query.
    @mcp.tool() async def search_papers(query: str, limit: int = 10) -> str: """Search for papers across multiple sources. args: query: the search query limit: the maximum number of results to return (default 10) """ if query == "": return "Please provide a search query." # Truncate long queries MAX_QUERY_LENGTH = 300 if len(query) > MAX_QUERY_LENGTH: original_length = len(query) query = query[:MAX_QUERY_LENGTH] + "..." try: # Search Semantic Scholar semantic_url = f"{SEMANTIC_SCHOLAR_API}/paper/search?query={query}&limit={limit}" semantic_data = await make_api_request(semantic_url) # Search Crossref crossref_url = f"{CROSSREF_API}?query={query}&rows={limit}" crossref_data = await make_api_request(crossref_url) results = [] if semantic_data and 'papers' in semantic_data: results.append("=== Semantic Scholar Results ===") for paper in semantic_data['papers']: results.append(format_paper_data(paper, "semantic_scholar")) if crossref_data and 'items' in crossref_data.get('message', {}): results.append("\n=== Crossref Results ===") for paper in crossref_data['message']['items']: results.append(format_paper_data(paper, "crossref")) if not results: return "No results found or error occurred while fetching papers." return "\n".join(results) except: return "Error searching papers."
  • Helper function to format paper data from Semantic Scholar or Crossref into a consistent readable string format. Called within search_papers for each result.
    def format_paper_data(data: dict, source: str) -> str: """Format paper data from different sources into a consistent string format.""" if not data: return "No paper data available" try: if source == "semantic_scholar": title = unicodedata.normalize('NFKD', str(data.get('title', 'No title available'))) authors = ', '.join([author.get('name', 'Unknown Author') for author in data.get('authors', [])]) year = data.get('year') or 'Year unknown' external_ids = data.get('externalIds', {}) or {} doi = external_ids.get('DOI', 'No DOI available') venue = data.get('venue') or 'Venue unknown' abstract = data.get('abstract') or 'No abstract available' tldr = (data.get('tldr') or {}).get('text', '') is_open = "Yes" if data.get('isOpenAccess') else "No" pdf_data = data.get('openAccessPdf', {}) or {} pdf_url = pdf_data.get('url', 'Not available') elif source == "crossref": title = (data.get('title') or ['No title available'])[0] authors = ', '.join([ f"{author.get('given', '')} {author.get('family', '')}".strip() or 'Unknown Author' for author in data.get('author', []) ]) year = (data.get('published-print', {}).get('date-parts', [['']])[0][0]) or 'Year unknown' doi = data.get('DOI') or 'No DOI available' result = [ f"Title: {title}", f"Authors: {authors}", f"Year: {year}", f"DOI: {doi}" ] if source == "semantic_scholar": result.extend([ f"Venue: {venue}", f"Open Access: {is_open}", f"PDF URL: {pdf_url}", f"Abstract: {abstract}" ]) if tldr: result.append(f"TL;DR: {tldr}") return "\n".join(result) + "\t\t\n" except Exception as e: return f"Error formatting paper data: {str(e)}"
  • Async helper function to make HTTP GET requests to external APIs with error handling and timeout. Used by search_papers to query Semantic Scholar and Crossref.
    async def make_api_request(url: str, headers: dict = None, params: dict = None) -> dict[str, Any] | None: """Make a request to the API with proper error handling.""" if headers is None: headers = { "User-Agent": USER_AGENT } async with httpx.AsyncClient() as client: try: response = await client.get(url, headers=headers, params=params, timeout=30.0) response.raise_for_status() return response.json() except Exception as e: return None
  • Input schema defined in the tool's docstring, specifying parameters query (str) and limit (int=10).
    """Search for papers across multiple sources. args: query: the search query limit: the maximum number of results to return (default 10) """

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/afrise/academic-search-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server