Your First Cordis Plugin
What is Cordis
Cordis is the plugin framework vendored underneath DeepSeek Harness. Five core concepts:
- A plugin is an object that implements a capability: a function with optional
injectandapply(ctx)fields, or aServicesubclass; - A context is a container of services: each service lives at a stable
ctx.<key>(e.g.ctx.tools,ctx.llm) and is looked up by key rather than imported; injectdeclares dependencies: a plugin starts only when its required services are ready; load order follows dependencies, not manual sequencing;- Typed events are for communication: dispatched via
emit,waterfall,parallel,serialand friends; - Registrations are reversible side effects: anything installed via
ctx.effect()orctx.on()is torn down automatically on unload.
The minimal plugin shape
A plugin is a module exporting an apply function; the framework calls it with ctx on load:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) {
// Register your capabilities here.
}
name is optional display metadata used in diagnostics. The function form is the most common; object form ({ name, inject, apply }) and class form (extending Service) are also supported. Function form is enough in most cases.
Register a tool
This complete example registers a greet tool on the harness tools service, callable by the model:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: { type: 'string', required: true, description: 'The name to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}
How it works:
inject: ['tools']makes Cordis wait for the tool registry, soctx.toolsis safe to use inapply;defineToolconvertsparametersinto the JSON Schema shown to the model, and derives and validatesargs;executereturns a canonical value declared byoutput.schema;output.renderturns it into model-facing content;- the
ctx.tools.register(...)registration is an effect, so the tool is deregistered automatically when the plugin unloads.
Load it in DSH
During development, mount the plugin into the Web UI with a --patch overlay. Create scratch-plugin/cordis.yml:
- insert:
- id: greet
name: '/absolute/path/to/your-plugin/src/greet-tool.ts'
The plugin path must be absolute. Then start with the overlay (pnpm dsh web from a source checkout, dsh web when installed):
pnpm dsh web --patch ./scratch-plugin/cordis.yml
Open http://127.0.0.1:3080 and send Use the greet tool to greet Ada. — the model can call greet and receives Hello, Ada!. The terminal logs the tool call and its result event.
To publish for others, package the plugin as a bundle declaring dsh.bundle and follow the dsh plugin --profile web add flow in Installing DSH Plugins.