Skip to main content
Glama
andybrandt

mcp-simple-pubmed

by andybrandt

search_pubmed

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

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
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