Claude 一旦不再只是純聊天介面,它的實用性就會大幅提升。
本機 MCP 伺服器能讓 Claude 與你的實際機器互動:讀取本機檔案、執行經核准的指令、擷取螢幕截圖、啟動應用程式。關鍵不在於原始存取權限,而在於受控的存取權限。
在這份指南中,我們將為 Claude 建立一個實用、範圍明確且足夠安全,能應用於真實工作流程的本機 MCP 伺服器。
在開始建構之前,先提個重點
本文刻意避開了不負責任的「電腦控制」版本。
我們不打算給 Claude 無限制的 shell 存取權、完整的檔案系統權限,或是在沒有防護措施的情況下讓它任意修改你的電腦。建立糟糕的本機 MCP 伺服器最快的方法,就是暴露一個巨大的 run_anything() 工具,然後稱之為創新。
更好的模式是:
- 允許清單中的目錄
- 允許清單中的指令
- 安全預設值
- 人類可讀的日誌
- 明確的回應
- 唯讀與操作型工具之間有清楚的區隔
如果 Claude 什麼都能做,你只是做出了展示品。
如果 Claude 能安全地做對的事情,你才做出了可用的工具。
為什麼這種架構值得學習
本機 MCP 伺服器的價值不在於新奇,而在於減少摩擦。
如果沒有本機工具層,你的工作流程會像這樣:
- 問 Claude 要怎麼做
- 複製答案
- 自己打開資料夾
- 自己執行指令
- 自己擷取螢幕截圖
- 把結果貼回對話框
有了本機 MCP 伺服器,這個循環就能大幅縮短。Claude 可以檢查它需要的上下文,使用範圍狹窄的工具,並根據你實際的機器狀態提供答案。
這在以下情境中非常實用:
- 開發工作流程
- 日誌檢查
- 內容運營
- 研究流程
- 桌面自動化
- 可重複的管理任務
而且因為工具層是你的,你可以精準選擇模型在哪裡停止。
想要學習 AI 並在職涯中保持領先嗎?
我分享:
1,000 多個 AI 提示詞
AI 工具與自動化
職涯與 LinkedIn 成長技巧
精選科技與生產力資源
在此訂閱:
追蹤我以獲得更多 AI 技巧
👇
我們要建立的設計

我們將建立一個包含五個工具的本機伺服器:
- list_files — 查看核准資料夾內有哪些內容
- read_file — 開啟安全的文字檔案
- run_command — 執行一組經過核准的本機指令
- take_screenshot — 將螢幕截圖儲存到指定位置
- open_target — 開啟應用程式、檔案、資料夾或網址

這個範圍是刻意設定的。
它足以讓 Claude 在本機機器上發揮實際效用,同時又不至於淪為不安全的一般用途自動化工具。
心智模型應該是這樣:
Claude → MCP 客戶端 → 本機 MCP 伺服器 → 狹窄工具 → 作業系統
Claude 永遠不該直接與你的作業系統對話。你的 MCP 伺服器是中間的控制層。
技術棧
對於本機建置,Python 是個乾淨的選擇,因為官方 MCP SDK 已成熟、FastMCP 抽象層簡潔,而且 Python 仍然是處理檔案系統、子程序和桌面腳本最簡單的語言 4 2。
我們將使用:
- Python 3.11+
- mcp[cli] 作為 MCP 伺服器執行環境
- mss 用於跨平台螢幕截圖
- 標準函式庫模組用於檔案存取、子程序呼叫和作業系統處理
建立一個新專案:
1mkdir local-mcp-server2cd local-mcp-server3uv init --python 3.114uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"
基於裝飾器的 FastMCP 風格能讓你避開協定層的繁瑣,專注於工具品質,而不是連線配置 4 5。
一個簡單的專案結構就很好用:
1mkdir local-mcp-server2cd local-mcp-server3uv init --python 3.114uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"
v1 不需要複雜的架構。你需要的是清晰度。
實際的伺服器程式碼
建立 server.py 並從基於政策的實作開始。
1from __future__ import annotations23import json4import os5import platform6import shlex7import subprocess8from pathlib import Path9from typing import Any1011import mss12from mcp.server.fastmcp import FastMCP1314app = FastMCP("local-computer-control", json_response=True)1516HOME = Path.home()17PROJECT_ROOT = Path(__file__).parent.resolve()18CAPTURE_DIR = PROJECT_ROOT / "captures"19CAPTURE_DIR.mkdir(exist_ok=True)2021ALLOWED_ROOTS = [22 HOME / "Documents",23 HOME / "Desktop",24 PROJECT_ROOT,25]2627ALLOWED_COMMANDS = {28 "pwd",29 "ls",30 "git status",31 "git diff --stat",32 "python --version",33 "node --version",34 "npm --version",35}3637READABLE_EXTENSIONS = {38 ".txt",39 ".md",40 ".json",41 ".py",42 ".js",43 ".ts",44 ".tsx",45 ".jsx",46 ".yaml",47 ".yml",48 ".toml",49 ".csv",50 ".log",51}5253def _resolve_path(raw_path: str) -> Path:54 path = Path(raw_path).expanduser().resolve()55 for root in ALLOWED_ROOTS:56 root = root.resolve()57 if path == root or root in path.parents:58 return path59 raise ValueError(f"不允許的路徑:{path}")6061def _ensure_safe_command(command: str) -> str:62 normalized = " ".join(shlex.split(command))63 if normalized not in ALLOWED_COMMANDS:64 raise ValueError(65 "指令不被允許。如果你真的需要,請手動將其加入 ALLOWED_COMMANDS。"66 )67 return normalized6869@app.tool()70def list_files(path: str = "~") -> dict[str, Any]:71 """列出核准目錄中的檔案與資料夾。"""72 target = _resolve_path(path)73 if not target.is_dir():74 raise ValueError(f"不是目錄:{target}")7576 items = []77 for child in sorted(target.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())):78 items.append(79 {80 "name": child.name,81 "path": str(child),82 "type": "directory" if child.is_dir() else "file",83 }84 )8586 return {87 "path": str(target),88 "count": len(items),89 "items": items,90 }9192@app.tool()93def read_file(path: str, max_chars: int = 12000) -> dict[str, Any]:94 """從核准位置讀取安全的文字檔案。"""95 target = _resolve_path(path)96 if not target.is_file():97 raise ValueError(f"不是檔案:{target}")98 if target.suffix.lower() not in READABLE_EXTENSIONS:99 raise ValueError(f"不支援的檔案類型:{target.suffix}")100101 content = target.read_text(encoding="utf-8", errors="replace")102 truncated = len(content) > max_chars103 content = content[:max_chars]104105 return {106 "path": str(target),107 "truncated": truncated,108 "content": content,109 }110111@app.tool()112def run_command(command: str, cwd: str | None = None, timeout: int = 15) -> dict[str, Any]:113 """執行一個核准的本機指令。"""114 safe_command = _ensure_safe_command(command)115 working_dir = _resolve_path(cwd) if cwd else PROJECT_ROOT116117 completed = subprocess.run(118 safe_command,119 shell=True,120 cwd=str(working_dir),121 capture_output=True,122 text=True,123 timeout=timeout,124 )125126 return {127 "command": safe_command,128 "cwd": str(working_dir),129 "returncode": completed.returncode,130 "stdout": completed.stdout.strip(),131 "stderr": completed.stderr.strip(),132 }133134@app.tool()135def take_screenshot(name: str = "latest") -> dict[str, Any]:136 """擷取螢幕截圖並儲存到本機。"""137 output_path = CAPTURE_DIR / f"{name}.png"138139 with mss.mss() as sct:140 sct.shot(output=str(output_path))141142 return {143 "saved": True,144 "path": str(output_path),145 }146147@app.tool()148def open_target(target: str) -> dict[str, Any]:149 """使用本機作業系統開啟核准的檔案、資料夾、應用程式或網址。"""150 system = platform.system().lower()151152 if target.startswith("http://") or target.startswith("https://"):153 resolved = target154 else:155 resolved = str(_resolve_path(target))156157 if system == "darwin":158 subprocess.run(["open", resolved], check=True)159 elif system == "windows":160 os.startfile(resolved) # type: ignore[attr-defined]161 else:162 subprocess.run(["xdg-open", resolved], check=True)163164 return {165 "opened": True,166 "target": resolved,167 }168169if __name__ == "__main__":170 app.run(transport="stdio")
這是一個精簡的伺服器,但重點不在於它有多短,而在於介面的形狀:
- 每個工具都有非常明確的職責
- 每個工具都回傳結構化資料
- 指令執行受到管控
- 檔案存取有根目錄限制
- 螢幕截圖會儲存到已知的資料夾
這正是你希望在本機 MCP 伺服器中看到的。
為什麼這些工具要這樣設計
關於代理工具的高階內容絕對不該只停留在「這裡是程式碼」。工具的設計形狀才是真正的學問。
list_files
這個工具讓 Claude 擁有安全的探索空間。它應該能夠回答像這樣的問題:
- 這個專案資料夾裡有什麼?
- Documents 裡有哪些筆記?
- 有沒有我可以檢查的日誌檔案?
但它不該變成一個遞迴的全磁碟爬蟲。
read_file
這通常是最實用的本機工具。大量真實工作仍然藏在本機的 Markdown 筆記、日誌、CSV、文件和專案檔案中。
max_chars 上限很重要。大型檔案會造成上下文與延遲問題。回傳整個巨大日誌檔的內容很少有意義。
run_command
這是大多數人最容易草率處理的地方。
安全的模式不是「允許 shell 存取,然後祈禱沒事」。安全的模式是「允許一組經過審查的精準指令」。這就是為什麼範例使用嚴格的允許清單。
take_screenshot
螢幕截圖工具很有價值,因為它能讓 Claude 參與桌面工作流程。即使你的第一個版本只將圖片儲存到磁碟,這對報告、UI 除錯、文件擷取和結構化交接來說已經很有用。
open_target
應用程式控制不需要從 GUI 自動化開始。對許多工作流程來說,「開啟正確的資料夾、檔案或網址」就足夠了。
這比假設你第一天就需要完整滑鼠游標自動化更耐久。
將伺服器連接到 Claude
本機 MCP 伺服器通常透過 stdio 執行,這表示 Claude 會在本機啟動程序,並直接透過 stdin/stdout 與之通訊。對於本機電腦控制伺服器來說,這是正確的預設選項,因為它能避免不必要的網路暴露 4 5。
Claude Desktop 透過設定支援本機 MCP 伺服器,它會幫你啟動伺服器程序。實務上,使用直譯器和腳本的絕對路徑是最不容易出錯的設定方式,因為本機 GUI 應用環境通常比你的終端機更嚴格 2。
一個最簡設定範例如下:
1{2 "mcpServers": {3 "local-computer-control": {4 "command": "/absolute/path/to/python",5 "args": [6 "/absolute/path/to/local-mcp-server/server.py"7 ]8 }9 }10}
如果你偏好使用 uv,那也沒問題:
1{2 "mcpServers": {3 "local-computer-control": {4 "command": "/absolute/path/to/uv",5 "args": [6 "--directory",7 "/absolute/path/to/local-mcp-server",8 "run",9 "python",10 "server.py"11 ]12 }13 }14}
儲存設定並重新啟動 Claude 後,伺服器工具應該會出現在本機 MCP 工具清單中。Claude Desktop 的本機 MCP 設定正是基於這個模型:啟動一個本機程序,透過 stdio 連接,然後將工具暴露給模型 2 3。

實際有用的測試提示詞
伺服器連接好之後,不要一開始就進行複雜的編排。從直接、基本的功能檢查開始。
試著使用這些提示詞:
- 「列出我桌面資料夾中的檔案。」
- 「讀取 ~/Documents/todo.md 並總結前三項優先事項。」
- 「在我的本機專案資料夾中執行 git status,並解釋有哪些變更。」
- 「拍一張名為 workspace-check 的螢幕截圖。」
- 「開啟我的專案 README。」
如果這些簡單的流程都能穩定運作,你就擁有一個值得擴展的伺服器。
如果不能,添加更多工具只會掩蓋真正的問題。
本機 MCP 伺服器真正有價值的地方
最明顯的用途是開發,但那只是其中一環。
開發者工作流程
Claude 可以:
- 檢查儲存庫資料夾
- 讀取設定檔
- 執行 git status
- 擷取錯誤狀態的螢幕截圖
- 開啟專案目錄
這已經消除了大量的上下文切換。
研究流程
Claude 可以:
- 列出研究資料夾
- 開啟並總結 Markdown 筆記
- 讀取結構化的 CSV
- 儲存來自工具或儀表板的螢幕截圖
- 開啟原始碼檔案或瀏覽器連結
內容工作流程
Claude 可以:
- 掃描草稿資料夾
- 讀取現有的文章點子
- 擷取設計參考的螢幕截圖
- 開啟正確的寫作檔案或網址
- 執行有限的指令來產生構建產物或草稿匯出
運維工作流程
Claude 可以:
- 從核准目錄檢查日誌
- 執行唯讀診斷指令
- 開啟相關資料夾或儀表板連結
- 儲存證據截圖
這就是這個架構的真正重點:不是作為噱頭的「電腦控制」,而是工作流程壓縮。
安全性層就是產品本身
這是太多技術文章輕描淡寫的部分。
本機 MCP 的危險之處不在於協定,而在於糟糕的權限設計。
如果你希望這個伺服器在展示之外還能實際使用,請儘早建立安全模型。

使用目錄允許清單
Claude 只能看到你明確核准的路徑。這就是為什麼 _resolve_path() 是檔案工具的核心。
使用指令允許清單
永遠不要在初版暴露任意 shell 執行。從你能逐行審計的精準指令開始。
區分唯讀工具與操作型工具
唯讀工具應該是預設值。操作型工具應該經過審慎引入。
記錄所有操作
即使是簡單的附加式 JSON 日誌,也能大幅提升除錯和信任度。
為寫入操作加入確認層
如果你之後添加了 write_file、move_file 或 delete_file,請讓這些工具要麼需要第二次確認權杖,要麼保持預設禁用。
考慮乾執行模式
對於操作型工具,乾執行模式常被低估。它能讓 Claude 在實際執行之前說明它打算做什麼。
盡可能以受限制的使用者執行
如果你認真對待本機自動化,不要給你的 MCP 伺服器超出必要的作業系統權限。
一個有用的經驗法則:
- 第 1 級: 唯讀工具
- 第 2 級: 低風險操作,如開啟檔案 / 開啟應用程式
- 第 3 級: 需確認的寫入操作
- 第 4 級: 你不該隨意暴露的破壞性操作
大多數人永遠不需要第 4 級。

v1 之後可以改進的地方
穩固的初版讓你贏得進階能力的機會。
一旦基礎伺服器穩定,接下來合理的升級是:
- 集中化政策檔案
將你的規則移至 config/policy.json,讓變更變成宣告式。
範例:
1{2 "allowed_roots": [3 "~/Documents",4 "~/Desktop",5 "./"6 ],7 "allowed_commands": [8 "pwd",9 "ls",10 "git status",11 "git diff --stat",12 "python --version"13 ]14}
- 結構化日誌
將工具呼叫、時間戳、引數和結果記錄到 logs/server.log 或 JSONL 檔案。
- 更安全的指令執行
不要使用單一的通用指令工具,而是將指令拆分為更狹窄的工具,例如:
- git_status
- show_current_directory
- list_project_files
這能讓 Claude 更容易選擇工具,也讓你更容易確保安全。
- 更好的螢幕截圖處理
你可以從「將螢幕截圖儲存到磁碟」進化為:
- 時間戳記擷取
- 作用中視窗擷取
- 區域擷取
- 檔案保留規則
- 特定作業系統的自動化轉接器
在 macOS 上,你之後可以加入 AppleScript 或 Shortcuts。在 Windows 上,加入 PowerShell 或 UI Automation。在 Linux 上,加入桌面特定的啟動器和視窗工具。
但這些應該等到基礎的本機核心可靠之後再進行。
人們在使用本機 MCP 伺服器時常犯的錯誤
這些錯誤是可以預測的。
錯誤 1:權限給得太早、太多
人們喜歡完全控制電腦的想法,但痛恨除錯的過程。從更小的範圍開始。
錯誤 2:工具名稱含糊不清
如果你的工具名稱不明確,Claude 就會用得很糟。請明確命名。
糟糕的例子:
- system_action
- computer_control
更好的例子:
- list_files
- read_file
- run_command
- take_screenshot
- open_target
錯誤 3:非結構化輸出
一團混雜的文字比乾淨的 JSON 物件更難讓 Claude 進行推理。
錯誤 4:沒有日誌
如果工具失敗了而你無法得知原因,系統就會變成猜謎遊戲。
錯誤 5:將模型當作控制層
Claude 是推理層。你的伺服器仍然必須是執行層。
這個區別不容妥協。
這種架構比純自動化更好的地方
傳統的桌面自動化通常是以下兩種之一:
- 脆弱的 GUI 腳本
- 需要人類精準知道何時執行的孤立腳本
本機 MCP 伺服器改變了這一點,因為 Claude 可以根據使用者的請求和可用上下文來決定使用哪個工具。
這意味著你不只是在自動化一個指令。你是在建立一個模型可以推理的本機能力層。
這就是為什麼 MCP 讓人覺得重要。它不僅僅是另一個整合模式。它是一種更乾淨的方式,將工具使用暴露給模型,而無需在應用層硬編碼每一種可能的工作流程。
你應該尊重的限制
即使是一個好的本機 MCP 伺服器也有實際的限制。
- 桌面自動化在不同的作業系統上可能不穩定。
- 螢幕截圖很有用,但並非萬能。
- 啟動應用程式很容易,可靠的 UI 操縱更困難。
- 通用的 shell 存取很危險。
- 如果工具輸出過大,上下文膨脹是真實問題。
- 對於任何重要的事項,人類審批仍然很有價值。
換句話說:不要混淆「模型可以執行動作」和「模型應該在沒有監管的情況下行動」。
更有價值的模式是協作控制,而不是盲目自主。





