← Back to Blog

Rotifer Now Installs into DeepSeek Harness

DeepSeek Harness is in developer preview and says openly it will make breaking changes. We shipped into it by declaring configuration that mounts the harness's own components — so there is nothing of ours for an API change to break.

Rotifer Now Installs into DeepSeek Harness

Rotifer now installs into DeepSeek Harness. What DSH loads from us is two lines of configuration and four skill files. Both configuration rows mount plugins the harness already ships — its own skill provider, its own MCP bridge. Nothing of ours runs inside it.

That was not minimalism for its own sake. It is what let us ship into a harness that openly warns it will break its own API.


The host is not the competitor

DeepSeek Harness opened in August: MIT, TypeScript, built on the Cordis meta-framework, with "everything is a plugin" as its organizing claim. The model layer, the tool layer, the agent loop, the sandbox, the UI — every one of them is a swappable row in a configuration tree.

The reflex when a large lab ships an agent runtime is to ask whether it competes with you. For Rotifer the answer is structural rather than strategic: a harness decides how an agent runs. Rotifer is about which capability an agent should be running, and whether a better one exists. Those stack; they do not collide. A harness with no capability layer is exactly where a capability layer belongs.

So the question was never whether to be there. It was how to be there without getting overtaken.

The obvious move was the wrong one

DSH is in developer preview and says plainly that breaking changes are coming. In the days we were looking at it, it moved through several release candidates and renumbered its own version scheme mid-stream. A native plugin written against that surface is a maintenance commitment measured in days.

The conclusion we initially drew from this — and it is worth naming because it was wrong — was that shipping should wait. Correct premise, wrong inference. We had enumerated two options, "write a native plugin now" and "wait," and never asked whether a third existed.

It did, and the ecosystem was already using it.

What a bundle actually is

In DSH, an installable unit is a bundle: an npm package or git checkout whose manifest declares one field, dsh.bundle.patch, pointing at a cordis.patch.yml. That patch file is a list of rows to insert into the composed configuration tree.

The critical property is what a row may reference. A row names a module specifier — and nothing requires that module to be yours. If the modules you name are ones the host already ships, your package contributes configuration and nothing else.

Ours names two:

- insert:
    - id: rotifer-skills
      name: '@deepseek-ai/dsh-skill-filesystem'
      config:
        providerName: rotifer
        includeDefaultRoots: false
        customSkillDirs: [ <this package's skills/ directory> ]

    - id: rotifer-mcp
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: rotifer
        transport: stdio
        command: npx
        args: ['-y', '@rotifer/mcp-server@0.16.1', '--tools=evolve']

The first is a second instance of the harness's own filesystem skill provider, scoped to our directory. includeDefaultRoots: false matters more than it looks: without it the provider would also rescan the user's project and personal skill directories, and could shadow a skill they wrote themselves. Scoped this way it can only ever contribute the four skills in our package.

The second is the harness's own MCP bridge, pointed at the Rotifer MCP server.

Neither row is code we wrote. The upgrade path for both is DeepSeek's problem, not ours — which is the entire point during a preview.

Zero code is a security property, not just an engineering one

The engineering benefit is easy to state: an API we do not call cannot break us.

The other consequence took us longer to appreciate. Installing this plugin does not place our code inside your agent runtime. There is nothing in the package to audit for behavior, because there is no behavior in the package — only a declaration of which of the host's own components to mount, and how.

For a plugin whose job includes installing third-party capabilities onto your machine, that distinction is worth more than convenience. The trust you extend is bounded to one process we spawn over stdio, with a pinned version you can inspect before it runs:

npm view @rotifer/mcp-server@0.16.1 dist.integrity

The launch line is narrowed as well as pinned. Undeclared, the server offers its full surface, including publishing and sign-in. Declared with --tools=evolve, it offers ten tools — search, compare, inspect, install, roll back, and run local agents — and refuses the rest. It omits --allow, so the sandbox escape hatches stay off. Nothing reachable through this plugin can publish on your behalf or sign you in.

Those ten tool schemas cost 6,736 bytes of JSON on every request while the row is mounted — measured, not estimated. Remove that row and the four skills keep working; they cost only their catalog entries. We would rather tell you the number than let you discover it in a token bill.

What the ecosystem taught us on the way

Reading how other capability vendors integrate turned out to be more useful than any amount of reasoning about it. The pattern above is not a clever workaround we invented; it is what integrations of this shape already do there. One agent framework with thousands of stars integrates its entire system through a single MCP row and no TypeScript of its own.

The most valuable thing we found was a failure mode nobody seems to be watching. DSH takes a skill's name from its frontmatter, not its directory. A skill file with a missing name or description, or a name that is not kebab-case, is dropped — with one log line and nothing else. It does not appear, and nothing fails.

Our four skills happened to comply, and happened to be namespaced already. But "happened to" is not a guarantee, so there is now a test asserting it. If you ship skills into that ecosystem, check your frontmatter — the silence is not confirmation.

What this does not do

Rotifer's purpose is to let agents find out that a better implementation of a capability exists and adopt it, with performance data rather than opinion deciding what "better" means. A harness ecosystem full of capabilities and no way to compare them is exactly the environment that question is for.

We are not there yet, and this release does not get us there. Plugins in that ecosystem are TypeScript modules; Genes are WASM artifacts with declared schemas that run in a sandbox and can be measured. Arena cannot score the former today, and we are not going to imply otherwise — a ranking that cannot be reproduced is worse than no ranking. Closing that gap is its own piece of work, and it is not this one.

What this release does is put the capability layer where the hosts are. The ranking half of the loop already runs on Genes: fitness is computed from runtime performance, automatically and without votes. Installing anything, though, waits for you — every replacement is proposed, approved, and reversible, and the copy it replaces is kept so you can roll back.


Install

The plugin installs from the repository rather than from npm, deliberately: the unscoped rotifer name on npm is a pointer package that exists to stop the toolchain being impersonated, and this plugin has no binary to stand in for it.

dsh plugin --profile web add "github:rotifer-protocol/rotifer-playground#path:/plugins/rotifer"

The same folder also serves Cursor, CodeBuddy, OpenClaw and Claude Code, from one source and one version — because a second copy of the same skills maintained beside the first is how two copies drift apart without anyone noticing.

Once installed, look for rotifer-skills and rotifer-mcp in Settings → Plugins. There is no single entry named "Rotifer": a bundle that mounts the host's own components contributes configured rows, not a package of its own name. That is the visible cost of shipping no code, and we think it is a good trade.

What's next

The gap named above — making capabilities from a harness ecosystem measurable at all — is the interesting problem, and the honest answer today is that it is unsolved. It needs a real answer rather than a metadata heuristic dressed up as a score.

In the meantime the capability layer is reachable from one more place than it was last week, and the launch line that reaches it is the same narrow, pinned one every other host gets. That part we could do now, so we did.