# iii-directory

> Engine introspection (functions / triggers / workers), workers registry proxy, and filesystem-backed skill + prompt reader.

| field | value |
|-------|-------|
| version | 0.4.1 |
| type | binary |
| repo | https://github.com/iii-hq/workers |
| supported_targets | x86_64-apple-darwin, aarch64-apple-darwin, i686-pc-windows-msvc, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, armv7-unknown-linux-gnueabihf |
| author | iii |

## installation

```sh
iii worker add iii-directory@0.4.1
```

## configuration

```yaml
- download_timeout_ms: 60000
  registry_url: https://api.workers.iii.dev
  skills_folder: ./skills
```

## readme

# iii-directory

Engine introspection, workers registry proxy, and filesystem-backed
skill + prompt reader for the [iii engine](https://github.com/iii-hq/iii).
Hosts four surfaces, all MCP-agnostic:

| Surface | What clients see | When to use it |
|---|---|---|
| **Skills** (`skills::*`, `skill::fetch`) | Markdown documents under `iii://{id}` plus an `iii://skills` index | Orientation: "when and why to use my worker's tools" |
| **Prompts** (`prompts::*`) | Static prompt templates listed by `prompts::list` and read by `prompts::get` | Parametric command templates the *user* invokes |
| **Directory** (`directory::*`) | Read-side enrichment over `engine::functions::list`, `engine::workers::list`, `engine::trigger-types::list`, `engine::triggers::list` | "What's connected to the engine right now?" |
| **Registry** (`registry::*`) | HTTP proxy over `api.workers.iii.dev` with the same shape as `directory::*` | "What's published in the public registry?" |

Skills and prompts are sourced from a single configured folder on disk
(`skills_folder`). The only write path is the **`skills::download`**
function, which pulls markdown into `skills_folder` from either the
[workers registry](https://workers.iii.dev) or a GitHub repo. Once
downloaded, files belong to the developer — edit them however you want.

`directory::*` and `registry::*` share the same `worker-list` /
`worker-info` envelope shape so callers can switch between the local
engine view and the published-registry view without re-learning the API.

## Table of contents

1. [Install](#install)
2. [Configuration](#configuration)
3. [Quickstart: download some skills](#quickstart-download-some-skills)
4. [On-disk layout](#on-disk-layout)
5. [URI scheme](#uri-scheme)
6. [Functions](#functions)
7. [Custom trigger types](#custom-trigger-types)
8. [Local development & testing](#local-development--testing)
9. [Migration from skills v0.2.x](#migration-from-skills-v02x)

---

## Install

```bash
iii worker add iii-directory
```

`iii worker add` fetches the binary, writes a config block into
`~/.iii/config.yaml`, and the engine starts the worker on the next
`iii start`.

---

## Configuration

```yaml
# Folder that backs every read (`iii://`, `skill::fetch`, `skills::list`,
# `prompts::*`) and every write from `skills::download`. Resolved
# relative to the directory of this config file.
skills_folder: ./skills

# Workers registry base URL — used by `skills::download` when a
# `worker=` source is specified. Override for self-hosted deployments.
registry_url: https://api.workers.iii.dev

# Timeout for a single download (`git clone` or HTTP request) in ms.
download_timeout_ms: 60000
```

The folder is created on first download if it doesn't exist.

---

## Quickstart: download some skills

```bash
# Pull a specific worker's skills + prompts at a fixed semver from
# the registry. Files land under `<skills_folder>/agent-memory/`.
iii trigger --function-id=skills::download \
  --payload='{"worker": "agent-memory", "version": "1.2.3"}'

# Same, but always fetch whatever's tagged `latest`.
iii trigger --function-id=skills::download \
  --payload='{"worker": "agent-memory", "tag": "latest"}'

# Pull a single subfolder out of a public GitHub repo via
# `git clone --depth 1`. Files land under
# `<skills_folder>/frontend-design/`.
iii trigger --function-id=skills::download \
  --payload='{
    "repo": "https://github.com/anthropics/skills",
    "skill": "frontend-design"
  }'
```

The response is `{ namespace, skills_written, prompts_written, source }`
where `skills_written` and `prompts_written` are arrays of relative
paths / prompt names that were materialised in this run.

After every successful download the worker fires the
`skills::on-change` and/or `prompts::on-change` trigger types so that
subscribers like the [`mcp`](../mcp/) worker can forward MCP
`notifications/list_changed` to their clients.

---

## On-disk layout

The worker assumes a fixed layout under `skills_folder`:

```text
skills_folder/
  <namespace>/                 # one folder per `skills::download` namespace
    index.md                   # → iii://<namespace>/index
    contacts.md                # → iii://<namespace>/contacts
    emails/send-email.md       # → iii://<namespace>/emails/send-email
    prompts/                   # ← magic marker for prompts
      send-email.md            # ← MCP slash-command (needs YAML frontmatter)
      triage.md
```

A few rules:

- **Skill ids** are the relative path under `skills_folder` with `.md`
  stripped. Each segment must satisfy `[a-z0-9_-]{1,64}` and the first
  segment must not be the literal `fn` (reserved for section URIs).
- **Prompts** live under any `*/prompts/*.md` path. They must start with
  a YAML frontmatter block declaring at least `description`; `name`
  is optional and overrides the file-stem default.
- Files anywhere else (i.e. *not* in a `prompts/` segment) are skills.

The download function namespaces by source:

| Source | Destination |
|---|---|
| `repo=URL skill=NAME` | `<skills_folder>/<NAME>/...` |
| `worker=NAME version=…` | `<skills_folder>/<NAME>/...` |
| `worker=NAME tag=…` | `<skills_folder>/<NAME>/...` |

Re-pulling the same source overwrites files **file-by-file** —
existing siblings outside the response set are preserved (so
hand-edited additions survive a re-pull).

---

## URI scheme

Same scheme as previous releases, anchored now on the filesystem:

| URI | Returns |
|---|---|
| `iii://skills` | Auto-rendered markdown index of every skill in `skills_folder`. |
| `iii://{id}` | The body at `<skills_folder>/{id}.md`. Any depth. First segment must NOT equal `fn`. |
| `iii://fn/{a}/{b}/.../{leaf}` | Trigger function `a::b::...::leaf` with `{}` and serve its output. Each `/` after `fn/` becomes `::`. |

The `skill::fetch` tool is a thin batched wrapper over the same
resolver. Pass either `uri` (single) or `uris` (array) to read several
sections in one round trip; sections are wrapped as `# {uri}\n\n{body}`
and joined with `\n\n---\n\n`.

There is no recursion guard on `iii://fn/<path>` URIs — the resolver
will trigger any function the engine exposes, including `state::*` and
`engine::*` infra. Adapters that surface `skill::fetch` to untrusted
callers should add their own filtering.

---

## Functions

Sixteen functions across four groups. All registrations are
namespace-clean; this worker is intentionally agnostic to MCP and any
other adapter.

### `skills::*` / `skill::fetch` (filesystem reader)

| Function ID | Description |
|---|---|
| `skills::download` | Pull markdown into `skills_folder`. Either `{repo, skill}` or `{worker, version|tag}`. |
| `skills::list` | Metadata-only listing of every fs-backed skill. |
| `skills::fetch_skill` | Batched read across one or more `iii://` URIs (returns plain markdown). |
| `skill::fetch` | Public alias of `skills::fetch_skill` on a non-`skills::*` namespace. |

### `prompts::*` (filesystem reader)

| Function ID | Description |
|---|---|
| `prompts::list` | Metadata-only listing of every fs-backed prompt. |
| `prompts::get` | Fetch one prompt's body + `{name, description, modified_at}`. Plain shape, no envelope. |

### `directory::*` (engine introspection)

| Function ID | Description |
|---|---|
| `directory::function-list` | List functions registered with the engine; filter by search/prefix/worker. |
| `directory::function-info` | Single-function detail: schemas, owning worker, registered triggers, bundled how-to. |
| `directory::trigger-list` | List trigger TYPES registered with the engine; filter by search/prefix/worker. |
| `directory::trigger-info` | Single trigger-type detail: configuration schema, return schema, instance count. |
| `directory::registered-trigger-list` | List registered trigger INSTANCES (subscriber rows). |
| `directory::registered-trigger-info` | Composite: instance + trigger-type detail + function detail. |
| `directory::worker-list` | List workers connected to the engine; same row shape as `registry::worker-list`. |
| `directory::worker-info` | One worker's `worker` envelope + functions + trigger types + registered triggers. |

### `registry::*` (workers registry HTTP proxy)

| Function ID | Description |
|---|---|
| `registry::worker-list` | Search published workers in `api.workers.iii.dev`. Same row shape as `directory::worker-list`. |
| `registry::worker-info` | Full registry detail for one worker: `worker` envelope (matching `directory::worker-info.worker`) plus `readme`, `api_reference`, `skills_tree`. |

Both `registry::*` responses are cached in-process for
`registry_cache_ttl_ms` (default 60s).

There is **no** `skills::register` / `prompts::register` — see
[Migration](#migration-from-skills-v02x) below.

---

## Custom trigger types

| Trigger type | Fires when | Payload to subscribers |
|---|---|---|
| `skills::on-change` | After a `skills::download` that wrote at least one skill markdown file | `{ "op": "download", "namespace": "<ns>", "source": "repo" \| "registry" }` |
| `prompts::on-change` | After a `skills::download` that wrote at least one prompt markdown file | `{ "op": "download", "namespace": "<ns>", "source": "repo" \| "registry" }` |

Dispatches are fire-and-forget (Void), so the download path doesn't
block on downstream latency.

---

## Local development & testing

### Run from source

```bash
cargo run --release -- --url ws://127.0.0.1:49134 --config ./config.yaml
```

### Tests

```bash
# Fast, offline — exercises the pure helpers (markdown / URI / validators)
# without needing an iii engine.
cargo test --lib

# Full BDD suite — requires an iii engine on ws://127.0.0.1:49134
# (or III_ENGINE_WS_URL). The git-backed download scenarios spin up
# a local fixture repo via `git init`; the registry-backed scenarios
# point a wiremock server at the worker's `registry_url` config.
cargo test

# One feature group at a time. Available tags:
#   @engine  @read  @prompts  @download  @download_repo  @download_registry
cargo test --test bdd -- --tags @download
```

The BDD harness lives under [tests/](tests/). Feature files mirror the
modules in [src/functions/](src/functions/). Step definitions under
[tests/steps/](tests/steps/) drive each feature through the same
`iii.trigger` path the production binary uses.

---

## Migration from skills v0.2.x

`skills` v0.3.0 is a breaking change. The state-backed registry and the
`skills::register` / `prompts::register` functions are gone. Workers in
this monorepo that called those functions at boot will start receiving
"function not found" errors on the registration call (the rest of the
worker keeps working).

The new flow:

1. The worker publishes its skills + prompts to the
   [workers registry](https://workers.iii.dev) as part of the standard
   release pipeline.
2. Operators run `iii trigger skills::download worker=<name> tag=latest`
   (or pin a version) once per worker on each machine that runs `mcp`.
3. The downloaded markdown lives at `<skills_folder>/<name>/...` and is
   served via `iii://` to MCP clients. Operators are free to edit the
   files locally; a re-pull will overwrite the response set but leave
   any hand-added sibling files alone.

Workers that ship a private repo can also seed via the GitHub source:

```bash
iii trigger --function-id=skills::download \
  --payload='{"repo": "https://github.com/<org>/<repo>", "skill": "<folder>"}'
```

The `mcp` worker's existing subscriptions to `skills::on-change` and
`prompts::on-change` continue to work unchanged — those triggers now
fire after every successful download instead of every register.

## api reference

```json
{
  "functions": [
    {
      "description": "List registered trigger instances (the link rows between trigger types and target functions). Filter by trigger_type, function_id, worker, or free-text search.",
      "metadata": {},
      "name": "directory::registered-trigger-list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "function_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "trigger_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "RegisteredTriggerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "RegisteredTriggerListEntry": {
            "properties": {
              "config_summary": {
                "description": "Truncated (~80 chars) JSON preview of `config` so listings stay scannable. Use `directory::registered-trigger-info` for the full payload.",
                "type": "string"
              },
              "function_id": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              },
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "config_summary",
              "function_id",
              "id",
              "trigger_type"
            ],
            "type": "object"
          }
        },
        "properties": {
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/RegisteredTriggerListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "registered_triggers"
        ],
        "title": "RegisteredTriggerListOutput",
        "type": "object"
      }
    },
    {
      "description": "Download skills + prompts into skills_folder. Pass {repo, skill} to clone a single skill folder from a GitHub repo (git clone --depth 1), or {worker, version|tag} to pull from the workers registry. Files in the destination namespace are overwritten file-by-file.",
      "metadata": {
        "tool": {
          "label": "Download skills"
        }
      },
      "name": "skills::download",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "repo": {
            "default": null,
            "description": "Source A: GitHub repo URL. Pair with `skill`.",
            "type": [
              "string",
              "null"
            ]
          },
          "skill": {
            "default": null,
            "description": "Source A: subfolder under `skills/` inside the repo. Doubles as the destination namespace inside `skills_folder`.",
            "type": [
              "string",
              "null"
            ]
          },
          "tag": {
            "default": null,
            "description": "Source B: registry tag to pull (e.g. `latest`). Mutually exclusive with `version`.",
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "default": null,
            "description": "Source B: explicit semver to pull. Mutually exclusive with `tag`.",
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "description": "Source B: workers registry name. Pair with exactly one of `version` / `tag`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "DownloadInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "namespace": {
            "type": "string"
          },
          "prompts_written": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "skills_written": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "source": true
        },
        "required": [
          "namespace",
          "prompts_written",
          "skills_written",
          "source"
        ],
        "title": "DownloadOutput",
        "type": "object"
      }
    },
    {
      "description": "Fetches the content of one or more skill resources identified by iii:// URIs. When you encounter iii:// links in skill instructions, use this tool to retrieve their contents (batch with `uris` when helpful).",
      "metadata": {
        "tool": {
          "label": "Fetch skill"
        }
      },
      "name": "skill::fetch",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "uri": {
            "default": null,
            "description": "A single iii:// URI to read. Must start with \"iii://\".",
            "type": [
              "string",
              "null"
            ]
          },
          "uris": {
            "default": null,
            "description": "One or more iii:// URIs to read in order. When both `uri` and `uris` are provided, `uris` wins (matches the TS reference implementation).",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "title": "FetchSkillInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "String",
        "type": "string"
      }
    },
    {
      "description": "Fetches the content of one or more skill resources identified by iii:// URIs. When you encounter iii:// links in skill instructions, use this tool to retrieve their contents (batch with `uris` when helpful).",
      "metadata": {},
      "name": "skills::fetch_skill",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "uri": {
            "default": null,
            "description": "A single iii:// URI to read. Must start with \"iii://\".",
            "type": [
              "string",
              "null"
            ]
          },
          "uris": {
            "default": null,
            "description": "One or more iii:// URIs to read in order. When both `uri` and `uris` are provided, `uris` wins (matches the TS reference implementation).",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "title": "FetchSkillInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "String",
        "type": "string"
      }
    },
    {
      "description": "Worker envelope plus the lists of functions, trigger types, and registered triggers it owns. The `worker` field has the same shape as registry::worker-info so callers can switch between local + registry surfaces with the same parser.",
      "metadata": {},
      "name": "directory::worker-info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Worker": {
            "description": "Shared worker envelope used by both `directory::worker-list` rows and the `worker` field of `directory::worker-info`. Field names line up with `registry::Worker` (see [`crate::functions::registry::Worker`]) so callers learn one shape across local + registry surfaces.",
            "properties": {
              "active_invocations": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "connected_at_ms": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "description": {
                "description": "Engine-side workers carry no description; field present for shape parity with `registry::Worker.description`. Always `None`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "description": "Engine-assigned connection id (directory-specific).",
                "type": "string"
              },
              "ip_address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "isolation": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Worker name as registered with the engine.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "runtime": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Connection state (e.g. `\"connected\"`, `\"disconnected\"`).",
                "type": "string"
              },
              "version": {
                "description": "Worker version string from the worker's published manifest.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "active_invocations",
              "connected_at_ms",
              "function_count",
              "id",
              "status"
            ],
            "type": "object"
          },
          "WorkerFunctionEntry": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "function_id",
              "name"
            ],
            "type": "object"
          },
          "WorkerRegisteredTriggerEntry": {
            "properties": {
              "function_id": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "function_id",
              "id",
              "trigger_type"
            ],
            "type": "object"
          },
          "WorkerTriggerTypeEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "id",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "functions": {
            "items": {
              "$ref": "#/definitions/WorkerFunctionEntry"
            },
            "type": "array"
          },
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/WorkerRegisteredTriggerEntry"
            },
            "type": "array"
          },
          "trigger_types": {
            "items": {
              "$ref": "#/definitions/WorkerTriggerTypeEntry"
            },
            "type": "array"
          },
          "worker": {
            "allOf": [
              {
                "$ref": "#/definitions/Worker"
              }
            ],
            "description": "Same shape as `worker-list` rows (and `registry::worker-info.worker`)."
          }
        },
        "required": [
          "functions",
          "registered_triggers",
          "trigger_types",
          "worker"
        ],
        "title": "WorkerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Fetch one filesystem-backed prompt by name. Returns the raw markdown body plus name, description, and modified_at — no envelope, no templating.",
      "metadata": {},
      "name": "prompts::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "PromptGetInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "body": {
            "description": "Raw markdown body (post-frontmatter) from disk.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "modified_at": {
            "description": "File mtime as RFC 3339.",
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "body",
          "description",
          "modified_at",
          "name"
        ],
        "title": "PromptGetOutput",
        "type": "object"
      }
    },
    {
      "description": "Full detail for one function: schemas, owning worker, registered triggers that target it, and any matching how-to skill from skills_folder.",
      "metadata": {},
      "name": "directory::function-info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "function_id": {
            "type": "string"
          }
        },
        "required": [
          "function_id"
        ],
        "title": "FunctionInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "HowGuide": {
            "properties": {
              "abs_path": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "frontmatter": {
                "$ref": "#/definitions/HowToFrontmatter"
              },
              "skill_id": {
                "type": "string"
              }
            },
            "required": [
              "abs_path",
              "body",
              "frontmatter",
              "skill_id"
            ],
            "type": "object"
          },
          "HowToFrontmatter": {
            "description": "Subset of frontmatter fields the scanner cares about. Anything else in the YAML block is preserved verbatim by `split_frontmatter` but ignored here.",
            "properties": {
              "function_id": {
                "default": null,
                "description": "Optional single function id (alternative to `functions`).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "functions": {
                "default": [],
                "description": "Optional list of function ids this how-to covers.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "title": {
                "default": null,
                "description": "Optional human-readable title (mirrors what an `# H1` would give).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "type": {
                "default": null,
                "description": "Required marker — only `type: how-to` files are considered.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "RegisteredTriggerSummary": {
            "properties": {
              "config": true,
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "config",
              "id",
              "trigger_type"
            ],
            "type": "object"
          }
        },
        "properties": {
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "function_id": {
            "type": "string"
          },
          "how_guide": {
            "anyOf": [
              {
                "$ref": "#/definitions/HowGuide"
              },
              {
                "type": "null"
              }
            ]
          },
          "metadata": true,
          "name": {
            "type": "string"
          },
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/RegisteredTriggerSummary"
            },
            "type": "array"
          },
          "request_schema": true,
          "response_schema": true,
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "function_id",
          "name",
          "registered_triggers"
        ],
        "title": "FunctionInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Fetch full registry metadata for one worker: worker envelope (same shape as registry::worker-list rows and directory::worker-info), readme, full API reference (functions + triggers schemas), and tree of skill/prompt file paths. Pass either `version` or `tag` (defaults to tag=\"latest\"). Results are cached for `registry_cache_ttl_ms`.",
      "metadata": {},
      "name": "registry::worker-info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`registry::worker-info` input. Pass either `version` or `tag`; if neither is provided we fall back to `tag: \"latest\"`.",
        "properties": {
          "name": {
            "description": "Worker name in the registry (e.g. `\"resend\"`).",
            "type": "string"
          },
          "tag": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "default": null,
            "description": "Mutually exclusive with `tag`. If neither is provided we fall back to `tag: \"latest\"`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ApiReference": {
            "properties": {
              "functions": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/ApiReferenceFunction"
                },
                "type": "array"
              },
              "triggers": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/ApiReferenceTrigger"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "ApiReferenceFunction": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "metadata": true,
              "name": {
                "type": "string"
              },
              "request_schema": true,
              "response_schema": true
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "ApiReferenceTrigger": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "invocation_schema": true,
              "metadata": true,
              "name": {
                "type": "string"
              },
              "return_schema": true
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "RegistryAuthor": {
            "properties": {
              "is_verified": {
                "default": false,
                "type": "boolean"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profile_picture": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "SkillsTree": {
            "properties": {
              "prompts": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/SkillsTreePrompt"
                },
                "type": "array"
              },
              "skills": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/SkillsTreeSkill"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "SkillsTreePrompt": {
            "properties": {
              "description": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "SkillsTreeSkill": {
            "properties": {
              "path": {
                "type": "string"
              }
            },
            "required": [
              "path"
            ],
            "type": "object"
          },
          "Worker": {
            "description": "Shared worker envelope used by both `registry::worker-list` rows and the `worker` field of `registry::worker-info`. Same field names as [`crate::functions::directory::Worker`] so callers learn one shape across local + registry surfaces.",
            "properties": {
              "author": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RegistryAuthor"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "repo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "version": {
                "description": "Latest published version (worker-list) or the resolved version (worker-info, when called with `version` / `tag`).",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "api_reference": {
            "$ref": "#/definitions/ApiReference"
          },
          "readme": {
            "type": [
              "string",
              "null"
            ]
          },
          "skills_tree": {
            "$ref": "#/definitions/SkillsTree"
          },
          "worker": {
            "allOf": [
              {
                "$ref": "#/definitions/Worker"
              }
            ],
            "description": "Same shape as `worker-list` rows (and `directory::worker-info.worker`)."
          }
        },
        "required": [
          "api_reference",
          "skills_tree",
          "worker"
        ],
        "title": "WorkerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "List every function registered with the engine. Filter by free-text search, namespace prefix, and/or worker name.",
      "metadata": {},
      "name": "directory::function-list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "prefix": {
            "default": null,
            "description": "Exact prefix match on `function_id` (e.g. `\"mem::\"`).",
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Case-insensitive substring match against `function_id` and `description`.",
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "description": "Exact worker-name match (the worker that registered the function).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "FunctionListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "FunctionListEntry": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              },
              "name": {
                "description": "Last `::` segment of `function_id`.",
                "type": "string"
              },
              "worker_name": {
                "description": "Worker that registered it (resolved via `WorkerInfo.functions[]`), or the first `::` segment of `function_id` as fallback.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "function_id",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "functions": {
            "items": {
              "$ref": "#/definitions/FunctionListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "functions"
        ],
        "title": "FunctionListOutput",
        "type": "object"
      }
    },
    {
      "description": "List every worker currently connected to the engine. Filter by name substring, runtime, or status. Same row shape as registry::worker-list so callers learn one envelope.",
      "metadata": {},
      "name": "directory::worker-list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "runtime": {
            "default": null,
            "description": "Exact runtime match (e.g. `\"rust\"`, `\"node\"`).",
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Case-insensitive substring match against `name`.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "default": null,
            "description": "Exact status match (e.g. `\"connected\"`).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "WorkerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Worker": {
            "description": "Shared worker envelope used by both `directory::worker-list` rows and the `worker` field of `directory::worker-info`. Field names line up with `registry::Worker` (see [`crate::functions::registry::Worker`]) so callers learn one shape across local + registry surfaces.",
            "properties": {
              "active_invocations": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "connected_at_ms": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "description": {
                "description": "Engine-side workers carry no description; field present for shape parity with `registry::Worker.description`. Always `None`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "description": "Engine-assigned connection id (directory-specific).",
                "type": "string"
              },
              "ip_address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "isolation": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Worker name as registered with the engine.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "runtime": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Connection state (e.g. `\"connected\"`, `\"disconnected\"`).",
                "type": "string"
              },
              "version": {
                "description": "Worker version string from the worker's published manifest.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "active_invocations",
              "connected_at_ms",
              "function_count",
              "id",
              "status"
            ],
            "type": "object"
          }
        },
        "properties": {
          "workers": {
            "items": {
              "$ref": "#/definitions/Worker"
            },
            "type": "array"
          }
        },
        "required": [
          "workers"
        ],
        "title": "WorkerListOutput",
        "type": "object"
      }
    },
    {
      "description": "Full detail for one trigger type: configuration schema, return schema, owning worker, and current instance count.",
      "metadata": {},
      "name": "directory::trigger-info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TriggerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "configuration_schema": {
            "description": "SDK 0.11.3 surfaces a single `trigger_request_format` that doubles as the per-instance configuration shape; expose it explicitly so callers don't have to know the alias."
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "instance_count": {
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "return_schema": true,
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "description",
          "id",
          "instance_count",
          "name"
        ],
        "title": "TriggerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "List filesystem-backed prompts (name, description, modified_at) from skills_folder.",
      "metadata": {},
      "name": "prompts::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "ListPromptsInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "PromptEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "modified_at": {
                "description": "File mtime as RFC 3339.",
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "modified_at",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "prompts": {
            "items": {
              "$ref": "#/definitions/PromptEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "prompts"
        ],
        "title": "ListPromptsOutput",
        "type": "object"
      }
    },
    {
      "description": "Full denormalized detail for one registered trigger: instance config + trigger-type detail + function detail.",
      "metadata": {},
      "name": "directory::registered-trigger-info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "RegisteredTriggerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "FunctionInfoOutput": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              },
              "how_guide": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/HowGuide"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "metadata": true,
              "name": {
                "type": "string"
              },
              "registered_triggers": {
                "items": {
                  "$ref": "#/definitions/RegisteredTriggerSummary"
                },
                "type": "array"
              },
              "request_schema": true,
              "response_schema": true,
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "function_id",
              "name",
              "registered_triggers"
            ],
            "type": "object"
          },
          "HowGuide": {
            "properties": {
              "abs_path": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "frontmatter": {
                "$ref": "#/definitions/HowToFrontmatter"
              },
              "skill_id": {
                "type": "string"
              }
            },
            "required": [
              "abs_path",
              "body",
              "frontmatter",
              "skill_id"
            ],
            "type": "object"
          },
          "HowToFrontmatter": {
            "description": "Subset of frontmatter fields the scanner cares about. Anything else in the YAML block is preserved verbatim by `split_frontmatter` but ignored here.",
            "properties": {
              "function_id": {
                "default": null,
                "description": "Optional single function id (alternative to `functions`).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "functions": {
                "default": [],
                "description": "Optional list of function ids this how-to covers.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "title": {
                "default": null,
                "description": "Optional human-readable title (mirrors what an `# H1` would give).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "type": {
                "default": null,
                "description": "Required marker — only `type: how-to` files are considered.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "RegisteredTriggerSummary": {
            "properties": {
              "config": true,
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "config",
              "id",
              "trigger_type"
            ],
            "type": "object"
          },
          "TriggerInfoOutput": {
            "properties": {
              "configuration_schema": {
                "description": "SDK 0.11.3 surfaces a single `trigger_request_format` that doubles as the per-instance configuration shape; expose it explicitly so callers don't have to know the alias."
              },
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "instance_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "return_schema": true,
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "description",
              "id",
              "instance_count",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "config": true,
          "function": {
            "anyOf": [
              {
                "$ref": "#/definitions/FunctionInfoOutput"
              },
              {
                "type": "null"
              }
            ],
            "description": "Full function detail for `function_id`. `None` if the function has been unregistered between calls."
          },
          "function_id": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "metadata": true,
          "trigger": {
            "anyOf": [
              {
                "$ref": "#/definitions/TriggerInfoOutput"
              },
              {
                "type": "null"
              }
            ],
            "description": "Full trigger-type detail for `trigger_type`. `None` if the type has been unregistered between calls."
          },
          "trigger_type": {
            "type": "string"
          },
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "config",
          "function_id",
          "id",
          "trigger_type"
        ],
        "title": "RegisteredTriggerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "List filesystem-backed skills (id, body length, modified_at) from skills_folder.",
      "metadata": {},
      "name": "skills::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "ListSkillsInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "SkillEntry": {
            "properties": {
              "bytes": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "modified_at": {
                "description": "File mtime as RFC 3339 (best effort; empty if unavailable).",
                "type": "string"
              }
            },
            "required": [
              "bytes",
              "id",
              "modified_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "skills": {
            "items": {
              "$ref": "#/definitions/SkillEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "skills"
        ],
        "title": "ListSkillsOutput",
        "type": "object"
      }
    },
    {
      "description": "List every trigger TYPE registered with the engine. Filter by search, prefix, worker. (For registered trigger instances, use directory::registered-trigger-list.)",
      "metadata": {},
      "name": "directory::trigger-list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "prefix": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "TriggerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TriggerListEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "description",
              "id",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "triggers": {
            "items": {
              "$ref": "#/definitions/TriggerListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "triggers"
        ],
        "title": "TriggerListOutput",
        "type": "object"
      }
    },
    {
      "description": "List workers from the public registry (api.workers.iii.dev) matching the free-text term `search`. Same row shape as directory::worker-list so callers learn one envelope. Results are cached for `registry_cache_ttl_ms` (default 60s).",
      "metadata": {},
      "name": "registry::worker-list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`registry::worker-list` input. Mirrors [`crate::functions::directory::WorkerListInput.search`] so callers can switch between local and registry surfaces without re-learning the API. Adds `limit` for paging because the registry is paged.",
        "properties": {
          "limit": {
            "default": null,
            "description": "Max results to return. Defaults to 20; capped at 100.",
            "format": "uint32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Free-text query forwarded to the registry as `?q=…`. Required — the public registry doesn't support an unscoped browse endpoint.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "WorkerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "RegistryAuthor": {
            "properties": {
              "is_verified": {
                "default": false,
                "type": "boolean"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profile_picture": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "Worker": {
            "description": "Shared worker envelope used by both `registry::worker-list` rows and the `worker` field of `registry::worker-info`. Same field names as [`crate::functions::directory::Worker`] so callers learn one shape across local + registry surfaces.",
            "properties": {
              "author": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RegistryAuthor"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "repo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "version": {
                "description": "Latest published version (worker-list) or the resolved version (worker-info, when called with `version` / `tag`).",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "workers": {
            "items": {
              "$ref": "#/definitions/Worker"
            },
            "type": "array"
          }
        },
        "required": [
          "workers"
        ],
        "title": "WorkerListOutput",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "Fires after every successful skills::download that wrote at least one prompt markdown file.",
      "invocation_schema": {},
      "metadata": {},
      "name": "prompts::on-change",
      "return_schema": {}
    },
    {
      "description": "Fires after every successful skills::download that wrote at least one skill markdown file.",
      "invocation_schema": {},
      "metadata": {},
      "name": "skills::on-change",
      "return_schema": {}
    }
  ]
}
```
