# apple-health-mcp: SQL over a CSV export, with the network boundary drawn at the client

> Neil Pullman's local-first MCP server for Apple Health, which stops SQL at the data directory, holds the whole export in memory and admits where its local-first claim ends: at your model provider.

**neiltron/apple-health-mcp** — Local-first Apple Health MCP server. Lets AI assistants answer questions about your sleep, workouts, activity and heart data from a local export.

- Repository: https://github.com/neiltron/apple-health-mcp
- Stars: 570 · Forks: 24
- Language: TypeScript
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/neiltron-apple-health-mcp

## The local-first claim stops at the client boundary

The privacy story is stated precisely, and the last clause is the one that matters. The server reads the exported files in place, it does not upload the export, and it makes no network requests. Then the page adds that query results returned to your MCP client may be sent to that client's configured model provider. So the data stays local until the answer leaves. The client configuration is a short JSON object, and the same values work for any MCP client.

```json
{
  "mcpServers": {
    "apple-health": {
      "command": "npx",
      "args": ["-y", "@neiltron/apple-health-mcp"],
      "env": {
        "HEALTH_DATA_DIR": "/path/to/your/unzipped/health-export"
      }
    }
  }
}
```

The file is the Claude Desktop configuration, and the page notes that other clients can use the same command, arguments, environment and stdio transport, with a restart of the client after changing configuration.

## Safeguards reduce side effects without isolating the process

The security section is unusually blunt about its own limits. DuckDB file access is restricted to the data directory, network access and temporary disk storage are disabled, and the database settings are locked. The one thing left open is that the data directory stays readable and writable so the importer can read the CSV files. The page then states what those controls are worth: they reduce accidental side effects from generated SQL, and they do not isolate the process. The recommended posture follows from that, namely run the server through a local stdio MCP client, do not expose it to an untrusted network client, and use process or OS isolation if the server has to accept untrusted SQL. Since the whole point is letting a language model write the SELECT statement, the isolation question is not a detail to defer.

## Every query can reach the whole export because there is no date window

History is all or nothing. The first request that needs a table loads that table's complete CSV history, and there is no date filter to narrow it, so a query can reach as far back as the export goes. Loaded tables stay in memory, and DuckDB is given a limit through MAX_MEMORY_MB, which defaults to 2048. The sizing guidance is concrete: roughly 1 GiB covers a two-year multi-table export, so the default leaves headroom, and a larger export needs the number raised. The behaviour at the ceiling is the useful part. The server never spills health rows to a temporary directory on disk, so an export that does not fit fails with an explicit error instead of quietly writing a partial copy of your medical history to a scratch directory. A second knob, CACHE_SIZE, defaults to 100 and caps the number of cached query results.

## Apple's own export.xml is not supported and only one app layout is read

The input format requirement is narrow and stated as a limitation twice. The server needs an Apple Health CSV export created with the Simple Health Export CSV app on an iPhone, and the native export.xml format is not currently supported. The export procedure is four steps: install and open that app, select All and pick the time range, transfer the archive to the computer running the client, then unzip it and point HEALTH_DATA_DIR at the resulting directory. Everything downstream depends on that layout, because table names come from the files present in your export rather than from a fixed schema. That is why the page tells you to start with health_schema, which reports table names, columns, units and sample rows, before writing any query of your own.

## The prepare script points git at the repository's own hooks directory

The package manifest has a few details worth reading before you clone it. Node.js 22 or newer is required by the engines field. The build is a bun build that targets node and marks duckdb as external, which is why the manifest also carries a pnpm block allowing duckdb's build step, since it is a native addon with a compiled component. And the prepare script runs git config core.hooksPath .githooks, which rewrites the hooks path of whichever repository you happen to be standing in when an install runs. That is the intended behaviour for the project's own clones, given the .githooks/ directory at the root, but it is the kind of thing to be aware of when running an install inside an existing working tree.

## The database is rebuilt from CSV on every launch

There is no persistent database, and the page is candid about why that matters. The DuckDB database lives in memory and is rebuilt for each server process, so every launch reloads from the CSV files. Persistent incremental import is described as planned future work rather than current behaviour, which is the sentence to weigh before you point this at a large export. Two further limitations sit alongside it. Device overlap can produce duplicate-looking measurements, and the guidance is that queries should account for sourceName where appropriate, which in practice means writing aggregation logic that treats the phone and watch as separate sources rather than assuming one device produced a reading. And health reports, whether weekly, monthly or custom, summarize recorded data and are explicitly not medical advice.

## Conclusion

This is a small, careful server for one specific job: letting a model ask questions about your own health export without giving it a key to a hosted service. The design choices are worth the read even if you never run it, because the safeguards section is explicit that its controls reduce accidental side effects from generated SQL without isolating the process, and the memory section says plainly that an export too large for the limit fails rather than spilling to disk. Use it if you export with the Simple Health Export CSV app and are comfortable giving a model provider query results. Do not point it at a shared network client, since the page's own instruction is to run it through a local stdio client and to use process or OS isolation for untrusted SQL. Check first that persistent import does not exist yet: the database is rebuilt from CSV on every launch, so each session pays the reload cost.

## FAQ

### Does apple-health-mcp read Apple's own export.xml?

No. It requires a CSV export produced by the Simple Health Export CSV app on an iPhone, and the native export.xml format is not currently supported. Only that CSV layout is read, and the export procedure is to select All, pick a range, transfer the archive and unzip it.

### Does the Apple Health MCP server upload my health data?

The server reads the exported files in place and makes no upload and no network requests of its own. Query results returned to the MCP client may still be sent to whatever model provider that client is configured with, which is where the local-first boundary sits.

### What does MAX_MEMORY_MB do in apple-health-mcp?

It sets the DuckDB memory limit in megabytes and defaults to 2048. Roughly 1 GiB covers a two-year multi-table export, the server never spills health rows to a temporary directory on disk, and an export too large for the limit fails with an explicit error.

### Can I use apple-health-mcp with an MCP client other than Claude Desktop?

Yes. The configuration shown is for Claude Desktop, and the page says other MCP clients can use the same command, arguments, environment and stdio transport. Node.js 22 or newer is required, and the client needs a restart after a configuration change.

### Is the health report from apple-health-mcp medical advice?

No. Reports summarize recorded data over a weekly, monthly or custom period, and the page states explicitly that they are not medical advice. It also warns that device overlap can produce duplicate-looking measurements and that queries should account for sourceName.

## Sources

- [Issues](https://github.com/neiltron/apple-health-mcp/issues)
- [License: MIT](https://github.com/neiltron/apple-health-mcp/blob/main/LICENSE)
- [neiltron/apple-health-mcp on GitHub](https://github.com/neiltron/apple-health-mcp)
- [README](https://github.com/neiltron/apple-health-mcp/blob/main/README.md)
- [Releases](https://github.com/neiltron/apple-health-mcp/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/neiltron-apple-health-mcp
