Di pertengahan 2026, ada satu proyek yang mencuri perhatian para developer AI: DeepSeek Harness (DSH). Dengan tagline “Everything is a Plugin”, DSH membangun seluruh kemampuan sebuah agen AI — tools, adaptor LLM, akses file, hingga agent loop itu sendiri — sebagai plugin yang dipasang ke sebuah konteks bersama. Hasilnya: Anda bisa menambah kemampuan baru ke agen AI tanpa menyentuh inti sistem, cukup dengan menulis satu modul TypeScript. Di tutorial ini kita akan membangun plugin tool pertama Anda — sebuah tool cuaca yang bisa dipanggil langsung oleh model AI — mulai dari nol hingga berjalan di Web UI.
Kenapa ini relevan sekarang? DeepSeek Harness melesat menjadi salah satu repositori paling banyak dibintangi di GitHub dalam hitungan minggu, dengan ekosistem plugin yang tumbuh cepat (routing, sidebar, hingga marketplace). Kemampuan menulis plugin DSH adalah skill yang sedang dicari untuk membangun agen AI yang bisa dikustomisasi.
DeepSeek Harness adalah runtime untuk agen AI yang dibangun di atas Cordis, sebuah framework plugin yang ringan. Dalam Cordis, setiap kemampuan didaftarkan sebagai service pada objek ctx (context), dan setiap plugin cukup mendeskripsikan apa yang ia sumbangkan. Pola ini membuat seluruh sistem menjadi modular: Anda bisa mengganti adaptor LLM, menambah tool, atau mengubah agent loop hanya lewat konfigurasi.
Konsep kuncinya sederhana: sebuah plugin adalah modul TypeScript yang mengekspor fungsi apply(ctx). Saat dimuat, Cordis memanggil apply dengan sebuah objek ctx — dan lewat objek inilah plugin mendaftarkan semua yang ia sumbangkan (tool, event listener, timer, dan lain-lain).
Sebelum mulai, pastikan Anda punya:
- Node.js 18+ (untuk
fetchglobal dan runtime modern) - pnpm sebagai package manager (
npm i -g pnpm) - Git untuk meng-clone repositori
- Pemahaman dasar TypeScript/JavaScript (tidak perlu mahir)
Tidak butuh API key apa pun untuk tutorial ini — contoh berjalan sepenuhnya tanpa kunci.
Mulailah dengan meng-clone repositori DeepSeek Harness, lalu install dependensinya dengan pnpm. Setelah itu buat direktori kerja untuk plugin tutorial — folder scratch-plugin/ sudah di-gitignore, jadi apa pun yang Anda tulis tidak menyentuh version control.
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
# Direktori kerja untuk plugin tutorial (diabaikan Git)
mkdir -p scratch-plugin/src
Sebuah plugin minimal hanyalah modul TypeScript yang mengekspor fungsi apply dan (opsional) nama. Buat file scratch-plugin/src/my-plugin.ts dengan isi berikut:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin berhasil dimuat!')
}
Perhatikan: tidak ada bootstrap framework di file ini. Plugin hanya mendeskripsikan apa yang ia sumbangkan; sisanya diurus oleh loader Cordis. Ekspor name bersifat opsional dan dipakai sebagai label saat diagnosa.
Agar plugin dimuat, kita perlu mendaftarkannya di file konfigurasi cordis.yml. Karena kita memuatnya sebagai overlay ke Web UI, kita memakai blok insert. Buat file scratch-plugin/cordis.yml (jalankan pwd dari root repositori untuk mendapat path absolut):
- insert:
- id: hello
name: '/absolute/path/ke/deepseek-harness/scratch-plugin/src/my-plugin.ts'
Path plugin harus absolut. File patch ini menyumbang konfigurasi tanpa mengubah direktori profil tempat loader meresolusi path modul.
Jalankan Web UI dengan overlay tersebut, lalu buka http://127.0.0.1:3080. Anda akan melihat log [hello-plugin] plugin berhasil dimuat! di terminal saat startup.
pnpm dsh web --patch ./scratch-plugin/cordis.yml
Tip: Jika plugin Anda tidak mencetak apa pun saat baru ditambahkan, periksa dulu ejaan path di cordis.yml. Entry yang modulnya gagal di-resolve dilaporkan lewat logger Cordis — bukan membuat proses crash — dan pesannya bisa terlewat sebelum ada console exporter yang aktif.
Ini bagian intinya. Dengan service tools, kita mendaftarkan tool — fungsi yang bisa dipanggil model AI saat menjawab pertanyaan. Kita memakai helper defineTool yang mengubah spesifikasi parameters menjadi JSON Schema (yang dilihat model), memvalidasi argumen dari model, lalu menjalankan execute.
Ganti isi scratch-plugin/src/my-plugin.ts dengan tool cuaca di bawah. Tool ini memanggil API publik Open-Meteo (gratis dan tanpa API key) dua kali: pertama geocoding nama kota menjadi koordinat, lalu mengambil suhu terkini.
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'cuaca-tool'
export const inject = ['tools']
async function geocode(kota: string) {
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(kota)}&count=1&language=id`
const res = await fetch(url)
if (!res.ok) return null
const data = (await res.json()) as {
results?: { latitude: number; longitude: number; name: string; country: string }[]
}
return data.results?.[0] ?? null
}
function kodeCuaca(code: number): string {
if (code === 0) return 'cerah'
if (code <= 3) return 'berawan'
if (code <= 48) return 'berkabut'
if (code <= 67) return 'hujan'
if (code <= 77) return 'salju'
if (code <= 82) return 'hujan lebat'
return 'badai'
}
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'cuaca',
description: 'Cari tahu suhu dan kondisi cuaca saat ini di sebuah kota.',
parameters: {
kota: { type: 'string', required: true, description: 'Nama kota, misalnya "Jakarta" atau "Bandung"' },
satuan: { type: 'string', description: 'Satuan suhu: "celsius" (default) atau "fahrenheit"' },
},
output: {
schema: {
type: 'object',
properties: {
kota: { type: 'string' },
suhu: { type: 'number' },
satuan: { type: 'string' },
kondisi: { type: 'string' },
},
},
render: (_args, value) => [{
type: 'text',
text: `Cuaca di ${value.kota}: ${value.suhu}°${value.satuan === 'fahrenheit' ? 'F' : 'C'}, ${value.kondisi}`,
}],
},
async execute(args, exec) {
const satuan = args.satuan === 'fahrenheit' ? 'fahrenheit' : 'celsius'
const lokasi = await geocode(args.kota)
if (!lokasi) {
return { kota: args.kota, suhu: 0, satuan, kondisi: 'kota tidak ditemukan' }
}
const url = `https://api.open-meteo.com/v1/forecast?latitude=${lokasi.latitude}&longitude=${lokasi.longitude}¤t=temperature_2m,weather_code&temperature_unit=${satuan}`
const res = await fetch(url, { signal: exec.signal })
const data = (await res.json()) as {
current: { temperature_2m: number; weather_code: number }
}
return {
kota: lokasi.name,
suhu: data.current.temperature_2m,
satuan,
kondisi: kodeCuaca(data.current.weather_code),
}
},
}))
}
Mari kita bedah bagian-bagian pentingnya:
inject: ['tools']membuat Cordis menahan plugin dalam status PENDING sampai registrytoolstersedia — jadictx.toolsdijamin siap di dalamapply.parametersmendefinisikan argumen yang boleh diberikan model.required: truemenandaikotawajib;satuanopsional (tanparequired).output.schemamendeklarasikan nilai kanonik yang dikembalikan, sementaraoutput.rendermengubah nilai itu menjadi teks yang dibaca model.execute(args, exec)adalah logika sebenarnya. Argumen sudah divalidasi otomatis, danexec.signalbisa dipakai untuk membatalkan kerja yang sedang berjalan.
Penting: execute harus mengembalikan satu nilai JSON kanonik sesuai output.schema — jangan mengembalikan blok konten atau teks bebas. Untuk kegagalan infrastruktur, lempar exception (registry akan menandainya sebagai error); untuk hasil domain yang sah, kembalikan nilainya meskipun renderer menjelaskan kondisi kurang ideal.
Restart perintah pnpm dsh web --patch ./scratch-plugin/cordis.yml, lalu di Web UI tanyakan: “Gunakan tool cuaca untuk mengecek suhu di Jakarta.” Model akan memanggil tool cuaca dan menerima hasilnya sebagai teks, misalnya “Cuaca di Jakarta: 31°C, cerah.”
Yang menarik: Anda tidak perlu menulis integrasi tambahan. Registrasi tool adalah effect — saat plugin di-unload (misalnya saat hot reload), tool otomatis ter-unregister, dan skema tool mengalir otomatis ke perakitan system prompt.
Selamat — Anda baru saja membangun plugin tool pertama Anda untuk DeepSeek Harness. Anda telah belajar anatomi plugin Cordis, cara mendaftarkan tool dengan defineTool, cara menyusun komposisi di cordis.yml, dan cara mengujinya di Web UI.
Untuk melangkah lebih jauh, coba pelajari: layanan (service) untuk berbagi kemampuan antar-plugin lewat ctx, konfigurasi plugin agar tool Anda bisa diatur pengguna, event seperti tools/result untuk mengamati setiap pemanggilan tool, dan Code Mode di mana setiap tool terlihat sebagai await tools.cuaca(args). Selamat mengeksplorasi — dunia “everything is a plugin” baru saja terbuka.
BengkelKode