跪拜 Guibai
← All articles
Artificial Intelligence

Building a Custom Tool for DeepSeek Harness, from Hello World to Installable Bundle

By 宇擎智脑科技 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Harness gives every tool the same execution pipeline regardless of origin, so custom tools, MCP servers, and built-ins all hit the same pre-execute hooks for permissions and approval. That uniformity removes a whole category of integration surprises when mixing tool sources in a single agent.

Summary

A new walkthrough takes a DeepSeek Harness plugin from a minimal console-logging module to a model-callable tool with configurable parameters, external resource management, and a distributable bundle. The framework's Cordis runtime calls `apply` at load time, passing a context object that automatically cleans up registrations on unload. Tools defined with `defineTool` go through a waterfall pipeline—pre-execute, execute, post-execute—that permission policies and approval mechanisms can intercept, the same pipeline used by MCP-bridged tools.

Configuration is handled by exporting a Schema alongside the plugin; invalid config fails loudly at load time rather than producing silent runtime bugs. Hot module replacement recycles the entire plugin fiber when `cordis.yml` changes, so config edits take effect without a process restart. External resources like database connection pools are managed through `ctx.effect()`, whose cleanup function runs automatically on dispose, dependency loss, or shutdown.

Packaging turns a plugin into an installable bundle with a `package.json` that declares `dsh.bundle`, a patch file, and a compiled entry point. Bundles install into named profiles and load in a defined order, with later layers winning on conflict. The walkthrough also covers service isolation—multiple agent groups can each get their own `ctx.shell` instance with separate timeout configs—and the three plugin forms: function, object, and class-based service providers.

Takeaways
Every Harness plugin is a module exporting an `apply(ctx)` function; the framework calls it at load and auto-cleans registrations on unload.
Tools are defined with `defineTool`, which takes a name, description, JSON Schema parameters, an output schema and renderer, and an async `execute` function.
All tools run through a shared waterfall pipeline: `tools/pre-execute` → `tools/execute` → your function → `tools/post-execute` → session event.
Configuration uses a Schema export; invalid config causes the plugin fiber to fail at load time instead of producing silent runtime misbehavior.
Hot module replacement recycles the entire plugin fiber when `cordis.yml` changes, so config edits apply without restarting the process.
`ctx.effect()` manages external resources like DB pools; its cleanup function runs automatically on dispose, dependency loss, or shutdown.
Bundles package a plugin with a `package.json` declaring `dsh.bundle`, a patch file, and an entry point; they install into named profiles via `dsh plugin add`.
Service isolation lets different agent groups see separate instances of the same service, each with its own configuration.
Plugins can take three forms: a function export, a default object export, or a class extending `Service` that other plugins can inject as a dependency.
Conclusions

Harness treats tool registration as a resource with a lifecycle—the disposer returned by `ctx.tools.register()` means tools appear and disappear cleanly when plugins hot-reload, which avoids stale tool listings that plague many agent frameworks.

Exporting a Schema alongside the plugin interface is a design choice that moves validation to load time. A misconfigured plugin fails immediately with a clear error rather than running with wrong defaults, which shifts debugging left by minutes or hours in production.

The execution pipeline is identical for native tools and MCP-bridged tools. That means a permission policy written for one tool source automatically applies to the other, eliminating the need for parallel security logic.

Service isolation through `isolate` in the YAML config means a single Harness process can host multiple agents with conflicting requirements—one with a 5-second shell timeout, another with 60 seconds—without them stepping on each other.

Concepts & terms
Cordis fiber
A lightweight unit of plugin lifecycle management in the Cordis framework. When a plugin's configuration or dependencies change, the old fiber is disposed (triggering cleanup) and a new fiber is created with the updated state, enabling hot module replacement without process restarts.
Waterfall event
A chained event pattern where each listener receives the event payload plus a `next()` function. Listeners must call `next()` to pass control to the next handler; skipping it short-circuits the chain, which is how permission hooks can block tool execution.
Service isolation
The ability to create multiple independent instances of the same service (e.g., `ctx.shell`) scoped to different plugin groups. Each group sees only its own instance with its own configuration, preventing cross-contamination between agents.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗