All articles

AI development

Claude Code Hooks, MCP and Skills: Configuration Guide

Configure Claude Code hooks, MCP connections and skills with bounded permissions, reproducible settings and verification steps.

A person working at multiple monitors in a dark room
Illustrative photograph from the original article, sourced from Unsplash.

Introduction

Parts 1 and 2 of this series covered the fundamentals and full-stack development workflows. You know how to scaffold projects, build features, and deploy applications with Claude Code. That puts you ahead of most users.

But Claude Code has a configuration layer that most people never touch. It is the difference between using Claude Code as a one-off assistant and having it function as a deeply integrated part of your development environment. This article covers the four systems that make that possible: CLAUDE.md project files, the hooks system, MCP server integration, and custom slash commands.

These are not theoretical features. At Luminous Digital Visions, we use every one of them on client projects. Hooks enforce our coding standards automatically. MCP servers connect Claude Code to our internal tools. Custom slash commands encode repeatable workflows that any team member can run with a single keystroke. The productivity gain compounds over time because you are not just using Claude Code. You are teaching it how your team works.

What You Will Learn

  • How to write effective CLAUDE.md project configuration files
  • The hooks system — PreToolUse, PostToolUse, and Notification events, matchers, and security gates
  • MCP server integration for connecting external tools and data sources
  • Custom slash commands for reusable workflows
  • Every built-in slash command and what it does
  • Advanced workflow patterns that combine these systems
Info: Claude Code supports multiple Claude models, and the exact names available to you may change over time. All configuration discussed in this article applies to Claude Code's terminal interface, invoked with the claude command.

CLAUDE.md Project Configuration

CLAUDE.md is a special file that Claude Code reads automatically when you start a session in a project directory. Think of it as a persistent system prompt for your project. It tells Claude Code about your codebase, your conventions, and your preferences without you having to repeat yourself every session.

Where to Place CLAUDE.md

The primary location is the root of your project directory. When you run claude from that directory (or any subdirectory), Claude Code reads the file and incorporates its contents as context. Claude Code also supports CLAUDE.md files in subdirectories (useful for monorepos where different packages have different conventions) and a user-level ~/.claude/CLAUDE.md file that applies global instructions across all projects.

my-project/
├── CLAUDE.md          ← Claude Code reads this automatically
├── src/
├── package.json
└── ...

What Goes in CLAUDE.md

A good CLAUDE.md file covers five things:

  1. Project overview — What the project is, its purpose, the tech stack
  2. Architecture decisions — Folder structure conventions, patterns used (MVC, service layer, etc.)
  3. Coding standards — Naming conventions, import ordering, error handling patterns
  4. Common tasks — How to run tests, build, deploy, and what scripts exist
  5. Constraints — Things Claude Code should never do (delete migrations, modify configs without asking, etc.)

A Real-World CLAUDE.md Example

# Project: SaaS Dashboard

## Tech Stack
- Next.js 15 with App Router and TypeScript
- Tailwind CSS for styling
- Prisma ORM with PostgreSQL
- Vitest for testing
- Deployed on Vercel

## Architecture
- `src/app/` — Next.js pages and API routes
- `src/components/` — Reusable UI components, organized by feature
- `src/lib/` — Utilities, database client, auth helpers
- `src/services/` — Business logic, one service per domain entity
- `prisma/` — Database schema and migrations

## Conventions
- Use named exports, not default exports (except for Next.js pages)
- All components are functional with TypeScript interfaces for props
- Use Zod for all input validation
- Error messages should be user-friendly, never expose internal details
- All API routes return { data, error, status } shape

## Commands
- `npm run dev` — Start development server
- `npm test` — Run Vitest in watch mode
- `npm run test:ci` — Run tests once with coverage
- `npm run db:migrate` — Run Prisma migrations
- `npm run db:seed` — Seed the database

## Rules
- Never modify prisma/migrations/ files directly
- Never commit .env files
- Always run tests before suggesting a task is complete
- When creating new API routes, always include input validation and error handling

Best Practices for CLAUDE.md

Keep it concise. Claude Code reads the entire file into context, so a 500-line CLAUDE.md wastes tokens on information that is rarely relevant. Aim for one to two pages. Focus on things Claude Code would not know from reading your code alone: project-specific conventions, team preferences, and constraints.

Update it as your project evolves. If you adopt a new library or change a naming convention, update CLAUDE.md. It should be a living document, not a snapshot from project kickoff.

Tip: Run /init inside a Claude Code session to generate a starter CLAUDE.md based on your project's current structure. Claude Code will analyze your codebase and produce a draft that you can refine.

Configure hooks with the current event structure

Hooks run at defined lifecycle events. Choose the event based on what the check needs to prevent or observe. A post-tool check happens after the tool has already run; it cannot undo that action merely by returning a failure.

Project settings use an object keyed by event name, with matcher and handler arrays. This example invokes an existing project check after a file edit:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "npm run check" }
        ]
      }
    ]
  }
}

The example assumes the project defines npm run check. Confirm its working directory and runtime before enabling it. Frequent checks should be quick enough to use during editing; reserve expensive validation for the appropriate release step.

Read the current hooks reference for event input, response fields and exit-code behavior. Test allowed and rejected operations explicitly. A string filter for a dangerous command is not a complete security boundary.

Add MCP access with a defined scope

An MCP server exposes capabilities to the coding session. Before connecting one, identify the data it can read and the actions it can perform. Review the provider and configuration with the same care as a development dependency.

Use current installation instructions for the selected server. Keep credentials out of committed configuration and grant the minimum access required for the task. A database review should begin with read-only access where possible; production write access needs explicit operational controls.

Test the connection with a harmless read, confirm the expected project or account and inspect available tools. Follow the Claude Code MCP documentation rather than an old example package that may be archived or unavailable.

Package repeatable work as skills

A repeatable workflow should state when to use it, the information it needs, the expected result and the checks before completion. Keep instructions specific to the repository and avoid duplicating its normal build documentation.

Claude Code supports skills through a SKILL.md file and continues to support relevant command workflows. Follow the current skills documentation for placement, invocation and optional supporting files.

Begin with a bounded task such as reviewing a proposed change or preparing a release checklist. Test that it selects the right files, reports failures and respects the permissions of the underlying tools. A workflow instruction is not a replacement for access control.

Built-in Slash Commands

Claude Code ships with built-in slash commands that cover common operations. Here is every built-in command and what it does:

  • /init — Generates a CLAUDE.md file by analyzing your project structure, dependencies, and existing code patterns. Run this once when you start using Claude Code in a new project.
  • /model — Switch between the Claude models available in your account during a session. Use the most capable model for complex reasoning, code review, and architecture work, and a faster lower-cost model for straightforward edits or boilerplate.
  • /settings — Open the settings configuration. Lets you view and modify user-level, project-level, and local settings including hooks and MCP server configurations.
  • /compact — Compress the current conversation to save context window space. Useful during long sessions where earlier messages are no longer relevant. Claude Code summarizes the conversation history so it can continue working without losing important context.
  • /review — Perform a code review of recent changes. Analyzes staged or unstaged git changes and provides feedback on code quality, bugs, and improvements.
  • /help — Show available commands and usage information.
  • /doctor — Diagnose Claude Code configuration issues. Checks your installation, authentication, settings files, and MCP server connections. Run this when something is not working as expected.
  • /login — Authenticate with your Anthropic account. Opens a browser for interactive login.
  • /logout — Sign out of your current session.
  • /export — Export the current conversation to a file. Useful for documentation, sharing, or archiving sessions.
  • /theme — Change the visual theme of the Claude Code terminal interface.
  • /output-style — Control the formatting style of Claude Code's responses.

Advanced Workflow Patterns

The real power comes from combining these systems. Here are patterns we have refined at Luminous Digital Visions across dozens of projects, including the kind of work we do through our AI systems and automation services.

Automated Quality Gates

Combine hooks and CLAUDE.md to enforce quality standards automatically. Your CLAUDE.md defines the standards. A PreToolUse hook blocks commits that do not meet them. A PostToolUse hook runs linters after every file edit.

This means every piece of code Claude Code generates goes through the same quality pipeline your team code goes through. No exceptions.

MCP-Powered Context

Connect a database MCP server and reference it in CLAUDE.md:

## Database Access
Claude Code has access to our development database via the PostgreSQL MCP server.
When debugging data issues or writing queries, query the database directly rather
than guessing at the schema. Use SELECT queries only — never run mutations through
the MCP server.

Now when you ask Claude Code to fix a bug related to data, it can inspect actual database records instead of working from assumptions.

Command Chains

Custom commands can reference each other conceptually. Build a workflow where /review catches issues, and then you run /test to verify fixes, and finally /deploy staging to ship. Each command is focused, but they chain together into a complete delivery workflow.

Team Onboarding

New team members clone the repo and get the project-level .claude/commands/ directory and .claude/settings.json with hooks automatically. For a full walkthrough of setting up development environments, see our development environment setup guide. Add this to your onboarding documentation:

1. Clone the repository
2. Run `npm install`
3. Run `claude` in the project root
4. Type `/help` to see available project commands
5. Type `/doctor` to verify your setup

The new developer gets the same hooks, the same custom commands, and the same CLAUDE.md context as the rest of the team from day one.

Tip: Version control your .claude/settings.json and .claude/commands/ directory. Keep machine-specific settings in .claude/settings.local.json and add that file to .gitignore. This gives you shared team configuration without leaking personal preferences or local paths.

Verify the configuration before sharing it

Keep a small test project for configuration changes. Test one allowed action, one rejected action, a missing dependency and a malformed input. Verify what the user sees when a hook fails.

Review the committed settings for credentials, machine-specific paths and accidental duplication. Record the installed Claude Code version and the documentation used. Roll back the configuration if it interrupts the normal development workflow.

Frequently Asked Questions

Yes. When Claude Code starts a session, it reads CLAUDE.md from the project root and includes its contents as context. This is why keeping it concise matters, since every token in CLAUDE.md reduces the context available for your actual conversation and code.

Yes. Claude Code now supports CLAUDE.md files at multiple levels: the project root, subdirectories within the project, and a user-level ~/.claude/CLAUDE.md for global instructions. In a monorepo, you can place a root CLAUDE.md with shared conventions and additional CLAUDE.md files in subdirectories with package-specific instructions. Claude Code merges these contextually based on the files you are working with.

If a hook script exits with any code other than 0 or 2, it is treated as a failure. The tool still proceeds because only exit code 2 blocks execution. Claude Code logs the error so you can diagnose it. Always test hook scripts outside of Claude Code first to catch crashes.

Hooks can write to stdout and stderr to communicate with Claude Code, but they do not modify the tool input directly. A PreToolUse hook either allows (exit 0) or blocks (exit 2) the tool. If you need to change the input, the hook should block and output a message explaining why, so Claude Code can retry with corrected parameters.

There is no hard limit, but each MCP server consumes memory and may contribute tools that take up context window space. In practice, three to five servers is typical. Use defer_loading for servers with many tools to manage context usage.

The $ARGUMENTS placeholder captures everything after the command name as a single string. For multi-line input, type the command and then continue typing your input. The entire text after the command name becomes the argument value.

Yes. Put shared MCP server configurations in your user-level settings at ~/.claude/settings.json. These servers will be available in every project. Project-specific servers go in .claude/settings.json within the project directory.

You can use the local settings file (.claude/settings.local.json) to override project hooks. Set an empty hooks array to disable them for your local environment. Alternatively, you can temporarily rename your hook scripts to prevent them from running.

The built-in /review command performs a general code review of your git changes. A custom review command can be tailored to your team's specific review criteria, coding standards, and focus areas. Many teams use both: /review for a quick check and a custom command for thorough reviews before merging.

Yes. Hook scripts are regular shell commands and can do anything your shell can do: make HTTP requests with curl, call Python scripts, query databases, send Slack notifications. Just be mindful of latency. A PreToolUse hook that makes a slow API call will delay every tool invocation it matches.

Hooks execute in the order they are defined in your settings.json array. If you have three PreToolUse hooks that all match the Bash tool, they run first, second, third. If any hook exits with code 2, the tool is blocked immediately and subsequent hooks for that invocation do not run. Plan your hook ordering so that fast checks (like pattern matching) run before slow checks (like API calls).

Hook scripts that take too long will be terminated by Claude Code. The exact timeout depends on the implementation, but you should keep hooks fast, ideally completing in under one second. A hook that makes a network request or runs a heavy computation will slow down every matching tool invocation. If you need to run something slow, consider using a PostToolUse hook instead so it does not block the tool execution.

First, test the hook script outside of Claude Code. Create a sample JSON payload that mimics what Claude Code sends on stdin and pipe it into your script: echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | bash your-hook.sh. Check the exit code with echo $?. Add logging to a file within the hook for persistent debug output. Use stderr for messages you want Claude Code to see, since anything written to stderr appears in the Claude Code session.

MCP servers handle authentication through environment variables passed in the env field of the server configuration in settings.json. For example, a GitHub MCP server would receive a GITHUB_TOKEN env var. The MCP server process uses that token to authenticate with the external service. Claude Code itself does not manage these credentials. It just passes the configured environment variables to the server process at startup.

MCP servers can implement their own rate limiting internally. If you are building a custom MCP server, add rate limiting logic before processing tool calls. For third-party MCP servers, check their documentation for built-in rate limit configuration. You can also use a PreToolUse hook that tracks call frequency to an MCP tool and blocks with exit code 2 if calls exceed a threshold within a time window.

Everything you type after the command name becomes the $ARGUMENTS value in the command's Markdown template. For example, if your command file contains "Run tests for: $ARGUMENTS" and you type /test src/services/authService.ts, the resulting prompt is "Run tests for: src/services/authService.ts". You can use $ARGUMENTS multiple times in the template, and it receives the same value each time.

Custom commands do not directly call other commands. However, you can design a command that encodes a multi-step workflow in its Markdown template. For complex chains, create a single command that includes all the steps. Alternatively, run one command, wait for it to complete, then run the next command in the same session. The session context carries forward, so the second command benefits from the work done by the first.

Claude Code reads CLAUDE.md files at multiple levels: the project root, subdirectories, and the user-level ~/.claude/CLAUDE.md. In a monorepo, place shared conventions in the root CLAUDE.md and package-specific instructions in subdirectory CLAUDE.md files. Claude Code merges these based on the files being worked on, with more specific (subdirectory) instructions taking precedence for relevant files. User-level instructions apply globally across all projects.

Settings are merged with local overriding project, and project overriding user. For hooks, arrays are concatenated, so user-level hooks run alongside project-level hooks. For MCP servers, project-level servers augment user-level servers. The local settings file (.claude/settings.local.json) takes highest priority, which is useful for developer-specific overrides that should not be committed to version control.

Hook scripts run with the same OS permissions as your user account. A malicious or misconfigured hook can read sensitive files, make network requests, or modify your system. Only use hook scripts from trusted sources. Review any hook script before adding it to your configuration. For team-shared hooks committed to version control, treat them with the same review rigor as production code. Never store secrets in hook scripts. Use environment variables instead.

Run /doctor in a Claude Code session to check MCP server connectivity. If a server fails to start, check the command and args in your configuration. Verify the MCP server package is installed (e.g., npx -y @modelcontextprotocol/server-postgres should download and run). Check that environment variables are correctly set. For custom MCP servers, add startup logging to confirm the process is running. MCP server processes run in the background, so check system process lists if you suspect a crash.

Yes. Since project-level custom commands live in .claude/commands/ within the repository, they are version-controlled like any other file. Different branches can have different command files. When you switch branches, the commands available in your Claude Code session correspond to the commands on that branch. This is useful for experimental workflow changes. Test new command behavior on a feature branch before merging to main.

Commit your .claude/commands/ directory to version control. Every team member who clones the repository gets the same set of project-level commands. Document available commands in your CLAUDE.md or project README so developers know what is available. For commands that should be available across all projects for your entire team, distribute user-level commands via your configuration management system or onboarding scripts.

Design hooks to fail gracefully. If a PostToolUse hook (like auto-formatting) fails, it should exit with a non-zero, non-two code so the tool result is not lost. Log the failure for later investigation but do not block the workflow. For PreToolUse hooks, use exit code 2 only for clear violations, not for ambiguous cases. Include descriptive error messages on stderr so Claude Code can explain the blockage to the user and suggest corrective action.

The official PostgreSQL MCP server maintains its own database connection. Connection pooling behavior depends on the specific MCP server implementation. If you are building a custom MCP server that talks to a database, use a connection pool library (like pg-pool for PostgreSQL) within your server code. This prevents the server from opening a new connection for every tool call, which is important for performance under frequent queries.

Continue the Series

Hooks, MCP servers, and custom commands transform Claude Code from a helpful assistant into an integral part of your development infrastructure. The initial setup takes an hour or two. The time savings compound every single day.

What We Covered

  • CLAUDE.md — Project configuration that persists across sessions
  • Hooks — PreToolUse, PostToolUse, and Notification automation with exit code 2 for blocking
  • MCP servers — External tool integration with dynamic loading and deferred tool discovery
  • Custom slash commands — Reusable workflows stored as Markdown templates
  • Built-in commands — Every slash command Claude Code ships with
  • Advanced patterns — Combining these systems for automated quality gates, team onboarding, and MCP-powered debugging

Up Next: Part 4

Security is not optional. In Part 4, we cover Claude Code Security (Anthropic's vulnerability scanning system that found over 500 vulnerabilities in production open-source code) alongside enterprise deployment patterns, CI/CD integration, and security best practices for AI-assisted development.

Continue to Part 4: Claude Code Security and Enterprise Workflows

Need Expert Help?

Configuring hooks, MCP servers, and team workflows takes experience. If you want your team running at peak productivity with Claude Code from day one, Luminous Digital Visions can help with setup, training, and ongoing support through our web development services.

Get a Free Consultation