Skip to main content
Glama
jbdamask

NIH RePORTER MCP

by jbdamask

search_combined

Search NIH-funded research projects and their related publications in a single query using filters like fiscal years, PI names, organization, funding type, and award amounts.

Instructions

Search for NIH projects and their related publications in a single query

Args:
    fiscal_years: Comma-separated list of fiscal years (e.g., "2022,2023")
    pi_names: Comma-separated list of PI names
    organization: Name of the organization
    org_state: Two-letter state code (e.g., "CA", "NY")
    funding_mechanism: Type of funding (e.g., "R01", "F32", "K99")
    ic_code: Institute or Center code (e.g., "NCI", "NIMH")
    min_amount: Minimum award amount
    max_amount: Maximum award amount
    covid_response: COVID-19 response category
    include_publications: Whether to include related publications
    publication_years: Comma-separated list of publication years
    limit: Maximum number of results to return (default: 10, max: 50)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
fiscal_yearsNo
pi_namesNo
organizationNo
org_stateNo
funding_mechanismNo
ic_codeNo
min_amountNo
max_amountNo
covid_responseNo
include_publicationsNo
publication_yearsNo
limitNo

Implementation Reference

  • The handler function for the 'search_combined' tool, registered via @mcp.tool(). It constructs criteria for NIH RePORTER API to search projects, optionally fetches related publications, and formats results in markdown.
    @mcp.tool()
    async def search_combined(
        # Project search parameters
        fiscal_years: Optional[str] = None,
        pi_names: Optional[str] = None,
        organization: Optional[str] = None,
        org_state: Optional[str] = None,
        funding_mechanism: Optional[str] = None,
        ic_code: Optional[str] = None,
        min_amount: Optional[float] = None,
        max_amount: Optional[float] = None,
        covid_response: Optional[str] = None,
        
        # Publication parameters
        include_publications: Optional[bool] = True,
        publication_years: Optional[str] = None,
        
        # General parameters
        limit: Optional[int] = 10
    ) -> str:
        """
        Search for NIH projects and their related publications in a single query
        
        Args:
            fiscal_years: Comma-separated list of fiscal years (e.g., "2022,2023")
            pi_names: Comma-separated list of PI names
            organization: Name of the organization
            org_state: Two-letter state code (e.g., "CA", "NY")
            funding_mechanism: Type of funding (e.g., "R01", "F32", "K99")
            ic_code: Institute or Center code (e.g., "NCI", "NIMH")
            min_amount: Minimum award amount
            max_amount: Maximum award amount
            covid_response: COVID-19 response category
            include_publications: Whether to include related publications
            publication_years: Comma-separated list of publication years
            limit: Maximum number of results to return (default: 10, max: 50)
        """
        try:
            logger.info(f"Combined search request received with parameters: {locals()}")
            
            # First, search for projects
            project_criteria = {}
            
            if fiscal_years:
                try:
                    # Handle escaped quotes and clean the input string
                    years_str = fiscal_years.replace('\\"', '').replace('"', '').strip()
                    years = [int(year.strip()) for year in years_str.split(",") if year.strip()]
                    if not years:
                        raise ValueError("No valid years found after parsing")
                    project_criteria["fiscal_years"] = years
                except ValueError as e:
                    logger.error(f"Invalid fiscal years format: {fiscal_years}, error: {str(e)}")
                    return f"Error: Invalid fiscal years format. Please provide comma-separated years (e.g., 2020,2021)"
            
            if pi_names:
                try:
                    # Handle escaped quotes and clean the input string
                    names_str = pi_names.replace('\\"', '').replace('"', '').strip()
                    names = [name.strip() for name in names_str.split(",") if name.strip()]
                    if not names:
                        raise ValueError("No valid names found after parsing")
                    project_criteria["pi_names"] = [{"any_name": name} for name in names]
                except Exception as e:
                    logger.error(f"Invalid PI names format: {pi_names}, error: {str(e)}")
                    return f"Error: Invalid PI names format. Please provide comma-separated names"
            
            if organization:
                project_criteria["org_names"] = [organization]
            
            if org_state:
                project_criteria["org_states"] = [org_state.upper()]
                
            if funding_mechanism:
                project_criteria["funding_mechanism"] = funding_mechanism.strip().strip('"').strip("'")
                
            if ic_code:
                project_criteria["agency_ic_admin"] = ic_code.strip().strip('"').strip("'").upper()
                
            if min_amount is not None or max_amount is not None:
                project_criteria["award_amount_range"] = {
                    "min_amount": min_amount if min_amount is not None else 0,
                    "max_amount": max_amount if max_amount is not None else float('inf')
                }
                
            if covid_response:
                project_criteria["covid_response"] = [covid_response]
            
            project_criteria["limit"] = min(max(1, limit), 50)
            
            logger.info(f"Searching for projects with criteria: {json.dumps(project_criteria, indent=2)}")
            project_results = await api_client.get_projects(project_criteria)
            
            # If we want publications, get them for each project
            if include_publications:
                project_nums = []
                for project in project_results.get("results", []):
                    if project.get("project_num"):
                        project_nums.append(project["project_num"])
                
                if project_nums:
                    logger.info(f"Found project numbers for publication search: {project_nums}")
                    pub_criteria = {
                        "criteria": {
                            "core_project_nums": project_nums
                        },
                        "limit": 100  # Get more publications since they're related
                    }
                    
                    # Only add publication years if explicitly specified by the user
                    if publication_years:
                        try:
                            years_str = publication_years.strip().strip('"').strip("'")
                            years = [int(year.strip()) for year in years_str.split(",")]
                            pub_criteria["criteria"]["publication_years"] = years
                            logger.info(f"Filtering publications by years: {years}")
                        except ValueError as e:
                            logger.error(f"Invalid publication years format: {publication_years}")
                            return f"Error: Invalid publication years format. Please provide comma-separated years without quotes (e.g., 2020,2021)"
                    
                    logger.info(f"Searching for publications with criteria: {json.dumps(pub_criteria, indent=2)}")
                    pub_results = await api_client.get_publications(pub_criteria)
                    
                    # Add publications to each project
                    pub_by_project = {}
                    for pub in pub_results.get("results", []):
                        proj_num = pub.get("core_project_num")
                        if proj_num:
                            if proj_num not in pub_by_project:
                                pub_by_project[proj_num] = []
                            pub_by_project[proj_num].append(pub)
                    
                    for project in project_results.get("results", []):
                        proj_num = project.get("project_num")
                        if proj_num in pub_by_project:
                            project["related_publications"] = pub_by_project[proj_num]
            
            return api_client.format_project_results(project_results, include_publications=include_publications)
            
        except Exception as e:
            logger.error(f"Combined search failed: {str(e)}", exc_info=True)
            return f"Combined search failed: {str(e)}\nPlease check the logs for more details."
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the search functionality but doesn't describe what the tool returns (e.g., format, structure), whether it's read-only or has side effects, performance characteristics, or error handling. The parameter list in the description adds some context but doesn't fully compensate for the lack of behavioral details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear purpose statement followed by a bullet-point-like parameter list. Each parameter explanation is concise and informative. However, the parameter section is lengthy (12 items), which is necessary given the complexity but slightly reduces efficiency. No wasted sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (12 parameters, no annotations, no output schema), the description is partially complete. It excels in parameter documentation but lacks output details, behavioral context, and sibling differentiation. For a search tool with many parameters, more guidance on results and usage would be beneficial, but the parameter coverage is strong.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0%, so the description must fully compensate. It provides detailed parameter semantics for all 12 parameters, including examples (e.g., '2022,2023' for fiscal_years, 'CA' for org_state), data types (e.g., 'comma-separated list', 'two-letter state code'), and defaults (e.g., 'default: 10, max: 50' for limit). This adds significant value beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Search for NIH projects and their related publications in a single query.' This specifies the verb ('search'), resources ('NIH projects and their related publications'), and scope ('in a single query'). However, it doesn't explicitly differentiate from sibling tools like 'search_projects' or 'search_publications' beyond the 'combined' aspect, which is why it doesn't reach a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus the sibling tools 'search_projects' or 'search_publications'. It doesn't mention prerequisites, alternatives, or specific contexts where this combined search is preferred over separate searches. The only implied usage is for combined queries, but this isn't explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/jbdamask/mcp-nih-reporter'

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