# Fresh Open Data — MCP wrapper for partners

`fresh-datasets.js` wraps the [Fresh Open Data API](../ai.md) as an **MCP
server over stdio**, so partners and colleagues get the datasets as tools in
Claude Desktop, Claude Code, or any MCP host — no HTTP hand-rolling, no keys,
no hassle. It is deliberately **zero-dependency**: one file, nothing to
`npm install`.

**Requires Node 18+** (global `fetch`, only exercised if you don't have a
local checkout — see below). **Node 22.5+ additionally unlocks the
`query_sql` tool** via the built-in `node:sqlite` module; on an older runtime
the server still starts and every other tool still works, `query_sql` is
just left out. Tested on Node v22.22.2.

(Not to be confused with the sibling `mcp-app/` directory — that's the UI
resource artifact for the *practice* MCP app of docs/REDESIGN.md Phase 1.
This directory is the data wrapper you can run today.)

## Quickstart

```sh
curl -O https://freshdentalsolutions.com/mcp/fresh-datasets.js

# Claude Code
claude mcp add fresh-dental-data -- node ./fresh-datasets.js
```

Claude Desktop — add to `claude_desktop_config.json`:

```json
{ "mcpServers": { "fresh-dental-data": {
    "command": "node", "args": ["/path/to/fresh-datasets.js"] } } }
```

## Tools

| Tool | What it does |
|---|---|
| `list_datasets` | Catalog: id, title, description, counts, source, license |
| `get_dataset` | One dataset's metadata + records (`limit`/`offset` for slices) |
| `search_records` | Substring search across all records, optionally per dataset |
| `lookup_tooth` | Resolve any tooth designation across Universal / FDI / Palmer — ambiguous inputs return every interpretation, labeled |
| `query_sql` | Read-only SQL over all datasets, loaded into an **in-memory SQLite** database — one table per dataset, dashes as underscores (currently `tooth_notation`, `tooth_eruption`, `oral_health_indicators`, `kb_sources`, `kb_articles`, `icd10cm_dental`, `oral_health_surveillance`, `water_fluoridation`, `oral_cancer_rates` — call `list_datasets` for the live set), single SELECT/WITH statement, 200-row cap. Joins and aggregates welcome. |

`query_sql` uses Node's **built-in** `node:sqlite` (still zero npm
dependencies): unflagged on Node 22.5+, which is this server's floor —
verified here on Node v22.22.2 (it still emits an `ExperimentalWarning`,
which this server filters out of its own logs). The connection is loaded
then locked with `PRAGMA query_only`, so writes are refused at the engine,
not just by pattern-matching. On older runtimes without `node:sqlite` the
tool simply isn't offered (omitted from `tools/list` and from the
`initialize` instructions) and every other tool still works — verified by
running this server under `node --no-experimental-sqlite`.

```sql
-- e.g. which teeth erupt before age 7?
SELECT t.name, t.fdi FROM tooth_notation t
JOIN tooth_eruption e ON e.dentition = t.dentition AND e.arch = t.arch AND e.tooth = t.tooth
WHERE e.eruption_unit = 'years' AND e.eruption_min <= 6;
```

Each dataset is also exposed as an MCP **resource** (`fresh-data://<id>`,
plus `fresh-data://index`).

## Data source resolution

1. `--base <url>` argument, or the `FRESH_DATA_BASE` env var
2. a local `../api/` directory (running from a checkout of this repo)
3. the hosted API at `https://freshdentalsolutions.com/api/`

Responses are cached in-memory per process; restart to pick up fresh data.

## Testing by hand

The transport is newline-delimited JSON-RPC on stdio:

```sh
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"lookup_tooth","arguments":{"value":"46"}}}' \
  | node fresh-datasets.js
```

Run this from inside a checkout of the repo (`mcp/fresh-datasets.js` sitting
next to `../api/`) and it answers instantly from disk, no network needed —
that's how it's verified above. Run it from a bare download of the single
file instead and it falls through to data source resolution step 3, the
hosted API — so it needs network access to `freshdentalsolutions.com` and
returns a plain JSON-RPC tool error (never a crash) if that's unreachable.

## Roadmap

A **hosted** Streamable-HTTP MCP endpoint — nothing to download at all —
ships with the practice MCP app (docs/REDESIGN.md, Phase 1) and exposes these
same dataset tools remotely; this file stays as the zero-infrastructure path
and the reference implementation of the dataset tool contract.

## Ground rules

Reference datasets (tooth notation, eruption chronology, ICD-10-CM dental
codes) are stable standard material. The hand-compiled headline indicators
are **rounded and flagged approximate**, each with its citation. The
federal surveillance datasets (NHANES oral health surveillance, water
fluoridation, oral/pharyngeal cancer rates) are pre-analyzed tables from
CDC/NCI sources, not raw microdata — see each dataset's `source` and
`retrieved_via` fields for exactly where they came from. Verify against the
cited source before clinical, research, or policy use, either way. Education
and interoperability data, not diagnosis.
