Skip to main content
Glama
andybrandt

mcp-simple-pubmed

by andybrandt

Search articles about medical and life sciences research available on PubMed.

search_pubmed
Read-only

Search PubMed for medical and life sciences research articles using keywords, field-specific queries, date ranges, and Boolean operators to find relevant studies.

Instructions

Search PubMed for medical and life sciences research articles.

You can use these search features:

  • Simple keyword search: "covid vaccine"

  • Field-specific search:

    • Title search: [Title]

    • Author search: [Author]

    • MeSH terms: [MeSH Terms]

    • Journal: [Journal]

  • Date ranges: Add year or date range like "2020:2024[Date - Publication]"

  • Combine terms with AND, OR, NOT

  • Use quotation marks for exact phrases

Examples:

  • "covid vaccine" - basic search

  • "breast cancer"[Title] AND "2023"[Date - Publication]

  • "Smith J"[Author] AND "diabetes"

  • "RNA"[MeSH Terms] AND "therapy"

The search will return:

  • Paper titles

  • Authors

  • Publication details

  • Abstract preview (when available)

  • Links to full text (when available)

  • DOI when available

  • Keywords and MeSH terms

Note: Use quotes around multi-word terms for best results.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
max_resultsNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The primary handler function for the 'search_pubmed' tool. It validates parameters, performs the search via PubMedClient, enriches results with URIs and URLs, and returns formatted JSON.
    async def search_pubmed(query: str, max_results: int = 10) -> str:
        """Search PubMed for medical and life sciences research articles.
    
        You can use these search features:
        - Simple keyword search: "covid vaccine"
        - Field-specific search:
          - Title search: [Title]
          - Author search: [Author]
          - MeSH terms: [MeSH Terms]
          - Journal: [Journal]
        - Date ranges: Add year or date range like "2020:2024[Date - Publication]"
        - Combine terms with AND, OR, NOT
        - Use quotation marks for exact phrases
    
        Examples:
        - "covid vaccine" - basic search
        - "breast cancer"[Title] AND "2023"[Date - Publication]
        - "Smith J"[Author] AND "diabetes"
        - "RNA"[MeSH Terms] AND "therapy"
    
        The search will return:
        - Paper titles
        - Authors
        - Publication details
        - Abstract preview (when available)
        - Links to full text (when available)
        - DOI when available
        - Keywords and MeSH terms
    
        Note: Use quotes around multi-word terms for best results.
        """
        try:
            # Validate and constrain max_results
            max_results = min(max(1, max_results), 50)
            
            logger.info(f"Processing search with query: {query}, max_results: {max_results}")
    
            # Perform the search
            results = await pubmed_client.search_articles(
                query=query,
                max_results=max_results
            )
            
            # Create resource URIs for articles
            articles_with_resources = []
            for article in results:
                pmid = article["pmid"]
                # Add original URIs
                article["abstract_uri"] = f"pubmed://{pmid}/abstract"
                article["full_text_uri"] = f"pubmed://{pmid}/full_text"
                
                # Add DOI URL if DOI exists
                if "doi" in article:
                    article["doi_url"] = f"https://doi.org/{article['doi']}"
                    
                # Add PubMed URLs
                article["pubmed_url"] = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
    
                # Add PMC URL only if PMCID is available
                if "pmcid" in article:
                    article["pmc_url"] = f"https://www.ncbi.nlm.nih.gov/pmc/articles/{article['pmcid']}/"
                
                articles_with_resources.append(article)
    
            # Format the response
            formatted_results = json.dumps(articles_with_resources, indent=2)
            logger.info(f"Search completed successfully, found {len(results)} results")
    
            return formatted_results
            
        except Exception as e:
            logger.exception(f"Error in search_pubmed")
            raise ValueError(f"Error processing search request: {str(e)}")
  • Registers the 'search_pubmed' tool with the FastMCP app, including metadata annotations for title and API hints.
    @app.tool(
        annotations={
            "title": "Search articles about medical and life sciences research available on PubMed.",
            "readOnlyHint": True,
            "openWorldHint": True  # Calls external PubMed API
        }
    )
  • Function signature defines input schema (query: str, max_results: int=10) and output str. Docstring provides detailed usage instructions serving as schema documentation.
    async def search_pubmed(query: str, max_results: int = 10) -> str:
        """Search PubMed for medical and life sciences research articles.
    
        You can use these search features:
        - Simple keyword search: "covid vaccine"
        - Field-specific search:
          - Title search: [Title]
          - Author search: [Author]
          - MeSH terms: [MeSH Terms]
          - Journal: [Journal]
        - Date ranges: Add year or date range like "2020:2024[Date - Publication]"
        - Combine terms with AND, OR, NOT
        - Use quotation marks for exact phrases
    
        Examples:
        - "covid vaccine" - basic search
        - "breast cancer"[Title] AND "2023"[Date - Publication]
        - "Smith J"[Author] AND "diabetes"
        - "RNA"[MeSH Terms] AND "therapy"
    
        The search will return:
        - Paper titles
        - Authors
        - Publication details
        - Abstract preview (when available)
        - Links to full text (when available)
        - DOI when available
        - Keywords and MeSH terms
    
        Note: Use quotes around multi-word terms for best results.
        """
  • Core helper method in PubMedClient that executes Entrez.esearch for PMIDs and fetches details using get_article_details.
    async def search_articles(self, query: str, max_results: int = 10) -> List[Dict[str, Any]]:
        """Search for articles matching the query.
    
        Args:
            query: Search query string
            max_results: Maximum number of results to return
    
        Returns:
            List of article metadata dictionaries
        """
        try:
            logger.info(f"Searching PubMed with query: {query}")
            results = []
            
            # Step 1: Search for article IDs
            handle = Entrez.esearch(db="pubmed", term=query, retmax=str(max_results))
            if not handle:
                logger.error("Got None handle from esearch")
                return []
                
            if isinstance(handle, http.client.HTTPResponse):
                logger.info("Got valid HTTP response from esearch")
                xml_content = handle.read()
                handle.close()
                
                # Parse XML to get IDs
                root = ET.fromstring(xml_content)
                id_list = root.findall('.//Id')
                
                if not id_list:
                    logger.info("No results found")
                    return []
                    
                pmids = [id_elem.text for id_elem in id_list]
                logger.info(f"Found {len(pmids)} articles")
                
                # Step 2: Get details for each article
                for pmid in pmids:
                    article = await self.get_article_details(pmid)
                    if article:
                        results.append(article)
            
            return results
    
        except Exception as e:
            logger.exception(f"Error in search_articles: {str(e)}")
            raise
  • Helper method that parses PubMed efetch XML response to extract structured article metadata including title, abstract, authors, publication details, identifiers, and MeSH terms.
    async def get_article_details(self, pmid: str) -> Optional[Dict[str, Any]]:
        """Get details for a specific article by PMID.
    
        Args:
            pmid: PubMed ID of the article
    
        Returns:
            Dictionary with article metadata or None if not found
        """
        try:
            logger.info(f"Fetching details for PMID {pmid}")
            detail_handle = Entrez.efetch(db="pubmed", id=pmid, rettype="xml")
            
            if detail_handle and isinstance(detail_handle, http.client.HTTPResponse):
                article_xml = detail_handle.read()
                detail_handle.close()
                
                # Parse article details
                article_root = ET.fromstring(article_xml)
                
                # Get basic article data
                article = {
                    "pmid": pmid,
                    "title": self._get_xml_text(article_root, './/ArticleTitle') or "No title",
                    "abstract": self._get_full_abstract(article_root) or "No abstract available",
                    "journal": self._get_xml_text(article_root, './/Journal/Title') or "",
                    "authors": [],
                    "keywords": [],
                    "mesh_terms": []
                }
                
                # Get authors
                author_list = article_root.findall('.//Author')
                for author in author_list:
                    last_name = self._get_xml_text(author, 'LastName') or ""
                    fore_name = self._get_xml_text(author, 'ForeName') or ""
                    if last_name or fore_name:
                        article["authors"].append(f"{last_name} {fore_name}".strip())
                
                # Get publication date
                pub_date = article_root.find('.//PubDate')
                if pub_date is not None:
                    year = self._get_xml_text(pub_date, 'Year')
                    month = self._get_xml_text(pub_date, 'Month')
                    day = self._get_xml_text(pub_date, 'Day')
                    article["publication_date"] = {
                        "year": year,
                        "month": month,
                        "day": day
                    }
                    
                # Get DOI and PMCID if available
                # Important: Only get ArticleIds from the main ArticleIdList, not from references
                pubmed_data = article_root.find('.//PubmedData')
                if pubmed_data is not None:
                    # Use direct child path to avoid getting IDs from ReferenceList
                    article_id_list_elem = pubmed_data.find('ArticleIdList')
                    if article_id_list_elem is not None:
                        for article_id in article_id_list_elem:
                            id_type = article_id.get('IdType')
                            if id_type == 'doi':
                                article["doi"] = article_id.text
                            elif id_type == 'pmc':
                                article["pmcid"] = article_id.text
    
                # Get Keywords
                keyword_list = article_root.findall('.//Keyword')
                for keyword in keyword_list:
                    if keyword.text:
                        # Clean up keyword text (remove trailing periods, etc.)
                        clean_keyword = keyword.text.strip().rstrip('.')
                        if clean_keyword:
                            article["keywords"].append(clean_keyword)
    
                # Get MeSH terms
                mesh_heading_list = article_root.findall('.//MeshHeading')
                for mesh_heading in mesh_heading_list:
                    descriptor = mesh_heading.find('DescriptorName')
                    if descriptor is not None and descriptor.text:
                        mesh_term = {
                            "descriptor": descriptor.text,
                            "ui": descriptor.get('UI', ''),
                            "qualifiers": []
                        }
    
                        # Get qualifiers if present
                        qualifiers = mesh_heading.findall('QualifierName')
                        for qualifier in qualifiers:
                            if qualifier.text:
                                mesh_term["qualifiers"].append({
                                    "name": qualifier.text,
                                    "ui": qualifier.get('UI', '')
                                })
    
                        article["mesh_terms"].append(mesh_term)
    
                return article
                
            return None
    
        except Exception as e:
            logger.exception(f"Error getting article details for PMID {pmid}: {str(e)}")
            return None

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other 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/andybrandt/mcp-simple-pubmed'

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