Claude Code Configuration — A Clean Multi-Project Setup

Published on Apr 12, 2026

Claude Code Configuration — A Clean Multi-Project Setup

Claude Code Configuration — A Clean Multi-Project Setup

Claude Code has a layered configuration system that can handle everything from MCP server connections to per-command permissions. This post explains how the config files work and shows a clean setup for managing multiple projects from a single machine.

Table of Contents

Config File Hierarchy

Claude Code uses several configuration files at two levels: global and per-project.

Global Files (apply to every session)

FilePurpose
~/.claude.jsonInternal state, auto-update setting
~/.claude/settings.jsonPermissions, MCP enablement, and preferences — the main global config
~/.claude/settings.local.jsonNot recommended for permissions (see note below)
~/.mcp.jsonMCP server definitions (standard MCP config)

Important: settings.json permissions always apply and cannot be overridden by per-project files. settings.local.json permissions get overridden when a per-project settings.local.json exists — making it unreliable for global permissions. Put everything you want to apply everywhere in settings.json.

Per-Project Files (override or extend global)

FilePurpose
project/.claude/settings.jsonProject-specific settings (safe to commit)
project/.claude/settings.local.jsonProject-specific permissions (local only)
project/CLAUDE.mdProject context and instructions for Claude

Global settings apply everywhere. Per-project files should only contain things that are genuinely project-specific.

MCP Server Configuration

MCP servers are defined in ~/.mcp.json. This is the standard MCP config file and works with other MCP clients too. All servers go in one place:

{
  "mcpServers": {
    "voicemode": {
      "command": "voicemode",
      "args": []
    },
    "paperless": {
      "command": "/home/kees/.npm-global/bin/paperless-mcp",
      "env": {
        "PAPERLESS_URL": "https://paper.example.com",
        "PAPERLESS_API_KEY": "your-api-key"
      }
    },
    "vikunja": {
      "command": "/home/kees/.npm-global/bin/vikunja-mcp",
      "env": {
        "VIKUNJA_API_BASE": "https://vikunja.example.com",
        "VIKUNJA_API_TOKEN": "your-token"
      }
    },
    "deepwiki": {
      "type": "http",
      "url": "https://mcp.deepwiki.com/mcp"
    },
    "context7": {
      "type": "http",
      "url": "https://mcp.context7.com/mcp"
    },
    "svelte": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@sveltejs/mcp"]
    }
  }
}

Three transport types are supported:

  • stdio — local binary or npx command (most common)
  • http — remote HTTP endpoint
  • sse — Server-Sent Events endpoint

Note: ~/.claude.json can also hold MCP server definitions under an mcpServers key. Avoid this — use ~/.mcp.json exclusively to keep a single source of truth.

Global Permissions

The file ~/.claude/settings.json controls what Claude can do without asking. This is the core of the setup:

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "alwaysThinkingEnabled": true,
  "permissions": {
    "allow": [
      "Read(/home/kees/**)",
      "Write(/home/kees/vscode/**)",
      "Edit(/home/kees/vscode/**)",
      "MultiEdit(/home/kees/vscode/**)",
      "NotebookEdit(/home/kees/vscode/**)",
      "Glob(/home/kees/**)",
      "Grep(/home/kees/**)",
      "LS(/home/kees/**)",

      "Bash(cd *)",
      "Bash(pnpm *)",
      "Bash(npm *)",
      "Bash(npx *)",
      "Bash(git *)",
      "Bash(find *)",
      "Bash(ls *)",
      "Bash(node *)",
      "Bash(python *)",
      "Bash(python3 *)",
      "Bash(docker *)",
      "Bash(docker-compose *)",
      "Bash(grep *)",
      "Bash(hugo *)",
      "Bash(curl *)",
      "Bash(ssh *)",
      "Bash(scp *)",
      "Bash(rsync *)",
      "Bash(go *)",
      "Bash(sudo *)",

      "WebSearch",
      "WebFetch",

      "mcp__voicemode",
      "mcp__paperless",
      "mcp__vikunja",
      "mcp__svelte",
      "mcp__deepwiki",
      "mcp__context7",
      "mcp__svelte-llm",
      "mcp__flowbite-svelte",
      "mcp__tanstack"
    ],
    "deny": [],
    "ask": []
  },
  "enableAllProjectMcpServers": true,
  "enabledMcpjsonServers": [
    "voicemode", "paperless", "vikunja",
    "deepwiki", "context7", "svelte-llm",
    "svelte", "flowbite-svelte", "tanstack"
  ]
}

Why settings.json and not settings.local.json? Through testing, we found that settings.local.json permissions get overridden when a per-project settings.local.json exists — they don’t merge. settings.json permissions always apply regardless of per-project files. Since ~/.claude/ is not a git repo, there’s no commit risk, so settings.json is the right place for global permissions.

Permission Patterns

File access uses glob patterns — broad read, scoped write:

"Read(/home/kees/**)"         // Read anything under home
"Write(/home/kees/vscode/**)" // Write only in project directories

Bash commands use prefix matching with wildcards:

"Bash(git *)"    // Any git command
"Bash(pnpm *)"   // Any pnpm command
"Bash(ssh *)"    // Any ssh command

MCP tools use server-level matching:

"mcp__vikunja"   // ALL tools from vikunja server
"mcp__paperless" // ALL tools from paperless server

This server-level format is important. Individual tool entries like "mcp__vikunja__list_projects" don’t reliably auto-approve. The short format "mcp__vikunja" (just the server prefix) matches all tools from that server and works correctly for auto-approval.

MCP Enablement

Two settings control which MCP servers are active:

"enableAllProjectMcpServers": true

This allows all MCP servers defined in ~/.mcp.json to connect. Without it, servers need explicit opt-in.

"enabledMcpjsonServers": ["vikunja", "paperless", "svelte"]

This lists which specific servers from ~/.mcp.json to enable. Combined with enableAllProjectMcpServers: true, this ensures everything connects.

Per-Project: Just CLAUDE.md

With global permissions handling everything, per-project settings.local.json files aren’t needed. Each project only needs a CLAUDE.md for context:

# CLAUDE.md

## Project Overview
SvelteKit app with PocketBase backend...

## Build Commands
pnpm dev | build | check

## Architecture
Key patterns and conventions...

This keeps project-specific knowledge (architecture, conventions, build commands) separated from permissions (which are global).

Auto-Updates

In ~/.claude.json, make sure auto-updates are enabled:

{
  "autoUpdates": true
}

Claude Code ships updates frequently. Staying on an old version means missing MCP improvements, permission system changes, and bug fixes.

Settings.json vs Settings.local.json

The split between settings.json and settings.local.json exists for a reason at the per-project level:

  • project/.claude/settings.json — Safe to commit. Team-wide settings.
  • project/.claude/settings.local.json — Never commit. Personal overrides.

At the global level (~/.claude/), the distinction matters less since nothing there is committed to a repo. However, there’s a critical behavioral difference:

  • settings.json permissions always apply — they cannot be overridden by per-project files.
  • settings.local.json permissions get overridden when a per-project settings.local.json exists — they don’t merge.

This means: put global permissions in settings.json, not settings.local.json. We learned this the hard way when Vikunja MCP tools kept prompting for approval in subprojects despite being allowed globally in settings.local.json — a per-project settings.local.json (created by approving a different tool) was silently overriding the global rules.

Our Setup

Running ~15 projects from one machine (SvelteKit apps, Hugo sites, FastAPI backends, Docker deployments to an Unraid server), the configuration is:

WhatWhereCount
MCP servers~/.mcp.json9 servers
Permissions~/.claude/settings.json~30 rules
Project contextEach project’s CLAUDE.md15 files
Project settings.local.jsonOnly if neededrare

The MCP servers include self-hosted tools (Paperless-NGX for documents, Vikunja for tasks), development tools (Svelte, Context7, Flowbite), and AI documentation (DeepWiki). All connect automatically in every project without per-session approval.

Tips

Restart after config changes. Claude Code reads configuration at session start. Changes to settings files won’t take effect until you start a new session.

Use broad Bash patterns. "Bash(git *)" is better than individual entries for git add, git commit, git push. The wildcard covers everything.

Scope writes tighter than reads. Reading ~/** is safe — it’s just looking at files. Writing should be scoped to your project directories.

Keep ~/.mcp.json clean. One entry per server. API keys in env blocks. No duplicates across config files.

Skip per-project permissions. If you find yourself creating project/.claude/settings.local.json, ask whether the permission should just be global instead. It almost always should. And remember: per-project settings.local.json files override the global settings.local.json — so if you do create one, your global rules stop applying in that project.

Put global permissions in settings.json. Don’t use settings.local.json for global permissions — they get silently overridden by per-project files. settings.json permissions are additive and always apply.