kortecxdocs
Tools

Add skills

A skill teaches an App how you want a job done: plain instructions plus a wish list of tools. It never grants any power of its own.

A skill is how you write down the way you want a job done. It is a small folder with two things in it: instructions in plain prose ("read the inbox, classify what needs attention, draft replies, never send"), and a wish list of the tools that job expects to use.

You attach a skill to an App. The agent then reads your instructions as part of its own briefing, and the wished tools show up in its tool menu — if, and only if, you already had them.

That last clause is the whole design. A skill teaches behaviour. It never grants power.

A skill grants nothing

When you run an App that carries a skill, Kortecx works out what the agent actually gets by intersecting the wish with everything else that has to be true:

granted = wish ∩ your grants ∩ callable on your runtime right now
  • wish — what the skill's manifest asked for.
  • your grants — the tool authority the calling identity already holds. A skill cannot widen it.
  • callable right now — tools this runtime can genuinely call at this moment: registered in its tool registry, and for a connector, an actual reachable connection.

A wish that survives all three folds into the App's entry agentic step — the first model step of its workflow file — which is the same step your instructions bind to, so the agent always sees the instructions and the tools together.

A wish that does not survive is dropped with a warning, and the run continues with whatever could be granted. Nothing bricks. You get an honest, smaller agent rather than a failed launch.

The line that matters

If the intersection is empty, the step stays a plain transform with no tools at all. A skill on its own can never mint authority. The runtime enforces exactly that: when nothing is callable, nothing is granted, no matter what the manifest wished for.

Worked through

Say you write a skill that wishes for gmail/search, gmail/read and gmail/draft, and you attach it to an App.

  • You have a working Gmail connection and the grants for all three → the agent can search, read and draft.
  • You have the connection but the runtime cannot dial it right now → all three drop with a warning. The run proceeds; the agent has your instructions and no Gmail tools, and should say so rather than invent results.
  • Someone else runs the same App → the run uses their own skill of that name, or is refused if they have none, and gets only the tools they hold. Your skill lends them nothing.

Note what is not in that wish list: gmail/send. The reference pack deliberately never wishes for an irreversible send. The wish set is the hard line; the instructions are the soft one. If you want a human in front of an irreversible action anyway, see Approvals.

What a skill pack looks like

skills
email-triage
skill.json
instructions.md
README.md

skill.json is the kortecx.skill/v1 manifest. instructions.md is the know-how — the part that actually teaches. README.md is optional human docs. The folder name must equal the manifest name; the loader refuses a mismatch. A pack is exactly those three flat files — a fourth file, or a subdirectory, fails validation.

skills/email-triage/skill.json
{
  "schema": "kortecx.skill/v1",
  "name": "email-triage",
  "version": "1",
  "description": "Triage a Gmail inbox: search for actionable mail, read what matters, and DRAFT replies for human review — this skill deliberately never wishes for send.",
  "tags": ["email", "gmail", "triage"],
  "tools": {
    "gmail/draft": "1",
    "gmail/read": "1",
    "gmail/search": "1"
  }
}

Every key in tools is a tool id at a version. A bare id is a capability the runtime bundles (retrieve on a self-run runtime; the managed cloud bundles no retrieve, so a wish for it is dropped there); a namespaced id like gmail/search is <connection-name>/<tool> from a connection.

The instructions are ordinary markdown. Here is the opening of the reference pack:

skills/email-triage/instructions.md
# Email triage

You triage the user's Gmail inbox. Your goal: surface what needs attention and
prepare replies — a human always reviews before anything is sent.

## Procedure

1. **Search narrowly.** Use `gmail/search` with a focused query …
2. **Read before judging.** For each candidate, use `gmail/read` …
3. **Draft, never send.** …

Managing the catalog

You can see the skills in your catalog in the console, and which App carries which.

Once an App that carries it has run, the console shows the part a skill is judged on: every call the agent made or had refused.

What validation refuses

A skill manifest is checked fail-closed — if a check cannot be satisfied, the manifest is rejected rather than accepted with the doubtful part ignored.

No authority may appear in a manifest, anywhere

Any object key that names authority — grant, secret, credential, executable and their kin — is refused at any depth in the manifest. toolGrants, client_secret, awsCredentials all fail the same way. A skill wishes; the runtime grants. There is no key you can add to change that.

The other refusals:

CheckWhat is refused
schemaAnything other than kortecx.skill/v1 — readers fail closed on a mismatch.
Unknown fieldsThe shape is closed. A manifest cannot smuggle a new rail past validation.
NumbersAny non-integer number, anywhere in the tree.
nameNot 1–64 characters of [a-z0-9._-]; or not equal to the pack folder name.
Tool idsNot one or two segments of [a-z0-9._-] — a/b/c, Gmail/Search, /x all fail.
VersionsNot an integer string — "latest" fails.
SizeA manifest over 64 KiB, or instructions over 256 KiB.
instructions.mdMissing or empty. The instructions are the skill's semantic core.
instructions_refPresent in a pack manifest. The runtime derives it when it stores the body; you never write it by hand.

A few wishes can also be dropped at run time for reasons that are not about authority at all: a tool with no definition in the runtime's registry, a tool whose isolation requirements cannot be reconciled with the others on that step, a tool whose filesystem scope is incomparable with the other tools on that step, and anything past the cap of 16 tools on one folded contract. Tools declared directly in the workflow file always win over a skill's wish for the same id, and are never evicted by one.

If a wish is `retrieve`

Retrieval reads from a dataset, and a dataset has to be indexed before it can be searched. See Datasets. Without one, a retrieve wish has nothing to read even when it is granted. On the managed cloud there is no retrieve tool, so the wish is dropped.

What a skill is not

  • Not code. A manifest carries markdown and a map of tool ids. The executable part is always a separate out-of-process connection or a capability the runtime bundles.
  • Not a credential. Secrets live in Secrets and connections in Connections. A skill only ever names a tool.
  • Not a workflow file. It attaches to an App you already have; it does not define the graph. For the graph, see Workflows.