Skip to main content
Glama
afrise

Academic Paper Search MCP Server

fetch_paper_details

Retrieve comprehensive details for a specific academic paper, including metadata and abstracts, by providing its unique identifier and preferred database source (Crossref or Semantic Scholar).

Instructions

Get detailed information about a specific paper.

Args:
    paper_id: Paper identifier (DOI for Crossref, paper ID for Semantic Scholar)
    source: Source database ("semantic_scholar" or "crossref")

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paper_idYes
sourceNosemantic_scholar

Implementation Reference

  • The main handler function for the 'fetch_paper_details' tool. It constructs the appropriate API URL based on the source, fetches the data using make_api_request, handles Crossref response structure, and formats the output using format_paper_data. Registered via @mcp.tool() decorator.
    @mcp.tool()
    async def fetch_paper_details(paper_id: str, source: str = "semantic_scholar") -> str:
        """Get detailed information about a specific paper.
    
        Args:
            paper_id: Paper identifier (DOI for Crossref, paper ID for Semantic Scholar)
            source: Source database ("semantic_scholar" or "crossref")
        """
        if source == "semantic_scholar":
            url = f"{SEMANTIC_SCHOLAR_API}/paper/{paper_id}"
        elif source == "crossref":
            url = f"{CROSSREF_API}/{paper_id}"
        else:
            return "Unsupported source. Please use 'semantic_scholar' or 'crossref'."
    
        data = await make_api_request(url)
        
        if not data:
            return f"Unable to fetch paper details from {source}."
    
        if source == "crossref":
            data = data.get('message', {})
    
        return format_paper_data(data, source)
  • Helper function to make HTTP requests to the Semantic Scholar or Crossref APIs with error handling and timeout.
    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
  • Helper function that formats the raw API response data from either source into a consistent human-readable string format, handling fields like title, authors, year, DOI, and source-specific fields.
    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)}"
  • server.py:134-134 (registration)
    The @mcp.tool() decorator registers the fetch_paper_details function as an MCP tool.
    @mcp.tool()
Install Server

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