> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 02-4 claude.json: What It Stores and Why You Should Know It

> ~/.claude.json is Claude Code's global state file, storing MCP server registrations, project trust records, and tool approval state. Knowing its structure lets you pinpoint root causes when MCP fails to load or trust dialogs keep reappearing, without blindly reinstalling tools.

export const McpTroubleshoot = ({lang = "zh"}) => {
  const data = {
    zh: {
      title: "MCP 連不上：分支診斷",
      subtitle: "選最接近你症狀的那一項，走分支找到對應的檢查點與修法",
      symptoms: [{
        id: "status",
        label: "工具完全看不到，或 /mcp 顯示 failed",
        icon: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "你是用 claude mcp add 加進來的，還是手改設定檔？",
          options: [{
            label: "用 claude mcp add 加的",
            checks: [{
              title: "確認 scope 存對地方",
              body: "執行 claude mcp list，找到 server 條目。local / user scope 存 ~/.claude.json，project scope 存 .mcp.json（專案根）。打開對應檔案，確認命令路徑或 URL 沒有 typo。"
            }, {
              title: "stdio server：確認 env 完整",
              body: "stdio 類型的 server 若缺 API key 或 token，會在啟動時靜默失敗。用 claude mcp get <name> 看 env 欄位，把缺的補進去再重啟。"
            }, {
              title: "http server：確認 URL 可達",
              body: "用 curl 或 Invoke-RestMethod 直接打 server URL，排除 URL 打錯或服務未啟動。"
            }]
          }, {
            label: "手改了設定檔",
            checks: [{
              title: "備份後重新用指令加",
              body: "先 Copy-Item ~/.claude.json ~/.claude.json.bak。刪掉手改的 entry，改用 claude mcp add --scope <scope> 指令加回，讓工具自己寫入正確格式，避免 JSON 格式錯誤或欄位不符。"
            }, {
              title: "驗證 JSON 格式",
              body: "若堅持手改，用 pwsh -c Get-Content ~/.claude.json | ConvertFrom-Json 驗證 JSON 合法；常見問題：最後一個 key 後面多一個逗號（trailing comma）、或字串沒有加引號。"
            }]
          }]
        }]
      }, {
        id: "pending",
        label: "/mcp 顯示 Pending approval 或 Rejected",
        icon: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "這個 server 是 project scope（來自 .mcp.json）嗎？",
          options: [{
            label: "是，來自 .mcp.json",
            checks: [{
              title: "進 Claude Code 核准",
              body: "project-scoped server 首次使用前必須在 Claude Code 內部核准，這是設計行為。啟動 Claude Code，它會彈出核准對話，選「核准此 session」或「永久核准」。Pending 不是連線壞，是等你核准。"
            }, {
              title: "若被 Rejected 要重置",
              body: "執行 claude mcp reset-project-choices，清掉所有核准 / 拒絕紀錄，再重新啟動讓它再問一次。"
            }]
          }, {
            label: "不是，是 local 或 user scope",
            checks: [{
              title: "Pending 不該出現在 local / user scope",
              body: "local 與 user scope 不走 .mcp.json 信任流程，不會顯示 Pending。若有這個狀態，可能是 scope 設錯。用 claude mcp list 確認 scope 欄位，必要時刪掉重加並指定正確 --scope。"
            }]
          }]
        }]
      }, {
        id: "invisible",
        label: "這個 server 在別的專案看不到",
        icon: "M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z",
        branches: [{
          question: "你想讓這個 server 跨所有專案都能用嗎？",
          options: [{
            label: "是，跨所有專案共用",
            checks: [{
              title: "改成 user scope",
              body: "用 claude mcp add --scope user ... 重新加。user scope 存在 ~/.claude.json 根層，對你所有專案可見。先把舊的 local scope 刪掉（claude mcp remove <name>）再重加。"
            }]
          }, {
            label: "是，要讓團隊都能用",
            checks: [{
              title: "改成 project scope 並進版控",
              body: "用 claude mcp add --scope project ... 加，工具會把設定寫進 .mcp.json（專案根）。把 .mcp.json 進版控，團隊 clone 後核准一次就能用。注意：.mcp.json 是供應鏈風險，不要在裡面放明文密鑰，改用 env 指向環境變數。"
            }, {
              title: "團隊成員也需要各自核准",
              body: "每個人 clone 並信任目錄後，Claude Code 會要求他們各自核准 project-scoped server，這是設計行為（見第 4 節供應鏈 Warning）。"
            }]
          }, {
            label: "不，只想在當前專案用",
            checks: [{
              title: "local scope 正是正確行為",
              body: "local scope 本來就只對當前專案可見，存在 ~/.claude.json 裡你的這個專案路徑下。確認你在正確的工作目錄（cd 到專案根再操作），key 是你的專案**絕對路徑**。"
            }]
          }]
        }]
      }, {
        id: "trust",
        label: "信任對話每次都重新跳出",
        icon: "M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.85-.559.094-.994.559-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "你在什麼作業系統？",
          options: [{
            label: "Windows",
            checks: [{
              title: "路徑大小寫或斜線不一致",
              body: "Claude Code 以專案絕對路徑為 key 比對信任記錄。Windows 上最常見的成因：路徑大小寫不一致（C:\\Users 對上 c:\\users）、反斜線與正斜線混用、或路徑結尾有 / 有時沒有。打開 ~/.claude.json，找 projects 物件，比對 key 與你實際開啟的工作目錄路徑是否完全一致（包含大小寫）。"
            }, {
              title: "用 PowerShell 查當前路徑",
              body: "在 Claude Code 啟動前先在 PowerShell 執行 (Get-Item .).FullName，看它吐出的絕對路徑格式，再對比 claude.json 裡的 key。不一致就刪掉那個 key，讓工具重新寫入。"
            }]
          }, {
            label: "macOS / Linux",
            checks: [{
              title: "確認 projects 下有正確路徑的 key",
              body: "打開 ~/.claude.json，在 projects 物件裡找你的專案路徑。若找不到，或路徑有 trailing slash 差異，信任就不會持久化。刪掉不對的 key，重新信任讓工具寫入。"
            }]
          }]
        }]
      }],
      backBtn: "← 選其他症狀",
      checkTitle: "檢查點與修法",
      fixLabel: "修法"
    },
    en: {
      title: "MCP Not Connecting: Branch Diagnosis",
      subtitle: "Pick the symptom that best matches yours, follow the branch to find the checkpoints and fixes",
      symptoms: [{
        id: "status",
        label: "Tool completely absent, or /mcp shows failed",
        icon: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "Did you add this server with claude mcp add, or by editing a config file by hand?",
          options: [{
            label: "Added with claude mcp add",
            checks: [{
              title: "Confirm the scope is stored in the right place",
              body: "Run claude mcp list and locate the server entry. local / user scope is in ~/.claude.json; project scope is in .mcp.json (project root). Open the relevant file and verify there are no typos in the command path or URL."
            }, {
              title: "stdio server: verify env vars are complete",
              body: "A stdio server silently fails at startup when an API key or token is missing. Run claude mcp get <name> to inspect the env field, fill in any missing values, then restart."
            }, {
              title: "http server: verify the URL is reachable",
              body: "Use curl or Invoke-RestMethod to hit the server URL directly and rule out a mistyped URL or a service that is not running."
            }]
          }, {
            label: "Edited the config file by hand",
            checks: [{
              title: "Back up, then re-add using the CLI",
              body: "First run Copy-Item ~/.claude.json ~/.claude.json.bak. Remove the hand-edited entry, then use claude mcp add --scope <scope> to add it back and let the tool write the correct format, avoiding JSON formatting errors or unexpected field shapes."
            }, {
              title: "Validate JSON syntax",
              body: "If you must hand-edit, run pwsh -c Get-Content ~/.claude.json | ConvertFrom-Json to confirm the JSON is valid. Common problems: a trailing comma after the last key, or a string value that is missing quotes."
            }]
          }]
        }]
      }, {
        id: "pending",
        label: "/mcp shows Pending approval or Rejected",
        icon: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "Is this a project-scope server (coming from .mcp.json)?",
          options: [{
            label: "Yes, from .mcp.json",
            checks: [{
              title: "Approve it inside Claude Code",
              body: "Project-scoped servers must be approved inside Claude Code before first use -- this is by design. Launch Claude Code; an approval dialog will appear. Choose 'Approve for this session' or 'Approve permanently'. Pending does not mean the connection is broken; it means you have not approved it yet."
            }, {
              title: "If it was Rejected, reset choices",
              body: "Run claude mcp reset-project-choices to clear all approval and rejection records, then restart so Claude Code can prompt again."
            }]
          }, {
            label: "No, it is local or user scope",
            checks: [{
              title: "Pending should not appear for local / user scope",
              body: "local and user scope servers do not go through the .mcp.json trust flow and should never show Pending. If you see it anyway, the scope is probably wrong. Use claude mcp list to verify the scope field; if needed, remove and re-add with the correct --scope flag."
            }]
          }]
        }]
      }, {
        id: "invisible",
        label: "Server is invisible in other projects",
        icon: "M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z",
        branches: [{
          question: "Do you want this server available across all your projects?",
          options: [{
            label: "Yes, across all my projects",
            checks: [{
              title: "Switch to user scope",
              body: "Re-add it with claude mcp add --scope user .... User scope is stored at the root of ~/.claude.json and is visible across all your projects. Remove the old local-scope entry first with claude mcp remove <name>, then re-add."
            }]
          }, {
            label: "Yes, shared across the team",
            checks: [{
              title: "Switch to project scope and commit .mcp.json",
              body: "Add with claude mcp add --scope project .... The tool writes the configuration to .mcp.json at the project root. Commit .mcp.json to version control; teammates can clone and approve once to use it. Caution: .mcp.json is a supply chain risk -- never put plaintext secrets in it. Reference env vars instead."
            }, {
              title: "Each team member must approve independently",
              body: "After each person clones and trusts the directory, Claude Code will ask them to approve the project-scoped server. This is by design (see the Section 4 supply chain Warning)."
            }]
          }, {
            label: "No, only for the current project",
            checks: [{
              title: "local scope is the correct behavior",
              body: "local scope is intentionally limited to the current project. It lives in ~/.claude.json under your project's absolute path. Make sure you are in the correct working directory (cd to the project root first), and that the key is your project's absolute path."
            }]
          }]
        }]
      }, {
        id: "trust",
        label: "Trust dialog fires every time the project opens",
        icon: "M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.85-.559.094-.994.559-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
        branches: [{
          question: "What operating system are you on?",
          options: [{
            label: "Windows",
            checks: [{
              title: "Path case or slash mismatch",
              body: "Claude Code uses the absolute project path as the key for trust records. The most common Windows causes: path case inconsistency (C:\\Users vs c:\\users), mixing backslashes and forward slashes, or trailing slash present sometimes but not others. Open ~/.claude.json, find the projects object, and compare the key against the exact path your working directory reports -- including case."
            }, {
              title: "Check the exact path with PowerShell",
              body: "Before starting Claude Code, run (Get-Item .).FullName in PowerShell to see the canonical absolute path, then compare it against the key in claude.json. If they differ, delete the stale key and let the tool re-write it when you re-trust."
            }]
          }, {
            label: "macOS / Linux",
            checks: [{
              title: "Confirm the correct path key exists in projects",
              body: "Open ~/.claude.json and look for your project's absolute path in the projects object. If it is missing or has a trailing-slash discrepancy, trust will not persist. Delete the mismatched key and re-trust to let the tool write the correct one."
            }]
          }]
        }]
      }],
      backBtn: "← Choose another symptom",
      checkTitle: "Checkpoints and fixes",
      fixLabel: "Fix"
    }
  };
  const t = data[lang] || data.zh;
  const [selectedSymptom, setSelectedSymptom] = useState(null);
  const [selectedOption, setSelectedOption] = useState(null);
  const [expandedCheck, setExpandedCheck] = useState(null);
  const symptomData = selectedSymptom !== null ? t.symptoms[selectedSymptom] : null;
  const branch = symptomData ? symptomData.branches[0] : null;
  const optionData = branch && selectedOption !== null ? branch.options[selectedOption] : null;
  const reset = () => {
    setSelectedSymptom(null);
    setSelectedOption(null);
    setExpandedCheck(null);
  };
  const selectSymptom = i => {
    setSelectedSymptom(i);
    setSelectedOption(null);
    setExpandedCheck(null);
  };
  const selectOption = i => {
    setSelectedOption(i);
    setExpandedCheck(null);
  };
  const css = `
    .mt-root {
      font-family: inherit;
      margin: 1.25rem 0;
    }
    .mt-header {
      margin-bottom: 1rem;
    }
    .mt-title {
      font-size: 1rem;
      font-weight: 700;
      color: #bf7551;
      margin: 0 0 0.2rem;
    }
    .mt-subtitle {
      font-size: 0.82rem;
      color: #7a6a58;
      margin: 0;
    }
    .dark .mt-subtitle { color: #9a8878; }
    .mt-symptom-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 0.6rem;
    }
    @media (max-width: 540px) {
      .mt-symptom-grid { grid-template-columns: 1fr; }
    }
    .mt-symptom-btn {
      display: flex;
      align-items: flex-start;
      gap: 0.6rem;
      padding: 0.7rem 0.85rem;
      border: 1.5px solid #d6cec4;
      border-radius: 8px;
      background: #faf8f4;
      cursor: pointer;
      text-align: left;
      transition: border-color 0.15s, background 0.15s;
      font-size: 0.88rem;
      color: #3a2e26;
      line-height: 1.45;
    }
    .dark .mt-symptom-btn {
      background: #23201c;
      border-color: #3e3630;
      color: #ddd0c4;
    }
    .mt-symptom-btn:hover {
      border-color: #bf7551;
      background: rgba(191,117,81,0.06);
    }
    .dark .mt-symptom-btn:hover {
      background: rgba(191,117,81,0.12);
    }
    .mt-symptom-icon {
      flex-shrink: 0;
      margin-top: 1px;
    }
    .mt-branch-box {
      border: 1.5px solid #e0d6cc;
      border-radius: 10px;
      background: #faf8f4;
      overflow: hidden;
    }
    .dark .mt-branch-box {
      background: #23201c;
      border-color: #3e3630;
    }
    .mt-branch-header {
      padding: 0.8rem 1rem;
      border-bottom: 1px solid #e0d6cc;
      display: flex;
      align-items: center;
      gap: 0.5rem;
    }
    .dark .mt-branch-header { border-color: #3e3630; }
    .mt-back-btn {
      background: none;
      border: none;
      color: #bf7551;
      cursor: pointer;
      font-size: 0.82rem;
      padding: 0;
      font-weight: 600;
    }
    .mt-back-btn:hover { text-decoration: underline; }
    .mt-symptom-label {
      font-size: 0.88rem;
      font-weight: 600;
      color: #3a2e26;
    }
    .dark .mt-symptom-label { color: #ddd0c4; }
    .mt-question-section {
      padding: 0.85rem 1rem;
      border-bottom: 1px solid #e0d6cc;
    }
    .dark .mt-question-section { border-color: #3e3630; }
    .mt-question-label {
      font-size: 0.78rem;
      color: #8a7a6a;
      margin-bottom: 0.5rem;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.04em;
    }
    .dark .mt-question-label { color: #9a8878; }
    .mt-question-text {
      font-size: 0.92rem;
      color: #3a2e26;
      font-weight: 600;
      margin-bottom: 0.7rem;
    }
    .dark .mt-question-text { color: #ddd0c4; }
    .mt-option-list {
      display: flex;
      flex-direction: column;
      gap: 0.4rem;
    }
    .mt-option-btn {
      padding: 0.5rem 0.8rem;
      border: 1.5px solid #d6cec4;
      border-radius: 7px;
      background: #fff;
      cursor: pointer;
      text-align: left;
      font-size: 0.87rem;
      color: #4a3c30;
      transition: border-color 0.15s, background 0.15s;
    }
    .dark .mt-option-btn {
      background: #2a2520;
      border-color: #4a3e34;
      color: #ccc0b0;
    }
    .mt-option-btn:hover {
      border-color: #bf7551;
      background: rgba(191,117,81,0.07);
    }
    .mt-option-btn.active {
      border-color: #bf7551;
      background: rgba(191,117,81,0.1);
      font-weight: 600;
      color: #8a4a24;
    }
    .dark .mt-option-btn.active {
      background: rgba(191,117,81,0.18);
      color: #dba880;
    }
    .mt-checks-section {
      padding: 0.85rem 1rem;
    }
    .mt-checks-label {
      font-size: 0.78rem;
      color: #8a7a6a;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      margin-bottom: 0.6rem;
    }
    .dark .mt-checks-label { color: #9a8878; }
    .mt-check-item {
      border: 1px solid #e0d6cc;
      border-radius: 7px;
      margin-bottom: 0.45rem;
      overflow: hidden;
    }
    .dark .mt-check-item { border-color: #3e3630; }
    .mt-check-toggle {
      width: 100%;
      padding: 0.55rem 0.75rem;
      background: #f4f0ea;
      border: none;
      cursor: pointer;
      text-align: left;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 0.5rem;
      font-size: 0.88rem;
      font-weight: 600;
      color: #3a2e26;
      transition: background 0.15s;
    }
    .dark .mt-check-toggle {
      background: #2e2a24;
      color: #ddd0c4;
    }
    .mt-check-toggle:hover { background: #ece6de; }
    .dark .mt-check-toggle:hover { background: #362e26; }
    .mt-check-toggle.open {
      background: rgba(191,117,81,0.12);
      color: #8a4a24;
    }
    .dark .mt-check-toggle.open {
      background: rgba(191,117,81,0.2);
      color: #dba880;
    }
    .mt-chevron {
      flex-shrink: 0;
      transition: transform 0.2s;
    }
    .mt-chevron.open { transform: rotate(180deg); }
    .mt-check-body {
      padding: 0.6rem 0.75rem;
      font-size: 0.85rem;
      line-height: 1.6;
      color: #5a4a3a;
      background: #fdfbf8;
    }
    .dark .mt-check-body {
      color: #c0a890;
      background: #1e1a16;
    }
    .mt-divider { border: none; border-top: 1px solid #e0d6cc; margin: 0.7rem 0; }
    .dark .mt-divider { border-color: #3e3630; }
  `;
  return <div className="mt-root">
      <style>{css}</style>
      <div className="mt-header">
        <div className="mt-title">{t.title}</div>
        <p className="mt-subtitle">{t.subtitle}</p>
      </div>

      {selectedSymptom === null && <div className="mt-symptom-grid">
          {t.symptoms.map((s, i) => <button key={s.id} className="mt-symptom-btn" onClick={() => selectSymptom(i)}>
              <svg className="mt-symptom-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#bf7551" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d={s.icon} />
              </svg>
              {s.label}
            </button>)}
        </div>}

      {selectedSymptom !== null && symptomData && <div className="mt-branch-box">
          <div className="mt-branch-header">
            <button className="mt-back-btn" onClick={reset}>
              {t.backBtn}
            </button>
          </div>

          {branch && selectedOption === null && <div className="mt-question-section">
              <div className="mt-question-label">{symptomData.label}</div>
              <div className="mt-question-text">{branch.question}</div>
              <div className="mt-option-list">
                {branch.options.map((opt, i) => <button key={i} className="mt-option-btn" onClick={() => selectOption(i)}>
                    {opt.label}
                  </button>)}
              </div>
            </div>}

          {selectedOption !== null && optionData && <div>
              <div className="mt-question-section">
                <div className="mt-question-label">{symptomData.label}</div>
                <div className="mt-question-text">{branch.question}</div>
                <div className="mt-option-list">
                  {branch.options.map((opt, i) => <button key={i} className={`mt-option-btn${selectedOption === i ? " active" : ""}`} onClick={() => selectOption(i)}>
                      {opt.label}
                    </button>)}
                </div>
              </div>
              <div className="mt-checks-section">
                <div className="mt-checks-label">{t.checkTitle}</div>
                {optionData.checks.map((c, i) => <div key={i} className="mt-check-item">
                    <button className={`mt-check-toggle${expandedCheck === i ? " open" : ""}`} onClick={() => setExpandedCheck(expandedCheck === i ? null : i)}>
                      <span>{c.title}</span>
                      <svg className={`mt-chevron${expandedCheck === i ? " open" : ""}`} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                        <polyline points="6 9 12 15 18 9" />
                      </svg>
                    </button>
                    {expandedCheck === i && <div className="mt-check-body">{c.body}</div>}
                  </div>)}
              </div>
            </div>}
        </div>}
    </div>;
};

export const LearnerPrimer = ({items = [], lang = "zh"}) => {
  const t = lang === "en" ? {
    title: "Before this unit, be honest with yourself",
    sub: "If you can't answer these, that gap is exactly what this unit closes."
  } : {
    title: "讀這個單元前，先誠實面對",
    sub: "這幾題答不出來，正是這個單元要替你補的洞。"
  };
  const css = `
  .lp-root{--lp-bg:#FAF7F1;--lp-surface:rgba(191,117,81,0.05);--lp-border:rgba(0,0,0,0.09);--lp-edge:rgba(191,117,81,0.42);--lp-text:#2b2722;--lp-dim:#6f6a62;--lp-accent:#bf7551;border:1px solid var(--lp-border);border-left:3px solid var(--lp-edge);border-radius:13px;background:var(--lp-bg);color:var(--lp-text);overflow:hidden;margin:1.25rem 0;}
  .dark .lp-root{--lp-bg:#1b1a18;--lp-surface:rgba(207,138,104,0.07);--lp-border:rgba(255,255,255,0.08);--lp-edge:rgba(207,138,104,0.5);--lp-text:#e7e3da;--lp-dim:#a8a299;--lp-accent:#cf8a68;}
  .lp-head{display:flex;align-items:center;gap:9px;padding:13px 18px 11px;border-bottom:1px solid var(--lp-border);background:var(--lp-surface);}
  .lp-ic{color:var(--lp-accent);flex-shrink:0;}
  .lp-htx{display:flex;flex-direction:column;gap:1px;min-width:0;}
  .lp-title{font-size:14px;font-weight:650;line-height:1.3;letter-spacing:.01em;}
  .lp-sub{font-size:12px;color:var(--lp-dim);line-height:1.4;}
  .lp-list{list-style:none;margin:0;padding:10px 18px 14px;display:flex;flex-direction:column;gap:0;}
  .lp-item{display:flex;align-items:baseline;gap:11px;padding:7px 0;font-size:14px;line-height:1.6;border-top:1px solid var(--lp-border);}
  .lp-item:first-child{border-top:none;}
  .lp-mark{flex-shrink:0;color:var(--lp-accent);font-size:13px;font-weight:700;line-height:1.55;font-variant-numeric:tabular-nums;opacity:.85;}
  .lp-q{flex:1 1 0;min-width:0;color:var(--lp-text);}
  @media (max-width:620px){.lp-head{padding:12px 14px 10px;}.lp-list{padding:8px 14px 12px;}}
  `;
  return <div className="lp-root">
      <style>{css}</style>
      <div className="lp-head">
        <svg className="lp-ic" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="6" /><circle cx="12" cy="12" r="2" /></svg>
        <span className="lp-htx">
          <span className="lp-title">{t.title}</span>
          <span className="lp-sub">{t.sub}</span>
        </span>
      </div>
      <ul className="lp-list">
        {items.map((q, i) => <li className="lp-item" key={i}>
            <span className="lp-mark">{String(i + 1).padStart(2, "0")}</span>
            <span className="lp-q">{q}</span>
          </li>)}
      </ul>
    </div>;
};

<LearnerPrimer
  lang="en"
  items={[
"claude.json is a state file, not a policy file. Don't hand-write policy into it.",
"Anything you 'always allow' by hand in claude.json gets overwritten on the next update.",
"MCP local scope is not the same as settings.local.json.",
"Trust dialog keeps reappearing? On Windows it's almost always a path-case mismatch.",
"A cloned repo that ships .mcp.json requires your approval before connecting by default.",
"Edit claude.json without a backup and you may lose all MCP and trust configuration.",
"When MCP won't connect, look at claude.json first, not at reinstalling the tool.",
]}
/>

<Info>
  **What this unit solves**

  `~/.claude.json` is Claude Code's global state file. It stores MCP server registrations, per-project trust and tool-approval records, and a collection of onboarding and account metadata. It is not a policy file for you to hand-edit; the tool writes to it. But knowing what it stores lets you open the file and locate the root cause directly when MCP won't load, permission dialogs keep firing, or trust state seems corrupted, rather than blindly reinstalling. This unit gives you a field-by-field tour, a verifiable MCP scope model, and a clear criterion for when hand-editing is acceptable and when it is not.
</Info>

## Learning objectives

* [ ] Describe the broad categories of information stored in `~/.claude.json` and identify the sections most useful for troubleshooting.
* [ ] Distinguish the responsibility boundary between `claude.json` (state and registration) and `settings.json` (behavioral policy), and avoid mixing them up.
* [ ] Explain MCP's three scopes (local / project / user), where each is stored, and how `.mcp.json` project trust works.
* [ ] Use the contents of `claude.json` to locate the root cause when an MCP server fails to load or a trust dialog fires repeatedly.

***

## 1. What claude.json is: a state file, not a policy file

`~/.claude.json` is a user-level state file located in your home directory (on Windows: `C:\Users\<your-account>\.claude.json`). Claude Code itself is the primary writer; if the file does not exist at first launch it is created automatically.

It has a fundamental boundary with the `settings.json` discussed in [02-3](/en/code-agent/configuration/settings-json):

| File             | The question it answers                                                                                             | Who writes it                     | Should you hand-edit it                                        |
| ---------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------- |
| `~/.claude.json` | "What has Claude Code remembered?" (which MCPs exist, does this project have trust, which tools have been approved) | Written automatically by the tool | No (with narrow exceptions for troubleshooting; see Section 5) |
| `settings.json`  | "How should Claude Code behave?" (allow / deny policy, hooks, env)                                                  | You                               | Yes                                                            |

**`settings.json` describes "how to behave"**: permission policy, hooks, env -- the policy file you edit. **`claude.json` records "what has happened"**: which MCP servers were registered, which project you trusted, which tools you clicked "always allow" on. This is state maintained by the tool, not something you write.

Keeping this boundary clear avoids the most common misuse: hand-writing "I always want to allow this tool" intent into `claude.json` rather than into `settings.json`'s `permissions.allow`. The former is state (written by the tool); the latter is policy (written by you). Mixing them means the tool's next state update will overwrite whatever you wrote by hand.

## 2. What it stores: a field tour

The most important section of `~/.claude.json` is `projects`: an object keyed by **absolute project paths**, with each project's private state underneath, including the MCP servers registered at local scope for that project. The structure the official documentation provides (after you add a local server from `/path/to/your/project`) looks like this \[1]:

```json theme={null}
{
  "projects": {
    "/path/to/your/project": {
      "mcpServers": {
        "stripe": {
          "type": "http",
          "url": "https://mcp.stripe.com"
        }
      }
    }
  }
}
```

Beyond `projects`, the file stores a batch of tool-managed metadata: whether onboarding is complete, theme preference, launch count, login account information, and similar internal state.

<Warning>
  **Anthropic does not publish a complete claude.json schema**

  The official documentation formally documents the MCP server structure at `~/.claude.json`'s `projects.<path>.mcpServers` \[1]. The remaining fields (theme, launch counter, account metadata, persisted tool approvals, etc.) are largely internal tool state; Anthropic has not published a complete schema (as of 2026-05). The descriptions of non-MCP fields in this section reflect what is commonly observed in practice, not a field-by-field official contract. This is exactly why you should not rely on hand-editing those fields: the format may change with tool updates.
</Warning>

When troubleshooting, the sections you will actually use are `projects.<your-project-path>.mcpServers` (first place to look when MCP won't connect) and the project's trust and tool-approval state (look here when the trust dialog keeps firing).

## 3. Division of labor with settings.json: memory vs. policy

Two practical rules: write policy into `settings.json`, not `claude.json`; let the tool maintain state, and do not reach around the tool to hand-edit trust records in `claude.json`.

## 4. MCP's three scopes and .mcp.json trust

This is the section most worth clarifying when learning `claude.json`, because MCP configuration is spread across different locations and putting it in the wrong place produces a frustrating "I clearly added this but it's invisible in my other project" experience. Claude Code's MCP servers have three scopes (as of 2026-05) \[1]:

| Scope               | Loaded by            | Shared with team          | Stored in                                    |
| ------------------- | -------------------- | ------------------------- | -------------------------------------------- |
| **local** (default) | Current project only | No                        | `~/.claude.json` (under that project's path) |
| **project**         | Current project only | Yes (via version control) | `.mcp.json` (project root)                   |
| **user**            | All your projects    | No                        | `~/.claude.json`                             |

When using `claude mcp add`, specify scope with `--scope` (options go **before** the server name, `--` precedes the command) \[1]:

```bash theme={null}
# local (default): current project only, private, stored in ~/.claude.json
claude mcp add --transport http stripe https://mcp.stripe.com

# project: team-shared, written to .mcp.json (committed to version control)
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp

# user: available across all your projects
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic
```

When the same server is defined in multiple places, Claude Code uses the **highest-priority complete entry** (fields are not merged across scopes): local > project > user > plugin-provided > claude.ai connector \[1].

<Warning>
  **MCP's "local scope" is not settings.local.json**

  This is a confusion point the official documentation explicitly calls out \[1]: a local-scoped MCP server lives in `~/.claude.json` (your home directory); the general local settings discussed in [02-3](/en/code-agent/configuration/settings-json) live in `.claude/settings.local.json` (the project directory). Both have "local" in the name but serve entirely different purposes and live in entirely different places. One means "this MCP server is visible only in this project and only to me"; the other means "my personal settings override layer."
</Warning>

**`.mcp.json` project trust**: the official documentation states explicitly that, for security, Claude Code requires you to approve a project-scoped server from `.mcp.json` before using it \[1]. Servers awaiting approval show `⏸ Pending approval` in `claude mcp list`; rejected servers show `✗ Rejected`. To reset these approval choices, run `claude mcp reset-project-choices`.

## 4.1 .mcp.json: the project-scope configuration file

`.mcp.json` lives at the project root and is the source of project-scoped MCP server definitions. Its role is distinct from `claude.json` and `settings.json`. Here is how the three map out (as of 2026-05) \[1]\[2]:

| File                          | Scope                                                    | Version-controlled | Who writes it                             | Who reads it |
| ----------------------------- | -------------------------------------------------------- | ------------------ | ----------------------------------------- | ------------ |
| `~/.claude.json`              | local (current project, private) and user (all projects) | No                 | Claude Code tool (automatic)              | Claude Code  |
| `~/.claude/settings.json`     | user global                                              | Your choice        | You                                       | Claude Code  |
| `.claude/settings.json`       | project                                                  | Yes (team-shared)  | You                                       | Claude Code  |
| `.claude/settings.local.json` | local personal override                                  | No (gitignored)    | You                                       | Claude Code  |
| `.mcp.json`                   | project MCP servers                                      | Yes (team-shared)  | You (or `claude mcp add --scope project`) | Claude Code  |

The format of `.mcp.json` is the same as the `mcpServers` block inside `~/.claude.json`:

```json theme={null}
{
  "mcpServers": {
    "stripe": {
      "type": "http",
      "url": "https://mcp.stripe.com",
      "headers": {
        "Authorization": "Bearer ${STRIPE_API_KEY}"
      }
    },
    "local-tools": {
      "type": "stdio",
      "command": "node",
      "args": [".claude/mcp-tools/index.js"],
      "env": {
        "LOG_LEVEL": "info"
      }
    }
  }
}
```

**Per-person enable/disable**: `settings.local.json` (not committed) accepts `enabledMcpjsonServers` and `disabledMcpjsonServers` to control which servers from `.mcp.json` are active for you, without affecting teammates \[2]:

```json theme={null}
{
  "enabledMcpjsonServers": ["stripe"],
  "disabledMcpjsonServers": ["local-tools"]
}
```

<Warning>
  **Supply chain risk: no plaintext secrets in `.mcp.json`**

  `.mcp.json` is committed to version control and is visible to the entire team, including git history. Always reference secrets (API keys, tokens) via environment variable syntax (`${ENV_VAR_NAME}`) and inject them from each person's local environment or CI. Writing a secret in plaintext pushes it to git and makes clean removal very difficult. Security checkpoint: when you clone an external repo, inspect its `.mcp.json` for unrecognized servers before trusting and approving the directory. For the full threat model see [03-3](/en/code-agent/judgment/security-privacy-supply-chain).
</Warning>

### MCP not connecting: branch diagnosis

<McpTroubleshoot lang="en" />

## 5. Security boundaries and troubleshooting: when to touch it, when not to

Most of the time, leave `claude.json` alone. There are two categories of situations where hand-editing is reasonable, and two where it will break state.

**Safe to hand-edit**: removing a stale MCP server entry (deleting one key from `mcpServers`); clearing an incorrect path trust record pointing to a project that has been moved.

**Do not hand-edit**: bypassing the tool to directly change trust or approval records will put the file state out of sync with what the tool believes; editing an MCP server command without updating its env vars will cause the server to fail at startup while Claude Code still attempts to connect.

<Warning>
  **Back up before editing**

  A damaged `claude.json` will disable all MCP and trust configuration. Back up first, then confirm the tool is working normally before removing the backup:

  ```powershell theme={null}
  Copy-Item ~/.claude.json ~/.claude.json.bak
  ```
</Warning>

**MCP troubleshooting order**:

<Steps>
  <Step title="Check claude.json or .mcp.json">
    Look at `~/.claude.json`'s `projects.<current-project-path>.mcpServers` (or `.mcp.json`) and verify the command path or URL for the server in question.
  </Step>

  <Step title="Verify env vars are complete">
    A stdio server missing an env key (API key or token) will fail to start.
  </Step>

  <Step title="Query status with tool commands">
    Use `claude mcp list` / `claude mcp get <name>` to inspect status, or use `/mcp` inside Claude Code to see connection state and tool count \[1].
  </Step>

  <Step title="Check for Pending approval">
    A project-scoped server showing `⏸ Pending approval` has not been approved yet -- the connection is not broken.
  </Step>
</Steps>

**Trust dialog firing repeatedly**: usually means there is no matching path in `projects`, or the path does not match. On Windows, pay particular attention to path case, mixing backslashes and forward slashes, and trailing slash inconsistencies -- any of these will be treated as a different project, triggering the trust request every time.

<Warning>
  **Supply chain risk: .mcp.json in an external repo**

  When you clone an external repo it may include a `.mcp.json` defining MCP servers you do not recognize. Although project-scoped servers require your approval by default \[1], if the `enableAllProjectMcpServers` auto-approve option is enabled, it effectively opens MCP execution to the external repo \[2]. Checkpoint: after cloning an external repo and before trusting the directory, look at its `.mcp.json` and your `~/.claude.json` to confirm no unrecognized servers have been added automatically. For the full threat model and related CVEs, see [03-3](/en/code-agent/judgment/security-privacy-supply-chain).
</Warning>

## Tool comparison

The "tool-maintained state file" and "MCP scope layering" concepts vary considerably in maturity across tools (as of 2026-05; exact paths and keys are subject to each vendor's current documentation):

| Concept                             | Anthropic Claude (primary)                                                  | OpenAI (Codex)                                            | Google (Gemini CLI)                   | Cursor                                 |
| ----------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------- | -------------------------------------- |
| Tool-maintained state file          | `~/.claude.json` (MCP registration, trust, tool approvals) \[1]             | Refer to OpenAI official docs                             | Refer to Google official docs         | Refer to Cursor official docs          |
| MCP server config location          | local / user stored in `~/.claude.json`; project stored in `.mcp.json` \[1] | MCP section in config.toml (exact keys per official docs) | `mcpServers` section in settings.json | `.cursor/mcp.json` (per official docs) |
| MCP scope layering                  | local / project / user, three layers \[1]                                   | Per official docs                                         | Per official docs                     | Per official docs                      |
| Project MCP requires trust approval | Yes, `.mcp.json` requires approval by default \[1]                          | Per official docs                                         | Per official docs                     | Per official docs                      |

<Note>
  **MCP is a cross-tool standard; scope models differ**

  MCP is an open standard and most agent tools support connecting to MCP servers. The differences lie in where configuration is stored, whether there is scope layering, and whether project-level servers require forced approval. Claude Code's three-layer local / project / user model combined with mandatory `.mcp.json` approval is currently the most complete \[1]; other tools typically have a single configuration location with weaker or less documented scope and trust models. Precise details are subject to each vendor's current official documentation.
</Note>

## Hands-on exercises

<Steps>
  <Step title="Read ~/.claude.json and identify the three key sections">
    Open the file, locate `projects.<one-of-your-project-paths>`, and identify the `mcpServers` underneath (the local-scope servers). Cross-reference with `claude mcp list` output to confirm the file contents match what the tool believes.
  </Step>

  <Step title="Simulate an MCP connection failure and locate it">
    First back up with `Copy-Item ~/.claude.json ~/.claude.json.bak`. Deliberately corrupt the command path of a stdio server, launch Claude Code, and observe its status with `/mcp` (it should show a failure). Then work through the Section 5 troubleshooting sequence to locate and correct it. Delete the backup once everything is confirmed working.
  </Step>
</Steps>

## Common pitfalls

<Warning>
  **Anti-pattern list**

  * **Hand-writing "always allow" into claude.json**: to batch-approve tools in advance, edit `settings.json`'s `permissions.allow` (policy). Hand-editing the approval records in `claude.json` (state) will be overwritten when the tool next updates state.
  * **Reinstalling the tool when MCP won't connect**: look first at whether the command path and env vars for the server entry in `claude.json` / `.mcp.json` are correct. Most problems are there, not in the tool itself.
  * **Clicking "allow for this session only" then complaining that every session asks again**: that option is intentionally non-persistent. To make it stick, choose the persistent option and let the tool write the state.
  * **Treating MCP's local scope as the same as settings.local.json**: they occupy different locations and serve different purposes (see the Section 4 callout). Store it in the wrong place and the server will not be found.
  * **Editing claude.json without a backup**: a corrupted file disables all MCP and trust configuration.
</Warning>

## Self-check

<Check>
  **The bar for passing this unit**

  1. Can you state in one sentence what distinguishes `claude.json` (state, written by the tool) from `settings.json` (policy, written by you)? If you want to batch-allow a tool, which file do you edit?
  2. You want an MCP server visible only in a specific project and not shared with the team. Which scope do you use and which file does it live in? What about sharing with the team?
  3. MCP local scope is stored in `~/.claude.json`. What is `.claude/settings.local.json`? How are the two different?
  4. The trust dialog keeps firing in the same project. Which section of `claude.json` do you check first? What is the most likely cause on Windows?
  5. You clone an external repo that ships a `.mcp.json`. By default, will its servers connect immediately? What setting would cause them to be auto-approved?
</Check>

## Sources and further reading

Factual claims are grounded in official documentation; fast-changing items are annotated as of 2026-05.

<div className="references">
  * \[1] Anthropic, "Connect Claude Code to tools via MCP," Claude Code Docs. (MCP three scopes local / project / user and storage locations; `~/.claude.json`'s `projects.<path>.mcpServers` structure; `.mcp.json` project-scoped server requires approval, `claude mcp reset-project-choices`, `⏸ Pending approval`; `claude mcp add --scope`; MCP local scope is distinct from settings.local.json; scope priority order; `.mcp.json` `mcpServers` format) [https://code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) (as of 2026-05)

  * \[2] Anthropic, "Claude Code settings," Claude Code Docs. (`enableAllProjectMcpServers` auto-approves project MCP; `enabledMcpjsonServers` / `disabledMcpjsonServers` for per-person enable/disable of `.mcp.json` servers; managed-layer `allowedMcpServers` / `deniedMcpServers`) [https://code.claude.com/docs/en/settings](https://code.claude.com/docs/en/settings) (as of 2026-05)
</div>

* Related: [02-3](/en/code-agent/configuration/settings-json) on the policy side of settings.json and local settings; [02-5](/en/code-agent/configuration/skills-hooks-subagents-plugins) on plugin-provided MCP servers; [01-6](/en/code-agent/foundations/harness-engineering) on where permissions and trust sit within the harness; [03-3](/en/code-agent/judgment/security-privacy-supply-chain) on the supply chain threat model for external repos and MCP; [04-9](/en/code-agent/customization/mcp-integration) for in-depth MCP integration configuration.
