Writing · Updated
Agent skills and MCP servers: what each one is for
I’m Abdul Moiz Shahzad. I maintain a package manager for agent skills, and at Aide I shipped our MCP server and the CLI beside it, so I have watched teams reach for one of these when they needed the other. A skill tells an agent how to do something with the capabilities it already has. An MCP server gives it a capability it did not have. Confusing the two produces tools nobody calls and instructions nobody can execute, and this piece is about telling them apart before you build.
Two things that look alike from the outside
From the outside both are a way to make an agent better at a job. You add one, the agent does something it did not do before, and the difference between a folder of Markdown and a running server is invisible to the person typing the request. That surface similarity is why the confusion is so common and so expensive. I have seen a team ship an MCP tool called something like write_launch_post whose entire implementation was a prompt, and I have seen a team write a two-page skill that told the agent to check a database it had no way to reach. Both worked in the demo. Neither worked on the second day.
The categories are cleaner than the products built on them. A skill is knowledge: it changes what the model knows about how to proceed. An MCP server is a capability: it changes what the model can cause to happen in the world. The rest of this piece is the mechanics behind that sentence, the ways each fails when asked to be the other, and a short procedure for deciding which one a given problem needs. I am going to be concrete about how each thing works, because the design mistakes come from treating them as abstractions.
What a skill is, mechanically
A skill is a folder with a SKILL.md file. The file starts with frontmatter, a name and a description, and the description is the trigger: at the start of a session the agent loads only the name and description of every installed skill, a few dozen tokens each, and decides from the description whether a skill applies to the task in front of it. When one does, it reads the body, which is ordinary Markdown instructions, and only then reads any reference files or runs any scripts the body points at. Anthropic calls this progressive disclosure, and it is the whole economic argument for skills: a hundred installed skills cost the model almost nothing until one is needed.
Everything a skill does, it does by being read. It cannot reach a system the agent cannot already reach, and it cannot force the agent to do anything; it can only make the right behaviour the obvious behaviour. Scripts change that slightly, since a skill can bundle a Python or shell script the agent runs, which makes the deterministic parts of a procedure deterministic. But the script runs with the agent’s own hands, on the agent’s own machine, with the agent’s own credentials. A skill has no existence apart from the runtime that reads it.
That is also what makes skills portable. The same folder works in Claude Code, Cursor, Codex, Copilot, Gemini CLI, and most of the others, because every one of them can read Markdown and run a script. Portability is a property of having no moving parts, and the installer I maintain exists because the one moving part, which directory each runtime reads, is annoying enough to want a tool for. I wrote up what that tool has to get right separately.
What an MCP server is, mechanically
The Model Context Protocol is a wire protocol. A server is a process, local over standard input and output or remote over HTTP, that speaks JSON-RPC and exposes three kinds of things: tools the model can call, resources it can read, and prompts it can pull in. Anthropic published it in November 2024, and the clients followed: an agent connects to the server, asks it to list its tools, and receives each tool’s name, description, and a JSON Schema for its arguments. Those definitions go into the model’s context. When the model decides to call one, the client sends the arguments to the server, the server runs real code, and the result comes back as the tool’s output.
Two consequences fall out of that design. First, the server owns execution. Its code runs on whatever machine it runs on, with whatever credentials it was given, behind whatever authentication the operator put in front of it, and the model never touches the underlying system directly. That is the boundary that makes MCP the right tool when the capability is dangerous, shared, or audited: the server can validate arguments, enforce permissions, and log every call, and none of that depends on the model behaving. Second, tool definitions are not free. Every tool the server exposes sits in the context on every request, whether or not it is used, and a client connected to several servers can spend a meaningful slice of its context window on tool schemas before the conversation starts. The exact fraction depends on the servers; the direction does not.
An MCP server is also software you operate. It has a version, a deployment, an authentication story, and users who will be confused when a tool’s schema changes underneath them. At Aide the server and the CLI shipped together for that reason: the same operations were reachable by a person from a shell and by a model from the protocol, and the server was the part that needed to be treated like production infrastructure because it was.
The dividing line: capability versus method
The question that settles most cases is whether the agent already can do the thing. If the capability exists and the problem is that the agent does it badly, inconsistently, or in the wrong order, the fix is knowledge, and knowledge is a skill. If the capability does not exist, no amount of instruction will conjure it, and the fix is a tool. A skill for reviewing pull requests makes sense because the agent can already read a diff and leave a comment; a skill for querying your production database makes no sense unless the agent already has a way in, and the way in is the tool.
A second question is how often the underlying facts change. A skill is text that was true when it was written. A house style, a release checklist, a way of structuring a test suite: these hold for months, and encoding them as instructions is right. A customer’s current plan, the state of a deployment, the contents of a ticket queue: these are different every time, and encoding them as anything other than a live call is a bug waiting for its first stale read. Live data wants a tool. Stable method wants a skill.
The third question is who has to be able to trust the outcome. An instruction the model may or may not follow is fine for a formatting preference and unacceptable for a deletion. When the consequence of the model ignoring the guidance is expensive or irreversible, the guidance has to become enforcement, and enforcement lives in code on the far side of a tool call. The distinction I use in every review is judgement versus capability, with a tie-breaker: anything that must happen the same way every time, or must not happen without a check, belongs behind a tool.
When a skill is asked to be a tool
The failure looks like this. A team needs the agent to file incidents in their tracker. Nobody wants to run a server, so they write a skill: here is the tracker URL, here is the API shape, here is the token in an environment variable, please file the incident. It works when the engineer who wrote it is watching. It fails the first time the agent runs in an environment without the variable, and it fails silently the first time the API changes, because a skill has no schema to reject a bad call and no server to return a useful error. The agent improvises a request body, gets a 400, and reports success or tries something creative. Neither is what anyone wanted.
The deeper problem is enforcement. Every safeguard in a skill is a sentence the model is asked to honour, and a sentence is not a permission check. If the skill says never file an incident against the production project without confirmation, the guarantee is exactly as strong as the model’s attention on a long day. A tool can refuse. A skill can only advise. Teams that put access behind advice discover the difference at the worst possible time, and the fix is always the same: move the capability behind a tool with a schema, and keep the skill for the part that was really method, which is when to file, what to include, and how to write the summary.
There is a legitimate middle ground, and it is the script. A skill that bundles a script turns a fuzzy instruction into a deterministic step, and for a local operation with no shared state, such as formatting files or running a check, that is the right weight of tool. The line I draw: a script inside a skill is fine when it runs with the user’s own hands on the user’s own machine and touches nothing that other people share. The moment a credential, a shared system, or an irreversible action enters, it is a server.
When a tool is asked to be a skill
The opposite failure is quieter and more common in teams that already run MCP servers. Having a server, they add tools for everything, including things that are method rather than capability: write_release_notes, summarize_thread, plan_migration. Each one is a prompt wrapped in a schema. The server receives some arguments, sends them to a model with a template, and returns the text. Nothing about that needed a server. The template would have been a skill, loaded only when relevant, editable in a pull request, readable by the agent in full rather than through an argument list.
The cost shows up in two places. The first is context: every one of those pseudo-tools occupies schema space on every request, so a server with forty tools makes every conversation start heavier, and the model’s tool selection gets worse as the list grows, because forty similar names are harder to choose between than five distinct ones. The second is lifecycle: a tool has a version and a deployment, and changing how release notes should read now requires a server release, a schema change, and a client that picks it up, for what should have been an edit to a Markdown file.
The tell is easy to spot in a tool list. A real tool’s output is something the model could not have produced itself: a row from a database, a file from a system it cannot read, the result of an action in the world. A pseudo-tool’s output is text the model could have written given the same instructions. If you can replace the tool call with a paragraph and lose nothing, it was a skill wearing a schema.
The CLI in the middle
There is a third thing, and for coding agents it is often the right answer to both questions: a command-line tool with JSON output. Coding agents can run shells. A CLI gives them a capability with a real implementation behind it, without a protocol server to operate, and a skill can teach the agent how to drive the CLI well. The capability lives in the binary, the method lives in the Markdown, and neither is pretending to be the other.
I have built this shape twice. marketing-cliships 76 skills and a 24-command CLI in one package; the CLI is the agent’s surface, JSON by default, and the skills are the playbook that says which command to run when and what to do with the result. When the playbook needs a capability the package does not own, such as encoding a video or scraping a page, it chains in an existing tool, ffmpeg or the Firecrawl CLI, and ships a skill beside it that documents how to use that tool safely. The installer I maintain works the same way from the other side: its README carries a protocol document an agent can read, and the agent then drives the CLI with npx, which means the skill-shaped instructions and the tool-shaped commands are the same product seen from two directions.
The CLI loses to MCP in exactly the cases where MCP earns its complexity: when the capability must be shared across machines, when the credential cannot live on the agent’s host, when the call must be authenticated and audited centrally, or when the client is not a coding agent with a shell at all. For a single engineer’s machine, a CLI plus a skill is lighter and just as capable. For a company’s systems, the server is the right answer, and the CLI beside it is how a person reaches the same operations without a model in the loop.
Using both without doubling the surface
Most production agents end up with both, and the design question becomes how to keep them from overlapping. The pattern that has held up for me: tools are nouns and verbs about the world, skills are procedures about the work. The MCP server exposes the smallest set of capabilities that the skills need, each with a strict schema and a clear error, and the skills carry everything about when to use them, in what order, with what checks, and how to present the result. A skill that names the tools it depends on is self-documenting; a tool that embeds a procedure is a maintenance trap.
Two practical rules follow. Keep the tool count small and the tool names distinct, because the model chooses among them on every turn and a crowded list degrades every choice. And keep the method out of the server: if you find yourself writing a tool description that explains a workflow, the workflow is a skill and the description should say only what the tool does and what it returns. The server team and the skill authors can then move at different speeds, which they will want to, because capabilities change rarely and methods change every week.
The failure to watch for once both exist is drift between them: a skill that references a tool argument that no longer exists, or a tool whose behaviour changed while the skill still describes the old one. Nothing in either format detects that automatically. The only defence I know is the boring one, which is a test that loads the skills, lists the tools, and checks that every tool a skill names is real and takes the arguments the skill says it takes.
Five questions before you build either
In the order I ask them:
- Can the agent already do this? If yes, you are writing a skill.
- Does the answer change between sessions? Live data is a tool; stable method is a skill.
- What happens if the model ignores the guidance? If the answer is expensive or irreversible, it needs enforcement, and enforcement is a tool.
- Does it need a credential, a shared system, or an audit trail? Then it is a server, and a script inside a skill is not enough.
- Could the output have been written by the model given the same instructions? Then it is a skill, whatever schema you were about to wrap it in.
When the answers split, which they often do, the split is the design: the capability goes behind the tool, the method goes into the skill, and the skill names the tool it needs.
Where this leaves the builder
The two formats are young and both will change. Skills will grow some way of declaring the tools they depend on, and MCP will grow better ways of loading tool definitions lazily, and the day both happen the line between them gets easier to hold rather than harder, because each will be better at its own job. Until then the discipline is manual: ask whether you are adding knowledge or capability, put each where it belongs, and refuse to let a prompt call itself a tool.
The thing I keep coming back to is that the model is the one component in the system that cannot be made reliable by instruction, and everything else can. Skills are how you make the most of that unreliable component. Tools are how you stop it from mattering where it must not. Build the second before you rely on the first.