# Skill anatomy

Source: https://opvs.ai/docs/guides/vendors/skill-anatomy

> The two files every skill needs, SKILL.md and schema.yaml, and the response contract agents rely on.

A skill is a folder with two files that matter. Everything else is optional.

```
my-skill/
├── SKILL.md          ← what the agent reads: when and how to use the skill
└── client/
    └── schema.yaml   ← the API contract: methods, parameters, auth, guidance
```

To see a real one, install any published skill and read it (see [Use skills in your AI tool](https://opvs.ai/docs/guides/use-skills-in-your-ai-tool.md)), or read a package's methods from the registry:

```bash
curl -s "https://market.opvs.ai/api/v1/registry/packages/@opvs-ai/agentboard" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d['methods_index']['agentboard'])[:10])"
```

## SKILL.md: the agent-facing page

The agent loads this file when the skill is in scope. Keep it short: a router, not a manual.

```yaml
---
name: my-skill
version: "1.0.0"
description: >
  One sentence on what this skill does, naming the words an agent would use for the job.
category: utility
requires_auth: true
requires_brand: true
triggers:
  - my-skill
  - a word the agent might think of
metadata:
  openclaw:
    primaryEnv: OPVS_PAT
---
```

- `name`: kebab-case, the same as the folder. No dots, `@` or `/`.
- `description`: the trigger. Agents match on it, so list the verbs and nouns of the job.
- `category`: free text. Look at what similar packages use (`data-collection`, `media`, `communication`, `integration`, `utility`, …) so workspaces find you under the same heading.
- `triggers`: extra words that help discovery.
- `primaryEnv`: documentation only. Do **not** add `requires.env` for variables another skill provides; see [Preflight and common mistakes](https://opvs.ai/docs/guides/vendors/preflight.md).

Below the frontmatter, write plain Markdown: a one-line tagline, pointers to sibling skills ("for X use that skill instead"), and one line per method.

## schema.yaml: the API contract

One `schema.yaml` drives everything the platform generates for your skill: typed agent tools, MCP tool definitions, an SDK module, CLI commands and an OpenAPI 3.1 document.

```yaml
name: my-skill
version: "1.0.0"
description: One sentence (same as SKILL.md).
category: utility
base_url: /api/v1/my-skill
auth:
  type: pat
  header: Authorization
  prefix: Bearer
  env_var: OPVS_PAT
defaults:
  format: yaml
  timeout_ms: 30000
guidance:
  use: "when to use this skill, one line"
  not:
    - "other-skill → what it does instead"
methods:
  doSomething:
    description: One sentence on what the method does.
    guidance:
      use: "when the agent has X and needs Y"
      next: "doSomethingElse"
      warn: "the one thing that catches first-time callers"
    http: POST /things
    params:
      thing_id:
        type: uuid
        required: true
        description: The thing to act on.
      filter:
        type: enum
        values: [open, closed, all]
        description: Optional filter.
    response:
      format: yaml
```

- **Method names** must match `^[a-zA-Z0-9_-]+$`. OpenAI's API refuses a tool list containing any other name, which breaks every agent that loads your skill.
- `defaults.format: yaml` saves a large share of tokens compared with JSON on agent-facing replies.
- `guidance` text is added to what the agent sees for each tool: `use`, `next` and `warn` steer it to the right call order without a long `SKILL.md`.
- `rate_limit`: declare one on every method that calls out, or package-wide. The [security review](https://opvs.ai/docs/guides/vendors/security-review.md) checks it.

The full guidance block, nested parameters and the other layers are on [The 4-layer skill format](https://opvs.ai/docs/guides/vendors/four-layer-format.md).

## The response contract

Every skill answers in the same shape, so agents can use any skill without reading its docs. The platform adds these fields from your `guidance` block; you write no code for them.

1. `_guidance`** sits beside your data, never around it.**

```yaml
   # right
   { id: "abc", status: "done", _guidance: "…" }
   # wrong: breaks every client that reads the flat shape
   { data: { id: "abc", status: "done" }, _guidance: "…" }
```

1. `telemetry_signal`** is one of exactly four values:** `absent`, `cold`, `warm`, `hot`.
2. **Call the free-text input **`intent`, not `query`, `q` or `prompt`. If an older method already uses another name, list it in `guidance.intent_aliases`.
3. **Skills are called by the manifest skill id.** Agents call `skill_call` (inside OPVS) or `tool_call` (over MCP) with the id from `manifest.skills[].id`, or with `@vendor/name`.

Optional: return diagnostics as a sibling `_findings: [...]` array, and put structured filters in one `additional_filters: {…}` object instead of many top-level fields.

If you skip the contract, the skill still loads, but agents lose search ranking, `skill_describe` answers and the "what next" hints.

## What is generated, and when

`opvs-skills generate my-skill` reads `client/schema.yaml` and writes the typed tools, MCP definitions, SDK, CLI commands and OpenAPI document. Run it locally to check what agents will see; you do not commit the output.

## Next

[Profession anatomy](https://opvs.ai/docs/guides/vendors/profession-anatomy.md), or go straight to [The 4-layer skill format](https://opvs.ai/docs/guides/vendors/four-layer-format.md).
