> ## 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：它存了什麼、為什麼你該認得它

> ~/.claude.json 是 Claude Code 的全域狀態檔，存 MCP server 註冊、專案信任紀錄、工具核准狀態。認得它的結構，能讓你在 MCP 連不上、信任對話反覆跳出時，直接定位根因，不再盲目重裝。

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="zh"
  items={[
"claude.json 是狀態檔，不是策略檔，別手寫策略進去。",
"你「永遠允許」手寫進 claude.json，下次更新被覆蓋。",
"MCP local scope 不等於 settings.local.json。",
"信任對話反覆跳出？Windows 上多半是路徑大小寫不一。",
"clone 外來 repo 帶 .mcp.json，預設要核准你才連。",
"手改 claude.json 不備份，下次連 MCP 都連不上。",
"排查 MCP 連不上先看 claude.json，不是重裝工具。",
]}
/>

<Info>
  **這個單元解決什麼問題**

  `~/.claude.json` 是 Claude Code 的全域狀態檔，存 MCP server 註冊、各專案的信任與工具核准紀錄、以及一堆 onboarding 與帳號後設資料。它不是給你手寫的策略檔，主要由工具自己寫入。但認得它存了什麼，能讓你在 MCP 連不上、權限對話反覆跳出、信任狀態錯亂時，直接打開檔案定位根因，而不是盲目重裝工具。這個單元給你欄位導覽、可查證的 MCP scope 模型，以及一條「何時能手改、何時別碰」的判準。
</Info>

## 學習目標

* [ ] 能說出 `~/.claude.json` 大致儲存哪幾類資訊，並指出排查時用得到的段落。
* [ ] 能分清 `claude.json`（狀態與註冊）與 `settings.json`（行為策略）的職責邊界，不混用。
* [ ] 能說明 MCP 的三種 scope（local / project / user）各存在哪，以及 `.mcp.json` 的專案信任如何運作。
* [ ] 能用 `claude.json` 的內容定位 MCP 不載入或信任對話重複跳出的根因。

***

## 1. claude.json 是什麼：狀態檔，不是策略檔

`~/.claude.json` 是使用者層級的狀態檔，位在你的 home 目錄（Windows 是 `C:\Users\<你的帳號>\.claude.json`）。它由 Claude Code 工具本身寫入為主，初次啟動時若不存在會自動建立。

它和 [02-3](/code-agent/configuration/settings-json) 的 `settings.json` 有一條根本分界：

| 檔案               | 回答的問題                                           | 誰寫     | 你該手動編輯嗎            |
| ---------------- | ----------------------------------------------- | ------ | ------------------ |
| `~/.claude.json` | 「Claude Code 記住了什麼」（MCP 在哪、這專案信任嗎、哪些工具核准過）      | 工具自動寫入 | 否（少數排查情境例外，見第 5 節） |
| `settings.json`  | 「Claude Code 應該怎麼行動」（allow / deny 策略、hooks、env） | 你      | 是                  |

**`settings.json` 描述「應該怎麼行為」**：權限策略、hooks、env，是你編輯的策略檔。**`claude.json` 記錄「已經發生了什麼」**：哪些 MCP server 註冊過、哪個專案你信任過、哪些工具你按了「永遠允許」。這是工具維護的狀態，不是給你手寫的。

記住這條分界，就能避開最常見的誤用：把「想永遠允許某工具」的意圖手寫進 `claude.json`，而不是寫 `settings.json` 的 `permissions.allow`。前者是狀態（工具寫的），後者是策略（你寫的），混用會在工具下次更新狀態時被覆蓋。

## 2. 它存了什麼：欄位導覽

`~/.claude.json` 最重要的段落是 `projects`：一個以**專案絕對路徑為 key** 的物件，每個專案底下記著它專屬的狀態，包括該專案 local scope 的 MCP server。官方文件給的結構長這樣（你從 `/path/to/your/project` 加一個 local server 後）\[1]：

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

除了 `projects`，這個檔案還存一批工具自管的後設資料：onboarding 是否完成、主題、啟動次數、登入帳號資訊等。

<Warning>
  **官方未提供 claude.json 的完整 schema 文件**

  Anthropic 官方文件正式記載的是 MCP server 在 `~/.claude.json` 的 `projects.<path>.mcpServers` 結構 \[1]；至於檔案裡其餘欄位（主題、啟動計數、帳號後設資料、工具核准的持久化等）多屬工具內部狀態，官方未公開完整 schema（截至 2026-05）。所以本節對非 MCP 欄位的描述是實務常見內容，不是逐欄官方契約。這也正是你不該依賴手改這些欄位的原因：格式可能隨版本變動。
</Warning>

排查時你會用到的，主要就是 `projects.<你的專案路徑>` 底下的 `mcpServers`（MCP 連不上時第一個看這裡），以及該專案的信任與工具核准狀態（信任對話反覆跳出時看這裡）。

## 3. 與 settings.json 的分工：記憶 vs 策略

實務上的兩條規則：策略寫 `settings.json`，別把策略塞進 `claude.json`；狀態由工具維護，別繞過工具去手改 `claude.json` 裡的信任紀錄。

## 4. MCP 的三種 scope 與 .mcp.json 信任

這是認識 `claude.json` 最需要釐清的一塊，因為 MCP 設定散在不同地方，存錯位置就會「我明明加了卻在別的專案看不到」。Claude Code 的 MCP server 有三種 scope（截至 2026-05）\[1]：

| Scope         | 載入範圍  | 與團隊共享  | 存在哪                      |
| ------------- | ----- | ------ | ------------------------ |
| **local**（預設） | 僅當前專案 | 否      | `~/.claude.json`（該專案路徑下） |
| **project**   | 僅當前專案 | 是（經版控） | `.mcp.json`（專案根）         |
| **user**      | 你所有專案 | 否      | `~/.claude.json`         |

用 `claude mcp add` 加時用 `--scope` 指定（options 要放在 server 名稱**前面**，`--` 後接命令）\[1]：

```bash theme={null}
# local（預設）：只在當前專案、私有，存 ~/.claude.json
claude mcp add --transport http stripe https://mcp.stripe.com

# project：團隊共享，寫進 .mcp.json（進版控）
claude mcp add --transport http paypal --scope project https://mcp.paypal.com/mcp

# user：跨你所有專案
claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic
```

同一個 server 在多處定義時，Claude Code 依優先序取**最高的那一份完整 entry**（不跨 scope 合併欄位）：local > project > user > plugin 提供 > claude.ai connector \[1]。

<Warning>
  **MCP 的「local scope」不是 settings.local.json**

  這是官方特別標出的易混點 \[1]：MCP 的 local-scoped server 存在 `~/.claude.json`（你的 home 目錄）；而 [02-3](/code-agent/configuration/settings-json) 講的 general local 設定存在 `.claude/settings.local.json`（專案目錄）。名字都有 local，位置與用途完全不同。一個是「這個 MCP server 只在這專案、只給我」，一個是「我的個人設定覆寫層」。
</Warning>

**`.mcp.json` 的專案信任**：官方明說，基於安全考量，使用來自 `.mcp.json` 的 project-scoped server 前，Claude Code 會先要求你核准 \[1]。等待核准的 server 在 `claude mcp list` 顯示 `⏸ Pending approval`，被拒的顯示 `✗ Rejected`。要重設這些核准選擇，用 `claude mcp reset-project-choices`。

## 4.1 .mcp.json：專案 scope 的設定檔

`.mcp.json` 放在專案根目錄，是 project-scoped MCP server 的設定來源。它的定位與用途和 `claude.json` / `settings.json` 不同，三者差異如下（截至 2026-05）\[1]\[2]：

| 檔案                            | scope                      | 進版控          | 誰寫                                    | 誰讀          |
| ----------------------------- | -------------------------- | ------------ | ------------------------------------- | ----------- |
| `~/.claude.json`              | local（當前專案私有）& user（跨所有專案） | 否            | Claude Code 工具自動寫入                    | Claude Code |
| `~/.claude/settings.json`     | user 全域                    | 依個人習慣        | 你                                     | Claude Code |
| `.claude/settings.json`       | project                    | 是（團隊共享）      | 你                                     | Claude Code |
| `.claude/settings.local.json` | local 個人覆寫                 | 否（gitignore） | 你                                     | Claude Code |
| `.mcp.json`                   | project MCP server         | 是（團隊共享）      | 你（或 `claude mcp add --scope project`） | Claude Code |

`.mcp.json` 的格式與 `~/.claude.json` 裡的 `mcpServers` 區塊相同：

```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"
      }
    }
  }
}
```

**個人啟停控制**：`settings.local.json`（不進版控）可用 `enabledMcpjsonServers` 與 `disabledMcpjsonServers` 控制哪些 `.mcp.json` 裡的 server 對你生效，不影響其他人 \[2]：

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

<Warning>
  **供應鏈風險：`.mcp.json` 裡禁止明文密鑰**

  `.mcp.json` 進版控，整個團隊都看得到它的內容，也包括 git 歷史。密鑰（API key / token）一律改用環境變數引用（`${ENV_VAR_NAME}`），由每個人的本機環境或 CI 注入；明文寫進 `.mcp.json` 等於把密鑰推上 git，難以乾淨清除。安全邊界：clone 外部 repo 時，先看它的 `.mcp.json` 有沒有不認得的 server，再決定是否信任並核准（詳見 [03-3](/code-agent/judgment/security-privacy-supply-chain)）。
</Warning>

### MCP 連不上：分支診斷

<McpTroubleshoot lang="zh" />

## 5. 安全邊界與排查：何時能動、何時別碰

`claude.json` 多數時候別手改，但有兩類情境手改是合理的，另兩類則會弄壞狀態。

**可以手改**：移除過期的 MCP server 項目（從 `mcpServers` 刪一個 key）；清掉指向已搬移專案的錯誤路徑信任紀錄。

**不要手改**：繞過工具直接改信任或核准紀錄，會讓檔案狀態與工具認知不一致；改了 MCP server 命令卻沒同步更新它的 env 變數，server 會啟動失敗，而 Claude Code 仍嘗試連線。

<Warning>
  **手改前先備份**

  `claude.json` 損壞會讓所有 MCP 與信任設定失效。手改前先備份，確認工具正常後再刪：

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

**MCP 連不上的排查順序**：

<Steps>
  <Step title="查 claude.json / .mcp.json">
    看 `~/.claude.json` 的 `projects.<當前專案路徑>.mcpServers`（或 `.mcp.json`）裡該 server 的命令路徑、URL 是否正確。
  </Step>

  <Step title="確認 env 變數完整">
    stdio server 缺 key 會啟動失敗。
  </Step>

  <Step title="用工具指令查狀態">
    用 `claude mcp list` / `claude mcp get <name>` 看狀態，或在 Claude Code 內用 `/mcp` 看連線與工具數 \[1]。
  </Step>

  <Step title="確認是否 Pending">
    project-scoped server 若顯示 `⏸ Pending approval`，是還沒核准，不是連線壞。
  </Step>
</Steps>

**信任對話反覆跳出的排查**：通常是 `projects` 裡沒有對應路徑、或路徑不符。Windows 特別注意：路徑大小寫、反斜線與正斜線、trailing slash 不一致都會被當成不同專案，於是每次都重新要求信任。

<Warning>
  **供應鏈風險：外來 repo 的 .mcp.json**

  clone 一個外部 repo 時，它可能帶 `.mcp.json`，裡面定義你不認得的 MCP server。雖然 project-scoped server 預設要你核准才會用 \[1]，但若 `enableAllProjectMcpServers` 這類自動核准選項被開啟，等於對外來 repo 敞開 MCP 執行權 \[2]。檢查點：clone 外部 repo 後、信任目錄前，先看一眼它的 `.mcp.json` 與你的 `~/.claude.json`，確認沒有不認得的 server 被自動加入。完整的威脅模型與相關 CVE 見 [03-3](/code-agent/judgment/security-privacy-supply-chain)。
</Warning>

## 工具對照

「工具自維護的狀態檔」與「MCP scope 分層」這兩個概念，各家成熟度差異大（截至 2026-05，精確路徑與鍵以各官方文件為準）：

| 概念              | Anthropic Claude（主範本）                                      | OpenAI (Codex)                   | Google (Gemini CLI)             | Cursor                      |
| --------------- | ---------------------------------------------------------- | -------------------------------- | ------------------------------- | --------------------------- |
| 工具自維護的狀態檔       | `~/.claude.json`（MCP 註冊、信任、工具核准）\[1]                       | 以 OpenAI 官方文件為準                  | 以 Google 官方文件為準                 | 以 Cursor 官方文件為準             |
| MCP server 設定位置 | local / user 存 `~/.claude.json`；project 存 `.mcp.json` \[1] | config.toml 內 MCP 區段（精確鍵以官方文件為準） | settings.json 的 `mcpServers` 區段 | `.cursor/mcp.json`（以官方文件為準） |
| MCP scope 分層    | local / project / user 三層 \[1]                             | 以官方文件為準                          | 以官方文件為準                         | 以官方文件為準                     |
| 專案級 MCP 需信任核准   | 是，`.mcp.json` 預設要核准 \[1]                                   | 以官方文件為準                          | 以官方文件為準                         | 以官方文件為準                     |

<Note>
  **MCP 是跨工具標準，scope 模型各異**

  MCP 本身是開放標準，多數 agent 工具都支援連接 MCP server；差別在「設定存哪、有沒有 scope 分層、專案級 server 要不要強制核准」。Claude Code 的 local / project / user 三層加 `.mcp.json` 強制核准目前最完整 \[1]；其他工具多半只有單一設定位置，scope 與信任模型較弱或未明確文件化，精確細節以各工具官方文件為準。
</Note>

## 動手做

<Steps>
  <Step title="讀出 ~/.claude.json，辨認三個關鍵段落">
    打開檔案，找到 `projects.<你的某個專案路徑>`，辨認它底下的 `mcpServers`（local scope 的 server）。對照 `claude mcp list` 的輸出，確認檔案內容與工具認知一致。
  </Step>

  <Step title="模擬一次 MCP 連線失敗並定位它">
    先 `Copy-Item ~/.claude.json ~/.claude.json.bak` 備份。把某個 stdio server 的命令路徑故意改錯，啟動 Claude Code，用 `/mcp` 觀察它的狀態（應顯示連線失敗），再用第 5 節的排查順序定位、改回。確認沒問題後刪備份。
  </Step>
</Steps>

## 常見誤區

<Warning>
  **反模式清單**

  * **把「永遠允許」手寫進 claude.json**：要批量預先允許工具，改 `settings.json` 的 `permissions.allow`（策略）。手改 `claude.json` 的核准紀錄（狀態）會在工具更新狀態時被覆蓋。
  * **MCP 連不上就重裝工具**：先看 `claude.json` / `.mcp.json` 裡 server 項目的命令路徑與 env 是否正確，多數問題在這裡，不在工具本身。
  * **信任對話跳出時點「僅此工作階段允許」，事後抱怨每次都要確認**：那個選項本來就不持久化。要它記住，選持久化選項讓工具寫入狀態。
  * **把 MCP 的 local scope 當成 settings.local.json**：兩者位置與用途不同（見第 4 節 callout）。存錯地方就會找不到 server。
  * **手改 claude.json 不備份**：檔案損壞會讓所有 MCP 與信任設定失效。
</Warning>

## 自我檢核

<Check>
  **通過本單元的標準**

  1. 你能不能用一句話分清 `claude.json`（狀態，工具寫）與 `settings.json`（策略，你寫）？要批量允許一個工具，你改哪個檔？
  2. 你想讓某個 MCP server 只對特定專案可見且不分享給團隊，該用哪個 scope、存在哪個檔？要團隊共享呢？
  3. MCP 的 local scope 存在 `~/.claude.json`，那 `.claude/settings.local.json` 又是什麼？兩者差在哪？
  4. 信任對話在同一個專案反覆跳出，你會先檢查 `claude.json` 的哪個段落？Windows 上最可能的成因是什麼？
  5. clone 一個帶 `.mcp.json` 的外部 repo，預設情況下它的 server 會直接執行嗎？什麼設定會讓它被自動核准？
</Check>

## 來源與延伸閱讀

事實主張依官方文件，快變動項標註截至 2026-05。

<div className="references">
  * \[1] Anthropic, "Connect Claude Code to tools via MCP," Claude Code Docs. （MCP 三種 scope local / project / user 與儲存位置；`~/.claude.json` 的 `projects.<path>.mcpServers` 結構；`.mcp.json` project-scoped server 需核准、`claude mcp reset-project-choices`、`⏸ Pending approval`；`claude mcp add --scope`；MCP local scope 不同於 settings.local.json；scope 優先序；`.mcp.json` 的 `mcpServers` 格式） [https://code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) （截至 2026-05）

  * \[2] Anthropic, "Claude Code settings," Claude Code Docs. （`enableAllProjectMcpServers` 自動核准專案 MCP；`enabledMcpjsonServers` / `disabledMcpjsonServers` 個人啟停 `.mcp.json` 中的 server；managed 層 `allowedMcpServers` / `deniedMcpServers`） [https://code.claude.com/docs/en/settings](https://code.claude.com/docs/en/settings) （截至 2026-05）
</div>

* 銜接：[02-3](/code-agent/configuration/settings-json) settings.json 的策略面與 local 設定、[02-5](/code-agent/configuration/skills-hooks-subagents-plugins) plugin 提供的 MCP server、[01-6](/code-agent/foundations/harness-engineering) 權限與信任在 harness 中的位置、[03-3](/code-agent/judgment/security-privacy-supply-chain) 外來 repo 與 MCP 的供應鏈威脅模型、[04-9](/code-agent/customization/mcp-integration) MCP 整合的深入設定。
