<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Philip D'Souza</title>
    <description>The latest articles on DEV Community by Philip D'Souza (@philipad).</description>
    <link>https://dev.to/philipad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4048242%2Fe490c7ae-56a7-462a-aa6d-f544d6bb9cdb.jpg</url>
      <title>DEV Community: Philip D'Souza</title>
      <link>https://dev.to/philipad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/philipad"/>
    <language>en</language>
    <item>
      <title>Connect Apple Health to opencode: Native MCP Setup</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Thu, 15 Oct 2026 06:45:48 +0000</pubDate>
      <link>https://dev.to/philipad/connect-apple-health-to-opencode-native-mcp-setup-28jg</link>
      <guid>https://dev.to/philipad/connect-apple-health-to-opencode-native-mcp-setup-28jg</guid>
      <description>&lt;p&gt;This walks through the exact config, the two values you type twice, and the tool-scoping glob that keeps 16 health tools out of your coding context until you actually ask for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The config
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"health-export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"health-export-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HEALTH_DATA_DIR"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/you/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;type&lt;/code&gt; and &lt;code&gt;command&lt;/code&gt; are required for a local server, and &lt;code&gt;command&lt;/code&gt; is an array, not a shell string, so you cannot chain with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;. &lt;code&gt;HEALTH_DATA_DIR&lt;/code&gt; points at the folder holding your &lt;code&gt;.health-cache.json&lt;/code&gt;. The server reads that local file and makes zero runtime network calls, so nothing leaves the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scoping the tools
&lt;/h2&gt;

&lt;p&gt;opencode registers each server's tools with the server name as a prefix, so &lt;code&gt;health-export&lt;/code&gt; becomes &lt;code&gt;health_export_get_trends&lt;/code&gt;, &lt;code&gt;health_export_compare_periods&lt;/code&gt;, and so on. That prefix is the handle for globs. If you use opencode for real code work, disable the health tools globally and switch them on for one agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"health_export_*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"health"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"health_export_*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify and query
&lt;/h2&gt;

&lt;p&gt;From the config directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;opencode mcp list
health-export  &lt;span class="nb"&gt;local  &lt;/span&gt;connected  16 tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it says &lt;code&gt;disconnected&lt;/code&gt;, it is almost always &lt;code&gt;npx&lt;/code&gt; missing from the PATH the TUI inherited. Then ask something real, naming the server so intent is unambiguous: "use the health-export server to call get_trends on resting heart rate over 14 days."&lt;/p&gt;

&lt;h2&gt;
  
  
  On-device vs cloud
&lt;/h2&gt;

&lt;p&gt;The architectures differ in where the computation happens, not how nice the chart looks. In a cloud health AI, the answer is computed on the vendor's servers because that is where the model lives, and an account is usually required to bill a tenant. With a local MCP bridge, the answer is computed on your iPhone for in-app questions and on your own machine for agent queries, the vendor's servers never hold the rows, and there is no account. The MCP surface is open source and read-only, so the same data can feed opencode, Claude, Cursor, or whatever you run next.&lt;/p&gt;

&lt;p&gt;Full setup guide, with the pairing option and timeout details: &lt;a href="https://www.healthexport.dev/blog/connect-apple-health-to-opencode-mcp" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/connect-apple-health-to-opencode-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>llm</category>
      <category>ai</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Apple pulled its AI Health overhaul from iOS 27.2 beta 4: what changed, and what you can run today</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Wed, 14 Oct 2026 06:01:46 +0000</pubDate>
      <link>https://dev.to/philipad/apple-pulled-its-ai-health-overhaul-from-ios-272-beta-4-what-changed-and-what-you-can-run-today-ohg</link>
      <guid>https://dev.to/philipad/apple-pulled-its-ai-health-overhaul-from-ios-272-beta-4-what-changed-and-what-you-can-run-today-ohg</guid>
      <description>&lt;p&gt;Apple pulled the redesigned Apple Intelligence Health app from iOS 27.2 beta 4 on October 13, 2026. Beta testers got the old Health app back: no Insights tab, no Apple Intelligence summaries, no readiness score. MacRumors and 9to5Mac both reported the removal and landed on the same read, that Apple re-timed the health overhaul to later in 2026 rather than cancelling it.&lt;/p&gt;

&lt;p&gt;Nothing about the features changed. Only the date, and there is no new date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was being held back
&lt;/h2&gt;

&lt;p&gt;The redesign, announced September 9, 2026, adds an Insights tab with Apple Intelligence summaries and a For You section, a Longevity tab with a Health Age estimate, a 0 to 10 readiness score, and camera-based movement assessments. The readiness score leans on the newest Apple Watch hardware. That is a large surface to ship, and Apple has pulled features from betas before and landed them a cycle later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that matters if you are buying
&lt;/h2&gt;

&lt;p&gt;Apple has not published a new date. "Later in 2026" is not a date, and a beta is not a release. If your decision depends on it, you are betting on someone else's roadmap. Some of it needs hardware you may not own. And none of it is addressable by your own tools: summaries, recommendations and a Health Age are views rendered inside one app on one phone. Your agent cannot call them. The redesign answers you; it does not give you a handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  On-device versus on a schedule
&lt;/h2&gt;

&lt;p&gt;Both paths compute on the phone. The difference is who owns the timeline and what you get to do with the result.&lt;/p&gt;

&lt;p&gt;Apple's redesign is out of beta 4 and re-timed, with no date. MetricBridge is on the App Store today: answers computed on the iPhone, no account and no server of ours in the path, 190 metrics as local JSON, and a zero-dependency MCP server with 16 read-only tools and 22 prompts for Claude, Cursor, opencode and any compliant client.&lt;/p&gt;

&lt;p&gt;There is a real difference between a model that describes your week and a computation that shows it. We publish a reproducible benchmark so you can check the claim instead of trusting it: run scripts/bench_deterministic_layer.py and you get the same provenance card an answer carries, every time.&lt;/p&gt;

&lt;p&gt;Full post: &lt;a href="https://dub.sh/K17wAcv" rel="noopener noreferrer"&gt;https://dub.sh/K17wAcv&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Where agents actually find MCP servers</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Tue, 13 Oct 2026 01:37:01 +0000</pubDate>
      <link>https://dev.to/philipad/where-agents-actually-find-mcp-servers-2o3</link>
      <guid>https://dev.to/philipad/where-agents-actually-find-mcp-servers-2o3</guid>
      <description>&lt;p&gt;Publishing an MCP server to npm is not the same as being discoverable. Agents do not browse a registry at runtime: a person or an agent reads a listing, copies a config, and their client launches your server. The official MCP Registry stores metadata that points at a package version, so bumping npm without republishing the registry leaves version=latest serving the old build. Directories (Glama, PulseMCP, mcp.so) are cached copies of that registry and lag behind it. Freshness, not vanity, is the ranking signal.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://dub.sh/7a9Nqpd" rel="noopener noreferrer"&gt;https://dub.sh/7a9Nqpd&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Run Apple Health data with your local LLM (Ollama + MCP)</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Mon, 12 Oct 2026 08:12:00 +0000</pubDate>
      <link>https://dev.to/philipad/run-apple-health-data-with-your-local-llm-ollama-mcp-6j7</link>
      <guid>https://dev.to/philipad/run-apple-health-data-with-your-local-llm-ollama-mcp-6j7</guid>
      <description>&lt;p&gt;Ollama does not implement MCP, so pointing a local model at Apple Health data takes one extra piece: an MCP-aware client that holds the connection and turns model output into tool calls. Goose, LM Studio, mcphost, and Open WebUI with mcpo all do this, and they share the same underlying server config.&lt;/p&gt;

&lt;p&gt;Once wired, the model routes your question. Ask about your HRV trend and it calls get_freshness, then compare_periods, and answers from your real export: 61 ms this week, up 4.4% from 58 ms. Zero runtime network calls, so the prompt and the numbers stay on your hardware.&lt;/p&gt;

&lt;p&gt;Full setup, including the "model describes tools but never calls them" fix: &lt;a href="https://dub.sh/yvDDhXQ" rel="noopener noreferrer"&gt;https://dub.sh/yvDDhXQ&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ollama does not speak MCP: wiring local Apple Health into a local LLM</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:42:19 +0000</pubDate>
      <link>https://dev.to/philipad/ollama-does-not-speak-mcp-wiring-local-apple-health-into-a-local-llm-3ogn</link>
      <guid>https://dev.to/philipad/ollama-does-not-speak-mcp-wiring-local-apple-health-into-a-local-llm-3ogn</guid>
      <description>&lt;p&gt;Every "run your local LLM on your own Apple Health data" tutorial ends the same way: install Ollama, done. Then nothing works. The reason is simple and worth saying plainly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama does not speak MCP.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ollama is an inference server. It runs the model and exposes an OpenAI-compatible chat endpoint on &lt;code&gt;http://localhost:11434&lt;/code&gt;. That is the whole job. MCP is a different protocol, one that runs between a client (the agent) and a server (the tool provider) over stdio or HTTP. Nothing in Ollama knows what a tool call over MCP is.&lt;/p&gt;

&lt;p&gt;So the tutorials are not wrong about the model. They just skip the part that holds the connection and translates tool calls: an MCP-aware client.&lt;/p&gt;

&lt;p&gt;One sentence to keep: Ollama is the model, the MCP client is the agent, the MCP server is the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you are actually wiring up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A local model server. Ollama, serving a tool-calling model. &lt;code&gt;ollama pull qwen3&lt;/code&gt; is a fine start.&lt;/li&gt;
&lt;li&gt;An MCP-aware client that can drive Ollama. Goose, LM Studio, mcphost, or Open WebUI behind mcpo.&lt;/li&gt;
&lt;li&gt;The MCP server itself. &lt;code&gt;health-export-mcp&lt;/code&gt; is a zero-dependency Node server that exposes 16 read-only tools and 22 prompts over a local Apple Health export.&lt;/li&gt;
&lt;li&gt;An export to read. The MetricBridge iOS app writes clean JSON from Apple Health to a folder you choose.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The server config is the same everywhere
&lt;/h2&gt;

&lt;p&gt;Every client wants the same two values: the command that starts the server, and the folder holding the export. The server is on npm, so there is nothing to clone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"health-export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"health-export-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HEALTH_DATA_DIR"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/you/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the quotes around &lt;code&gt;HEALTH_DATA_DIR&lt;/code&gt;. The default path contains spaces and an &lt;code&gt;iCloud~&lt;/code&gt; prefix, and a bare path will fail to resolve. On Linux the same path expands to &lt;code&gt;~/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you do not have an export yet, &lt;code&gt;npx -y health-export-mcp --demo&lt;/code&gt; starts the server against bundled sample data so you can test the client wiring before you have real numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goose
&lt;/h2&gt;

&lt;p&gt;Edit &lt;code&gt;~/.config/goose/config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;health-export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
    &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;health-export-mcp"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;envs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HEALTH_DATA_DIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/Users/you/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;goose session&lt;/code&gt; and the tools show up. Goose talks to Ollama on its own, so you only configure the MCP server here.&lt;/p&gt;

&lt;h2&gt;
  
  
  mcpHost (the short path for a pure Ollama stack)
&lt;/h2&gt;

&lt;p&gt;mcphost is the leanest option when Ollama is the only provider you care about. It runs the model and the MCP client in one process.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;~/health-mcp.json&lt;/code&gt; with the same &lt;code&gt;mcpServers&lt;/code&gt; block, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcphost &lt;span class="nt"&gt;-m&lt;/span&gt; ollama:qwen3 &lt;span class="nt"&gt;--config&lt;/span&gt; ~/health-mcp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole stack: one binary, one model, one server, nothing off the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  LM Studio
&lt;/h2&gt;

&lt;p&gt;LM Studio has its own MCP support and reads &lt;code&gt;~/.lmstudio/mcp.json&lt;/code&gt;. Use the same &lt;code&gt;mcpServers&lt;/code&gt; shape. Point the chat at a local model and the server appears in the tools list for any conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open WebUI via mcpo
&lt;/h2&gt;

&lt;p&gt;Open WebUI does not speak MCP either. The bridge is &lt;code&gt;mcpo&lt;/code&gt;, which wraps a stdio MCP server and exposes it as an OpenAPI HTTP server that Open WebUI can call as a tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mcpo
mcpo &lt;span class="nt"&gt;--port&lt;/span&gt; 8080 &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; health-export-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;HEALTH_DATA_DIR&lt;/code&gt; in the environment before you run it, then add &lt;code&gt;http://localhost:8080&lt;/code&gt; as a tool server in Open WebUI. Your local model now has the tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real turn looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Using my data, how did my HRV trend this week compared with last week?

[qwen3] -&amp;gt; get_freshness
        -&amp;gt; compare_periods { metric: "hrv", thisWeek: true, lastWeek: true }

        Your HRV averaged 61 ms this week, up 4.4% from 58 ms last
        week. Resting heart rate fell from 56 to 55 bpm. Both are
        deviations from your own recent baseline, not a verdict.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model never leaves your machine. The export never leaves your machine. The only network call in the whole loop is the one Ollama makes to itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things that trip people up
&lt;/h2&gt;

&lt;p&gt;Composer-style clients do not always auto-discover MCP tools. If the model claims it cannot see your data, name the server in the prompt. This is the most common cause of "my local model ignores the tools."&lt;/p&gt;

&lt;p&gt;And check staleness before you trust a trend. &lt;code&gt;get_freshness&lt;/code&gt; and &lt;code&gt;get_health_metrics&lt;/code&gt; report the coverage window the server actually read, so you can tell a real change from a gap in the export.&lt;/p&gt;

&lt;p&gt;The server is read-only by design: 16 tools, 22 prompts, zero write paths back into HealthKit. That is deliberate. The interesting question was never whether a local model can read your health data. It is whether it can do it without the data ever leaving the room.&lt;/p&gt;

&lt;p&gt;Source and setup: [healthexport.dev/blog/run-apple-health-data-with-local-llm-ollama-mcp](&lt;a href="https://dub.sh/9Xcc4Aq" rel="noopener noreferrer"&gt;https://dub.sh/9Xcc4Aq&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Apple Foundation Models vs a deterministic stats layer: why on-device is not the same as showing your work</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:25:37 +0000</pubDate>
      <link>https://dev.to/philipad/apple-foundation-models-vs-a-deterministic-stats-layer-why-on-device-is-not-the-same-as-showing-4426</link>
      <guid>https://dev.to/philipad/apple-foundation-models-vs-a-deterministic-stats-layer-why-on-device-is-not-the-same-as-showing-4426</guid>
      <description>&lt;p&gt;Apple's Foundation Models framework puts a roughly 3-billion-parameter model on the phone. That is the part everyone reports. The part that matters for health data is that it is still a generative model: it predicts tokens, it does not compute.&lt;/p&gt;

&lt;p&gt;So when it tells you your HRV trended up, there is no sample count, no date range, and no reproducible arithmetic behind that sentence. And a 4,096-token session window means you cannot just hand it a year of readings and ask.&lt;/p&gt;

&lt;p&gt;The split that actually works: the model routes and phrases, a deterministic stats layer computes. @Generable and the Tool protocol are designed for this. The model opens a tool, the tool does the math on your real samples, and the result is byte-identical across runs. A real provenance card from our benchmark: 49.6 ms seven-day average, dayCount 7 against coverageDays 30, +16.3% versus the prior 14 days, sha256 f2e87621.&lt;/p&gt;

&lt;p&gt;On-device is not the same as deterministic. One predicts a plausible number. The other shows you the work.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://www.healthexport.dev/blog/apple-foundation-models-vs-deterministic-stats-layer?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261011-hea216-fmstats" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/apple-foundation-models-vs-deterministic-stats-layer?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261011-hea216-fmstats&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Apple Foundation Models vs a deterministic stats layer</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:22:58 +0000</pubDate>
      <link>https://dev.to/philipad/apple-foundation-models-vs-a-deterministic-stats-layer-4d0d</link>
      <guid>https://dev.to/philipad/apple-foundation-models-vs-a-deterministic-stats-layer-4d0d</guid>
      <description>&lt;p&gt;Apple's Foundation Models framework gives iOS apps a free, offline 3B model with a 4,096 token window per session. It is good infrastructure, and the privacy property is real. It is also not the same claim as "this app shows its work", and 2026 is full of apps quietly treating the two as one.&lt;/p&gt;

&lt;p&gt;A language model predicts the next token. It does not evaluate arithmetic. Ask it for the average of seven values and it produces a plausible string, because plausible strings appeared in its training distribution. Run it twice and you can get 49.6 and 49.8. Both pass a schema check. Neither is a function of your data.&lt;/p&gt;

&lt;p&gt;The framework's own Tool protocol points at the right answer: do not ask the model to compute, ask it to call something that computes.&lt;/p&gt;

&lt;p&gt;In MetricBridge that is AskEngine.swift, a plain compute layer. It parses a question into one of 16 tool calls, resolves the metric name, runs the aggregation over your recorded rows, and returns a struct called ComputedFacts. The struct carries the canonical metric name, the unit, a range label, the count of days that actually have data, the raw (day, value) rows, the headline aggregate and its label, and a direction. It also carries two honesty fields: coverageDays beside dayCount, so a "most recent value" answer still reflects the whole series, and unevenWindows, so a comparison with a different number of recorded days on each side says so instead of printing span totals next to a per-day rate.&lt;/p&gt;

&lt;p&gt;The model never computes a figure. It gets a short summary and phrases it.&lt;/p&gt;

&lt;p&gt;You can check that claim yourself. The repo commits a script, bench_deterministic_layer.py, that builds a seeded 30-day HRV series and prints a provenance card. Headline 49.6 ms (7-day average), +16.3% versus the prior 14 days (mean 42.6 ms), dayCount 7 against coverageDays 30. Run it twice: byte-identical output, sha256 f2e87621b46b2fe04cd81a86cb2183ed2e271237ca8d23b9123171445359d9d7 both times. The card closes with "figures computed by the layer: 5, by the model: 0."&lt;/p&gt;

&lt;p&gt;None of this diagnoses or monitors anything. Where a metric moves away from your recent range, the honest word is a deviation from your personal baseline, never abnormal. Being able to trace a figure does not make it a diagnosis.&lt;/p&gt;

&lt;p&gt;Full write-up, including the provenance card and the side-by-side: &lt;a href="https://www.healthexport.dev/blog/apple-foundation-models-vs-deterministic-stats-layer?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261011-hea215-foundationmodels" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/apple-foundation-models-vs-deterministic-stats-layer?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261011-hea215-foundationmodels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>ios</category>
      <category>llm</category>
    </item>
    <item>
      <title>On-device health AI that answers questions: no cloud, no upload</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Sat, 10 Oct 2026 08:08:43 +0000</pubDate>
      <link>https://dev.to/philipad/on-device-health-ai-that-answers-questions-no-cloud-no-upload-3a01</link>
      <guid>https://dev.to/philipad/on-device-health-ai-that-answers-questions-no-cloud-no-upload-3a01</guid>
      <description>&lt;p&gt;Most health AI apps upload your Apple Health data to a server. This is the alternative: a model that runs on the phone, answers real questions about your readings, and shows the samples behind every answer.&lt;/p&gt;

&lt;p&gt;The privacy part is not a feature on a list, it is the architecture. The model runs on your iPhone. Your heart rate, HRV and sleep never leave the device. The question, the computation and the answer are all local. There is no upload and no cloud account in the path.&lt;/p&gt;

&lt;p&gt;Provenance is the second half. When the app says your HRV averaged 62 ms this week, it shows the metric, the date range, the raw sample count, the computation and a chart. You can tap through to the individual readings (58, 64, 67, 61, 59) and confirm the math yourself. That is the difference between trusting an app and knowing you are right.&lt;/p&gt;

&lt;p&gt;If you run your own agents, the same 190 metrics export as clean JSON and a zero-dependency, read-only MCP server lets Claude, Cursor or opencode query the live data on your machine with no network calls at all.&lt;/p&gt;

&lt;p&gt;Full write-up with the provenance breakdown: &lt;a href="https://dub.sh/dfEIIKS" rel="noopener noreferrer"&gt;https://dub.sh/dfEIIKS&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ios</category>
      <category>mobile</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Connect Apple Health to ChatGPT via webhook: your data, their agent</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Fri, 09 Oct 2026 08:14:59 +0000</pubDate>
      <link>https://dev.to/philipad/connect-apple-health-to-chatgpt-via-webhook-your-data-their-agent-2g60</link>
      <guid>https://dev.to/philipad/connect-apple-health-to-chatgpt-via-webhook-your-data-their-agent-2g60</guid>
      <description>&lt;p&gt;ChatGPT is good at reasoning over health data and cannot reach Apple Health on its own. It does not speak MCP, so it cannot call a local tool the way Cursor or Claude can. A webhook bridge fixes that, and the key design choice is whose endpoint receives the data.&lt;/p&gt;

&lt;p&gt;The flow is one direction: your iPhone POSTs structured JSON to a URL you configure, and you feed that into ChatGPT. The app never sends your data to a server run by the developer. The payload is clean and typed: every metric has a stable lower-case name, an explicit unit, and dates with a UTC offset, so the model never guesses. The same names the MCP server returns.&lt;/p&gt;

&lt;p&gt;Three ways to wire it up, from zero code to fully automated: export to a file and drag it into ChatGPT, stand up a fifteen-line local FastAPI receiver on your LAN, or use n8n or Home Assistant if you already run them. Then the privacy distinction. The MCP path keeps the data on your machine with zero network traffic. The webhook path adds a second hop, from your endpoint to OpenAI, and that hop is when the data leaves your control. Use MCP when your agent supports it, the webhook when you specifically want ChatGPT's reasoning.&lt;/p&gt;

&lt;p&gt;Full guide, with the real payload, the FastAPI receiver, and a full ChatGPT walkthrough: &lt;a href="https://dub.sh/hBVeLDr" rel="noopener noreferrer"&gt;https://dub.sh/hBVeLDr&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>chatgpt</category>
      <category>mobile</category>
    </item>
    <item>
      <title>HealthKit to JSONL vs JSON: one snapshot survives a cut sync, the other gives you nothing</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Thu, 08 Oct 2026 20:22:57 +0000</pubDate>
      <link>https://dev.to/philipad/healthkit-to-jsonl-vs-json-one-snapshot-survives-a-cut-sync-the-other-gives-you-nothing-2jem</link>
      <guid>https://dev.to/philipad/healthkit-to-jsonl-vs-json-one-snapshot-survives-a-cut-sync-the-other-gives-you-nothing-2jem</guid>
      <description>&lt;p&gt;Two apps can write the exact same Apple Health records to disk and behave nothing alike the moment a sync gets interrupted.&lt;/p&gt;

&lt;p&gt;I benchmarked it on 2026-10-08: 3,200 daily records, 400 days across 8 metrics, written once as a JSON array and once as JSONL, then both truncated at 100,000 bytes to simulate a cut-off iCloud sync. The JSON array returned a parse error and 0 recoverable records. The JSONL file gave back 1,355. The two files differed by 1 byte.&lt;/p&gt;

&lt;p&gt;The reason is structural. A JSON array is one document, valid only once the closing bracket lands, so a half-written snapshot is not JSON at all. JSONL is one object per line, valid incrementally, so a reader keeps every complete record it already has.&lt;/p&gt;

&lt;p&gt;Check any snapshot yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; yourfile.jsonl
jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'select(.metric=="hrv_sdnn")'&lt;/span&gt; health.jsonl
&lt;span class="nb"&gt;grep &lt;/span&gt;2026-10-07 health.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the other side of the trade, HealthMirror writes plain-text JSONL into its own iCloud container and its local server reads per-metric subfolders. MetricBridge takes the whole-file route: a compact .health-cache.json snapshot a zero-dependency MCP server reads, with get_freshness reporting the age. Different read patterns, different formats.&lt;/p&gt;

&lt;p&gt;Full write-up with the benchmark table and the failure modes: &lt;a href="https://www.healthexport.dev/blog/healthkit-to-jsonl-vs-json?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261008-hea195-jsonl" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/healthkit-to-jsonl-vs-json?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261008-hea195-jsonl&lt;/a&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>ios</category>
      <category>json</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Apple Health export.xml is too big. Here is what actually works.</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Thu, 08 Oct 2026 08:25:00 +0000</pubDate>
      <link>https://dev.to/philipad/apple-health-exportxml-is-too-big-here-is-what-actually-works-567l</link>
      <guid>https://dev.to/philipad/apple-health-exportxml-is-too-big-here-is-what-actually-works-567l</guid>
      <description>&lt;p&gt;Apple Health exports everything you have ever recorded into a single export.xml. If you have worn a Watch for a year or two, that file is routinely hundreds of megabytes. Your text editor hangs, Numbers refuses it, and pasting it into ChatGPT or Claude is not an option.&lt;/p&gt;

&lt;p&gt;Why it is so large: it is a complete, uncompressed event log rather than a summary. Every heart-rate reading your Watch has taken is its own XML element, and each one carries the full type identifier, source device, unit and three timestamps. That is around 250 bytes of markup to store one number. The payload is small; the wrapper is not.&lt;/p&gt;

&lt;p&gt;The paste problem is not close. At roughly 4 characters per token, a 200 MB export is on the order of 50 million tokens. Frontier models top out in the hundreds of thousands, two orders of magnitude short. Bigger context windows do not close that gap.&lt;/p&gt;

&lt;p&gt;Three ways out: stream and filter the records you want (free), collapse it to daily aggregates (free), or skip XML entirely by reading HealthKit on the phone and writing aggregated JSON that a local MCP server can query as live tools. The first two are chores you repeat every time you want fresh data; the third removes the file from the loop.&lt;/p&gt;

&lt;p&gt;Full guide, with the token arithmetic, the three fixes, and the tool comparison: &lt;a href="https://www.healthexport.dev/blog/apple-health-export-xml-too-big?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261008-hea193-xmltoobig" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/apple-health-export-xml-too-big?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261008-hea193-xmltoobig&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Apple Health data for AI: why clean JSON beats CSV and XML</title>
      <dc:creator>Philip D'Souza</dc:creator>
      <pubDate>Wed, 07 Oct 2026 08:09:31 +0000</pubDate>
      <link>https://dev.to/philipad/apple-health-data-for-ai-why-clean-json-beats-csv-and-xml-bh9</link>
      <guid>https://dev.to/philipad/apple-health-data-for-ai-why-clean-json-beats-csv-and-xml-bh9</guid>
      <description>&lt;p&gt;Apple Health exports as a single zipped export.xml, and that one file is where a lot of AI health analysis quietly goes wrong. It mixes every record type together, repeats verbose HealthKit attribute names on every reading, and can run to hundreds of megabytes. It is the format HealthKit chose, not the one a language model reads well.&lt;/p&gt;

&lt;p&gt;The usual fix is to convert it. CSV is compact and imports anywhere, but it is flat and header-dependent: the value means something only while the header row is attached, and that breaks the moment rows are pasted or windowed in a conversation. XML has all the structure but pays for it in verbosity and an unfamiliar schema.&lt;/p&gt;

&lt;p&gt;Clean JSON is the sweet spot. Keys are inline labels, units travel with values, and nesting lets a scalar, a pair and a series sit in one payload. The next step is to stop shipping files at all and let the agent query the exact metrics it needs over MCP, so it gets a small typed object instead of your whole history.&lt;/p&gt;

&lt;p&gt;Full comparison, with the same HRV reading in all three formats, the five-dimension table, and how health-export-mcp turns 190 HealthKit metrics into queryable JSON: &lt;a href="https://www.healthexport.dev/blog/apple-health-json-vs-csv-xml-for-ai?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261007-hea190-jsoncsvxml" rel="noopener noreferrer"&gt;https://www.healthexport.dev/blog/apple-health-json-vs-csv-xml-for-ai?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=exp-20261007-hea190-jsoncsvxml&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
