As of August 18, 2026, the official development guide supports Node.js 22.19+ and 24+, while CI covers Node 22.19, 24, and 26. That single compatibility range is enough to shape your first decision: choose the extension scenario first, then build the smallest plugin that proves one capability. Do not begin with a large plugin suite. Treat configuration, permissions, and compatibility testing as separate deliverables during the preview period. (Official development guide)

This week’s recommended action: classify your feature as a tool, model provider, interface, or workflow extension, then create one profile that can load and remove it without changing the team’s global setup.

This guide is for you if you:

  • Need to package internal scripts as DeepSeek Harness tools.
  • Need to connect a custom model endpoint or company gateway.
  • Need to build a Web UI or remote interaction feature.
  • Need a repeatable Mac development environment and plugin acceptance process for a small team.

Last updated August 18, 2026. Technical details were checked against the official README, architecture documentation, development guide, contribution guide, and current package metadata on the master branch.

01

Start with the extension boundary

DeepSeek Harness describes itself as an open-source developer preview where everything is a plugin. Its architecture uses Cordis, where plugins contribute services, typed events, and reversible effects to a shared context. The model adapter, tool registry, session log, and agent loop are all replaceable parts of the runtime. (Official architecture documentation)

That design gives you flexibility, but it also creates a common failure mode: putting unrelated responsibilities into one package.

Use this classification before writing TypeScript:

Extension goal Recommended plugin boundary Keep separate when
Expose an internal script or API action Tool plugin The tool needs different permissions, credentials, or release timing
Add a model provider or custom endpoint Model provider plugin Different teams own routing, secrets, or model catalogs
Add browser views or remote interaction Interface plugin Host services and browser assets have different failure modes
Compose repeatable runtime behavior Workflow or profile layer Test, experiment, and production tasks need different settings

A single plugin is appropriate when the feature has one owner, one permission model, and one test contract. Split the feature when any of these conditions change:

  • It needs a separate API key.
  • It must be disabled for some profiles.
  • It has a different release schedule.
  • It can fail without taking the core agent workflow down.
  • It requires both server-side and browser-side code.
  • It needs a different approval or sandbox policy.

The advantage is operational, not cosmetic. A small plugin can be disabled, rolled back, and tested independently. A large package may look convenient at first, but every change expands the compatibility surface.

02

Build the smallest tool plugin first

For most Agent engineers, the tool scenario is the safest starting point. It has a narrow contract:

  1. Define one capability.
  2. Declare its input shape.
  3. Return a predictable output shape.
  4. Register the tool.
  5. Return diagnostic errors when execution fails.

A conceptual dsh-plugin layout can look like this:

my-dsh-plugin/
├── package.json
├── src/
│   ├── index.ts
│   └── tool.ts
├── patches/
│   └── tool.yml
├── test/
│   └── tool.test.ts
└── README.md

This is a design model, not a promise that every plugin uses exactly these filenames. The official architecture states that bundles declare themselves in package.json through a dsh field. A profile uses dsh.profile to list bundles, while a bundle uses dsh.bundle to point to its patch file. Confirm the current examples before copying a scaffold. (Official architecture documentation)

A tool should have one success signal for each layer:

Layer Success signal Fallback if it fails
Loading The profile boots without a module or configuration error Remove the plugin from the profile and boot the baseline profile
Discovery The tool appears in the runtime’s available tool set Check registration metadata and profile composition
Execution Valid input returns a typed result Return a structured error with operation and cause
Failure handling Invalid input or denied access is diagnosable Disable the tool rather than retrying blindly

The error path matters as much as the success path. Avoid returning only a generic message such as failed. Include the operation, safe input context, and a recovery hint. Never expose API keys, filesystem secrets, or raw authorization headers in the error payload.

A useful tool contract separates three concerns:

  • Input validation: reject missing, malformed, or unsupported values.
  • Capability execution: call the script, service, or system operation.
  • Result normalization: return a stable structure even when the underlying service changes.

This makes the plugin easier to test with mocks and easier to replace later.

A tool plugin scenario

Suppose you want an agent to inspect a deployment status. Do not combine status inspection, restart operations, log deletion, and credential provisioning in the first package. Start with a read-only status tool.

Its first version should prove:

  • The profile can load the plugin.
  • The agent can discover the tool.
  • A valid project identifier returns normalized status data.
  • An invalid identifier returns a useful error.
  • A permission failure does not trigger a destructive fallback.

Only after this works should you add write operations. If the restart action needs elevated permissions, make it a separate tool or separate plugin. That keeps the approval boundary visible.

03

Keep model providers and credentials outside the code

A model provider plugin has a different responsibility from a normal tool plugin. It adapts a model interface, endpoint, request format, streaming behavior, or model catalog. It should not quietly become a general-purpose configuration package for every tool in the team.

The official development guide documents environment-based credentials, including DEEPSEEK_API_KEY and an optional DEEPSEEK_BASE_URL. It also states that real API end-to-end suites skip themselves when the key is absent. (Official development guide)

Use this separation:

Configuration item Store in Do not store in
API key Environment variable or ignored local environment file TypeScript source or committed YAML
Base endpoint Profile configuration or environment variable Hard-coded request helper
Model name Profile-specific configuration Shared constant used by every environment
Model catalog Provider configuration with validation A copied list inside each tool
Permission policy Profile or runtime policy layer A hidden boolean inside the provider

Your repository should contain safe placeholders and documented variable names. It should not contain working credentials, personal endpoints, or a developer’s private model alias.

A provider plugin also needs a clear failure policy. If the configured endpoint is unavailable, return a provider-level error. Do not silently switch to another model unless the profile explicitly defines that fallback. Silent routing makes test results difficult to reproduce and can create unexpected costs or data flows.

For teams, create at least three configuration modes:

  • Experiment: permits rapid endpoint and model changes.
  • Test: uses fixed values and deterministic mocks where possible.
  • Continuous task: uses stable credentials, explicit permissions, and persistent workspace.

The code can stay the same. The profile and configuration layer should change.

04

Host and Client code need separate checks

Interface plugins and remote interaction features are more demanding because they cross the Host and Client boundary.

The official development guide says the repository uses isolated Host and Client aggregates. Ordinary packages are registered in exactly one aggregate. It also documents a build order that runs Host type checking and bundling before Client type checking and bundling, followed by the Web build. (Official development guide)

The verified build sequence is:

tsc -b tsconfig.host.json
tsdown --env.DSH_BUILD_FACE host
tsc -b tsconfig.client.json
tsdown --env.DSH_BUILD_FACE client
pnpm run build:web

Do not copy this structure into every plugin without checking its role. The documentation specifically warns that ordinary Client plugins produce their Node loader and browser bundle during the Client phase, while api/remotes is an exceptional split package. (Official development guide)

For a Web UI plugin, validate the boundary in this order:

  1. Host registration: confirm the service or remote method exists on the server side.
  2. Generated contract: confirm the Client-facing type or remote declaration is produced.
  3. Client import: confirm browser code resolves the generated interface.
  4. Remote call: confirm request and response shapes match.
  5. Browser rendering: confirm loading, empty, error, and permission states.
  6. Rebuild behavior: confirm a clean build produces the same artifacts.

The main hidden cost is assuming that a browser component can directly import a Host implementation. It should not. Keep server capabilities behind the documented remote boundary. Keep browser assets in the Client side. If the feature does not need a browser surface, do not introduce one.

Preview warning: DeepSeek Harness explicitly states that compatibility-breaking changes will occur during the developer preview. Record the verified commit, package version, Node version, and review date for every internal release. (Official repository README)

05

Use profiles to compose workflows

A profile is a named composition stored in the Harness home. It lists bundles, keeps installed out-of-tree plugins, and stores the user’s cordis.patch.yml. The architecture documentation explains that profile layers are applied in order, followed by profile patches, home-level patches, and optional command-line overlays. (Official architecture documentation)

That makes profiles a better workflow mechanism than copying the entire plugin project for each task.

Profile Purpose Typical policy
experiment Try new tools or providers Broad logging, isolated credentials, easy rollback
test Verify plugin behavior Fixed versions, mocks, strict permissions
continuous Run recurring agent tasks Stable bundle list, limited write access, persistent workspace

The important rule is to keep the plugin code immutable across these profiles. Change the composition and configuration layer instead.

For example:

  • The experiment profile enables a new provider and verbose telemetry.
  • The test profile enables the same provider with a mock endpoint.
  • The continuous profile enables only approved tools and a fixed model route.

This avoids the team problem where two engineers modify one global configuration file and neither knows which plugin combination produced a failure.

Loading a plugin into a profile

Use this sequence:

  1. Identify whether the plugin is a bundle or an out-of-tree package.
  2. Add it to a dedicated development profile.
  3. Add only the required configuration rows or patch entries.
  4. Boot the profile without the plugin and record the baseline.
  5. Boot the profile with the plugin and compare the runtime tree.
  6. Test discovery before testing real execution.
  7. Remove the plugin and confirm the baseline returns.

The architecture documentation provides an inspection path for viewing the tree that the machine actually boots. Use the current command from that document rather than relying on a community shortcut. (Official architecture documentation)

06

FAQ: practical plugin decisions

What belongs in a DeepSeek Harness plugin?

A plugin should own one replaceable runtime responsibility. That may be a tool, model adapter, persistence service, approval policy, Web feature, or workflow component. If two capabilities have different credentials, permissions, owners, or release dates, split them. The official architecture favors composition through Cordis rather than a privileged core that must be patched directly.

What is the minimum useful TypeScript structure?

Start with package metadata, one TypeScript entry point, the registration or mounting code, and one behavior test. Add a patch file only when the plugin needs configuration rows or bundle composition. Keep the first package small enough that you can explain its loading path in one paragraph. Treat any extra directory as a response to a tested requirement, not as decoration.

How should a team manage profile differences?

Keep one plugin implementation and compose several profiles. The experiment profile can change quickly. The test profile should pin behavior and credentials. The continuous profile should minimize permissions and unexpected dependencies. Store profile changes in reviewable configuration rather than asking engineers to edit the same home-level file manually.

What is the safest way to handle preview compatibility?

Record the exact source commit, package version, Node version, package manager version, and date of validation. Run the same smoke test after upgrades. If a plugin fails to load, roll back the plugin or profile first. Do not immediately patch internal runtime code, because that makes the next upstream change harder to diagnose.

07

Release checks should be a separate deliverable

The plugin is not ready when it works once on the author’s machine. It is ready when another engineer can install it in a clean profile, observe its behavior, and remove it without damaging the baseline.

Run these checks before publishing:

Check What to verify Pass condition
Type safety TypeScript references, generated contracts, and public types Type check exits successfully
Minimal runtime Plugin enabled in an otherwise clean profile Harness boots and reaches the expected mode
Discovery Tool, provider, UI route, or workflow appears Capability is visible through the intended registry or interface
Permissions Allowed and denied operations Denials are explicit and non-destructive
Failure diagnosis Invalid input, unavailable service, malformed response Error includes a safe cause and recovery path
Clean installation New checkout or clean environment No hidden local dependency is required
Uninstall rollback Plugin removed from the profile Baseline profile still boots
Version record Commit, package version, Node range, date Another engineer can reproduce the test

The official development guide identifies pnpm run typecheck as the initial setup completion signal. It also documents pnpm run build, pnpm run check:all, and the repository’s CI compatibility coverage. Use the smallest relevant check for local iteration, then run the broader gate before a release candidate. (Official development guide)

Do not claim support for a Node version merely because the code happens to run on it once. Match the versions documented by the current project files and CI configuration.

08

A decision path for your first plugin

Use these conditions:

  • If the feature exposes one action with a clear input and output contract, choose a tool plugin. Otherwise, move the capability behind a service boundary first.
  • If the feature changes model routing, endpoint behavior, or model discovery, choose a provider plugin. Otherwise, keep it out of the provider layer.
  • If the feature needs browser rendering or remote calls, choose a Host and Client design. Otherwise, avoid adding Web code.
  • If the feature combines several independent capabilities, use profiles and bundles. Otherwise, keep one minimal plugin until the contracts are proven.
  • If credentials differ by environment, put them in configuration or environment variables. Otherwise, you risk leaking secrets or producing non-reproducible tests.
  • If the plugin cannot be removed without editing the core, stop and redesign the boundary. A plugin should be independently disableable.
  • If the preview changes break the plugin, roll back the profile or package first. Do not hide the failure with an untracked internal patch.

This is the central advantage of DeepSeek Harness plugin development: you can test the boundary before you invest in the full product feature.

09

Choose the development environment after the plugin shape is known

A local laptop is fine for a short tool experiment. It becomes less convenient when you need repeated builds, multiple profiles, clean-environment tests, remote review, or a shared acceptance process. The current approach often leaves you with four real drawbacks:

  • The environment disappears when the developer closes the machine or changes projects.
  • Teammates reproduce different Node, pnpm, and package states.
  • Long-running builds and Web UI checks compete with normal work.
  • A clean rollback environment is difficult to preserve.

A dedicated Mac development environment is not automatically the right answer. Buying hardware makes more sense for long-term, heavy workloads or when you need physical peripherals. For temporary plugin work, preview testing, onboarding, or a team that needs repeatable remote access, renting a persistent Mac can be easier to justify.

You can compare the available Mac development options from VpsMesh, review Mac rental pricing, or use a remote Mac environment for cloud-based development. The practical test is simple: estimate how often you rebuild, how many profiles you must preserve, and how many people need access. If those three numbers keep rising, a retained remote Mac environment may cost less in coordination time than repeatedly rebuilding local setups.