The first plugin for DeepSeek Harness

In Harness, a plugin is a TypeScript module that exports an apply function.

The framework calls apply when loading the plugin, passing in a ctx (context object).

We register capabilities through ctx, such as event listeners, tools, and LLM adapters.

ctxContext is the context object that the framework passes to each plugin.

ctxIt is both the entry point for registering capabilities and records all resources registered by the plugin.


Create a local project

We need to install from source first:

git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

Next, create a scratch-plugin project to hold our plugin.

Execute in the repository root directory:

mkdir -p scratch-plugin/src

Minimal plugin: hello-plugin

Create my-plugin.ts under scratch-plugin/src.

cd scratch-plugin/src

Below is a complete, usable plugin configuration—nothing is missing.

Example

// File path: scratch-plugin/src/my-plugin.ts
import type { Context } from '@deepseek-ai/cordis'

// name is the plugin name, used to identify this plugin in logs and configuration.
export const name = 'hello-plugin'

// apply is the plugin's entry point: the framework calls it when loading the plugin.
export function apply(ctx: Context) {
  // The required dependencies are ready before apply is executed (see Part 9)
  console.log('[hello-plugin] plugin loaded!')
}

This code does only one thing: it prints a line of log when loaded. It doesn't register any capabilities, but it is already a qualified plugin.


Three forms of the plugin

In addition to the function form shown above, plugins also support object form and class form.

FormSyntaxApplicable scenarios
Function formExport an independent apply functionFor most plugins, this is the simplest and most direct approach.
Object formexport default an object with name / inject / applyWhen you need to declare metadata at the same time
Class formexport default a Service subclassWhen a plugin needs to provide services to other plugins

Function form

Exporting name and apply separately is the default style in the official examples.

Example

// File path: scratch-plugin/src/my-plugin.ts (function form)
import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // Register capabilities here
}

Object form

Put name, inject, and apply into a default-exported object.

Example

// Object form: a default export object
import type { Context } from '@deepseek-ai/cordis'

export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // ...
  },
}

Class form

The class form inherits the Service base class and is suitable for plugins that provide services externally.

Example

// Class form: Service subclass, can provide services externally
import { Service, type Context } from '@deepseek-ai/cordis'

export default class MyService extends Service {
  static inject = ['tools']

  constructor(ctx: Context) {
    // The first parameter is ctx, the second is the service name
    super(ctx, 'myService')
    // Put synchronous initialization in the constructor
  }
}

How to choose

In most cases, the function form is sufficient.

When a plugin needs to provide services to other plugins, use the class form.

Tip: The core of the class form is super(ctx, 'service name').

The full mechanism of services and dependencies will be covered in Part 14.

插件结构与加载示意

other extensions