AI & Development

Advanced Claude Code: Hooks, MCP, and Custom Commands — The Claude Code Mastery Series

Extend Claude Code with Hooks, MCP Servers, Custom Slash Commands, and CLAUDE.md Configuration

Phase 3/5

Series

The Claude Code Mastery Series

Deep dive into Claude Code's advanced features: CLAUDE.md project configuration, hooks system (PreToolUse/PostToolUse), MCP server integration, custom slash commands, and built-in commands for maximum productivity.

32 min read|February 24, 2026
Claude CodeHooksMCP

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.

The Hooks System

Hooks are Claude Code's automation layer. They let you run shell commands automatically before or after Claude Code uses a tool. This is where you enforce team standards, automate formatting, block dangerous operations, and integrate with external systems, all without manual intervention.

How Hooks Work

There are three hook events:

  • PreToolUse — Fires before a tool runs. You can inspect the tool name and input, then decide whether to allow it, modify it, or block it.
  • PostToolUse — Fires after a tool completes. You can inspect the result and run follow-up actions like linting or logging.
  • Notification — Fires when Claude Code sends a notification (for example, when a background task completes or when the agent needs your attention). Useful for integrating with external notification systems like Slack or desktop alerts.

Hooks are configured in settings.json at three levels:

LevelFile LocationScope
User~/.claude/settings.jsonAll projects for this user
Project.claude/settings.jsonThis project, shared with team (commit this)
Local.claude/settings.local.jsonThis project, this machine only (gitignore this)

Hook Structure

Each hook is a JSON object with three properties:

{
  "hooks": [
    {
      "event": "PreToolUse",
      "matcher": "Bash",
      "command": "/path/to/your/script.sh"
    }
  ]
}
  • event"PreToolUse", "PostToolUse", or "Notification"
  • matcher — Optional regex pattern that matches the tool name. If omitted, the hook fires for every tool.
  • command — A shell command to execute. Receives JSON on stdin with information about the tool invocation.

Exit Codes Matter

The exit code from your hook command determines what happens:

  • Exit 0 — Tool proceeds normally
  • Exit 2 — Tool is blocked from executing. Claude Code receives a message that the tool was blocked by a hook.
  • Any other exit code — Hook is considered failed, but the tool still proceeds

What Hooks Receive on Stdin

When your hook script runs, it receives a JSON payload on stdin containing the tool name and its input parameters. For a PreToolUse hook on the Bash tool, the stdin might look like:

{
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /important-directory"
  }
}

Your script reads this JSON, inspects it, and decides what to do.

Real Hook Examples

Block Dangerous Commands

This PreToolUse hook prevents Claude Code from running destructive shell commands:

#!/bin/bash
# block-dangerous-commands.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Block dangerous patterns
if echo "$COMMAND" | grep -qE 'rm\s+-rf\s+/|DROP\s+DATABASE|truncate|format\s+c:'; then
  echo "BLOCKED: Dangerous command detected: $COMMAND" >&2
  exit 2
fi

exit 0

Configure it in .claude/settings.json:

{
  "hooks": [
    {
      "event": "PreToolUse",
      "matcher": "Bash",
      "command": "/path/to/block-dangerous-commands.sh"
    }
  ]
}

Auto-Format After File Writes

This PostToolUse hook runs Prettier on any file that Claude Code writes:

#!/bin/bash
# auto-format.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [ -n "$FILE_PATH" ] && [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx|json|css)$ ]]; then
  npx prettier --write "$FILE_PATH" 2>/dev/null
fi

exit 0
{
  "hooks": [
    {
      "event": "PostToolUse",
      "matcher": "Write|Edit",
      "command": "/path/to/auto-format.sh"
    }
  ]
}

Run ESLint After Edits

#!/bin/bash
# lint-check.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [ -n "$FILE_PATH" ] && [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
  RESULT=$(npx eslint "$FILE_PATH" 2>&1)
  if [ $? -ne 0 ]; then
    echo "ESLint issues found:" >&2
    echo "$RESULT" >&2
  fi
fi

exit 0

Log All Tool Usage

For audit purposes, log every tool invocation to a file:

#!/bin/bash
# log-tool-usage.sh
INPUT=$(cat)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
echo "$TIMESTAMP | $TOOL | $(echo "$INPUT" | jq -c '.tool_input')" >> .claude/tool-usage.log
exit 0
⚠️

Warning: Hook scripts run with the same permissions as your user account. A misconfigured hook can block all tool usage or cause unexpected side effects. Test hooks in a scratch project before deploying them to a production codebase. Use exit code 2 carefully because it completely blocks the tool from executing.

MCP Server Integration

MCP (Model Context Protocol) servers extend Claude Code's capabilities by connecting it to external tools and data sources. Instead of asking Claude Code to make API calls or read from databases directly, you can expose structured tools through an MCP server that Claude Code invokes naturally during conversation.

How MCP Servers Work

An MCP server is a process that runs alongside Claude Code and exposes a set of tools. Claude Code discovers these tools at startup, and they appear alongside the built-in tools. When Claude Code decides it needs data from your database, or wants to create a GitHub issue, it calls the appropriate MCP tool just like it calls its built-in file read or bash tools.

Configuration

MCP servers are configured in settings.json under the "mcpServers" key:

{
  "mcpServers": {
    "my-database": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
      },
      "cwd": "/path/to/project"
    }
  }
}

Each server has:

  • command — The executable to run
  • args — Command-line arguments
  • env — Environment variables passed to the server process
  • cwd — Working directory for the server

Available MCP Servers

The MCP ecosystem has grown substantially. Here are servers that we use regularly at Luminous Digital Visions:

Database servers — Connect Claude Code directly to PostgreSQL, MySQL, or SQLite. Claude Code can query your database, inspect schemas, and help you write optimized queries based on actual data shapes.

GitHub server — Create issues, review pull requests, manage labels, and search repositories without leaving your Claude Code session.

File system server — Expose specific directories with custom access controls, useful for giving Claude Code read-only access to documentation or configuration directories.

Custom API servers — Build your own MCP server to wrap internal APIs, CMS platforms, or monitoring tools. If your team has an internal dashboard API, an MCP server lets Claude Code query it directly.

Dynamic Tool Updates

MCP servers support list_changed notifications. This means if your server adds or removes tools while running (for example, when a new database table is created), it can notify Claude Code to refresh its tool list without restarting the session.

Deferred Loading

When MCP servers expose many tools, they can consume a significant portion of Claude Code's context window. The defer_loading option tells Claude Code to only load tool descriptions when they exceed 10% of the available context. Tools are still available, but their descriptions are loaded on demand rather than all at once.

Building a Custom MCP Server

If you have an internal API that your team uses frequently, wrapping it in an MCP server is worth the investment. Here is the basic structure:

claude "Create a simple MCP server in TypeScript that exposes three tools: (1) get-deployment-status that calls our internal API at https://api.internal.com/deployments/:id, (2) list-recent-deployments that returns the last 10 deployments, (3) trigger-deployment that kicks off a new deployment for a given service name. Use the @modelcontextprotocol/sdk package."
💡

Tip: Start with the officially maintained MCP servers before building custom ones. The PostgreSQL, GitHub, and filesystem servers cover most common needs. Custom servers make sense when you need Claude Code to interact with proprietary systems specific to your organization.

Custom Slash Commands

Custom slash commands turn multi-step workflows into single invocations. They are Markdown files that contain prompt templates, stored in specific directories. When you type the command name in a Claude Code session, the file contents become the prompt.

File Locations

  • User-level commands: ~/.claude/commands/ — Available in every project
  • Project-level commands: .claude/commands/ — Available only in this project, shareable with team via version control

Creating a Custom Command

Create a Markdown file named after the command. For a /deploy command:

.claude/commands/deploy.md

The file contents become the prompt. Use $ARGUMENTS as a placeholder for dynamic input:

Deploy the application to the specified environment.

Environment: $ARGUMENTS

Steps:
1. Run the test suite and confirm all tests pass
2. Build the application with production settings
3. Run database migrations if there are pending ones
4. Deploy using the appropriate method for the environment:
   - "staging": Deploy to Vercel preview
   - "production": Deploy to Vercel production with --prod flag
5. Run a health check on the deployed URL
6. Report the deployment status and URL

Now typing /deploy staging in a Claude Code session executes this entire workflow.

Practical Custom Command Examples

Code Review Command

File: .claude/commands/review.md

Review the changes in the current branch compared to main.

Focus on:
1. Logic errors and potential bugs
2. Security vulnerabilities (SQL injection, XSS, auth bypasses)
3. Performance issues (N+1 queries, unnecessary re-renders, missing memoization)
4. TypeScript type safety (avoid 'any', use proper generics)
5. Missing error handling
6. Test coverage gaps

For each issue found, explain the problem, its severity (critical/warning/info), and provide a specific fix.

If $ARGUMENTS is specified, focus the review on those files: $ARGUMENTS

Test Generation Command

File: .claude/commands/test.md

Generate comprehensive tests for the specified file or module.

Target: $ARGUMENTS

Requirements:
- Use Vitest for the test framework
- Test the happy path, edge cases, and error conditions
- Mock external dependencies (database, APIs, file system)
- Use descriptive test names that explain the expected behavior
- Aim for 90%+ coverage of the target module
- Follow our existing test patterns in the __tests__ directory

Database Migration Command

File: .claude/commands/migrate.md

Create a new Prisma migration for the following schema change:

$ARGUMENTS

Steps:
1. Modify the prisma/schema.prisma file with the requested changes
2. Run `npx prisma migrate dev --name <descriptive-name>` to create the migration
3. Run `npx prisma generate` to update the client
4. Update any affected service files to use the new schema
5. Update or create tests for affected services
6. Run the test suite to confirm nothing is broken

Command Design Tips

Keep commands focused on a single workflow. A command that does too many unrelated things is hard to debug when something goes wrong. Use $ARGUMENTS to make commands flexible without making them overly complex. And document your project-level commands in the project's README or CLAUDE.md so the whole team knows they exist.

ℹ️

Info: Project-level commands in .claude/commands/ should be committed to version control. This way, the entire team has access to the same slash commands. User-level commands in ~/.claude/commands/ are personal and stay on your machine.

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.

Schema Markup

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Does CLAUDE.md get sent as part of every prompt?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. When Claude Code starts a session, it reads CLAUDE.md files from the project root, any relevant subdirectories, and the user-level ~/.claude/CLAUDE.md, then includes their contents as context. This is why keeping them concise matters — every token in CLAUDE.md reduces the context available for your actual conversation and code."
      }
    },
    {
      "@type": "Question",
      "name": "What happens if a hook script crashes?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "If a hook script exits with any code other than 0 or 2, it is treated as a failure. The tool still proceeds — only exit code 2 blocks execution. Claude Code logs the error so you can diagnose it."
      }
    },
    {
      "@type": "Question",
      "name": "Can hooks modify the tool input before it executes?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "How many MCP servers can I run simultaneously?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "Can I share MCP server configurations across projects?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "What is the execution order when multiple hooks match the same tool?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Hooks execute in the order they are defined in settings.json. If a PreToolUse hook exits with code 2, subsequent hooks for that tool invocation do not run — the tool is blocked immediately."
      }
    },
    {
      "@type": "Question",
      "name": "Is there a timeout limit for hook scripts?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Hook scripts that run too long will be terminated. Keep hooks fast — ideally under one second. Slow hooks delay every matching tool invocation and degrade the interactive experience."
      }
    },
    {
      "@type": "Question",
      "name": "How do I debug a hook that is not working correctly?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Test hook scripts outside Claude Code first by piping sample JSON input and checking the exit code and output. Add logging to a file within the hook script to trace execution. Use stderr for debug messages that Claude Code will display."
      }
    }
  ]
}
{
  "@context": "https://schema.org",
  "@type": "SoftwareApplication",
  "name": "Claude Code",
  "description": "Anthropic's agentic coding tool with an advanced configuration layer including CLAUDE.md project files, PreToolUse and PostToolUse hooks, MCP server integration for external tools, and custom slash commands for reusable workflows.",
  "applicationCategory": "DeveloperApplication",
  "operatingSystem": "macOS, Linux, Windows (via WSL)",
  "softwareVersion": "Current release",
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "USD",
    "description": "Usage-based pricing through Anthropic API. Free to install via npm."
  },
  "featureList": [
    "CLAUDE.md project configuration for persistent context",
    "PreToolUse and PostToolUse hook system",
    "MCP server integration for external tools and data sources",
    "Custom slash commands with $ARGUMENTS templating",
    "Built-in slash commands including /init, /model, /compact, /review, and /doctor",
    "Three-tier settings: user-level, project-level, and local",
    "Dynamic MCP tool updates and deferred loading"
  ],
  "author": {
    "@type": "Organization",
    "name": "Anthropic"
  }
}
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Set Up Claude Code Hooks for Automated Quality Enforcement",
  "description": "Step-by-step guide to configuring PreToolUse and PostToolUse hooks in Claude Code to automate formatting, block dangerous commands, and enforce coding standards.",
  "tool": {
    "@type": "SoftwareApplication",
    "name": "Claude Code"
  },
  "step": [
    {
      "@type": "HowToStep",
      "position": 1,
      "name": "Create the hook script",
      "text": "Write a bash script that reads JSON from stdin, inspects the tool_name and tool_input fields, and exits with code 0 to allow, code 2 to block, or any other code for a non-blocking failure."
    },
    {
      "@type": "HowToStep",
      "position": 2,
      "name": "Make the script executable",
      "text": "Run 'chmod +x /path/to/your-hook.sh' to ensure the script can be executed by Claude Code."
    },
    {
      "@type": "HowToStep",
      "position": 3,
      "name": "Configure the hook in settings.json",
      "text": "Add a hook entry to .claude/settings.json with the event type (PreToolUse or PostToolUse), an optional matcher regex for the tool name, and the command path to your script."
    },
    {
      "@type": "HowToStep",
      "position": 4,
      "name": "Test the hook in a scratch project",
      "text": "Start a Claude Code session in a test project and trigger the matching tool to verify the hook fires correctly and produces the expected behavior."
    },
    {
      "@type": "HowToStep",
      "position": 5,
      "name": "Deploy to your team",
      "text": "Commit the hook script and .claude/settings.json to version control so all team members get the same automated quality enforcement."
    }
  ]
}

Frequently Asked Questions

Does CLAUDE.md get sent as part of every prompt?

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.

Can I have multiple CLAUDE.md files in different directories?

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.

What happens if a hook script crashes?

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.

Can hooks modify the tool input before it executes?

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.

How many MCP servers can I run simultaneously?

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.

Do custom slash commands support multi-line $ARGUMENTS?

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.

Can I share MCP server configurations across projects?

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.

Is there a way to disable hooks temporarily?

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.

What is the difference between /review and a custom review command?

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.

Can hooks call external APIs?

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.

What is the execution order when multiple hooks match the same tool?

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).

Is there a timeout limit for hook scripts?

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.

How do I debug a hook that is not working correctly?

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.

How does MCP server authentication work?

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.

Can I set up rate limiting for MCP server tool calls?

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.

How do I pass arguments to custom slash commands?

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.

Can I chain custom commands together in sequence?

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.

How does CLAUDE.md inheritance work in monorepos?

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.

What are the settings.json precedence rules when user, project, and local settings conflict?

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.

What security considerations apply to hook scripts?

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.

How do I monitor MCP server health and diagnose connection issues?

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.

Can I version custom commands so different branches use different command behavior?

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.

How do I share custom commands across a team?

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.

What is the best strategy for hook error recovery?

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.

Does MCP support connection pooling for database servers?

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

Related Articles

Need Help Implementing This?

Our team at Luminous Digital Visions specializes in SEO, web development, and digital marketing. Let us help you achieve your business goals.

Get Free Consultation