<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mlx on RockB</title><link>https://baeseokjae.github.io/tags/mlx/</link><description>Recent content in Mlx on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 30 Aug 2026 13:04:27 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/mlx/index.xml" rel="self" type="application/rss+xml"/><item><title>Rapid-MLX DSH Provider: Native Apple Silicon Inference for the DeepSeek Harness</title><link>https://baeseokjae.github.io/posts/rapid-mlx-dsh-provider/</link><pubDate>Sun, 30 Aug 2026 13:04:27 +0000</pubDate><guid>https://baeseokjae.github.io/posts/rapid-mlx-dsh-provider/</guid><description>Connect the DeepSeek Harness to Rapid-MLX on Apple Silicon with a native dsh provider plugin that auto-reads model facts, fixes reasoning truth, and does memory-aware compaction.</description><content:encoded><![CDATA[<p>The Rapid-MLX DSH Provider is a native npm plugin for the DeepSeek Harness (dsh) that lets the agent read its model configuration straight from a running Rapid-MLX server&rsquo;s <code>/v1/models</code> endpoint instead of hand-written <code>settings.yaml</code>. Instead of copying context windows, reasoning efforts, and <code>max_model_len</code> values by hand — and re-copying them every time you switch models — the provider serves those facts directly from Rapid-MLX&rsquo;s JSON schema, keeps reasoning control truthful, and compacts the context at the real memory limit of your Apple Silicon machine.</p>
<h2 id="what-is-the-rapid-mlx-dsh-provider-and-why-does-deepseek-harness-need-it">What is the Rapid-MLX DSH Provider and why does DeepSeek Harness need it</h2>
<p>DeepSeek Harness is a 204K-star LLM-agent harness built on a strict philosophy: &ldquo;Everything is a Plugin.&rdquo; That philosophy is what makes a separate provider necessary — and useful. When you want to run a local model inside dsh, you do not patch the harness. You write a conformant <code>LlmAdapter</code> plugin that tells dsh the name of the model, the size of its effective context, and how to route reasoning and tool calls.</p>
<p>The Rapid-MLX DSH Provider (<code>@raullenchai/dsh-provider</code>) is exactly that adapter, written natively for <a href="https://github.com/raullenchai/Rapid-MLX">Rapid-MLX</a>, the 3,584-star &ldquo;fastest local AI engine for Apple Silicon.&rdquo; The pairing removes the most error-prone step in local-agent setup: hand-maintaining per-model config. Without the provider, every model swap means opening <code>settings.yaml</code> and hand-writing <code>contextWindow</code>, <code>reasoningEfforts</code>, and <code>max_model_len</code>. With it, dsh asks the server what model facts are real, and the server answers from a single source of truth.</p>
<h2 id="before-you-begin-requirements">Before you begin: requirements</h2>
<p>The provider is small, but the surrounding stack is specific. Before you install anything, confirm you have each of these:</p>
<ul>
<li><strong>Node.js &gt;= 22.15.</strong> This is not optional. dsh imports Node&rsquo;s Zstd stream API without declaring it in its own dependency tree, so an older Node runtime will fail at import time rather than at boot.</li>
<li><strong>A running Rapid-MLX server.</strong> The provider reads model facts from the server&rsquo;s <code>/v1/models</code> endpoint, so Rapid-MLX must be up and listening before dsh starts.</li>
<li><strong>Apple Silicon.</strong> Rapid-MLX is an Apple-metal inference engine, so an M-series Mac (M1, M2, M3, M4 — including Ultra variants) is implied. The upstream verification ran on an M3 Ultra.</li>
<li><strong>A model already pulled into Rapid-MLX.</strong> Context and reasoning facts are per-model, so at least one model must be stored locally before the first provider run.</li>
</ul>
<p>These four are the whole setup surface. There is no separate compiler, no build step, and no additional Python environment, which distinguishes this provider from the MLX-LM-based alternative covered later in this guide.</p>
<h2 id="install-the-provider-plugin">Install the provider plugin</h2>
<p>You can install the provider either from npm or directly from source, and neither path involves a build step.</p>
<p><strong>From npm</strong> — the published package is at version <code>0.3.0</code> as of 2026-08-30:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 304 25"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>

    </svg>
  
</div>
<p><strong>From source</strong> — clone the repository and install:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 512 57"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='8' y='36' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>

    </svg>
  
</div>
<p>Whichever route you take, the provider registers itself as a dsh plugin. The upstream project verifies the source install against <code>dsh 0.1.0-rc.7</code> and reports API compatibility with <code>rc.8</code>. If you are on <code>npm latest</code> for the harness, the plugin remains API-compatible. Because the harness treats anything under <code>dsh.bundle</code> as a plugin, the provider becomes available the moment dsh starts with it on the bundle path — there is no manual registration dialog and no build output.</p>
<h2 id="point-the-agent-at-the-route-in-settingsyaml">Point the agent at the route in settings.yaml</h2>
<p>After installing the plugin, you tell dsh which HTTP route to use. This is the one piece of config you do write by hand, and it is intentionally small: a single endpoint pointing at your Rapid-MLX <code>/v1</code> OpenAPI-compatible route.</p>
<p>In <code>settings.yaml</code>, set the provider&rsquo;s route to your Rapid-MLX server&rsquo;s OpenAI-compatible base URL. Rapid-MLX is a drop-in OpenAI replacement, so the route you already use for Claude Code, Cursor, or Aider is the same one you hand to the dsh provider. From that point on, the heavy lifting — model names, context windows, reasoning efforts, and max model length — comes from the server rather than from your editor.</p>
<p>This single-line indirection is the entire point: you stop hand-writing model facts and start letting the server own them. When you switch from one pulled model to another, the provider follows the server&rsquo;s reported schema automatically.</p>
<h2 id="the-three-things-it-fixes">The three things it fixes</h2>
<p>The provider&rsquo;s value lands in three concrete behaviors.</p>
<h3 id="1-no-more-hand-written-model-config">1. No more hand-written model config</h3>
<p>The biggest win is eliminating per-model manual config. When <code>contextWindow</code>, <code>reasoningEfforts</code>, and <code>max_model_len</code> are all served by Rapid-MLX&rsquo;s <code>/v1/models</code> JSON schema, there is nothing to hand-write when you switch models. Swap the model, and dsh follows automatically because it reads the same facts the server uses to run the model.</p>
<h3 id="2-reasoning-control-finally-reports-the-truth">2. Reasoning control finally reports the truth</h3>
<p>Local model configs have a chronic lie: they declare reasoning settings for models that do not actually have a reasoning parser. The provider fixes this because Rapid-MLX reports whether a model really has a reasoning parser, and the dsh provider surfaces that truth in the reasoning-control selector. You stop telling the agent to reason with a model that physically cannot.</p>
<h3 id="3-compaction-timed-to-real-memory-not-advertised-windows">3. Compaction timed to real memory, not advertised windows</h3>
<p>The third fix is the subtlest and the most valuable on Apple Silicon: the provider prefers the server&rsquo;s <code>max_model_len</code> — a memory-fitted ceiling — over the model&rsquo;s advertised <code>context_window</code>. Apple Silicon unified memory is the real constraint on context length, and hardware-aware capacity beats the marketing number that hand-copied config frequently drifts away from. The result is compaction that actually matches your machine.</p>
<h2 id="manage-models-with-five-tools-and-the-rapid-mlx-command">Manage models with five tools and the /rapid-mlx command</h2>
<p>The provider keeps its surface deliberately small — five tools total, split by which surface owns each fact:</p>
<table>
  <thead>
      <tr>
          <th>Tool</th>
          <th>Surface</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>rapid_mlx_serving</code></td>
          <td>HTTP</td>
          <td>Interacts with the Rapid-MLX server over HTTP</td>
      </tr>
      <tr>
          <td><code>rapid_mlx_cached</code></td>
          <td>CLI</td>
          <td>Reads cached model state</td>
      </tr>
      <tr>
          <td><code>rapid_mlx_pull</code></td>
          <td>CLI</td>
          <td>Pulls a model into local storage</td>
      </tr>
      <tr>
          <td><code>rapid_mlx_remove</code></td>
          <td>CLI</td>
          <td>Removes a stored model</td>
      </tr>
      <tr>
          <td><code>rapid_mlx_health</code></td>
          <td>HTTP + CLI</td>
          <td>Health-checks the server and CLI surfaces</td>
      </tr>
  </tbody>
</table>
<p>In addition to these tools, the provider exposes a <code>/rapid-mlx</code> slash command in the agent interface for quick model management. The split is deliberate: facts owned by the server surface flow through the HTTP tools, and facts owned by the local process flow through the CLI tools. Keeping the two apart avoids the confusion that a single mixed-surface tool tends to create.</p>
<h2 id="verified-results-on-m3-ultra-with-dsh-010-rc7">Verified results on M3 Ultra with dsh 0.1.0-rc.7</h2>
<p>The provider is not theoretical. The upstream README documents an end-to-end verification on an M3 Ultra against <code>dsh 0.1.0-rc.7</code>. In that run, a multi-step bug-fixing task completed in 36 seconds on <code>qwen3.6-35b-8bit</code>. That number matters because it shows the plugin doing real work — a realistic agent task with multiple tool calls and reasoning steps — not just booting and idling. The 36-second figure is the best single proof that the provider&rsquo;s route plumbing, reasoning routing, and tool dispatch all function as a unit under load.</p>
<h2 id="what-it-does-not-do-yet">What it does not do yet</h2>
<p>Being precise about the current gaps saves you from assuming features that do not exist. The provider — and Rapid-MLX itself — still has roadmap items that are not wired up:</p>
<ul>
<li><strong><code>recommended_sampling</code>.</strong> Server-suggested sampling parameters are not yet consumed by the provider.</li>
<li><strong><code>tool_call_parser</code>.</strong> Fast tool-call parsing directly on the server is not yet exposed through the provider.</li>
<li><strong>Memory-aware capacity as a first-class feature.</strong> The provider prefers <code>max_model_len</code> today, but fully adaptive capacity derived purely from the memory footprint of the loaded model is the roadmap&rsquo;s biggest remaining win. Right now you get the memory-fitted ceiling the server already reports; the future step is capacity that actively recomputes with load.</li>
</ul>
<p>None of these block the core three fixes above, but they are worth knowing so your expectations match the current release.</p>
<h2 id="conformance-with-the-official-llmadapter-contract">Conformance with the official LlmAdapter contract</h2>
<p>Because DeepSeek Harness is plugin-driven, the bar for a new provider is conformance with the official <code>LlmAdapter</code> contract defined in <code>docs/cookbook/adding-an-llm-adapter.md</code>. The Rapid-MLX DSH Provider treats this as a test obligation: it ships per-protocol-obligation tests, one per rule in the contract, so conformance is not asserted in prose but demonstrated with executable tests.</p>
<p>This is a meaningful design choice. Most adapter plugins pass a smoke test and call it done. This provider structures its suite around the harness&rsquo;s own &ldquo;protocol obligations,&rdquo; which means each method the harness relies on — model enumeration, context negotiation, reasoning-effort handling, tool-call framing — has a targeted test backing it. For anyone who has been burned by an agent plugin that silently violates a harness contract, this is the difference between a provider you can trust and one you debug at 2am.</p>
<h2 id="three-gotchas-before-you-edit-the-provider">Three gotchas before you edit the provider</h2>
<p>If you plan to extend or debug the plugin, these three details will save you real time:</p>
<ol>
<li><strong><code>dsh.bundle</code> makes it a plugin.</strong> DeepSeek Harness treats anything under <code>dsh.bundle</code> as a plugin. If you fork the provider, keeping your code inside that bundle path is what makes the harness load it.</li>
<li><strong><code>LlmReasoningEffortInfo.name</code> is required.</strong> The reasoning-effort contract does not allow a missing <code>name</code> field. If the server reports a reasoning parser that maps to an effort entry without a name, the harness will reject it.</li>
<li><strong>DSH has no tool role.</strong> Unlike some OpenAI-style APIs, DeepSeek Harness has no dedicated &ldquo;tool&rdquo; role in its message protocol. If you extend the provider, route tool-call framing through the roles dsh actually supports rather than assuming a <code>tool</code> role exists.</li>
</ol>
<h2 id="comparison-rapid-mlx-dsh-provider-vs-dsh-llm-mlx-and-other-mlx-mac-agents">Comparison: rapid-mlx DSH provider vs dsh-llm-mlx and other MLX Mac agents</h2>
<p>The Rapid-MLX DSH provider is not the only way to run MLX models inside the DeepSeek Harness. The closest alternative is <code>dsh-llm-mlx</code>, which takes a different architectural route:</p>
<table>
  <thead>
      <tr>
          <th>Aspect</th>
          <th>Rapid-MLX DSH Provider</th>
          <th>dsh-llm-mlx</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Underlying engine</td>
          <td>Rapid-MLX server</td>
          <td>mlx-lm / mlx-vlm</td>
      </tr>
      <tr>
          <td>Integration path</td>
          <td>Native <code>@raullenchai/dsh-provider</code> plugin</td>
          <td>DSH built-in OpenAI-compatible adapter</td>
      </tr>
      <tr>
          <td>Server ownership</td>
          <td>Rapid-MLX runs as its own engine</td>
          <td>Can manage <code>mlx_lm.server</code> / <code>mlx_vlm.server</code> for the lifetime of the dsh process</td>
      </tr>
      <tr>
          <td>Bind address (managed)</td>
          <td>Rapid-MLX default</td>
          <td><code>127.0.0.1</code></td>
      </tr>
      <tr>
          <td>External server reuse</td>
          <td>Yes (OpenAI-compatible)</td>
          <td>Yes, e.g. <code>http://127.0.0.1:18080/v1</code></td>
      </tr>
      <tr>
          <td>Extra runtime needed</td>
          <td>Node &gt;= 22.15 only</td>
          <td>Local Python env with <code>mlx-lm</code> / <code>mlx-vlm</code></td>
      </tr>
      <tr>
          <td>OS for managed startup</td>
          <td>Apple Silicon</td>
          <td>Apple Silicon macOS</td>
      </tr>
  </tbody>
</table>
<p>The deciding factor is usually which engine you already run. If you use Rapid-MLX (with its 4.2x-over-Ollama claim, 0.08s cached TTFT, and 17 tool parsers), the native provider is the natural fit. If you prefer the mlx-lm stack and want dsh to own the model server lifecycle, <code>dsh-llm-mlx</code> is the better match. Both sit above a separate category of MLX coding agents — projects like <code>mlx-code</code>, the 57-star git-native coding agent for Mac — that prove the broader demand for Apple Silicon native agentic inference even though they are not dsh providers themselves.</p>
<h2 id="faq-and-troubleshooting">FAQ and troubleshooting</h2>
<p><strong>Do I need a GPU to use the Rapid-MLX DSH provider?</strong>
No. The provider is a config/adapter plugin — it does no inference itself. It delegates all model execution to a running Rapid-MLX server on your Apple Silicon machine, so you need an M-series chip, but no separate discrete GPU.</p>
<p><strong>Can the provider work with dsh 0.1.0-rc.8 or the npm release?</strong>
Yes. The plugin was verified end-to-end against dsh <code>0.1.0-rc.7</code>, is API-compatible with <code>rc.8</code>, and the harness <code>npm latest</code> remains compatible with the published provider at <code>0.3.0</code>.</p>
<p><strong>Why does the provider prefer max_model_len over context_window?</strong>
Because on Apple Silicon the unified memory footprint is the real ceiling on how long a context can grow, and <code>max_model_len</code> is the server&rsquo;s memory-fitted ceiling. The model&rsquo;s advertised <code>context_window</code> is often a marketing maximum that hand-copied config drifts away from, so preferencing the server&rsquo;s figure makes compaction match your actual hardware.</p>
<p><strong>How is this different from the built-in OpenAI-compatible adapter in dsh?</strong>
The built-in adapter requires you to hand-maintain model facts in <code>settings.yaml</code> per model. The Rapid-MLX provider reads <code>contextWindow</code>, <code>reasoningEfforts</code>, and <code>max_model_len</code> from the server&rsquo;s <code>/v1/models</code> schema automatically, so switching models needs no manual re-config and reasoning settings report the server&rsquo;s truth.</p>
<p><strong>What happens if I run Node older than 22.15?</strong>
dsh imports Node&rsquo;s Zstd stream API without declaring it, so on an older Node the harness fails at import time. Upgrade to Node &gt;= 22.15 before troubleshooting anything else, because this error surfaces before the provider even loads.</p>
]]></content:encoded></item></channel></rss>