Custom Skills für OpenClaw bauen: eigene KI-Fähigkeiten programmieren
Direkte Antwort: Ein OpenClaw-Skill besteht aus einer
Skill.md-Datei (Beschreibung und Trigger) plus optionalen Tools (TypeScript-Funktionen mit JSON-Schema). Du erstellst einen Ordner unterskills/, schreibst die Markdown-Datei mit klaren Triggern, definierst Tool-Schemas und implementierst die Logik. OpenClaw lädt den Skill automatisch und Claude entscheidet selbst, wann er aktiv wird.
OpenClaw ist nur halb so mächtig ohne eigene Skills. Die Out-of-the-box-Antworten von Claude sind beeindruckend, aber das Magische passiert erst, wenn dein Bot deine Notion-DB aktualisieren, deine MOCO-Rechnungen ziehen oder deinen Wetter-Service abfragen kann. Und das Beste: Skills bauen ist deutlich einfacher als das ganze Plugin-Ökosystem von OpenAI oder die Tool-Choreographie von LangChain. Wenn du einmal verstanden hast, wie ein Skill aufgebaut ist, kannst du in 30-60 Minuten einen neuen schreiben.
In diesem Tutorial bauen wir einen vollständigen Notion-Skill, der Claude beibringt, neue Tasks in einer Notion-DB anzulegen. Du lernst dabei alles, was du brauchst, um danach eigene Skills zu schreiben: Skill-Anatomie, Trigger-Design, Tool-Schemas, Error-Handling, Testing und Distribution.
Was ist ein OpenClaw-Skill überhaupt?
Ein Skill ist eine paketierte Fähigkeit, die du Claude über OpenClaw zur Verfügung stellst. Technisch besteht er aus zwei Komponenten:
Skill.md: Eine Markdown-Datei, die beschreibt, was der Skill kann, wann er aktiv werden soll, und welche Tools er bereitstellt.tools/-Ordner (optional): TypeScript- oder JavaScript-Module, die echte Logik ausführen können (HTTP-Calls, Datenbank-Zugriffe, File-System-Operationen).
Wenn ein Skill keine Tools hat, ist er ein reiner "Kontext-Skill" – er wird nur aktiv, um Claude zusätzliche Informationen oder Verhalten zu geben (z. B. "antworte immer im Stil eines Anwalts" oder "die Firmen-DSE ist [folgendes]").
Wenn ein Skill Tools hat, kann Claude diese Tools aufrufen wie Funktionen. OpenClaw kümmert sich um die JSON-RPC-artige Kommunikation, das Schema-Validating und das Zurückspielen der Ergebnisse.
Die Anatomie einer Skill.md
Eine minimale Skill.md sieht so aus:
---
name: notion-task-creator
version: 1.0.0
description: Erstellt neue Tasks in einer Notion-Datenbank
triggers:
- "neue aufgabe"
- "task anlegen"
- "notion task"
- "todo hinzufügen"
---
# Notion Task Creator
Du kannst neue Tasks in der Notion-Datenbank anlegen. Wenn der User eine
Aufgabe erwähnt, frag nach dem Titel, einem optionalen Fälligkeitsdatum
und einer Priorität (low/medium/high).
## Tools
- `create_task`: Legt einen neuen Task in Notion an
Das Frontmatter ist Konfiguration, der Rest ist eine Anleitung, die Claude direkt liest, sobald der Skill aktiv ist. Hier kannst du erklären:
- Was der Skill kann
- In welchem Tonfall geantwortet werden soll
- Welche Edge-Cases beachtet werden müssen
- Welche Daten zwingend abgefragt werden müssen, bevor das Tool gerufen wird
Behandle die Skill.md wie eine Job-Description für Claude. Klare Sprache, konkrete Beispiele, eindeutige Trigger.
Das Beispiel: Notion-Skill von 0 auf 100
Wir bauen jetzt den vollständigen Skill. Du brauchst:
- Einen aktiven OpenClaw-Server (Setup siehe Hostinger-Anleitung)
- Einen Notion-Account mit einer Datenbank für Tasks
- Einen Notion-Integrations-Token
Notion vorbereiten
In Notion gehst du auf https://www.notion.so/my-integrations und erstellst eine neue interne Integration namens "OpenClaw". Du bekommst einen Token im Format secret_XXXX.... Speichere ihn.
Dann öffnest du deine Tasks-Datenbank in Notion, klickst auf die drei Punkte oben rechts → "Connections" → wählst deine "OpenClaw"-Integration. Damit hat OpenClaw Lese-/Schreibrechte auf diese DB.
Die Datenbank-ID liest du aus der URL aus: https://www.notion.so/dein-workspace/abc123def456...?v=.... Der Teil zwischen dem letzten / und dem ? ist die ID.
Skill-Ordner anlegen
cd /opt/openclaw
mkdir -p skills/notion-task-creator/tools
cd skills/notion-task-creator
Skill.md schreiben
---
name: notion-task-creator
version: 1.0.0
description: Erstellt neue Tasks in einer Notion-Datenbank
triggers:
- "neue aufgabe"
- "task anlegen"
- "notion task"
- "todo hinzufügen"
- "merk dir bitte"
---
# Notion Task Creator
Du kannst neue Tasks in der Notion-Datenbank anlegen.
## Wann du aktiv wirst
Werd aktiv, wenn der User von einer neuen Aufgabe spricht, die er
festhalten will. Typische Phrasen: "Bitte merk dir, dass ich morgen X
muss", "Trag mir bitte ein, dass...", "Neue Aufgabe: ...".
## Was du tun sollst
1. Frag nach dem **Task-Titel**, falls er nicht klar ist
2. Frag nach einem **Fälligkeitsdatum**, falls relevant (auch "morgen",
"nächste Woche" sind okay – konvertier sie in ISO-Datumsformat)
3. Frag nach einer **Priorität** (low, medium, high) – falls nicht
genannt, default ist "medium"
4. Bestätige dem User die Eingabe, bevor du das Tool aufrufst
5. Ruf `create_task` auf
6. Bestätige Erfolg mit der erstellten Task-URL
## Beispiel-Dialog
User: "Merk dir bitte, dass ich morgen Steuerunterlagen abgeben muss."
Du: "Klar. Ich lege das als Task mit Fälligkeit morgen an. Welche Priorität - high?"
User: "Ja, high."
Du: [ruft create_task auf]
Du: "Erledigt. Steht jetzt drin: https://notion.so/..."
## Tools
- `create_task`: Legt einen neuen Task in Notion an
- Argumente: title (string, required), due_date (string, ISO-Format, optional), priority (low|medium|high, optional)
- Rückgabe: task_url (string)
Das Tool implementieren
Erstelle tools/create_task.ts:
import { Tool } from '@openclaw/sdk';
export const create_task: Tool = {
name: 'create_task',
description: 'Erstellt einen neuen Task in der Notion-Datenbank',
schema: {
type: 'object',
properties: {
title: {
type: 'string',
description: 'Der Titel des Tasks'
},
due_date: {
type: 'string',
description: 'Fälligkeitsdatum im ISO-Format (YYYY-MM-DD)',
pattern: '^\\d{4}-\\d{2}-\\d{2}$'
},
priority: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: 'Priorität'
}
},
required: ['title']
},
handler: async ({ title, due_date, priority = 'medium' }) => {
const NOTION_TOKEN = process.env.NOTION_TOKEN;
const DATABASE_ID = process.env.NOTION_TASKS_DB_ID;
if (!NOTION_TOKEN || !DATABASE_ID) {
throw new Error('Notion-Konfiguration fehlt in .env');
}
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${NOTION_TOKEN}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
body: JSON.stringify({
parent: { database_id: DATABASE_ID },
properties: {
Name: { title: [{ text: { content: title } }] },
...(due_date && {
'Due Date': { date: { start: due_date } }
}),
Priority: {
select: { name: priority.charAt(0).toUpperCase() + priority.slice(1) }
}
}
})
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Notion-API-Fehler: ${error}`);
}
const data = await response.json();
return {
task_url: data.url,
task_id: data.id,
created_at: data.created_time
};
}
};
In .env ergänzen:
NOTION_TOKEN=secret_DEIN-TOKEN
NOTION_TASKS_DB_ID=abc123def456...
OpenClaw neu starten:
sudo systemctl restart openclaw
Test in deinem Bot: "Merk dir bitte, dass ich morgen meine Steuererklärung abgeben muss." Bei korrekter Konfig fragt Claude nach Priorität und legt den Task an.
Best Practices für Skill-Design
Ein guter Skill ist nicht nur funktional, sondern auch wartbar und sicher. Sechs Prinzipien, die ich aus 20+ selbstgeschriebenen Skills gelernt habe:
1. Trigger sind kein Auto-Activator
Trigger im Frontmatter sind Hinweise für OpenClaw, ab wann der Skill kontextuell relevant wird. Sie aktivieren den Skill nicht zwangsweise. Claude entscheidet weiterhin selbst, ob das Tool aufgerufen wird, basierend auf dem Gesprächsverlauf. Schreib trotzdem 5-10 Trigger pro Skill, je natürlichsprachlicher, desto besser.
2. Halte Tool-Schemas eng
Je präziser dein JSON-Schema, desto weniger müllt Claude in dein Tool. Nutze:
enumfür vordefinierte Werte (statt freier Strings)patternfür Format-Validierung (z. B. ISO-Daten)minimum/maximumfür Zahlenrequired-Listen für Pflichtfelderdescriptionmit konkreten Beispielen
3. Validation außen, nicht im Prompt
Wenn ein User "next friday" als Datum sagt, soll Claude das in 2026-05-15 umwandeln. Das geht im Prompt. Sicherer ist aber ein Datums-Parser im Handler:
import { parse } from 'chrono-node';
const parsed = chrono.parse(due_date)[0]?.start.date();
if (!parsed) throw new Error('Datum nicht erkannt');
So fängst du auch Fehler ab, wenn Claude mal halluziniert.
4. Idempotenz wo möglich
Wenn der User zweimal "neue Aufgabe X" sagt, willst du nicht zwei Notion-Einträge. Implementiere Idempotenz, z. B. über einen Hash aus Titel+User+Tag, der vor dem Insert geprüft wird.
5. Logging für Debugging
Ein Skill, der "manchmal nicht funktioniert", ist die Hölle. Logge in jedem Handler den Input, das Resultat (oder den Error) und einen Timestamp:
import { log } from '@openclaw/sdk';
log.info('create_task called', { title, due_date, priority });
// ... try/catch mit log.error ...
6. Niemals Secrets in Skill-Repos
.env-Files gehören in .gitignore. Wenn dein Skill API-Keys braucht, dokumentier in der Skill.md, welche .env-Variablen er erwartet, aber commit niemals den Key selbst.
Tipp: Custom Skills laufen direkt auf deinem VPS. Hostinger KVM 2 mit 8 GB RAM ist dafür ideal: viel Platz für Logs, DB und parallele Skills. Hostinger ansehen →Affiliate-Link — wir erhalten eine Provision, wenn du über diesen Link bestellst. Für dich ändert sich am Preis nichts.
Testing: So stellst du sicher, dass dein Skill funktioniert
Drei Test-Layer, die ich für jeden ernsthaften Skill schreibe:
Unit-Test für den Handler
import { describe, it, expect, vi } from 'vitest';
import { create_task } from './tools/create_task';
describe('create_task', () => {
it('legt einen Task mit minimalen Argumenten an', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
url: 'https://notion.so/mock',
id: 'mock-id',
created_time: '2026-05-10T10:00:00Z'
})
});
const result = await create_task.handler({ title: 'Steuerunterlagen' });
expect(result.task_url).toBe('https://notion.so/mock');
});
it('wirft Fehler bei invalidem Datum', async () => {
await expect(
create_task.handler({ title: 'Test', due_date: 'morgen' })
).rejects.toThrow();
});
});
Integration-Test mit echtem Notion-Sandbox
Ein zweites Notion-Workspace nur fürs Testen, dort lässt du echte API-Calls laufen. Das ist langsamer, fängt aber API-Brüche.
End-to-End-Test im Bot
Schreib eine Test-Konversation mit deinem Bot, beobachte die Logs:
[OpenClaw] Skill 'notion-task-creator' activated (trigger: "merk dir bitte")
[notion-task-creator] create_task called: { title: "Steuerunterlagen", due_date: "2026-05-11", priority: "high" }
[notion-task-creator] create_task success: https://notion.so/abc...
Wenn du diesen Log-Pfad einmal sauber durchgespielt hast, kannst du dem Skill vertrauen.
Distribution: Skills mit ClawHub teilen
Wenn dein Skill so gut ist, dass andere ihn nutzen sollen, gibts mehrere Wege:
Privates Git-Repo (für Teams/Kunden)
Pack den Skill-Ordner in ein eigenes Git-Repo (mein-notion-skill), gib ihm ein README mit Setup-Anleitung. In OpenClaw klonst du das Repo als Git-Submodul oder per npm install git+ssh://git@github.com/....
ClawHub (öffentliche Verteilung)
ClawHub ist die OpenClaw-Skill-Registry – analog zu npm, aber für Skills. Du veröffentlichst per:
claw skill publish
Voraussetzungen:
- Ein verifizierter ClawHub-Account
- Eine vollständige
Skill.mdmitversion,author,license - Bestandene Linting-Checks (
claw skill lint) - Optional: Test-Suite für CI-Validierung
Veröffentlichte Skills landen unter clawhub.dev/skills/dein-username/skill-name. Andere User können sie mit claw skill install dein-username/skill-name ziehen.
Pull-Request in den Core-Skill-Index
Für sehr breit nützliche Skills (z. B. "GitHub-Issues-Lookup", "Wetter", "Wikipedia") kannst du einen PR in das offizielle OpenClaw-Repo machen. Wenn akzeptiert, ist dein Skill out-of-the-box bei jeder Installation dabei.
Häufige Fallstricke beim Skill-Bauen
"Mein Skill wird nie aktiv"
Wahrscheinlichkeiten:
- Trigger zu spezifisch oder unnatürlich → mehr Trigger-Phrasen testen
descriptionim Frontmatter zu vage → Claude weiß nicht, wann der Skill passt- Skill-Datei nicht gefunden →
claw skill listzeigt geladene Skills
"Tool wird gerufen, aber mit falschen Argumenten"
Schema präziser machen. Wenn ein Argument optional ist, dokumentiere im Description-Feld klar, wann es weggelassen werden soll.
"Fehler in Notion-API erscheint dem User als nackter Stack-Trace"
Im Handler try/catch und einen sauberen Error-Wrap:
catch (err) {
return {
error: true,
message: 'Konnte Task nicht anlegen. Stimmt die Notion-Verbindung noch?'
};
}
Claude liest das error: true und antwortet user-freundlich.
"Skill blockiert den Bot mehrere Sekunden"
Lange Tool-Calls (HTTP, DB) blockieren OpenClaws Event-Loop nicht, aber den Konversationsfluss. Falls dein Tool >5 Sekunden dauert, gib während der Ausführung ein "Moment, ich check das gerade…" zurück und stream das Ergebnis später nach.
Erweiterte Patterns für fortgeschrittene Skills
Memory-Awareness
Skills können auf den Memory-Store zugreifen, um Kontext über frühere Konversationen zu haben:
import { memory } from '@openclaw/sdk';
const lastTask = await memory.get(userId, 'last_task');
So kann dein Notion-Skill z. B. "Erstelle einen Folge-Task zum letzten" verstehen.
Multi-Step-Tool-Chains
Manche Skills brauchen mehrere Tool-Calls in Sequenz: Suche Notion-DB-Schema → Erstelle Task → Verlinke mit Projekt. Definiere das in der Skill.md klar als Workflow, Claude orchestriert das automatisch.
Skill-übergreifende Kommunikation
Wenn dein Notion-Skill und dein Calendar-Skill zusammenarbeiten sollen ("erstell den Task UND blockier mir 1h im Kalender"), nutze die Skill-Bus-API von OpenClaw:
import { skillBus } from '@openclaw/sdk';
await skillBus.invoke('calendar', 'block_time', { duration: 60 });
Tipp: Multiple Custom Skills + Memory-DB + Logs brauchen Platz. Hostinger KVM 2 mit 100 GB SSD reicht für hunderte Skills und jahrelange Logs. Hostinger ansehen →Affiliate-Link — wir erhalten eine Provision, wenn du über diesen Link bestellst. Für dich ändert sich am Preis nichts.
Skill-Architektur-Patterns aus der Praxis
Mit der Zeit kristallisieren sich Patterns heraus, die wiederholt funktionieren. Sechs davon, die ich auf jeden produktiven Skill anwende.
Pattern 1: Read-Tools vor Write-Tools
Wenn ein Skill schreibende Operationen anbietet (create_task, send_email, update_record), gib auch immer das passende Read-Tool dazu (list_tasks, find_email, get_record). Das erlaubt Claude, vor dem Write zu validieren, ob die Aktion sinnvoll ist:
User: "Schließ den Steuer-Task ab"
Claude: [list_tasks aufrufen, finden] [update_task]
Ohne Read-Tool muss Claude raten oder zurückfragen.
Pattern 2: Idempotenz-Keys
Schreibende Tools sollten einen optionalen idempotency_key akzeptieren. Wenn derselbe Key zweimal kommt, wird die Operation nicht doppelt ausgeführt:
const cached = await db.get('idempotency', key);
if (cached) return cached.result;
const result = await actuallyDoIt(args);
await db.set('idempotency', key, { result, ts: Date.now() });
Schützt vor Double-Submits bei Netzwerk-Hiccups.
Pattern 3: Pagination by Default
List-Tools (list_tasks, search_emails) sollten immer paginieren, auch wenn der typische Datensatz klein ist. Default limit: 20, optional offset oder cursor. Ohne Pagination explodieren deine Token-Kosten, sobald der Datensatz wächst.
Pattern 4: Strukturierte Errors
Statt nackten Errors gib strukturierte Fehler zurück, die Claude parsen kann:
return {
success: false,
error_code: 'NOTION_RATE_LIMIT',
message: 'Notion-API ist gerade rate-limited. In 30 Sekunden nochmal versuchen.',
retry_after_seconds: 30
};
Claude kann dann sinnvoll reagieren ("Ich versuche es in einer halben Minute erneut") statt User-unfreundliche Stack-Traces zu zeigen.
Pattern 5: Dry-Run-Modus
Sensible Tools sollten einen dry_run: true-Modus haben, der die Aktion simuliert, aber nicht ausführt:
User: "Lösche alle alten Tasks"
Claude: [delete_tasks dry_run:true] → bekommt Liste zurück
Claude: "Das würde 47 Tasks löschen. Soll ich das wirklich machen?"
User: "Ja"
Claude: [delete_tasks dry_run:false]
Pattern 6: Skill-Versions-Migration
Wenn du einen Skill aktualisierst und das Schema ändert, dokumentiere Migrations-Pfade in der Skill.md:
## Migration
- v1.x → v2.0: Das Argument `priority` ist jetzt enum statt string.
Alte Werte werden automatisch gemappt: "wichtig" → "high", etc.
Toolchain für Skill-Entwicklung
Die Tools, die meine Skill-Produktivität deutlich gesteigert haben:
claw skill init
Generiert ein neues Skill-Skelett mit allen Boilerplate-Files:
claw skill init my-new-skill
cd skills/my-new-skill
# Skill.md, tools/, package.json, .gitignore sind da
claw skill test
Local-Testing-Runner, der den Skill mit gemockten Inputs durchspielt:
claw skill test --skill notion-task-creator --input "Erstell einen Task"
Output: Welcher Trigger gefeuert hat, welche Tools gerufen wurden, welche Args.
claw skill lint
Statischer Linter für Skill.md und Tool-Schemas:
claw skill lint
# > Skill.md: missing 'license' in frontmatter
# > tools/create_task.ts: schema property 'priority' has no description
Tracing in Production
Mit OpenTelemetry-Integration kannst du Skill-Aufrufe in Tools wie Honeycomb oder Datadog tracen. Für Solo-Setups ist eine simple Pino-Logging-Ausgabe meist genug.
FAQ
Brauche ich TypeScript für Skills?
Nein. JavaScript geht auch. TypeScript empfehle ich aber stark, weil das JSON-Schema des Tools im Code-Editor mit Auto-Complete und Type-Checking arbeitet. Das spart 50 % der Bugs.
Wie viele Skills lädt OpenClaw maximal?
Technisch unbegrenzt, praktisch wirds bei 30-50 Skills unübersichtlich, weil Claude jeden Skill-Header im Kontext hat. Dann lieber Skills bündeln (z. B. ein "office"-Skill mit Notion + Calendar + Mail) oder Skills nur on-demand laden (Lazy-Loading via loadOn: trigger in der Skill.md).
Können Skills externe Pakete benutzen?
Ja, jede npm-Library ist möglich. Pack die Dependencies in package.json deines Skill-Ordners. OpenClaw installiert sie beim ersten Laden automatisch.
Was, wenn ich einen Skill löschen will?
Den Skill-Ordner aus skills/ entfernen und OpenClaw neu starten. Memory-Daten, die der Skill geschrieben hat, bleiben in der DB – die musst du manuell aufräumen.
Wie hoch sind die Anthropic-Kosten pro Skill-Call?
Ein Skill mit ~500 Wörtern in der Skill.md kostet ~150 Tokens zusätzlich pro Anfrage. Bei Claude Sonnet 4.7 sind das ~0.0005 € pro Skill, also Peanuts. Erst bei 50+ aktiven Skills mit langen Beschreibungen wirds spürbar (~0.025 € pro Anfrage).
Kann ich Skills in Privacy-kritischen Domänen einsetzen?
Ja, aber sei vorsichtig mit Tool-Outputs, die in den Konversations-Kontext gehen. Wenn dein Tool z. B. PII zurückgibt, bleibt das in Claudes Konversations-Memory hängen und wird bei jeder Folge-Anfrage erneut an Anthropic geschickt. Solche Daten lieber nur kurz halten oder gleich abstrahieren ("Ich habe 3 Tasks gefunden" statt der vollen Liste).
Fazit
Custom Skills sind die Killer-Feature von OpenClaw. Mit einem klar strukturierten Skill.md und einem schlanken Tool-Modul kannst du in einer Stunde Fähigkeiten bauen, die kommerziellen KI-Plattformen monatelange Roadmap-Items wären. Wichtig sind drei Dinge: präzise Schemas, ehrliche Trigger und sauberes Error-Handling.
Wenn du gerade erst anfängst, bau dir zuerst 1-2 simple Skills (z. B. ein Wetter-Skill oder ein Calculator-Skill ohne externe APIs), bevor du dich an komplexe Integrationen wie Notion oder MOCO machst. Die Iterations-Geschwindigkeit lernst du nur über Erfahrung – jeder geschriebene Skill macht den nächsten besser. Und wenn du zufällig einen baust, der auch anderen helfen könnte, freut sich ClawHub über deinen Beitrag.