Skip to main content
Glama
emi-dm
by emi-dm

search_pubmed

Search PubMed to retrieve scientific articles with configurable filters for titles, abstracts, and keywords. Use boolean operators to refine results and access publication details.

Instructions

Search PubMed and return a list of article JSON objects.

Parameters: query: Free-text user query; boolean operators (AND/OR/NOT) supported by PubMed. max_results: Maximum number of records to retrieve (retmax). title: If True, include Title field in search restriction (ti / tiab). abstract: If True, include Abstract field in search restriction (ab / tiab). keywords: If True, expand search to Author Keywords (ot) and MeSH Headings (mh).

Field logic: - title and abstract both True => core search uses [tiab] - only title True => uses [ti] - only abstract True => uses [ab] - neither title nor abstract True => no restriction (all fields) - keywords True => additionally OR with [ot] and [mh] versions of the query

Returns: List[dict]: Each dict contains pmid, title, authors, abstract, journal, publication_year, publication_month, url.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
max_resultsNo
titleNo
abstractNo
keywordsNo

Implementation Reference

  • Core handler function for the 'search_pubmed' tool. Decorated with @mcp.tool for registration. Implements PubMed search via Bio.Entrez, handles query refinement, fetches article details, and returns structured list of dicts with pmid, title, authors, etc.
    @mcp.tool
    async def search_pubmed(query: str,
                      max_results: int = 10,
                      title: bool = True,
                      abstract: bool = True,
                      keywords: bool = True):
        """Search PubMed and return a list of article JSON objects.
    
        Parameters:
            query: Free-text user query; boolean operators (AND/OR/NOT) supported by PubMed.
            max_results: Maximum number of records to retrieve (retmax).
            title: If True, include Title field in search restriction (ti / tiab).
            abstract: If True, include Abstract field in search restriction (ab / tiab).
            keywords: If True, expand search to Author Keywords (ot) and MeSH Headings (mh).
    
        Field logic:
            - title and abstract both True => core search uses [tiab]
            - only title True => uses [ti]
            - only abstract True => uses [ab]
            - neither title nor abstract True => no restriction (all fields)
            - keywords True => additionally OR with [ot] and [mh] versions of the query
    
        Returns:
            List[dict]: Each dict contains pmid, title, authors, abstract, journal, publication_year,
                        publication_month, url.
        """
        try:
            if not isinstance(query, str) or not query.strip():
                print("Empty query provided; returning empty result list.")
                return []
            if max_results <= 0:
                max_results = 10
    
            # Build refined query with field tags
            refined_query = _build_field_query(query.strip(), title, abstract, keywords)
    
            # Search PubMed for article IDs using Entrez.esearch
            print(f"Searching for: {refined_query}")
            handle = Entrez.esearch(db="pubmed", term=refined_query, retmax=str(max_results))
            search_record = Entrez.read(handle)
            handle.close()
            
            # Get the list of PMIDs
            pmid_list = search_record["IdList"]
            total_count = search_record["Count"]
    
            print(f"Se encontraron {total_count} artículos. Los primeros {len(pmid_list)} PMIDs son: {pmid_list}")
            
            if not pmid_list:
                print("No articles found for your query.")
                return []
            
            # Fetch detailed information for each PMID using Entrez.efetch
            handle = Entrez.efetch(db="pubmed", id=pmid_list, rettype="xml", retmode="xml")
            records = Entrez.read(handle)
            handle.close()
            
            papers_list = []
            
            # Process each article
            for record in records['PubmedArticle']:
                # Get PMID
                pmid = str(record['MedlineCitation']['PMID'])
                
                # Get article details
                article = record['MedlineCitation']['Article']
                
                # Get title
                title = str(article.get('ArticleTitle', 'No title found'))
                
                # Get authors
                authors = []
                if 'AuthorList' in article:
                    for author in article['AuthorList']:
                        if 'ForeName' in author and 'LastName' in author:
                            authors.append(f"{author['ForeName']} {author['LastName']}")
                        elif 'LastName' in author:
                            authors.append(str(author['LastName']))
                
                # Get abstract
                abstract = "No abstract found"
                if 'Abstract' in article and 'AbstractText' in article['Abstract']:
                    abstract_parts = article['Abstract']['AbstractText']
                    if isinstance(abstract_parts, list):
                        # Handle multiple abstract sections
                        abstract_texts = []
                        for part in abstract_parts:
                            if hasattr(part, 'get') and part.get('@Label'):
                                # Structured abstract with labels
                                abstract_texts.append(f"{part['@Label']}: {part}")
                            else:
                                abstract_texts.append(str(part))
                        abstract = " ".join(abstract_texts)
                    else:
                        abstract = str(abstract_parts)
                
                # Get publication date
                pub_date_info = article.get('Journal', {}).get('JournalIssue', {}).get('PubDate', {})
                year = str(pub_date_info.get('Year', 'Unknown'))
                month = str(pub_date_info.get('Month', 'Unknown'))
                
                # Get journal name
                journal = str(article.get('Journal', {}).get('Title', 'No journal found'))
                
                # Create paper dictionary
                paper = {
                    "pmid": pmid,
                    "title": title,
                    "authors": authors,
                    "abstract": abstract,
                    "journal": journal,
                    "publication_year": year,
                    "publication_month": month,
                    "url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
                }
                
                papers_list.append(paper)
            
            print(f"Successfully processed {len(papers_list)} articles.")
            return papers_list
            
        except HTTPError as http_err:
            print(f"HTTP error during Entrez request: {http_err}")
            return []
        except Exception as e:
            print(f"An error occurred: {e}")
            import traceback
            traceback.print_exc()
            return []
  • Identical core handler function for the 'search_pubmed' tool in the remote server version. Registered via @mcp.tool decorator.
    @mcp.tool
    async def search_pubmed(query: str,
                      max_results: int = 10,
                      title: bool = True,
                      abstract: bool = True,
                      keywords: bool = True):
        """Search PubMed and return a list of article JSON objects.
    
        Parameters:
            query: Free-text user query; boolean operators (AND/OR/NOT) supported by PubMed.
            max_results: Maximum number of records to retrieve (retmax).
            title: If True, include Title field in search restriction (ti / tiab).
            abstract: If True, include Abstract field in search restriction (ab / tiab).
            keywords: If True, expand search to Author Keywords (ot) and MeSH Headings (mh).
    
        Field logic:
            - title and abstract both True => core search uses [tiab]
            - only title True => uses [ti]
            - only abstract True => uses [ab]
            - neither title nor abstract True => no restriction (all fields)
            - keywords True => additionally OR with [ot] and [mh] versions of the query
    
        Returns:
            List[dict]: Each dict contains pmid, title, authors, abstract, journal, publication_year,
                        publication_month, url.
        """
        try:
            if not isinstance(query, str) or not query.strip():
                print("Empty query provided; returning empty result list.")
                return []
            if max_results <= 0:
                max_results = 10
    
            # Build refined query with field tags
            refined_query = _build_field_query(query.strip(), title, abstract, keywords)
    
            # Search PubMed for article IDs using Entrez.esearch
            print(f"Searching for: {refined_query}")
            handle = Entrez.esearch(db="pubmed", term=refined_query, retmax=str(max_results))
            search_record = Entrez.read(handle)
            handle.close()
            
            # Get the list of PMIDs
            pmid_list = search_record["IdList"]
            total_count = search_record["Count"]
    
            print(f"Se encontraron {total_count} artículos. Los primeros {len(pmid_list)} PMIDs son: {pmid_list}")
            
            if not pmid_list:
                print("No articles found for your query.")
                return []
            
            # Fetch detailed information for each PMID using Entrez.efetch
            handle = Entrez.efetch(db="pubmed", id=pmid_list, rettype="xml", retmode="xml")
            records = Entrez.read(handle)
            handle.close()
            
            papers_list = []
            
            # Process each article
            for record in records['PubmedArticle']:
                # Get PMID
                pmid = str(record['MedlineCitation']['PMID'])
                
                # Get article details
                article = record['MedlineCitation']['Article']
                
                # Get title
                title = str(article.get('ArticleTitle', 'No title found'))
                
                # Get authors
                authors = []
                if 'AuthorList' in article:
                    for author in article['AuthorList']:
                        if 'ForeName' in author and 'LastName' in author:
                            authors.append(f"{author['ForeName']} {author['LastName']}")
                        elif 'LastName' in author:
                            authors.append(str(author['LastName']))
                
                # Get abstract
                abstract = "No abstract found"
                if 'Abstract' in article and 'AbstractText' in article['Abstract']:
                    abstract_parts = article['Abstract']['AbstractText']
                    if isinstance(abstract_parts, list):
                        # Handle multiple abstract sections
                        abstract_texts = []
                        for part in abstract_parts:
                            if hasattr(part, 'get') and part.get('@Label'):
                                # Structured abstract with labels
                                abstract_texts.append(f"{part['@Label']}: {part}")
                            else:
                                abstract_texts.append(str(part))
                        abstract = " ".join(abstract_texts)
                    else:
                        abstract = str(abstract_parts)
                
                # Get publication date
                pub_date_info = article.get('Journal', {}).get('JournalIssue', {}).get('PubDate', {})
                year = str(pub_date_info.get('Year', 'Unknown'))
                month = str(pub_date_info.get('Month', 'Unknown'))
                
                # Get journal name
                journal = str(article.get('Journal', {}).get('Title', 'No journal found'))
                
                # Create paper dictionary
                paper = {
                    "pmid": pmid,
                    "title": title,
                    "authors": authors,
                    "abstract": abstract,
                    "journal": journal,
                    "publication_year": year,
                    "publication_month": month,
                    "url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
                }
                
                papers_list.append(paper)
            
            print(f"Successfully processed {len(papers_list)} articles.")
            return papers_list
            
        except HTTPError as http_err:
            print(f"HTTP error during Entrez request: {http_err}")
            return []
        except Exception as e:
            print(f"An error occurred: {e}")
            import traceback
            traceback.print_exc()
            return []
  • Supporting helper function used by search_pubmed to construct field-restricted PubMed queries (title, abstract, keywords/MeSH).
    async def _build_field_query(user_query: str, in_title: bool, in_abstract: bool, in_keywords: bool) -> str:
        """Build a PubMed (Entrez) query applying field restrictions.
    
        Fields mapping used:
        - Title: [ti]
        - Abstract: [ab]
        - Title/Abstract convenience: [tiab]
        - Keywords (Other Term): [ot] (Author provided keywords)
        - MeSH Headings: [mh]
    
        Strategy:
        - If only one of title or abstract is selected, use that specific field tag.
        - If both selected, use tiab (lets PubMed optimize) plus ot/mh if requested.
        - Keywords option expands with OR clauses for ot and mh.
        - Parentheses ensure proper boolean grouping.
        """
        core_clauses: List[str] = []
    
        if in_title and in_abstract:
            # tiab covers both Title and Abstract text
            core_clauses.append(f"({user_query})[tiab]")
        elif in_title:
            core_clauses.append(f"({user_query})[ti]")
        elif in_abstract:
            core_clauses.append(f"({user_query})[ab]")
        else:
            # No field restriction for title/abstract selected, let user_query as-is (PubMed default: all fields)
            core_clauses.append(f"({user_query})")
    
        if in_keywords:
            # Include author keywords (ot) and MeSH terms (mh) as expansion
            keywords_clause = f"({user_query})[ot] OR ({user_query})[mh]"
            # Combine with previous core clauses using OR to broaden search
            core_group = " OR ".join(core_clauses)
            combined = f"({core_group}) OR ({keywords_clause})"
            return combined
    
        return " OR ".join(core_clauses)
  • Identical query builder helper in remote version.
    async def _build_field_query(user_query: str, in_title: bool, in_abstract: bool, in_keywords: bool) -> str:
        """Build a PubMed (Entrez) query applying field restrictions.
    
        Fields mapping used:
        - Title: [ti]
        - Abstract: [ab]
        - Title/Abstract convenience: [tiab]
        - Keywords (Other Term): [ot] (Author provided keywords)
        - MeSH Headings: [mh]
    
        Strategy:
        - If only one of title or abstract is selected, use that specific field tag.
        - If both selected, use tiab (lets PubMed optimize) plus ot/mh if requested.
        - Keywords option expands with OR clauses for ot and mh.
        - Parentheses ensure proper boolean grouping.
        """
        core_clauses: List[str] = []
    
        if in_title and in_abstract:
            # tiab covers both Title and Abstract text
            core_clauses.append(f"({user_query})[tiab]")
        elif in_title:
            core_clauses.append(f"({user_query})[ti]")
        elif in_abstract:
            core_clauses.append(f"({user_query})[ab]")
        else:
            # No field restriction for title/abstract selected, let user_query as-is (PubMed default: all fields)
            core_clauses.append(f"({user_query})")
    
        if in_keywords:
            # Include author keywords (ot) and MeSH terms (mh) as expansion
            keywords_clause = f"({user_query})[ot] OR ({user_query})[mh]"
            # Combine with previous core clauses using OR to broaden search
            core_group = " OR ".join(core_clauses)
            combined = f"({core_group}) OR ({keywords_clause})"
            return combined
    
        return " OR ".join(core_clauses)
  • pubmed_server.py:51-51 (registration)
    @mcp.tool decorator registers the search_pubmed function as an MCP tool.
    @mcp.tool
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/emi-dm/PubMed-MCP'

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