---
sidebar_position: 4
title: Skills
---

# Skills

A **skill** is a named bundle of MCP tools (and optional prompt fragments) that a node can opt into. Skills let you compose tool access per-node without giving every node every tool.

See also: [Skills reference](../skills/index.md) for per-skill docs (tools, setup, code samples).

## Built-in skills

`@zibby/skills` ships these:

| Skill ID | What it adds |
|---|---|
| `browser` | Playwright MCP — browse, click, fill, screenshot |
| `github` | GitHub MCP — issues, PRs, file edits, branches |
| `jira` | Jira MCP — tickets, comments, transitions |
| `slack` | Slack MCP — read/write channels, threads, DMs |
| `memory` | Test memory database — version-controlled (Dolt) knowledge from prior runs |

## Enabling on a node

```js
import { registerSkill } from '@zibby/agent-workflow';
import { browserSkill } from '@zibby/skills';

registerSkill(browserSkill);

graph.addNode('research', {
  prompt: 'Find pricing for {{input.product}}',
  outputSchema: Price,
  agent: 'cursor',
  skills: ['browser'],
});
```

Two effects:
1. The agent gets the Browser MCP tools at this node only.
2. The skill's prompt fragment (telling the agent how to use the tools) is appended to the prompt.

## Custom skills

Implement the `Skill` shape:

```js
import { registerSkill } from '@zibby/agent-workflow';

registerSkill({
  id: 'pdf',
  serverName: 'pdf-mcp',
  tools: ['pdfExtract', 'pdfRender'],
  promptFragment: 'When working with PDFs, use the pdfExtract tool first.',
});
```

Now any node can opt in via `skills: ['pdf']`.

## Why per-node, not per-graph?

Two reasons:

- **Tool scoping** — a node that's planning shouldn't have Slack write access; a node that's posting status shouldn't have file-write tools. Per-node skills give you least-privilege automatically.
- **Prompt size** — every enabled skill adds prompt fragments. Don't pay for tools you won't use.

## Function skills (no MCP server needed)

For lightweight cases, register a function skill:

```js
import { registerSkill } from '@zibby/agent-workflow';

registerSkill({
  id: 'pricing',
  type: 'function',
  fn: async ({ product }) => {
    const r = await fetch(`https://my.api/price?p=${product}`);
    return r.json();
  },
  description: 'Look up product pricing.',
});
```

The agent sees `pricing` as a callable tool, gets your function's return value back as a tool result. No MCP server to spin up.
