Chapter 1 of 8 · 11 min read
Prompt, skill, tool, server, sub-agent: what each hands the model
On this page
The release-notes agent had been shipping for six weeks and getting the format wrong on roughly a third of runs. The company's internal format is not complicated: four sections in a fixed order (Breaking, Added, Fixed, Internal), a table with a ticket-ID column next to every line item, and no marketing adjectives, so "significantly improved" gets rewritten as "reduced p95 by 40 ms" or dropped. The agent produced good prose and then put Fixed before Added, or omitted the ticket-ID column entirely, or described a cache change as "a major step forward for our platform".
Three proposals landed on the thread within a day. One engineer wanted a tool, format_release_notes(text: str) -> str, that would take the model's draft and reshape it in Python. Another wanted a skill: a file containing the section order, the column spec, the adjective ban, and one worked example of a correct release note. A staff engineer wanted an MCP server exposing format_release_notes, on the grounds that three other agents would eventually need the same formatting and a server is how you share things. All three would have produced correctly formatted release notes in a demo. They differ enormously in what they cost to run for a year, and the way to see that is to ask what kind of gap the request actually is before asking which mechanism to reach for.
Five ways to give a model something it did not ship with
Those three proposals are drawn from a list of five, and the list is the whole vocabulary of this topic. A language model as it arrives can produce text and do nothing else. It knows a great deal about the world in general, nothing whatsoever about your company's conventions, and it has no way to read your database, open a ticket or find out what deployed last night. Everything a team builds to close that distance is an extensibility mechanism: a way of handing the model, at run time, either information it was not trained on or the ability to make something happen outside its own reply. Five of them are in general use, and an argument like the one on that thread is what it looks like when three of the five are proposed for the same requirement and nobody has said out loud what each one is.
The system prompt is the block of standing instructions that sits above the conversation, ahead of the user's first message, and is re-read by the model on every turn. It exists because the model retains nothing between sessions and has no way to learn your organisation, so whatever it must always obey has to be supplied again each time it runs; you meet it as a CLAUDE.md or AGENTS.md file in a repository, or as the "custom instructions" box in a product. A skill holds the same kind of text in a separate file with a short description attached, and the runtime keeps only that description in view, reading the body into the conversation when a request appears to match it. Skills exist because a prompt containing every procedure the agent might ever need grows long enough to be expensive and to dilute its own instructions; in practice a skill is a markdown file in a directory the agent scans at startup.
A tool is a function in your own codebase that you have described to the model in a schema — a name, a sentence of purpose, and typed parameters — so that the model, in place of writing an answer, may emit a request to call it. Your runtime executes the function and feeds the return value back into the conversation as text. Tools exist because generated text cannot by itself query a table, charge a card or create a Jira issue, and you meet them as functions registered with the agent when the process starts. An MCP server is that same arrangement with the function body moved out of your process into one that somebody deploys separately, reachable over the Model Context Protocol, the open convention for describing and calling tools across a wire; servers exist so that several different agents, possibly written by different teams in different languages, can call one implementation, and so that a credential can live somewhere the calling agent does not. You meet a server as a few lines of configuration naming a URL and a token. 6.16 already worked through the cases where that move is the wrong one.
A sub-agent is a second model loop that the first one can start: a fresh conversation with its own instructions, its own restricted list of tools, and a single result handed back to its caller when it finishes. Sub-agents exist because some tasks take twenty turns of searching to establish one fact, and those twenty turns would otherwise accumulate in the main conversation and crowd out whatever the user was originally asking about. You meet one as a named agent definition that the parent invokes in the same way it invokes a tool.
Naming the five does not settle the release-notes thread, because each proposal names a real mechanism and each is coherent about its own implementation. What separates them is which kind of gap the release-notes format actually is, and that turns out to be a question about the requirement rather than about the five.
Three gaps, and the test for each
Every capability request an agent team receives is one of three things, and the categories are distinguished by questions you can answer without knowing anything about the implementation.
The first is a knowledge gap: the model would produce the right output if it were carrying some information it currently is not. The test is whether anything has to leave the process. If the correct answer is fully determined by facts, conventions, formats or procedures that could be written down, and no network call, no disk read, no clock and no system of record is involved, it is a knowledge gap. The release-notes format is one. Nobody needs to look anything up to know that Breaking comes first.
The second is an action gap: something outside the process must happen, or must be read at the moment of asking. Creating a Jira ticket, querying the pricing table, reading yesterday's deploy log, sending the email. The test is whether the answer depends on state the model cannot carry because it changes without the model's involvement, or whether the point of the operation is its side effect. Actions are where determinism genuinely lives, because the body of the operation is code you wrote.
The third is a delegation gap: the work is a multi-turn sub-task whose intermediate steps would crowd out everything else in the conversation. Digging through six months of logs to find when a config value changed is not one action; it is twenty tool calls, most of which produce output nobody will ever need again. The test is whether the intermediate results have value after the sub-task finishes. If they do not, and there are many of them, delegation is on the table.
The five mechanisms map onto these three gaps unevenly, which is the first useful thing the classification buys you.
| Gap | Mechanisms that address it | Mechanisms that cannot |
|---|---|---|
| Knowledge | System prompt, skill | Tool, server, sub-agent |
| Action | Tool, MCP server | Prompt, skill |
| Delegation | Sub-agent | Everything else |
Classifying does not choose for you. It eliminates. Deciding that the release-notes format is a knowledge gap rules out three of the five candidates immediately, including two of the three that were actually proposed, and leaves a real question — prompt or skill — that the next chapter takes up.
What each mechanism physically puts in front of the model
The five mechanisms are often drawn as five rows of one table, which suggests they are five variations on a theme. They are not. Two of them add text to the model's context, two of them add a callable interface plus a schema describing it, and one of them starts a second model loop. Looking at the artefacts makes the difference concrete.
A system prompt entry is text that is present on every turn:
# Release notes
Sections in this order: Breaking, Added, Fixed, Internal.
Every line item is a table row with a TICKET-ID column.
No marketing adjectives. Replace them with the measured change or drop them.A skill is the same kind of text with a description attached, and the description is what the runtime matches against the request to decide whether to load the body:
---
name: release-notes-format
description: The company's release-notes format. Use when writing, editing or
reviewing release notes, changelogs or version announcements.
---
Sections in this order: Breaking, Added, Fixed, Internal.
...A tool is a schema the model sees plus a body it does not:
FORMAT_TOOL = {
"name": "format_release_notes",
"description": "Reformat draft release notes into the company format.",
"input_schema": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
}
def format_release_notes(text: str) -> str:
sections = parse_sections(text)
ordered = [sections.get(k, "") for k in ("Breaking", "Added", "Fixed", "Internal")]
return render_table(ordered, strip_adjectives=True)An MCP server is that same schema, moved: the model still sees a name, a description and an input schema, and the body now lives in a process someone deploys, behind a transport and an authentication story. What the agent host holds is configuration.
{
"mcpServers": {
"release-tooling": {
"url": "https://release-tooling.internal.example.com/mcp",
"headers": { "Authorization": "Bearer ${RELEASE_TOOLING_TOKEN}" }
}
}
}A sub-agent is a second loop with its own context window, its own tool list and its own turn budget, invoked by the parent and returning a single result.
---
name: log-triage
description: Search historical logs to find when a specific value changed.
tools: [log_search, log_fetch]
---
You are given a config key and a date range. Find the deploy that changed it.
Return the deploy ID, the timestamp and the old and new values. Nothing else.Set side by side, the row that matters is not what each one can do but what each one costs to have. A prompt costs input tokens on every turn forever. A skill costs a description on every turn and its body only when it fires. A tool costs a schema on every turn and a function call when invoked. A server costs the same schema plus a deployment, a credential and a network hop. A sub-agent costs a fresh context, re-serialized from nothing, plus a whole second conversation.
The tool is deterministic; calling it is not
The strongest-sounding argument on that PR thread was for the tool: code runs the same way every time, and a prompt does not, so put the format rules in Python where they can be tested. It is the argument that feels like the responsible engineering choice, and it misplaces the variance rather than removing it.
The body of format_release_notes is deterministic. Given the same draft it produces the same output, and you can unit-test it against fixtures. Whether the model calls it is a decision made by the model on each run, which is exactly as probabilistic as the decision to follow a formatting rule in a prompt. We ran the tool version against 200 recorded release-note requests drawn from the last two quarters of merges. The model invoked format_release_notes on 163 of them. On the remaining 37 it wrote the release notes directly and confidently, and those 37 outputs had the same defect distribution as the pre-tool baseline: wrong section order on 14, missing ticket-ID column on 11, at least one banned adjective on 19.
There is a second failure the fixture tests cannot see. On 9 of the 163 successful invocations the model called the tool, received correctly formatted text, and then paraphrased it into the response rather than passing it through — reordering the table, in three cases, back into the order it preferred. A tool return value is not the agent's output; it is more text in the context, which the model then writes around. So the measured end-to-end correctness of the tool implementation was 154 of 200, or 77%, against a deterministic function with 100% unit-test coverage.
The honest way to state this is that a tool converts a content question into a routing question. That trade is worth making when the content question is genuinely hard for a model and the routing question is easy — arithmetic over a large table, a cryptographic signature, an idempotent write — because those are cases where the model has no way to be right by itself and a strong signal telling it to call. It is a poor trade when the content question was "follow these four rules" and you have introduced an invocation decision that did not previously exist.
What classification does not settle
Naming the gap does not tell you which mechanism inside the category to use, and the categories with two members are the ones where most real arguments happen. Knowledge splits into prompt and skill along an axis of residency and routing. Action splits into tool and server along an axis of ownership and operation. Both of those splits have their own chapter, because both turn on properties that have nothing to do with what the capability does.
Classification also says nothing about difficulty or importance. A knowledge gap can be the hardest problem on the roadmap — the release-notes format is trivial, but "how our company decides whether a change is breaking" is a genuine piece of institutional knowledge that takes several attempts to write down well. Teams sometimes reason from "this matters a lot" to "this deserves a real service", and those properties are unrelated. The mechanism follows the gap, and the effort follows the difficulty, separately.
And the classification is a statement about the requirement as posed, which can be wrong. A request that looks like a knowledge gap is an action gap if the knowledge changes underneath you: if the release-notes format were owned by a docs team that revised it monthly and published it at a URL, then the correct answer at generation time is not something the model can carry, and fetching it becomes a real action. What separates those two worlds is change rate, and 6.16 already made change rate one of the axes that decides where a boundary belongs. The point here is only that the same axis can move a requirement across gap categories, not merely across implementations of one.
Having ruled the server and the tool out of the release-notes decision, what remains is the choice between writing the format into the system prompt and putting it in a skill. Those are the two cheapest mechanisms available and they are usually treated as interchangeable, which is where the next chapter starts: one of them is paid for on every turn of every session whether or not the task involves release notes, and the other is free until something decides it is relevant, at a price paid in whether that decision is correct.
The extensibility landscape — read the current version at https://daviddominguez.dev/learn/en/extensibility-landscape/prompt-skill-tool-server-sub-agent-what-each-hands-the-model