DeepSeek Harness Turns LLMs Into an Execution Layer With User-Written Plugins
DeepSeek Harness Plugin Development in Practice: From Minimal Plugin to Automated Daily Report
Introduction
DeepSeek's open-source Harness (codenamed "Black Whale") is not a new model, but an "execution layer" wrapped around large models. The core slogan is "Everything is a plugin" — models, tools, interfaces, and session records are all assembled from plugins. This article dissects Harness's plugin mechanism (the Cordis framework) from a technical perspective, providing a complete workflow from environment setup, minimal plugin writing, and configuration mounting to automation practice, along with an objective assessment of the current version's boundaries and security considerations.
Problem Definition: The Gap from "Chat" to "Execution"
Most users only stay at the conversation level after getting Harness, like "buying a CNC machine and using it as a hammer to drive nails every day." The real way to unlock its value is to write plugins and outsource repetitive labor to AI. Harness has only been released for a week, and community plugins under the dsh-plugin tag on GitHub have already exceeded 900.
Architecture Analysis: The Cordis Plugin Framework
Harness's underlying layer uses the Cordis plugin framework. The core idea: all capabilities are "plugins," and a plugin is essentially a TypeScript file that exports a fixed structure. The official 195 packages (command execution, session recording, etc.) are all composed of plugins, and user plugins have exactly the same status as official plugins.
Environment Setup
- Requires Node.js 22.19+ (verify with
node --version) - Start command:
npx @deepseek-ai/dsh web - Local interface:
http://127.0.0.1:3080(data is entirely local) - First-time use requires configuring a DeepSeek API Key (register at platform.deepseek.com)
Minimal Plugin Implementation
A minimal plugin that registers a hello_world tool:
// my-plugin.ts
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'my-plugin'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'hello_world',
description: 'Greet someone, used to verify if the plugin loaded successfully',
parameters: {
who: { type: 'string', required: true, description: 'The person to greet' }
},
output: {
schema: { type: 'object', additionalProperties: false, properties: { message: { type: 'string', required: true } } },
render: (_args, value) => [{ type: 'text', text: value.message }]
},
async execute(args) {
return { message: `Hello, ${args.who}, plugin loaded successfully` }
}
}))
}
Pitfalls Encountered
- Must use named exports:
export defaultcauses the injection declaration to be silently discarded. The plugin appears to load but does not take effect, and no error is reported. - Schema constraints: The
schemafor object-type outputs must declareadditionalProperties: false, otherwise registration fails. - Injection order:
inject: ['tools']means wait for the tool registry to be ready before executing.
Configuration Mounting
Create a cordis.yml to declare plugin mounting information:
- insert:
- id: my-plugin
name: '/absolute/path/to/your/directory/my-plugin.ts'
Note: The path must be an absolute path (Cordis does not support relative paths). Start with the patch:
pnpm dsh web --patch ./cordis.yml
Trigger "use hello_world to greet Xiaohu" in the conversation, and receiving "Hello, Xiaohu, plugin loaded successfully" confirms success.
Practice: Automatically Generate Daily Report Drafts
Pain point: Writing a daily report before leaving work every day and summarizing five daily reports into a weekly report every Friday is repetitive, mechanical, and easy to miss.
import { defineTool } from '@deepseek-ai/dsh-tools'
import { readFileSync, writeFileSync, existsSync } from 'fs'
export const name = 'daily-draft'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'gen_daily_draft',
description: 'Read yesterday\'s to-do list and generate today\'s daily report draft according to a template',
parameters: {
workspace: { type: 'string', required: true, description: 'Absolute path to the workspace directory' }
},
output: { schema: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', required: true } } },
render: (_a, v) => [{ type: 'text', text: `Draft generated, ${v.path}` }] },
async execute(args) {
const todoPath = `${args.workspace}/todo.md`
const draft = existsSync(todoPath)
? `【Draft Pending Confirmation】\nYesterday\'s to-dos:\n${readFileSync(todoPath, 'utf-8')}\n(Please fill in after verifying the actual progress of the day)`
: '【Draft Pending Confirmation】No yesterday to-do record found today, please supplement.'
const out = `${args.workspace}/daily-$(date +%F).md`
writeFileSync(out, draft)
return { path: out }
}
}))
}
Design Points: Only generates a draft and clearly marks it as "pending confirmation," never fabricating content that did not happen for the user — mitigating AI hallucination risks through rule constraints.
Packaging and Publishing
Using absolute paths locally is fine, but publishing to the community requires declaring in package.json:
{
"name": "dsh-daily-draft",
"type": "module",
"main": "my-plugin.ts",
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
Install command: dsh plugin --profile web add github:account/repo
Vibe Coding Tip: The DSH plugin structure (single file + apply entry + YAML) is naturally suited for AI generation. The specification can be submitted to tools like Cursor to generate complete source files in seconds.
Boundaries and Security
| Dimension | Description |
|---|---|
| Version Stability | Current 0.1.0 pre-release version. Officials have explicitly stated interfaces will still be adjusted. Recommended for experimentation, not production deployment. |
| Security Risk | Plugins are essentially code running on the local machine. Installing plugins from unknown sources is equivalent to handing over system permissions. Only install official or highly-starred plugins. |
| Technical Barrier | Requires some TypeScript foundation, but the learning curve is gentler compared to a full programming language. |
Summary
DeepSeek has opened the door to the "execution layer" through Harness. Core viewpoint: True automation is not about learning all the tools, but about only needing to say one sentence, and the tools string themselves together to work. Next step direction: Chain the daily report plugin to a weekly report plugin to achieve automatic Friday summaries.