讓 Claude Code 維護我的 WordPress:從 MCP 到 AI-maintained Portfolio
ENGINEERING CASE STUDY / AI AGENTS
如果我願意讓 AI 修改自己的網站,我到底願意給它多少權限?
這篇不是「怎麼叫 AI 幫忙寫 Blog」。這是一份把 Claude Code 透過 MCP 接到一個真實對外站台的整合紀錄:tool interface、permission boundary、SSH forced command,以及在這中間踩到的一堆非常傳統的 infrastructure 問題。
1. Why I Built This
這個 Portfolio 站跑在我自己的 Synology NAS 上,以容器的方式部署,同時放履歷、Project Case Study、技術 Blog 與 Research Notes。
問題出在文章與專案開始變多之後。每一次調整都要進 WordPress Admin,手動建文章、改分類、加圖片、回頭補既有文章的 internal link、維持 Project 與 Blog 兩條結構一致。這些工作本身不難,但它們重複、瑣碎,而且會隨著內容量線性增加——這正好是最不應該由人手動做的那一類事。
所以我開始研究一個具體的問題:能不能讓 Claude Code 透過 MCP,在一組受控的權限下直接維護這個 WordPress?
2. From Chatbot to Tool-using Agent
這個專案真正有趣的地方不是「Claude 幫我生成了一篇文章」。生成文字是 LLM 最不稀奇的能力。
有趣的是:Claude 可以透過一組標準化的 Tool Interface,讀取、修改與管理一個 CMS 的內容。角色從 Text Generator 變成 Tool-using Agent。
LLM 本身只知道文字。它不知道這個站上有哪些文章、哪一篇該改、Media Library 裡有什麼、Category 怎麼分、也不知道要怎麼真的執行一個 CMS Action。MCP 補上的就是這一層:Agent 與外部工具之間的標準化介面。它不是「AI 的 API」,而是把外部系統的能力以明確、具名、可列舉的 tool 形式暴露出來,讓 model 能呼叫、也只能呼叫這些。
這一點很關鍵:因為 tool 是可列舉的,權限就變成一件可以設計的事,而不是一個模糊的信任問題。
3. Architecture
整條路徑從我的工程筆記開始,到一個被人按下發布的頁面結束:
Engineering Notes
source of truth
↓
Claude Code
tool-using agent
↓
MCP Client
structured tool calls
↓
Restricted SSH / Tool Boundary
forced command · no interactive shell
↓
WordPress MCP Adapter
explicit, scoped tools only
↓
WordPress
Posts · Pages · Media
HUMAN-IN-THE-LOOP
Draft → Human Review → Publish
圖中刻意省略實際的 network topology、host、port 與金鑰資訊。這張圖要表達的是責任邊界,不是連線細節。
人負責意圖與驗收;Claude Code 負責規劃與執行;SSH 與 MCP 這一層是唯一的執行通道,同時也是權限邊界;WordPress 是被維護的目標系統。Agent 沒有任何繞過這條路徑的方式,因為繞過的能力本來就不存在於介面上。
4. The Hard Part Wasn’t AI
整個整合過程最花時間的部分,跟 model 或 prompt 完全無關。
最先擋住我的是這一行:
Could not resolve hostname
接下來要處理的是一整組非常傳統的東西:SSH config 的 host alias 寫法、非預設的 SSH port、known_hosts 的 host key 驗證、key authentication 是否真的被採用、以及在 key 沒被接受時悄悄 fallback 到密碼登入——這種 fallback 最麻煩的地方在於它「看起來能動」,於是你會誤以為金鑰設定已經成功。
這段除錯經驗本身沒什麼特別,但它說明了一件事:Agent Integration 到最後,仍然會回到 authentication、網路與主機設定這些最傳統的 infrastructure 問題。「接上 AI」聽起來像是一個 AI 題目,實際上它大部分時間是一個系統整合題目。
5. How Much Permission Should an AI Agent Have?
這是整篇文章的核心。最省事的做法當然是直接給 shell 或資料庫存取——一次解決所有問題。但那等於把整台主機的風險,交給一個非決定性的執行者。
我把 CMS 上的操作分成三層來想:
LEVEL 1 — READ
讀取 Posts、Pages、Categories、Media metadata。低風險,但這一層決定了 Agent 有沒有辦法做出正確判斷——沒有讀的能力,它只能猜。
LEVEL 2 — CONTENT EDITING
建立 draft、更新既有內容、調整 Category / Tag、改寫 Excerpt、指派 Featured Image。這是 Agent 真正創造價值的一層,也是我願意開放的範圍。
LEVEL 3 — HIGH-RISK
Publish、Delete、Theme 修改、Plugin 修改、系統設定。這些操作的共同特徵是:影響範圍超出單一內容,而且不一定能靠 revision 復原。這一層不應該理所當然地整包開給 Agent。
實際落到這個站上,結果是這樣的:MCP 這一側根本沒有提供 shell、任意 SQL、user management、plugin 安裝、theme 修改這些工具。不是用規則禁止,而是介面上不存在——沒有一條「用一個通用工具做到所有事」的逃生門。
另外兩個比較細的設計:Agent 以自己的 WordPress 使用者身分運作,修改 draft 的工具只接受它自己擁有的 draft,別人的內容會被直接拒絕;讀取環境資訊的工具只回傳維護所需的中介資訊,不吐出資料庫憑證、設定檔、使用者資料或 log。上傳媒體的工具也只吃圖片位元組,不接受任意檔案、URL 或伺服器路徑——這是為了避免它變成一個泛用的檔案寫入介面。
要說明的是:更新已發布內容前會先留下一份 WordPress revision,revision 也可以被列出與還原。這是一層保險,但我不會把它說成一套完整的 rollback 機制——它就是 WordPress 內建的 revision,沒有更多。
6. Forced Command Instead of Full Shell
這是我覺得最值得寫下來的安全設計。
「Agent 需要 SSH 連線」這句話,很容易被直接翻譯成「Agent 需要一個 shell」。但 SSH 金鑰可以被限制成只能執行指定的 command(forced command)。也就是說,即使連線建立了,拿到的也不是一個互動式 shell,而是一個固定的進入點。
SSH Connection
↓
Forced Command
↓
MCP Entry Point
THIS
SSH
↓
Full Shell
NOT THIS
差別在於最壞情況。如果 Agent 拿到的是互動式 shell,那麼「內容權限」和「系統權限」就被綁在一起了:一個內容層面的錯誤判斷,有機會變成主機層面的事故。Forced command 把這兩件事切開——Claude 需要的是「修改 WordPress Content 的能力」,不是「操作整台 NAS 的能力」。
這裡不公開實際的 authorized_keys 內容、host、port 或金鑰。概念本身才是重點,細節公開沒有任何好處。
7. Human-in-the-loop Publishing
目前的 Content Workflow 是這樣的:
- 我提供 research / source material 與意圖
- Claude 整理內容、建立 WordPress draft、或更新既有文章
- 我做最後確認:敏感資訊、真實性、用詞是否過度宣稱
- 由我決定是否 publish
系統這一側對應的設計是:建立內容的工具一律強制 draft 狀態,publish 是另一個獨立且明確的動作,不會因為「建了一篇文章」就順便發生。
但我要誠實地標示清楚:這不是一套 approval system。系統強制的只有「新內容從 draft 開始」這一件事;「公開內容我一定自己讀過再發」是我的操作慣例,不是平台幫我擋住的。兩者不一樣,混為一談就是在誇大自己的系統。
8. Preventing Hallucinated Portfolio Content
這個網站是 Technical Portfolio。如果 Claude 自行補上 KPI、補上沒做過的架構、補上「導入後效率提升 40%」、補上其實沒有跑在 production 的使用情況——那這個站就從 Portfolio 變成 Fiction。而且是那種在面試第二個問題就會被拆穿的 Fiction。
所以內容流程上建立了一個 Source of Truth 的概念:
Personal Engineering Notes
↓
Structured Facts
↓
Claude
↓
Article
規則只有一條,但是硬規則:沒有出現在 Source of Truth 裡的事實,不得自行補完。不知道就省略,寧可文章短一點、少一個漂亮的數字,也不要多一個查無此事的宣稱。
這條規則同時也套用在這篇文章上。本文提到的每一項能力與限制,都對應到實際存在的 tool 或實際跑過的流程;沒做到的部分,我在下面第 10 節直接列出來。
9. Using the Agent as a Maintainer
用久了之後我發現,Agent 最有價值的用途不是「新增文章」,而是 Maintenance。新增一篇文章是一次性的工作;維護一個持續長大的網站才是那個會不斷回來找你的成本。
具體像是:找出還沒有 Featured Image 的文章、補上架構圖、修正過時的描述、補 internal link、統一 Category / Tag 的用法、把 Excerpt 補齊、改善在手機上看起來有問題的排版。這些事情人做起來很煩、很容易漏,但對 Agent 來說只是「先讀清單、再逐項處理」。
換句話說:AI Agent 比較像網站的 Maintainer,而不是 Blog Writer。
Featured Image 與 Media
目前正在進行的一項是讓文章列表不再只是純文字:Project、Blog、Research Note 都希望有自己的 Featured Image,讓卡片式的列表維持視覺一致性。Agent 在這件事上負責找出缺圖的文章、上傳媒體、指派 Featured Image。至於圖片生成本身還沒有完全自動化地串進這條流程裡,所以我不會把它寫成「已完成」。
Research Notes
接下來想加進來的是 Research Notes:這週做了什麼、遇到什麼 bug、哪些方案失敗了、正在研究什麼、下一步要驗證什麼。
Research Note 不需要假裝每週都有成功成果。保留失敗、實驗、除錯過程與未完成的工作,比一整排成功故事更接近真實的 engineering log——也更接近我實際在做的事。
10. Portfolio 本身也是 Project
以前 Portfolio 的角色很單純:展示 Projects。現在這個站變成兩件事同時成立——它展示 Projects,而它自己的 infrastructure 也是其中一個 Project。WordPress、NAS hosting、container、Cloudflare、MCP、Claude Code、Agent workflow,全部都在這個站上同時被使用與被展示。
這讓它變成一個 dogfooding project:你正在讀的這個頁面,本身就是這套流程的產出。
我不會說的話
為了不讓這篇變成宣傳稿,把界線標清楚。以下這些描述不成立,我也不會拿來形容這個專案:
- Fully Autonomous Website
- Zero-human CMS
- AI completely manages my website
- Self-evolving website
準確的描述是:AI-assisted CMS maintenance、Agentic Content Workflow、Human-in-the-loop Portfolio Maintenance、MCP-powered WordPress Automation。差別不是修辭,是它實際上能做到什麼。
11. What I Learned
- Agent Engineering 不只是 Prompt Engineering。決定結果的往往是介面設計,不是措辭。
- Permission design 是 Agent Architecture 的一部分。「要開哪些 tool」這個決定,本身就是系統設計,而且比 model 選型更影響最壞情況。
- Integration 最後還是會碰到 SSH / Auth / Infrastructure。AI 專案的困難處,經常不在 AI 那一段。
- 讓 AI 產生內容很容易,讓 AI 安全地改系統比較難。前者是能力問題,後者是邊界問題。
- 對公開的 Portfolio 來說,human review 仍然必要。不是因為 AI 寫得不好,而是因為這個站上的每一句話都要能被我本人負責。
這個專案的 Case Study 頁面在這裡:AI-managed WordPress。其他專案可以看 Projects;同樣跑在這台 NAS 上的監控與 logging 環境,寫在 Home Lab Observability。