Build an agent skill

Write, test and submit an Agent Skill: a folder with a SKILL.md and optional scripts that gives an IXO or Qi agent one repeatable, reviewable capability.

An Agent Skill is a folder that teaches an AI agent one repeatable capability. Its SKILL.md tells the agent when to use the capability, what information it needs, which steps to follow, which tools or scripts it may use, what output to produce, and how to handle errors, uncertainty and user confirmation. In this guide you build a skill called claim-evidence-checklist, test its script, validate it and submit it for review.

With a skill, an agent does not improvise from a prompt. It loads the skill, follows its instructions, runs its documented scripts, and produces output that fits a real workflow. That makes its behaviour repeatable and easier to review.

When to build a skill

Build a skill when the capability is reusable. For example:

  • Evidence review checklist. Compare the evidence a claim requires with the evidence submitted.
  • Document generator. Produce structured output such as invoices, reports, attestations or summaries.
  • Data transformer. Convert input files into a structured format that can be reviewed or submitted into a workflow.
  • Workflow assistant. Take an agent through a known operational process with clear steps and outputs.
  • API interaction pattern. Give an agent safe, documented instructions for an approved outside service.
  • IXO task helper. Prepare claim metadata, evidence summaries, credential checklists or workflow reports.

A skill can help an agent prepare, inspect, summarise or generate. It does not replace protocol permissions, backend services or governed state changes. An action that changes state on IXO still goes through a declared API, MCP tool, SDK or governed workflow.

Skill or something else?

Pick the building block that matches what you are building.

You are buildingUse
Reusable agent instructions, scripts, templates or generated filesAn Agent Skill, the subject of this guide
A live tool interface that agents call to reach IXO servicesAn MCP tool. See Model Context Protocol
A backend oracle service that observes state, evaluates evidence or triggers workflowsThe Agentic Oracles ADK. See Agentic Oracles
A React interface where users talk to oraclesThe Oracles Client SDK
A protocol-level state change or governed integrationIXO Protocol and the SDK reference, not a skill file alone

How skills work

Skills use progressive disclosure. At startup, an agent can discover skills from their metadata, such as the skill name and description. When a skill is relevant, the agent loads its SKILL.md instructions. If the skill includes scripts, the agent can run them and use their output without loading the script source into its context.

So SKILL.md carries the weight. The agent relies on it to know when to use the skill, what inputs to collect, how to run the scripts and how to read their results.

Required structure

Every skill is a folder, and the folder must contain SKILL.md:

skills/
└── your-skill-name/
    └── SKILL.md

A fuller skill can include prompts, scripts, examples, templates and its own license:

skills/
└── your-skill-name/
    ├── SKILL.md
    ├── prompts/
    │   └── detailed.md
    ├── scripts/
    │   └── helper.py
    ├── examples/
    │   └── example-input.json
    ├── templates/
    │   └── template.html
    └── LICENSE.txt

Naming rules

The folder name must match the name field in SKILL.md. Use lowercase letters, numbers and single hyphens.

Use:

claim-evidence-checklist
digital-mrv-report
invoice-creator

Avoid:

ClaimEvidenceChecklist
claim_evidence_checklist
claim--evidence
-claim-review
claim-review-

Before you start

You need:

  • A checkout of the repository you are contributing the skill to. The commands below run from its root.
  • Python, to run the helper script.
  • Git, to submit the skill.

Create your first skill

The skill in this example, claim-evidence-checklist, helps an agent produce a checklist that compares the evidence a claim requires with the evidence submitted.

1. Create the folder

From the repository root:

mkdir -p skills/claim-evidence-checklist/scripts
mkdir -p skills/claim-evidence-checklist/examples

2. Add SKILL.md

Create:

skills/claim-evidence-checklist/SKILL.md

Use this template:

---
name: claim-evidence-checklist
description: Generate a review checklist for an IXO claim by comparing required evidence against submitted evidence and identifying gaps before human or oracle review.
license: Apache-2.0
compatibility: Claude, Qi Agents
allowed-tools: Read Write Bash
metadata:
  author: IXO
  version: "1.0.0"
  category: verification
---

# Claim Evidence Checklist

## Overview

This skill helps an AI agent prepare a structured evidence checklist for an IXO claim.

Use it when a user needs to review whether a claim has enough evidence for verification, attestation, dispute resolution, or escalation.

The skill does not verify the claim by itself. It prepares a checklist that can support human review, Agentic Oracle evaluation, or a governed IXO workflow.

## When to use this skill

Use this skill when the user asks to:

- review evidence for a claim
- identify missing evidence
- prepare a verification checklist
- compare submitted evidence against program requirements
- summarize evidence readiness before claim review
- support a digital MRV or outcomes verification process

Do not use this skill to make final verification decisions unless the surrounding workflow explicitly grants that authority.

## Inputs needed

Ask for or locate:

1. Claim ID or claim reference
2. Claim type
3. Entity or project the claim is about
4. Required evidence list
5. Submitted evidence list
6. Review rules or acceptance criteria, if available
7. Reviewer or verifier role, if relevant

If any required information is missing, ask for it or mark it as unknown.

## Workflow

Follow these steps:

1. Identify the claim and the entity it concerns.
2. List the evidence required for this claim type.
3. List the evidence that has been submitted.
4. Match submitted evidence against required evidence.
5. Mark each requirement as:
   - `present`
   - `missing`
   - `partial`
   - `unknown`
6. Summarize risks, gaps, and next actions.
7. Do not invent evidence.
8. Do not treat user-provided statements as verified IXO state unless they come from an approved source.
9. If a governed workflow action is required, direct the user to the relevant IXO API, SDK, MCP tool, or review process.

## Output format

Return a concise review report:

```text
Claim: [claim ID]
Claim type: [claim type]
Entity: [entity or project]

Evidence checklist:
- [status] [required evidence] — [submitted evidence or gap]

Summary:
- Present:
- Missing:
- Partial:
- Unknown:

Recommended next action:
- [request more evidence / send to verifier / escalate / continue review]
```

## Script

This skill includes a helper script:

```bash
python scripts/build_checklist.py examples/claim-review-input.json checklist.md
```

The script reads JSON input and writes a Markdown checklist. It classifies each requirement as present, missing, or optional-not-submitted (counted in `unknown`); extend the script if you need a `partial` state.

Use the script when the user provides structured claim and evidence data or asks for a file output.

## Script input format

```json
{
  "claim_id": "claim-001",
  "claim_type": "delivery-completed",
  "entity": "Clean Cooking Project A",
  "required_evidence": [
    {
      "type": "delivery_receipt",
      "description": "Signed delivery receipt from field partner",
      "required": true
    },
    {
      "type": "geo_photo",
      "description": "Geotagged installation photo",
      "required": true
    }
  ],
  "submitted_evidence": [
    {
      "type": "delivery_receipt",
      "title": "Signed delivery receipt",
      "reference": "evidence-123"
    }
  ]
}
```

## Script output

The script writes a Markdown checklist and prints a JSON summary.

Expected success output (matches what the script prints for the example input above):

```json
{
  "status": "success",
  "claim_id": "claim-001",
  "claim_type": "delivery-completed",
  "entity": "Clean Cooking Project A",
  "required": 2,
  "submitted": 1,
  "present": 1,
  "missing": 1,
  "unknown": 0,
  "output_file": "checklist.md"
}
```

## Safety and correctness rules

- Do not invent claim state, evidence, permissions, or verification results.
- Separate user-provided information from verified IXO-backed state.
- Treat this checklist as decision support, not final attestation.
- Ask for clarification when required evidence or claim type is missing.
- Do not expose private claim data, credentials, or evidence outside the requested workflow.
- Do not submit, attest, reject, or dispute a claim unless the user explicitly asks and the required governed interface is available.

The next steps add the script and the example file that SKILL.md refers to.

3. Add a helper script

Create:

skills/claim-evidence-checklist/scripts/build_checklist.py

Use this script:

#!/usr/bin/env python3
"""
Build a Markdown evidence checklist for a claim.

Usage:
  python scripts/build_checklist.py <input.json> <output.md>

Example:
  python scripts/build_checklist.py examples/claim-review-input.json checklist.md
"""

import json
import sys
from pathlib import Path
from typing import Any, Optional


def load_json(path: str) -> dict[str, Any]:
    with open(path, "r", encoding="utf-8") as file:
        return json.load(file)


def normalize_type(value: Any) -> str:
    return str(value or "").strip().lower()


def build_checklist(data: dict[str, Any]) -> tuple[str, dict[str, Any]]:
    claim_id = data.get("claim_id", "unknown")
    claim_type = data.get("claim_type", "unknown")
    entity = data.get("entity", "unknown")

    required_evidence = data.get("required_evidence", [])
    submitted_evidence = data.get("submitted_evidence", [])

    if not isinstance(required_evidence, list):
        raise ValueError("required_evidence must be a list")

    if not isinstance(submitted_evidence, list):
        raise ValueError("submitted_evidence must be a list")

    submitted_by_type: dict[str, list[dict[str, Any]]] = {}

    for item in submitted_evidence:
        if not isinstance(item, dict):
            continue

        evidence_type = normalize_type(item.get("type"))
        if not evidence_type:
            continue

        submitted_by_type.setdefault(evidence_type, []).append(item)

    rows: list[str] = []
    missing = 0
    present = 0
    unknown = 0

    for requirement in required_evidence:
        if not isinstance(requirement, dict):
            unknown += 1
            rows.append("- unknown — Invalid evidence requirement entry")
            continue

        evidence_type = normalize_type(requirement.get("type"))
        description = requirement.get("description", evidence_type or "Unnamed evidence requirement")
        is_required = requirement.get("required", True)

        matches = submitted_by_type.get(evidence_type, [])

        if matches:
            present += 1
            references = ", ".join(
                str(match.get("reference") or match.get("title") or "submitted evidence")
                for match in matches
            )
            rows.append(f"- present — {description} ({references})")
        elif is_required:
            missing += 1
            rows.append(f"- missing — {description}")
        else:
            unknown += 1
            rows.append(f"- unknown — Optional evidence not submitted: {description}")

    recommended_action = (
        "Request missing evidence before verification."
        if missing > 0
        else "Ready for reviewer or verifier assessment."
    )

    markdown = "\n".join(
        [
            f"# Evidence Checklist: {claim_id}",
            "",
            f"Claim type: {claim_type}",
            f"Entity: {entity}",
            "",
            "## Checklist",
            "",
            *rows,
            "",
            "## Summary",
            "",
            f"- Present: {present}",
            f"- Missing: {missing}",
            f"- Unknown or optional: {unknown}",
            "",
            "## Recommended next action",
            "",
            recommended_action,
            "",
        ]
    )

    summary = {
        "status": "success",
        "claim_id": claim_id,
        "claim_type": claim_type,
        "entity": entity,
        "required": len(required_evidence),
        "submitted": len(submitted_evidence),
        "present": present,
        "missing": missing,
        "unknown": unknown,
    }

    return markdown, summary


def main(
    input_file: Optional[str] = None,
    output_file: Optional[str] = None,
    **kwargs: Any,
) -> dict[str, Any]:
    if not input_file:
        raise ValueError("input_file is required")

    if not output_file:
        raise ValueError("output_file is required")

    data = load_json(input_file)
    markdown, summary = build_checklist(data)

    output_path = Path(output_file)
    output_path.write_text(markdown, encoding="utf-8")

    summary["output_file"] = str(output_path)
    return summary


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python scripts/build_checklist.py <input.json> <output.md>", file=sys.stderr)
        sys.exit(1)

    try:
        result = main(input_file=sys.argv[1], output_file=sys.argv[2])
        print(json.dumps(result, indent=2))
    except Exception as error:
        print(f"Error: {error}", file=sys.stderr)
        sys.exit(1)

4. Add example input

Create:

skills/claim-evidence-checklist/examples/claim-review-input.json

Use this example:

{
  "claim_id": "claim-001",
  "claim_type": "delivery-completed",
  "entity": "Clean Cooking Project A",
  "required_evidence": [
    {
      "type": "delivery_receipt",
      "description": "Signed delivery receipt from field partner",
      "required": true
    },
    {
      "type": "geo_photo",
      "description": "Geotagged installation photo",
      "required": true
    },
    {
      "type": "beneficiary_confirmation",
      "description": "Beneficiary confirmation record",
      "required": true
    }
  ],
  "submitted_evidence": [
    {
      "type": "delivery_receipt",
      "title": "Signed delivery receipt",
      "reference": "evidence-123"
    },
    {
      "type": "geo_photo",
      "title": "Installation photo",
      "reference": "evidence-456"
    }
  ]
}

5. Test the script

From the repository root:

cd skills/claim-evidence-checklist
python scripts/build_checklist.py examples/claim-review-input.json checklist.md

Expected terminal output:

{
  "status": "success",
  "claim_id": "claim-001",
  "claim_type": "delivery-completed",
  "entity": "Clean Cooking Project A",
  "required": 3,
  "submitted": 2,
  "present": 2,
  "missing": 1,
  "unknown": 0,
  "output_file": "checklist.md"
}

Open checklist.md and check that it lists the present and missing evidence clearly.

6. Validate the skill

Return to the repository root:

cd ../..
./scripts/validate-skill.sh skills/claim-evidence-checklist

The validator checks that:

  • The skill folder exists.
  • The folder name uses the required format.
  • SKILL.md exists.
  • YAML frontmatter exists.
  • The name field exists.
  • The name field matches the folder name.
  • The description field exists.
  • The description length is within the allowed limit.

Fix any validation errors before you submit.

7. Submit the skill

Use the normal contribution flow:

git checkout -b add-claim-evidence-checklist
git add skills/claim-evidence-checklist
git commit -m "Add claim-evidence-checklist skill"
git push origin add-claim-evidence-checklist

Then open a pull request against the main repository. Review and publishing says what happens next.

Check the result

Your skill is ready to submit when all of these are true:

  • The script prints "status": "success" with the counts shown in step 5, and checklist.md exists.
  • The validator passes, or you have made the same checks by hand.
  • Everything in the Pre-submit checklist holds.

When something goes wrong

What you seeWhat it meansWhat to do
Usage: python scripts/build_checklist.py <input.json> <output.md>The script was run with fewer than two argumentsPass both the input file and the output file
Error: required_evidence must be a list or Error: submitted_evidence must be a listThe input JSON has the wrong shapeMake the field a JSON array, as in the example input
./scripts/validate-skill.sh is not foundYour checkout does not ship the validatorMake the validator's checks by hand, as in step 6
The validator reports a name errorThe folder name and the name field differ, or the name breaks the naming rulesRename the folder or the field so that they match and follow the naming rules

Writing a strong SKILL.md

SKILL.md is the agent's operating manual. A good one tells the agent exactly what to do, so it does not have to guess.

Start with the purpose

Explain the capability in one or two sentences.

Good:

This skill helps an agent prepare an evidence checklist for an IXO claim.

Weak:

This skill helps with claims.
Define when to use it

Give clear trigger conditions.

Example:

Use this skill when the user asks to review evidence, identify missing documents, or prepare a claim for verification.
List required inputs

Tell the agent what information it must collect before acting.

Example:

Required inputs:
- claim ID
- claim type
- required evidence list
- submitted evidence list
- reviewer role
Give step-by-step instructions

Avoid vague instructions. Make the workflow explicit.

Example:

1. Identify the claim type.
2. Load the required evidence list.
3. Compare submitted evidence against each requirement.
4. Mark each item present, missing, partial, or unknown.
5. Produce a summary and recommended next action.
Specify the output format

Agents are more reliable when they know exactly what to produce.

Use tables, JSON schemas, Markdown templates, or structured sections.

Document scripts

If the skill includes scripts, document each script in SKILL.md.

Include:

  • what the script does
  • how to run it
  • required inputs
  • expected outputs
  • common errors
Add safety rules

Tell the agent what not to do.

For IXO and Qi workflows, make clear that the agent must not invent verified state, bypass permissions, expose private data, or perform state-changing actions outside declared interfaces.

Script standards

Skills can include scripts for work that should be deterministic or reusable, or that is too detailed to keep in the agent's context. Every script must meet three requirements:

It runs from the command line

The agent must be able to execute it through a shell command.

python scripts/build_checklist.py input.json output.md

It exposes a returning main function

Python scripts should provide a main() function that accepts keyword arguments and returns a result (the tutorial script uses typing.Optional for path arguments).

from typing import Any, Optional

def main(
    input_file: Optional[str] = None,
    output_file: Optional[str] = None,
    **kwargs: Any,
) -> dict[str, Any]:
    return {"status": "success", "output_file": output_file or ""}

It is documented in SKILL.md

The agent should not have to read the source code to understand normal usage.

Prefer standalone scripts with no external dependencies. If a script needs dependencies, document them.

Frontmatter reference

FieldRequiredPurpose
nameYesSkill identifier. Must match the folder name.
descriptionYesShort discovery description used by agents to decide when the skill is relevant.
licenseNoLicense for the skill, such as MIT or Apache-2.0.
compatibilityNoAgent runtimes or environments the skill is designed for.
allowed-toolsNoTool access the skill expects, such as Read, Write, or Bash.
metadataNoAdditional key-value information such as author, version, or category.

Write the description with care. It is one of the first things an agent reads when it decides whether the skill applies.

IXO and Qi safety guidance

A skill shapes how an agent behaves. Write it as operational instructions, not as an open-ended prompt. For IXO and Qi workflows:

  • Do not invent state. If a claim, credential, domain or evidence record is not available, mark it as missing or unknown.
  • Respect authority. Say which actions need a user, a reviewer, an oracle, a credential or a governed workflow.
  • Separate support from decision. A skill can support review. It does not make a final attestation unless the workflow allows it. On IXO, a decision oracle produces facts and the IXO decision engine decides. See Design an evaluator.
  • Use declared interfaces. Send state-changing actions through IXO APIs, SDKs, MCP tools or protocol workflows.
  • Protect sensitive data. Keep secrets, credentials, private keys, access tokens and private evidence out of skill files.
  • Make outputs inspectable. Prefer structured output that people, agents and workflow systems can review.

Common skill patterns

Instruction-only skill

For simple behaviour that needs no script: writing style guides, review checklists, workflow instructions, question-answering procedures and data classification rules.

skills/
└── greeting-assistant/
    └── SKILL.md

Script-backed skill

For deterministic processing: generating files, transforming data, validating inputs, producing structured reports, and calculating totals or metrics.

skills/
└── report-generator/
    ├── SKILL.md
    ├── scripts/
    │   └── generate_report.py
    └── examples/
        └── input.json

Template-backed skill

For output that follows a reusable document structure: invoices, attestations, MRV reports, evidence summaries and governance briefs.

skills/
└── attestation-summary/
    ├── SKILL.md
    ├── templates/
    │   └── summary-template.md
    └── examples/
        └── example.md

IXO workflow skill

For a repeatable IXO or Qi workflow: claim intake, evidence readiness checks, handoff packets for reviewers, programme rule checklists, and support for agentic oracle reviews.

skills/
└── digital-mrv-claim-review/
    ├── SKILL.md
    ├── scripts/
    │   └── build_review_packet.py
    └── examples/
        └── mrv-claim-input.json

Pre-submit checklist

Before you open a pull request, check that:

  • The folder name matches the skill name.
  • SKILL.md exists.
  • SKILL.md has valid YAML frontmatter.
  • name and description are present.
  • The description says when the skill should be used.
  • The instructions are clear, structured and complete.
  • Scripts run from the command line.
  • Scripts have a returning main() function.
  • Scripts are documented in SKILL.md.
  • The example inputs work.
  • No secrets or credentials are included.
  • No harmful or unsafe instructions are included.
  • The local validator passes, when your repository provides scripts/validate-skill.sh.

When that script exists:

./scripts/validate-skill.sh skills/your-skill-name

Review and publishing

After you open a pull request, the skill is reviewed before it is published. Review checks:

  • correctness
  • instruction quality
  • security
  • compliance with the skill structure
  • safe script behaviour
  • clear documentation

When the skill is approved and merged, it is packaged and published. The published skill gets a content identifier, or CID, which agents can use to fetch that version of the skill. CIDs are content-addressed, so any change to the skill creates a new CID.

Next steps