Creating Custom Skills
This guide shows how to build a minimal, production-ready skill that is consistent with the SDK, Quickstart, and platform conventions.
What a Skill Provides
@toolfunctions — executable capabilities.@promptproducers — guide LLM behaviour.@hookhandlers — react to lifecycle events (e.g.,on_message).@handoffdeclarations — route to other agents when needed.- Optional
@http/@websocketendpoints — custom REST / WS handlers mounted under the agent. - Declared dependencies — ensure other skills are present (e.g., memory).
Minimal Skill
import { Skill, tool, hook, handoff } from 'webagents';
import type { Context, HookData, ClientEvent } from 'webagents';
class NotesSkill extends Skill {
readonly name = 'notes';
readonly dependencies = ['memory'];
@tool({ description: "Add a note to the user's short-term memory" })
async addNote(params: { text: string }): Promise<{ status: string; text: string }> {
// In a real implementation, call the memory skill here.
return { status: 'saved', text: params.text };
}
@hook({ lifecycle: 'on_message' })
async normalizeMessage(data: HookData, ctx: Context) {
return data;
}
@handoff({
name: 'notes-auditor',
description: 'Route audit requests to the auditor',
})
async *routeToAuditor(events: ClientEvent[]) {
yield { type: 'response.delta', delta: 'auditor handling' } as const;
}
}Adding HTTP Endpoints (Optional)
import { Skill, http } from 'webagents';
class NotesSkill extends Skill {
readonly name = 'notes';
@http({ path: '/notes', method: 'POST', scopes: ['owner'] })
async createNote(req: Request): Promise<Response> {
const payload = await req.json();
return Response.json({ received: payload, status: 'ok' });
}
}- Endpoints are mounted under your agent path when served.
scope/scopescan restrict access toowneroradmin.
Use Your Skill in an Agent
import { BaseAgent } from 'webagents';
import { SessionSkill } from 'webagents/skills/session';
const agent = new BaseAgent({
name: 'notes',
instructions: 'You help users capture and recall short notes.',
model: 'openai/gpt-4o-mini',
skills: [new SessionSkill(), new NotesSkill()],
});Serve Your Agent
import { serve } from 'webagents';
await serve(agent, { port: 8000 });Best Practices
- Keep one clear responsibility per skill.
- Validate inputs in tools and HTTP handlers.
- Use
scope/scopesappropriately (all,owner,admin). - Prefer async for I/O and external API calls.
- Leverage dependencies for cross-skill collaboration.