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
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| max_results | No |
Implementation Reference
- mcp_simple_pubmed/server.py:45-118 (handler)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)}")
- mcp_simple_pubmed/server.py:38-44 (registration)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 } )
- mcp_simple_pubmed/server.py:45-75 (schema)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