
I shipped both for the same Vue design system, a month apart, and kept both. A skill carries judgement and goes stale; an MCP server carries facts and stays fresh. Here is how the choice actually breaks down.
In January I published an MCP server for my Vue design system, so an assistant could look up components instead of inventing props. In February I published a skill for the same design system.
The obvious question is why both. A skill is markdown in a folder — no server, no process, no npx. If that works, the server is a lot of machinery for nothing.
They carry different things, and it took shipping both to see it clearly.
Here is the first section of mine, before any component is mentioned:
Design Thinking (Do This First)
Before writing any code, commit to a clear aesthetic direction. Empathy DS provides the components — you provide the creative vision.
Color strategy: 1 dominant color + 1-2 accents. Typography: Pair fonts thoughtfully — a distinctive display font with a clean body font. Spacing: Generous whitespace for marketing/editorial; tighter for data-heavy UIs.
None of that is a fact you can look up. There's no getAestheticDirection() and there shouldn't be. It's the judgement a good designer applies before touching a component, written down so a model applies it too.
That's what a skill is for. Conventions, process, the order to do things in, the taste that separates code that works from work that is good. A lookup tool has nowhere to put any of it.
It also does something a tool cannot: it fires on its own. The description says what triggers it — building a Vue app, a dashboard, a landing page — and the model brings it in when the situation matches. A tool waits to be called.
Worth saying plainly, because people assume a skill is a bigger thing than it is. Mine is four files.
skill/
SKILL.md the whole skill, 7.6KB
references/
component-api.md props, slots, variants — 10.7KB
blocks.md prebuilt page patterns — 14.9KB
bin/cli.js an installer
SKILL.md is markdown with two lines of frontmatter:
---
name: empathy-ds
description: Build beautiful, accessible Vue 3 web applications using the
Empathy DS design system. Use this skill when users want to create websites,
web apps, UI components, or interfaces using Vue 3 with the Empathy DS
component library. Triggers include requests to build Vue apps, create modern
UIs, design web pages, build dashboards, landing pages, forms, admin panels…
Do NOT use for React, plain HTML, or non-Vue projects unless specifically
asked to use Empathy DS.
---
# Empathy DS Skill
...
The description is the whole loading mechanism, and it's the part to labour over. It isn't a summary for humans — it is what the model reads to decide whether this situation is one where the skill applies. So it lists concrete triggers (dashboards, landing pages, forms, admin panels) rather than describing the library, and it says what not to fire on. Mine explicitly rules out React and plain HTML, because without that it kept volunteering itself on projects it had nothing to offer.
Write it as a matching rule, not a blurb.
The references are progressive disclosure. SKILL.md stays short and ends with pointers:
For detailed component props, slots, and variants: see `references/component-api.md`
For pre-built page patterns and blocks: see `references/blocks.md`
The main file is what always loads; the references are read only when the work needs them. That's how you keep a 33KB skill from costing 33KB on every task.
Distribution is an npm package. The skill directory ships in files, and a bin entry makes it installable:
{
"bin": { "empathyds-skills": "./bin/cli.js" },
"files": ["bin/", "skill/", "README.md"]
}
npx @empathyds/skills install
The CLI copies the skill directory into the project and that's genuinely all it does. It's a directory copy with a prompt for the destination. Everything else in that file is colours and a menu.
The destination is the part worth getting right, and it is the part I got wrong first. Claude Code only discovers skills under .claude/skills/<name>/SKILL.md (or ~/.claude/skills/ for a personal one, or a plugin's own skills/ directory). Put the folder anywhere else and nothing happens — no error, no warning, the description simply never gets read and the skill never triggers. I had mine under a vendor-neutral .agents/skills/ so the same folder could serve more than one tool, which is a reasonable thing to want and does not work on its own. What works is keeping the canonical copy there and symlinking it into place:
mkdir -p .claude/skills
ln -s ../../.agents/skills/empathy-ds .claude/skills/empathy-ds
One directory of truth, one link per tool that expects its own layout. If a skill is not firing, check this before you touch the description — a skill in the wrong directory and a skill with a bad description look identical from the outside.
You could skip the CLI entirely and tell people to copy a folder. The package exists so there's a versioned artifact with a changelog, and so updating is npx rather than "go and re-copy that folder from GitHub".
getComponent("input") returns six props, from the registry, right now.
And "right now" is the whole argument. My skill ships two reference files, about 25KB of component API, frozen at the moment I published it. Add a prop to a component and those files are wrong until I republish the package and every user reinstalls it.
The server fetches the registry over the network and caches it for five minutes. It can't be wrong for longer than that.
That's not a small difference. The failure mode a design-system assistant has is confidently using an API that does not exist — which is exactly what a stale snapshot causes. A frozen reference doesn't just fail to help; it actively reintroduces the bug you were trying to fix.
My skill is about 33KB in total. That's fine, and it loads when it is relevant.
The registry is 112KB — sixty components with props, variants, slots and examples. You could put that in a skill. You shouldn't. Any given task needs three components out of sixty, and the other fifty-seven are paying rent in the context window.
A skill wants to be small enough to read every time. A lookup wants to be complete. Those pull in opposite directions, and trying to satisfy both gives you a skill too big to load cheaply and a reference too partial to trust.
| Skill | MCP server | |
|---|---|---|
| Carries | Judgement, conventions, process | Facts that change |
| Freshness | Frozen at publish | Live, cached 5 minutes |
| Cost | Loaded when relevant | Only when called |
| Triggering | Fires on its own | Waits to be called |
| Reach | Claude | Any MCP client |
| Effort | An afternoon | A package to maintain |
The questions sort cleanly once you see it that way. How should I approach this interface? is a skill. What props does the accordion take? is a tool call.
Answering the second with a frozen markdown file is how you end up back at the beginning, with an assistant confidently writing a prop that used to exist.
If you only ship one, ship the skill. It's an afternoon of work, there's nothing to host, nothing to version against your docs, and it captures the thing that is genuinely hard to write down. Most libraries never need more.
Add the server when one of two things is true. Your API surface is big enough that pasting it into markdown is silly — somewhere north of twenty or thirty components, in my experience. Or it changes often enough that a snapshot goes stale between releases.
If you ship both, do not duplicate. This is the mistake I nearly made. The temptation is to put the component list in the skill as well, so it works without the server. Don't. Now you have two sources of truth and the markdown one is always the stale one. Let the skill say use the MCP tools to look up components and leave the facts in one place.
I assumed the skill would make the server redundant — same information, simpler delivery.
It went the other way. Writing the skill forced me to separate what I know about my design system from what I can look up about it, and they turned out to be almost disjoint sets. The taste isn't in the registry. The props aren't in my head.
Two artifacts, because there were two kinds of knowledge all along.
What is CSR, SSR, SSG, ISR (Different Rendering Strategies) and which framework does it better (Angular, React, Vue)
A comparison of CSR, SSR, SSG, and ISR rendering strategies across Angular, React, and Vue frameworks.
What is the Array Reduce Method in JavaScript?
Learn how the Array reduce method works in JavaScript with practical examples for summing values, flattening arrays, and more.