# Claude Code skills, MCP and CLAUDE.md answer different questions

> Most Claude Code skills advice is a list of skills to install. The useful question is where a piece of know-how should live: CLAUDE.md, a skill, an MCP server or a hook.

Published 2026-10-28 by Nick Sawinyh.
Tags: ai, agents, engineering.
Canonical: https://sawinyh.com/blog/claude-code-skills-vs-mcp-vs-claude-md/

---

**TL;DR** Claude Code skills, MCP servers, CLAUDE.md and hooks are not four versions of one thing. CLAUDE.md is context loaded every session, a skill is know-how loaded when the task matches its description, an MCP server is access to a system, and a hook is enforcement the model cannot argue with. Most mistakes come from putting something in the wrong one: a procedure in CLAUDE.md, an API integration in a skill, a must-hold rule in either. The description line decides whether a skill ever fires, so it deserves more care than the body. My own setup shows each case: an X research skill paired with a hook, an article skill with a gate script inside it, a Search Console MCP server that had access but not permission, and a skill that fired on the wrong request. The honest limit is that my skills sit in a gitignored directory and exist on one machine.

A skill is the most flexible tool Claude Code gives you, and that flexibility is why people put the wrong things in it.

Search for [claude code skills](https://code.claude.com/docs/en/skills) and the results are Reddit threads, awesome-lists and GitHub collections of skills to install. They answer "which skills should I have." The question that decides whether your setup works is the one underneath: for a given piece of know-how, which of four places should it live in?

The [hub post](https://sawinyh.com/blog/claude-code-best-practices-harness-not-prompt/) argues that Claude Code best practices are not advice, and that the ones that survive move out of the prompt into harness layers. [Hooks](https://sawinyh.com/blog/claude-code-hooks-enforce-not-advise/) are the layer that enforces. This post is about the layer that teaches, and about the three neighbours it keeps getting confused with.

## Four places, four jobs

Anthropic's own [extension overview](https://code.claude.com/docs/en/features-overview) states the split in one sentence: CLAUDE.md handles always-on context, skills handle on-demand knowledge and workflows, MCP handles external connections, and hooks handle automation. The part worth drawing out is what each one is bad at.

| | CLAUDE.md | Skill | MCP server | Hook |
|---|---|---|---|---|
| Job | Remember: facts true every session | Teach: know-how for a matching task | Connect: access to a system | Enforce: a check the model cannot argue with |
| Loads | Full content, every session | Description every session, body when used | Tool names at start, schemas on demand | Nothing, runs outside the conversation |
| Who decides it applies | Always applies | The model, from the description (or you, with a slash command) | The model, when it needs the tool | The harness, on the event |
| Can it guarantee anything | No, it is context | No, the model interprets it | Only what the server permits | Yes |
| Fails when | It grows past what is read, or holds procedures | The description is vague, or it duplicates an integration | The scope is narrower than the task | It fires on things that do not matter |

The "can it guarantee anything" row says only one of these four has a guarantee. The "loads" row says why the other three differ in cost, and the cost is what pushes content from one into another.

The documentation is blunt about the first. CLAUDE.md content is [treated as context, not enforced configuration](https://code.claude.com/docs/en/memory), and for anything that must hold, it points to a PreToolUse hook. The same page recommends targeting under 200 lines per file and says an entry that is "a multi-step procedure or only matters for one part of the codebase" belongs in a skill.

Here is the whole decision as a sequence of questions, asked in order.

<div style="display:flex;justify-content:center;margin:1.5em 0;"><pre style="background:#24292e;color:#e1e4e8;padding:0.9rem 1.2rem;border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:0.84em;line-height:1.45;margin:0;width:max-content;max-width:100%;white-space:pre;word-wrap:normal;box-sizing:border-box;overflow-x:auto;">  Must it hold even if the model
  decides otherwise?
        │
   yes ─┴─ no
    │      │
    ▼      ▼
  HOOK   Does it need a system the
         model cannot reach today?
              │
         yes ─┴─ no
          │      │
          ▼      ▼
        MCP    Is it needed every session?
        (+ a      │
        skill) yes ─┴─ no
                │      │
                ▼      ▼
            CLAUDE.md  SKILL
&nbsp;
   each answer to "no" hands the
   content to a softer layer</pre></div>

Read it top to bottom and the order is a statement of priorities. Enforcement comes first because it is the only layer that does not depend on the model agreeing. The skill is last because it is the default home for anything that is neither a guarantee, nor access, nor universal.

## A skill is a description plus a body

The format is smaller than the ecosystem around it suggests. A skill is a directory with a `SKILL.md` file: YAML frontmatter, then Markdown instructions. Per the [skills documentation](https://code.claude.com/docs/en/skills), no frontmatter field is required. Only `description` is recommended, `name` defaults to the directory name, and unknown fields are ignored without an error.

Where it lives decides who gets it:

| Location | Path | Applies to |
|---|---|---|
| Personal | `~/.claude/skills/<name>/SKILL.md` | All your projects on this machine |
| Project | `.claude/skills/<name>/SKILL.md` | Sessions in that repository |
| Nested | `<subdir>/.claude/skills/<name>/SKILL.md` | Sessions started in or below that directory |
| Plugin | `<plugin>/skills/<name>/SKILL.md` | Namespaced as `plugin-name:skill-name` |

If the same name exists at several levels, enterprise wins over personal and personal wins over project. Claude Code also watches the skill directories, so adding or editing a skill takes effect in the current session without a restart.

The loading model is the part that explains everything else. In a regular session, skill descriptions are loaded into context so Claude knows what exists, and the full content loads only when the skill is invoked, either by you typing `/name` or by Claude matching the description to your task. Once invoked, the rendered content enters the conversation as a single message and stays there. That is why a skill costs little until used, and why the description is the only part of it that is always present.

Frontmatter also changes who triggers it. `disable-model-invocation: true` keeps Claude from loading the skill on its own and keeps its description out of context, which the documentation recommends for skills with side effects. `user-invocable: false` does the opposite: Claude can load it, but it is hidden from the slash menu.

## The description decides whether anything happens

The body of a skill is what you write carefully. The description is what determines whether the body is ever read.

The documentation says Claude matches your task against skill descriptions, and that if they are vague or overlap, Claude may load the wrong skill or miss one that would help. The `description` and `when_to_use` text is truncated at 1,536 characters in the listing, so the trigger belongs at the front.

A practitioner thread that circulated in September put the same point plainly. [@claudeskills101](https://x.com/claudeskills101/status/2103862178130657609) described the trigger as "a two line description that tells Claude when to use it, which is the whole magic of the system," and advised naming the exact task it should fire on instead of something vague, then testing by asking for the task without mentioning the skill. That test is the right one. A skill you only ever invoke by name has not been tested as a skill.

Descriptions that work do two things in two sentences: say what the skill does, then say when to use it, in the words a request would actually contain. Here is the one from my article skill.

```yaml
---
name: sawinyh-article-prep
description: Draft, gate, and ship a blog post for sawinyh.com (Astro content collection). Use when writing a new post, turning research or notes into an article, or checking a draft against house style.
---
```

It names the site, so it will not fire on someone else's blog post. It lists the phrasings a request would use: writing a post, turning notes into an article, checking a draft. And the x-research skill opens with its trigger: "Use whenever content must be pulled from X/Twitter for any reason." That is a description written for the request, not for a catalogue.

The failure runs the other way too. A description that is too broad fires on requests it should not touch, and that is what happened to me.

## The Codex misfire was a description doing its job

When I asked for a "Codex review," the agent kept reaching for a Codex plugin skill. That skill routes through a forwarder subagent and a companion script. I meant the `codex` CLI, called directly.

The plugin skill's listed description is "Delegate investigation, an explicit fix request, or follow-up rescue work to the Codex rescue subagent." Nothing in that is wrong. It names Codex and it names delegation, and my request named Codex and asked for a second opinion. The match was reasonable. It was also the wrong tool, and a better-written description on my side would not have helped, because the description was not mine.

The fix I made was a rule in my global CLAUDE.md saying that Codex means the CLI. That is advice, the thing this series argues against. It works because the situation is narrow and the wording is unambiguous, but it is still context the model can talk past.

The harness has tighter tools for skills you did not write, but they did not reach this one. The documentation describes `skillOverrides` in settings for hiding a skill without editing its file, and it also says plugin skills are not affected by it. This was a plugin skill. That left disabling the whole plugin, which I did not want, or a rule, and I chose the rule. What I can say is that a skill that triggers on the wrong request is a description problem, and the fix belongs next to the description, not in a sentence somewhere else that asks the model to remember.

A related failure: a guessed `--model` flag on the `codex` command failed outright with a 400, because that model was not available on the account type. That one has nothing to do with skills. It is here because it is the same shape: a confident guess about a system's contents, with the system answering no.

## MCP is access, and access is not know-how

The Search Console server I use is a clean example of the other boundary. It gives Claude access to queries, URL inspection and sitemaps. What it does not give is any sense of what to do with them, and as it turned out, not full permission either.

Its OAuth scope is read-only. `sitemaps_submit` returns "Permission denied" even though the account is the site owner. The account has the rights. The token does not. No amount of instruction in a skill or in CLAUDE.md changes that, because the limit is in what the server was granted, and it is exactly the kind of fact that a connection layer owns.

The note that the Google sitemap submission is blocked, and that the next session should not retry the call expecting a different result, lives in a memory file, not in a skill. A skill could carry it too. The point is that the server cannot: access says what is possible, and something else has to say what is worth trying.

This is Anthropic's own framing of the pairing: MCP gives Claude purpose-built tools for an external system, with the connection and authentication handled by the server, and a skill gives Claude knowledge about how to use those tools effectively, such as your schema and query patterns. If you find yourself writing a skill that describes how to call an HTTP API by hand, that is an MCP server or a CLI wearing a skill's clothes. If you find yourself writing an MCP server to hold your team's conventions, it is the reverse.

[@akshay_pachaar](https://x.com/akshay_pachaar/status/2085791632457433119) drew the same line in August when writing about a cross-vendor plugin package format: a skill is a folder of written instructions the agent reads when it needs them, and an MCP server is a small program that connects the agent to a tool, an API or a database. The two are packaged side by side in a plugin and fail separately, which is a reasonable summary of why you do not want to merge them.

## The pair that works: a skill and the hook that guards it

My X research setup is the case I would copy. It has no MCP server at all. Access is a CLI, `bird`, which returns a post's full text, author, timestamp and engagement counts as JSON. The know-how is a skill.

The reason it exists is that WebFetch and web search do not work on x.com: a real post URL returns HTTP 402 with no body. So the skill teaches the right path, and it encodes things that calling the CLI from memory gets wrong:

- A response that changes shape when a query paginates, so `jq 'length'` reports 2 on a result that contains dozens of posts.
- A search that is chronological only, so a bare keyword query returns noise unless you add an engagement floor.
- A hard cap on how many results a single search returns.

None of that is a fact about my project, so it does not belong in CLAUDE.md. None of it is access, so it does not belong in an MCP server. It is know-how that matters only on one kind of task. That is the skill's job.

The skill alone is not enough. An agent that has never been told about the skill, or that decides WebFetch is faster, will try WebFetch on the URL first. So a PreToolUse hook, `block-x-webfetch`, refuses WebFetch on x.com. The hook stops the wrong path, and the skill teaches the right one. I wrote more about why the stopping half cannot be a sentence in the [hooks post](https://sawinyh.com/blog/claude-code-hooks-enforce-not-advise/).

The skill's failure mode is the other half of the table: skills fail when they never trigger, or when they duplicate access an MCP server should provide. Each covers the other's gap, which is the reason to build them as a pair.

## A script inside a skill

The article skill shows a different feature: a skill can carry code. The documentation describes supporting files, with `SKILL.md` as the overview and navigation and scripts alongside it that are executed rather than loaded. It recommends keeping `SKILL.md` under 500 lines and moving reference material into separate files.

Mine has a gate script. It checks the frontmatter contract, bans em dashes, flags sentences that start with a quantity, and checks that every number in the summary and FAQ appears in the body. Because the script runs through Bash and is not read into context, it costs nothing until it is called.

The skill tells Claude to run the script before shipping. The script is deterministic, so once it runs, the result is not a matter of interpretation. But nothing forces it to run. As I have set it up, no hook makes the run mandatory, so the check is exactly as reliable as Claude following the instruction. A gate script inside a skill belongs to the teaching layer. It is not enforcement.

That distinction tells you where to put a rule. If a check is worth running every time, wire it to an event. If it is worth running when a particular kind of task comes up, put the script in the skill that teaches that task.

## Where each thing goes

Put it together and the sorting is mechanical.

- A fact true of the project in every session: build command, layout, a never-do-X line. CLAUDE.md, kept short.
- A procedure with steps, or reference material needed for one kind of task. A skill, with a description written for the request.
- A system Claude cannot reach, or an action it cannot take. An MCP server, scoped to what the task needs, and a skill if its use needs explaining.
- A rule that must hold regardless of what the model decides. A hook.

Content moves between them as you learn. The documentation's own trigger list says to add a convention to CLAUDE.md after Claude gets it wrong twice, and to capture a playbook as a skill when you paste it into chat for the third time. That is the direction things travel: from nothing, to CLAUDE.md, to a skill, and for the few that cannot be allowed to fail, to a hook.

## The limit of mine

Both of my project skills, the article skill and an SEO skill, live in a `.claude` directory that is gitignored. They exist on one machine. If that disk dies, or a collaborator clones the repo, the know-how is gone.

The documentation says to commit a project skill so your team gets it too. I have not, and the cost is real: the setup that produces these posts cannot be reproduced from the repo. A skill is the right home for this know-how and the wrong place to keep the only copy.

Deciding what belongs in the repository and what stays on the machine is still open for me.

## Frequently asked questions

### What is the difference between a Claude Code skill and MCP?

An MCP server gives Claude access to an external system: tools, data and the authentication to reach them. A skill gives Claude knowledge, such as how to use that system, what your schema looks like or which steps a workflow takes. Anthropic's documentation describes the pairing directly: MCP provides the connection and a skill teaches Claude how to use it well. A skill cannot reach an outside system on its own, and an MCP server does not know your conventions.

### Where do Claude Code skills live?

A skill is a directory containing a SKILL.md file. Personal skills go in ~/.claude/skills/<skill-name>/SKILL.md and apply to all your projects on that machine. Project skills go in .claude/skills/<skill-name>/SKILL.md and apply to sessions in that repository, and the documentation suggests committing them so your team gets them. Plugins can also ship skills, namespaced as plugin-name:skill-name. When the same name exists at several levels, enterprise wins over personal and personal wins over project.

### What does SKILL.md frontmatter require?

Nothing is strictly required. The documentation says all fields are optional and only description is recommended so Claude knows when to use the skill. If name is omitted it defaults to the directory name, and if description is omitted Claude Code uses the first non-empty line of the markdown. The description and when_to_use text is truncated at 1,536 characters in the skill listing, so the key use case should come first.

### Should I put instructions in CLAUDE.md or in a skill?

Put it in CLAUDE.md if Claude should always know it: build commands, conventions, project layout, never-do-X rules. Put it in a skill if it is reference material needed sometimes or a multi-step procedure. The documentation suggests keeping each CLAUDE.md under 200 lines and moving procedures out to skills, because CLAUDE.md loads in full on every session while a skill loads only its description until it is used.


---

Source: Nick Sawinyh, https://sawinyh.com/blog/claude-code-skills-vs-mcp-vs-claude-md/
Nick Sawinyh writes on AI, engineering, product, and crypto business development at sawinyh.com.
Site index for agents: https://sawinyh.com/llms.txt
