> For the complete documentation index, see [llms.txt](https://docs.tessl.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tessl.io/reference/skill-schemas.md).

# Skill schemas

A launchable skill can declare typed **input** and **output** contracts in its `SKILL.md` frontmatter. When you run the skill with `tessl launch skill --cloud`, Tessl validates the launcher-supplied inputs against the declared input schema before the skill runs, and validates the recorded result against the declared output schema when it finishes — so a launch either conforms to the contract or fails fast with a field-level diagnostic.

This page covers authoring the `schemas` block, the `$ref` forms it accepts, how those refs are resolved, and the structured `result` a launch returns.

## Authoring the `schemas` block

Declare schemas under a single top-level `schemas` key in the skill's `SKILL.md` frontmatter. It has two optional slots, `inputs` and `output`:

```yaml
---
name: greet
description: Greet someone by name.
schemas:
  inputs:
    $ref: ./schemas/inputs.schema.json
  output:
    $ref: ./schemas/output.schema.json
---
```

Each slot is either an **inline JSON Schema** or a single **`$ref`** reference. Both slots are optional and independent — declare one, both, or neither:

* **`schemas.inputs`** — validated against the launcher-supplied inputs (the `INPUTS` object, keyed by the skill's own placeholder names) before the skill runs. A missing required input fails the launch during preparation rather than being discovered mid-run.
* **`schemas.output`** — describes the structured payload the skill produces. It is composed into the returned result (see [The returned result](#the-returned-result)).

A slot may be written inline instead of via a `$ref`:

```yaml
schemas:
  output:
    type: object
    properties:
      message: { type: string }
    required: [message]
    additionalProperties: false
```

For a complete, copyable example — a `greet` skill with both slots and a bundled `./schemas/` directory — see the [quickstart-typed-skill](https://github.com/tesslio/monorepo/tree/main/apps/docs/tiles/quickstart-typed-skill/README.md) tile.

## `$ref` forms

A slot's `$ref` must be a non-empty string and the **sole** key of the slot (you cannot mix `$ref` with other keys). Three forms are accepted:

| Form       | Example                                     | Resolved from                                                                    |
| ---------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
| **inline** | `{ type: object, ... }` (no `$ref`)         | Used as-is; nothing is fetched.                                                  |
| **local**  | `{ $ref: ./schemas/output.schema.json }`    | A file read relative to the `SKILL.md` directory. Must start with `./` or `../`. |
| **remote** | `{ $ref: https://example.com/schema.json }` | Fetched over HTTPS at launch preparation time.                                   |

Anything else (a bare name, an `http://` URL, a fragment-only `#/...` pointer) is rejected.

### Local `$ref`s are contained to the skill bundle

A local `$ref` is resolved relative to the directory holding the `SKILL.md`, but it is **contained** to the skill's bundle: a ref may point at a sibling or a shared file elsewhere in the same bundle (`../shared/output.schema.json`), but one that escapes the bundle directory — lexically or through a symlink — is rejected. This lets skills that genuinely share a contract stay DRY by pointing a local `$ref` at one shared file, without a ref reaching outside the bundle.

### Remote `$ref`s are fetched safely

A remote `$ref` is fetched only at launch **preparation** time (never during plain validation), over a guarded HTTPS client:

* **HTTPS only**, with no embedded credentials.
* **No private or internal targets** — `localhost`, private/reserved IP ranges, and link-local / unique-local IPv6 are refused. The host is re-resolved and pinned to the validated address at connection time, so a DNS-rebinding trick cannot swap in an internal address after the check.
* **Bounded** — a 10-second timeout and a 1 MiB body cap; HTTP redirects are refused outright.

The fetched body must be valid JSON describing a JSON Schema object.

## Resolution precedence

When a launch prepares a skill, each declared slot is resolved into a fully inline JSON Schema:

1. **Read** the `schemas` block from the skill's `SKILL.md` frontmatter. A skill with no `schemas` block declares no contract — its inputs are not validated and its output is left unconstrained.
2. **Resolve** each slot by its form: an inline schema is used as-is; a local `$ref` is read from the bundle; a remote `$ref` is fetched.
3. **Inline** the resolved schema. Resolution is **single-level** — a `$ref` inside a resolved schema document is not followed. Author self-contained schema files.

If a slot cannot be resolved (a malformed block, an unreadable local file, an unfetchable or unsafe remote URL), the launch fails during preparation with a diagnostic naming the failing slot, rather than proceeding with a partial contract.

## The returned result

Every cloud launch returns a fixed **envelope**, regardless of whether the skill declared any schemas:

```json
{
  "status": "succeeded",
  "summary": "One sentence on what the skill did.",
  "output": { }
}
```

* **`status`** — `succeeded` when the skill did its work, or `no-action` when it made no change. This is a coarse "did it change anything" flag; it is distinct from the launch run's own status.
* **`summary`** — a short human-readable sentence on what the skill did.
* **`output`** — the skill-governed structured payload.

When the skill declares a `schemas.output`, that resolved schema is **composed into** the envelope's `output` property and `output` becomes **required** — the skill must produce a payload conforming to its declared schema, and the launch validates it. When the skill declares no output schema, `output` is unconstrained and may be omitted. Composition never overrides the envelope: the `{ status, summary, output }` shape is always enforced.

### Consuming the result from the CLI

`tessl launch skill --cloud` renders the returned result when a launch completes, and `--json` emits the whole envelope for a script to consume:

```sh
$ tessl launch skill engteam/greet --cloud --repo engteam/my-repo --json
{
  "launchRunId": "…",
  "status": "completed",
  "result": {
    "status": "succeeded",
    "summary": "Greeted Ada.",
    "output": { "message": "Hello, Ada!", "recipient": "Ada" }
  }
}
```

See [`tessl launch skill`](/reference/cli-commands.md#tessl-launch-skill) for the full flag reference.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tessl.io/reference/skill-schemas.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
