Skip to main content
Glama
jsevarts

OSTI MCP Server

by jsevarts
README.md
# OSTI MCP Server

An [MCP](https://modelcontextprotocol.io) server that exposes [OSTI.gov](https://www.osti.gov)'s
public search API as tools Claude can call directly — so Claude can search and
retrieve DOE-sponsored scientific literature (technical reports, journal
articles, theses/dissertations, patents, datasets) in the middle of a
conversation.

## What it does

Two tools:

- **`search_osti`** — keyword/author/identifier search over OSTI records,
  with filters for research organization, sponsoring DOE office,
  publication date range, product type (journal article, technical report,
  thesis, patent, dataset), and full-text availability. Returns a `total`
  count of all matching records (not just the current page) alongside the
  page itself; `rows` is capped at 100 per request.
- **`get_osti_record`** — fetch full metadata (abstract, subjects,
  identifiers, links to citation and full-text pages) for a single record by
  its OSTI ID.

No API key or authentication is required — this only uses OSTI's public
read/search API, not the authenticated ELINK submission API.

## Install

Requires Python 3.10+. Dependencies are declared once in `pyproject.toml`,
so pick whichever environment manager you use:

**pip / venv:**

```bash
git clone https://github.com/jsevarts/mcp-osti.git
cd mcp-osti
python3 -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
```

**conda:**

```bash
git clone https://github.com/jsevarts/mcp-osti.git
cd mcp-osti
conda env create -f environment.yml
conda activate osti-mcp
```

## Try it out

Sanity-check the server directly:

```bash
python osti_mcp_server.py
```

Or open the [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
for interactive testing:

```bash
mcp dev osti_mcp_server.py
```

## Register with Claude Code / Claude Desktop / Claude Science

Point at the Python interpreter inside whichever environment you created
above — e.g. `venv/bin/python3` for pip/venv, or the output of
`conda run -n osti-mcp which python3` for conda.

**Claude Code:**

```bash
claude mcp add --transport stdio osti -- /full/path/to/python3 /full/path/to/osti_mcp_server.py
```

**Claude Desktop / Claude Science** (Settings → Developer → Edit Config, add
to `mcpServers`):

```json
{
  "mcpServers": {
    "osti": {
      "command": "/full/path/to/python3",
      "args": ["/full/path/to/osti_mcp_server.py"]
    }
  }
}
```

Restart the app afterward.

## Example query

> "Search OSTI for recent DOE-funded work on phase-field modeling of grain
> boundary migration, and pull the full abstract for the most relevant
> result."

## API notes

Verified against the live API, not just the [docs page](https://www.osti.gov/api/v1/docs):

- `GET /records` returns a bare JSON array at the top level (no `records`
  wrapper). Pagination metadata (`X-Total-Count`, `Link`) comes back in
  response headers, not the JSON body.
- `GET /records/{osti_id}` **also** returns a JSON array (containing zero or
  one record), not a single object — easy to miss.
- `publication_date_start`/`_end` must be sent as `MM/DD/YYYY`. Sending ISO
  dates causes an HTTP 500 rather than a clean error. This server accepts
  ISO `YYYY-MM-DD` dates from the caller and converts them internally, so
  Claude can pass normal ISO dates without knowing about this quirk.
- OSTI doesn't publish a documented hard rate limit. It does send an
  `X-Rate-Limit-Remaining` header, but it read `0` on every request observed
  during testing even as requests kept succeeding — it doesn't appear to be
  reliably actionable, so this server doesn't throttle on it. Keep `rows`
  modest and avoid tight request loops.

## Testing

```bash
pip install -e ".[dev]"   # or: conda env create -f environment.yml && conda activate osti-mcp
python -m pytest tests/
```

Tests mock `requests.get`, so they run offline.

## License

MIT — see [LICENSE](LICENSE).