Hooks make something happen automatically in response to Claude Code's own events; MCP servers give it access to external systems it has no built-in way to reach.
1Hooks React to Claude Code's Own Events
A hook is configured to run a command automatically when a specific event happens — after a file edit, before a command runs, at session start. Unlike a custom command, it needs no deliberate invocation: it fires on its own, which makes it the right tool for anything that should always happen without being asked.
2MCP Servers Reach Systems Outside the Conversation Entirely
Claude Code has built-in file and shell access, but nothing built-in for a team's specific issue tracker, internal API, or database. An MCP server exposes that system as a defined set of callable tools, with real schemas for inputs and outputs — the same protocol covered in the dedicated MCP Masterclass, applied here as a Claude Code extension point.
3Step-by-Step Breakdown
Two Different Extension Points. Custom commands and subagents extend what you ask Claude Code to do. Hooks and MCP servers extend what happens automatically around it: hooks react to its own events, MCP servers give it access to systems it has no built-in way to reach, like Fixly's issue tracker.
A Hook: Auto-Lint After Every File Edit. Fixly's team wants every file edit auto-linted immediately, without asking. A hook configured for the 'after file edit' event runs the linter automatically each time Claude Code changes a file — no request needed, and no chance of forgetting to ask.
How is a hook different from a custom slash command like /wrapup from Module 4's earlier lesson?
- →A hook fires automatically in response to an event (like a file edit), with no request needed, while a slash command still has to be invoked deliberately.
- →They're functionally identical, just with different names.
An MCP Server for Fixly's Issue Tracker. Fixly's team tracks bugs in a separate issue tracker Claude Code has no built-in access to. Describe what an MCP server for it would need to expose.
Automatic Where It Should Be, Connected Where It Needs to Be. Hooks remove the need to ask for something that should always happen; MCP servers remove the need to manually bridge Claude Code to a system it can't otherwise reach. Next module: production workflows — git, pull requests, and shipping this whole feature for real.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
N/A — hooks and MCP servers run as local processes.
N/A — hooks and MCP servers run as local processes.
N/A — hooks and MCP servers run as local processes.
N/A — hooks and MCP servers run as local processes.
Accessibility (A11y)
1Keep Hook Commands Fast and Non-Blocking Where Possible
A slow hook (like a full test suite on every single edit) delays every action Claude Code takes — scope hooks to fast checks like linting a single changed file, saving heavier checks for an explicit command.
"after-edit": "npm run lint -- --fix $FILE"SEO Implications
- 1
Target 'Claude Code hooks tutorial' and 'connecting Claude Code to external tools' separately
Users search for the event-driven automation feature and for the external-integration capability as distinct needs.
Best Practices
Use Hooks for 'Always,' Commands for 'Sometimes,' MCP for 'Elsewhere'
If something should happen every time an event occurs, it's a hook. If it's a repeated but deliberate request, it's a custom command. If it requires reaching a system outside the project entirely, it needs an MCP server.
Frequent Bugs
Manually asking Claude Code to lint after every edit, instead of configuring a hook that does it automatically and consistently.
Configure an after-edit hook for checks that should always run, removing the chance of forgetting to ask.
Real-World Examples
The Forgotten Manual Lint
Before configuring a hook, a developer occasionally forgot to ask for a lint pass after a quick edit, letting a style issue slip through. An after-edit hook running the linter automatically removed that gap entirely — every edit gets checked, with no request required.
// Before: lint sometimes forgotten
// After: after-edit hook -> lint runs on every single edit