Skip to main content
Glama
rdwj

NIH RePORTER MCP Server

by rdwj
README.md
# NIH RePORTER MCP Server

An MCP server that wraps the [NIH RePORTER API v2](https://api.reporter.nih.gov/), enabling AI agents to search and analyze NIH-funded research projects and their linked publications. No API key required.

## Tools

| Tool | Description |
|------|-------------|
| `nih_search_projects` | Search for NIH research projects by text, PI, organization, funding, and more |
| `nih_get_project` | Get full details for a specific project by application ID or project number |
| `nih_find_publications` | Find PubMed publications linked to NIH grants |

### nih_search_projects

Search the NIH RePORTER database for federally funded research projects. Multiple criteria are combined with AND logic; within list parameters, values use OR logic.

**Parameters:**
- `text` (string, optional): Free-text search across titles, abstracts, and terms
- `text_operator` ("and" | "or", default "and"): How multi-word queries are combined
- `text_fields` ("all" | "title" | "abstract" | "terms", default "all"): Which fields to search
- `pi_name` (string, optional): PI name, e.g. "Smith, John" or "Smith"
- `organization` (string, optional): Organization name (partial match)
- `activity_codes` (list of string, optional): e.g. ["R01", "R21"]
- `agencies` (list of string, optional): NIH IC abbreviations, e.g. ["NCI", "NIMH"]
- `fiscal_years` (list of int, optional): e.g. [2024, 2025]
- `funding_mechanism` (list of string, optional): e.g. ["RP", "SB"]
- `spending_categories` (list of int, optional): RCDC category IDs
- `states` (list of string, optional): US state abbreviations
- `award_amount_min` / `award_amount_max` (int, optional): Dollar range
- `project_start_after` / `project_start_before` (string, optional): YYYY-MM-DD
- `exclude_subprojects` (bool, default true): Exclude subprojects
- `include_active_only` (bool, default false): Only active projects
- `detail_level` ("summary" | "full", default "summary"): Summary returns key fields; full includes abstracts
- `sort_by` ("relevance" | "date" | "amount", default "relevance"): Sort order
- `limit` (int, default 10, max 50): Results per page
- `offset` (int, default 0): Pagination offset
- `search_id` (string, optional): Reuse a prior search for pagination

**Example:**
```json
{
  "text": "machine learning",
  "agencies": ["NIGMS"],
  "activity_codes": ["R01"],
  "fiscal_years": [2025],
  "limit": 5
}
```

### nih_get_project

Get full details for a specific NIH-funded project. Use after finding a project of interest via `nih_search_projects`.

**Parameters:**
- `appl_id` (int, optional): Application ID, e.g. 10878415
- `project_num` (string, optional): Full project number, e.g. "5R01CA123456-03"

One of `appl_id` or `project_num` is required.

**Example:**
```json
{"appl_id": 10878415}
```

### nih_find_publications

Find PubMed publications linked to NIH-funded projects. Returns PMIDs that can be used with PubMed APIs for full citation details.

**Parameters:**
- `core_project_nums` (list of string, optional): e.g. ["R01CA123456"]. Supports wildcard `*`.
- `appl_ids` (list of int, optional): Application IDs
- `pmids` (list of int, optional): PubMed IDs (reverse lookup: which grants funded this paper?)
- `limit` (int, default 50, max 500): Results per page
- `offset` (int, default 0): Pagination offset

At least one of the search parameters is required.

**Example:**
```json
{"core_project_nums": ["R01CA123456"]}
```

## Quick Start

### Local Development

```bash
make install
make run-local

# Test with cmcp (in another terminal)
cmcp ".venv/bin/python -m src.main" tools/list
```

### Deploy to OpenShift

```bash
make deploy PROJECT=my-project
```

### Client Configuration

**STDIO (local):**
```json
{
  "mcpServers": {
    "nih-reporter": {
      "command": ".venv/bin/python",
      "args": ["-m", "src.main"]
    }
  }
}
```

**HTTP (remote):**
```json
{
  "mcpServers": {
    "nih-reporter": {
      "url": "https://<route>/mcp/"
    }
  }
}
```

## Development

### Running Tests

```bash
make test

# Or directly
.venv/bin/pytest tests/ -v

# Single test file
.venv/bin/pytest tests/tools/test_nih_search_projects.py -v
```

### Adding Tools

Create a Python file in `src/tools/` using the `@mcp.tool` decorator:

```python
from typing import Annotated
from pydantic import Field
from fastmcp import Context
from fastmcp.exceptions import ToolError
from src.core.app import mcp

@mcp.tool(
    annotations={"readOnlyHint": True, "openWorldHint": True},
    timeout=30.0,
)
async def my_tool(
    param: Annotated[str, Field(description="Parameter description")],
    ctx: Context = None,
) -> dict:
    """Tool description for the LLM."""
    return {"result": param}
```

Generate scaffolds with:
```bash
fips-agents generate tool my_tool --description "Tool description" --async --with-context
```

### Project Structure

```
src/
├── core/
│   ├── app.py          # Shared FastMCP instance
│   ├── server.py       # Server bootstrap (load + run)
│   ├── loaders.py      # Dynamic component discovery
│   ├── auth.py         # Optional JWT authentication
│   └── logging.py      # Logging configuration
├── tools/
│   ├── nih_client.py          # Shared HTTP client with rate limiting
│   ├── nih_search_projects.py # Project search tool
│   ├── nih_get_project.py     # Project detail tool
│   └── nih_find_publications.py # Publication search tool
├── resources/          # (none currently)
├── prompts/            # (none currently)
└── middleware/         # (none currently)
tests/
└── tools/
    ├── test_nih_search_projects.py  # 55 tests
    ├── test_nih_get_project.py      # 8 tests
    └── test_nih_find_publications.py # 9 tests
```

### Dependencies

- **fastmcp** >= 2.11.3 -- MCP server framework
- **httpx** >= 0.28.0 -- Async HTTP client for NIH API calls

### Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `MCP_TRANSPORT` | `stdio` | Transport: `stdio` or `http` |
| `MCP_HTTP_HOST` | `127.0.0.1` | HTTP bind address |
| `MCP_HTTP_PORT` | `8000` | HTTP port |
| `MCP_HTTP_PATH` | `/mcp/` | HTTP endpoint path |
| `MCP_LOG_LEVEL` | `INFO` | Logging level |
| `MCP_HOT_RELOAD` | `0` | Enable hot-reload for development |
| `MCP_SERVER_NAME` | `fastmcp-unified` | Server name in MCP responses |

## NIH API Notes

- **No authentication** required. The API is publicly accessible.
- **Rate limit**: 1 request per second (enforced by the shared client).
- **Result limit**: The API can return at most 15,000 records per search. The tool warns when this limit is hit.
- **Documentation**: [api.reporter.nih.gov](https://api.reporter.nih.gov/)

## License

This project is licensed under the MIT License.

TDQS

A4.2/5.0

Scored across 3 tools

Disambiguation5/5

The three tools have clearly distinct purposes: searching for projects, retrieving full project details, and finding linked publications. Each tool's description clarifies its specific role, leaving no ambiguity for an agent to choose the appropriate tool.

Naming Consistency5/5

All tool names follow a consistent snake_case convention with a common 'nih_' prefix and verb_noun structure: nih_search_projects, nih_get_project, nih_find_publications. This pattern is predictable and easy to parse.

Tool Count5/5

With only 3 tools, the set is minimal but well-scoped for the core read-only operations of the NIH RePORTER database. Each tool serves a distinct essential function without overlap or redundancy.

Completeness4/5

The tools cover the primary read operations: search, detail retrieval, and publication linkage. However, there is no support for listing related entities (e.g., all projects by a PI or organization) or exporting results, which could be useful but are not critical for the stated purpose.

Maintenance

ActivityInactive
ResponsivenessNo issues