Mod, hook, skill or MCP server? How to pick the right way to extend Claude Code
By VCA Newsroom
Once you have used Claude Code for a while, you start wanting it to behave your way: follow your team's conventions, never touch .env, talk to your issue tracker, show you something it doesn't show by default. Claude Code gives you four ways to do that: skills, settings hooks, MCP servers, and, since version 2.1.287, mods. They overlap, which makes it easy to pick the wrong one.
The short rule: use the simplest mechanism that can do the job. The rest of this guide explains what each one is for, using the comparison in the official mods overview.
1. Skills: when you keep pasting the same instructions
A skill is a Markdown file of instructions, SKILL.md, that Claude reads when it needs it. It changes what Claude knows and does, not what the tool can technically do. Pick it when you notice yourself pasting the same paragraph into chat again and again.
Per the skills docs, personal skills live in ~/.claude/skills/<name>/SKILL.md and project skills in .claude/skills/<name>/SKILL.md (commit those to share with your team). A minimal example:
---
name: summarize-changes
description: Summarizes uncommitted changes and flags risks. Use when asking what changed or wanting a commit message.
---
## Current changes
!`git diff HEAD`
## Instructions
Summarize the changes above in two or three bullet points, then list any risks you notice.
Claude loads the skill automatically when your request matches the description, or you run it directly with /summarize-changes. The !`git diff HEAD` line runs before Claude sees the skill, so the current diff is injected into the prompt.
Tips from the docs: write a keyword-rich description, since that is what Claude matches on; keep SKILL.md under 500 lines and move detail to supporting files; and add disable-model-invocation: true to anything with side effects (deploys, commits) so it only runs when you type the command.
2. Settings hooks: when a rule must always hold
Instructions in a skill or CLAUDE.md are advice. Claude usually follows them, but not with certainty. When something must happen every time, use a settings hook: a shell command that Claude Code runs at a fixed point in its lifecycle, such as before a tool call (PreToolUse) or after one (PostToolUse).
The hooks guide shows a classic one: block edits to protected files. You register a script in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }
]
}
]
}
}
The script reads the tool call as JSON on stdin, checks the file path against patterns like .env or .git/, and exits with code 2 to block the edit. Whatever it writes to stderr is passed back to Claude as feedback, so Claude can choose another approach. Exit code 0 means "no objection", and the normal permission flow applies.
Pick a settings hook when you want to block, allow, or log an event with a script, and you don't need any interface.
3. MCP servers: when Claude needs to reach another system
Skills and hooks work with what Claude Code already has. If Claude needs a new capability, such as reading tickets, querying a database, or editing a design file, you want an MCP server: an external process or service that gives Claude new tools.
The MCP docs show the one-line setup for a remote server:
claude mcp add --transport http notion https://mcp.notion.com/mcp
Add --scope project and the server is written to .mcp.json in your repo, so everyone on the team gets the same tools when they check out the code. MCP servers can be written in any language, and the same server works with other MCP-compatible agents, not just Claude Code.
4. Mods: when you need to change Claude Code itself
A mod is a plugin with JavaScript or TypeScript functions that Claude Code calls inside its own process. That gives it powers the other three don't have: it can draw panes and buttons, restyle Claude Code's own interface, rewrite a prompt or tool call in flight, or add a /command that runs instantly with no Claude turn.
Here is the core of the example from the mods overview, which counts tool calls and shows the count beside the spinner:
let calls = 0
export function register(on) {
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
That power has a cost. A mod runs with your full permissions and is not sandboxed: it can read your secrets, see every prompt, and approve tool calls for you. Only install mods you trust, and run claude plugin validate on one first to see which events it hooks and which APIs it calls.
A quick decision checklist
Ask these questions in order and stop at the first "yes":
- Is the problem that Claude doesn't know something or forgets how you work? → Write a skill.
- Must a rule hold every single time, and a script can check it? → Add a settings hook.
- Does Claude need to talk to an outside system? → Connect an MCP server.
- Do you need new interface, or to change how Claude Code itself behaves? → Build a mod.
You don't have to choose just one. A plugin can bundle all four, so a team can ship a skill with its conventions, a hook that enforces them, an MCP server for its tracker, and a mod with a status pane, all in one install. Start with the smallest piece that solves today's problem, and add the next one only when you hit its limit.
SOURCES
Auto-generated by Vibe Coding Academy on October 4, 2026, grounded in the real sources linked above. We review for accuracy, but please verify time-sensitive details against the primary sources.
SECOND OPINION · BY VIBE CODING ACADEMY
Your agent says it’s done. What needs checking?
Paste your coding-agent conversation for supported claims, visible problems, and useful next steps. Reviews only the material you provide; no account needed to start.
Review a session