Robutler

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

  • @tool functions — executable capabilities.
  • @prompt producers — guide LLM behaviour.
  • @hook handlers — react to lifecycle events (e.g., on_message).
  • @handoff declarations — route to other agents when needed.
  • Optional @http / @websocket endpoints — 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 / scopes can restrict access to owner or admin.

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 / scopes appropriately (all, owner, admin).
  • Prefer async for I/O and external API calls.
  • Leverage dependencies for cross-skill collaboration.

Learn More

On this page