{%hackmd BJOT7rhoyl %} {%hackmd rJ_1SdPkxx %} # HackMD MCP 伺服器設定指南 HackMD 在 `https://mcp.hackmd.io` 提供遠端 [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 伺服器。透過 MCP,Claude、Cursor、Windsurf 等 AI 助理可以直接在對話中讀取與編輯你的 HackMD 筆記,無需手動複製貼上。 ## 目錄 - [事前準備](#事前準備) - [取得 API 金鑰](#取得-api-金鑰) - [用戶端設定](#用戶端設定) - [Claude Desktop](#claude-desktop) - [Cursor](#cursor) - [Windsurf](#windsurf) - [Claude Code(CLI)](#claude-code-cli) - [其他 MCP 用戶端](#其他-mcp-用戶端) - [可用工具](#可用工具) - [可用資源](#可用資源) - [疑難排解](#疑難排解) --- ## 事前準備 - HackMD 帳號(免費或付費方案皆可) - 支援 MCP 的 AI 用戶端(Claude Desktop、Cursor、Windsurf 等) - HackMD API 金鑰(請參閱下方說明) --- ## 取得 API 金鑰 1. 登入 [HackMD](https://hackmd.io)。 2. 前往 **設定** → **API**(或直接開啟 `https://hackmd.io/settings#api`)。 3. 點擊 **Generate API Token**,輸入名稱後複製金鑰。 > 請妥善保管你的 API 金鑰。任何持有此金鑰的人都能讀取與修改你的筆記。 --- ## 用戶端設定 所有用戶端使用相同的伺服器 URL 與驗證標頭: | 欄位 | 值 | |------|----| | **伺服器 URL** | `https://mcp.hackmd.io` | | **傳輸協定** | Streamable HTTP | | **驗證標頭** | `Authorization: Bearer YOUR_API_TOKEN` | --- ### Claude Desktop 1. 開啟 Claude Desktop。 2. 前往 **Settings** → **Developer** → **Edit Config**。 這會開啟 `claude_desktop_config.json`: - macOS:`~/Library/Application Support/Claude/claude_desktop_config.json` - Windows:`%APPDATA%\Claude\claude_desktop_config.json` 3. 加入 HackMD 伺服器設定: ```json { "mcpServers": { "hackmd": { "url": "https://mcp.hackmd.io", "headers": { "Authorization": "Bearer YOUR_API_TOKEN" } } } } ``` 4. 儲存檔案並**完全重新啟動 Claude Desktop**。 5. 在對話輸入框中找到工具圖示,點擊後應可看到 HackMD 工具清單。 --- ### Cursor > **即將推出 — 零設定安裝:** HackMD 官方 Cursor Plugin 正在等待 Marketplace 審核。一旦上架,安裝後即可自動完成 MCP 伺服器與技能的設定,無需以下手動步驟。 在 Plugin 上架前,請透過 `mcp-remote` 進行設定。Cursor 的 MCP 設定支援 `${env:VAR}` 環境變數插值,讓 API 金鑰不必直接寫入設定檔。 #### 步驟一 — 設定環境變數 在你的 shell 設定檔(`~/.zshrc`、`~/.bash_profile` 等)中加入 API 金鑰: ```bash export HMD_API_ACCESS_TOKEN=YOUR_API_TOKEN ``` 重新載入設定檔: ```bash source ~/.zshrc ``` > Cursor 會繼承啟動它的 shell 環境變數。請在設定檔中加入變數後,**完全重新啟動 Cursor**(非僅重新載入視窗)才能生效。 #### 步驟二 — 編輯 Cursor MCP 設定 建立或編輯 `~/.cursor/mcp.json`: ```json { "mcpServers": { "hackmd-mcp": { "command": "npx", "args": [ "mcp-remote@0.1.38", "https://mcp.hackmd.io/", "--header", "Authorization:Bearer ${env:HMD_API_ACCESS_TOKEN}" ] } } } ``` 完全重新啟動 Cursor,`hackmd-mcp` 伺服器即會出現在 Composer 工具清單中。 #### 在終端機驗證(選用) ```bash export HMD_API_ACCESS_TOKEN=YOUR_API_TOKEN npx mcp-remote@0.1.38 https://mcp.hackmd.io/ --header "Authorization:Bearer $HMD_API_ACCESS_TOKEN" ``` 連線成功時,終端機會印出伺服器的功能清單並正常結束。 --- ### Windsurf 1. 前往 **Windsurf Settings** → **Cascade** → **Model Context Protocol (MCP)**。 2. 點擊 **Add Server**,或直接編輯 `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "hackmd": { "serverUrl": "https://mcp.hackmd.io", "headers": { "Authorization": "Bearer YOUR_API_TOKEN" } } } } ``` 3. 在 MCP 面板中點擊 **Refresh**,確認 `hackmd` 顯示為已連線。 --- ### Claude Code(CLI) 在終端機執行以下指令: ```bash claude mcp add --transport http hackmd https://mcp.hackmd.io \ --header "Authorization: Bearer YOUR_API_TOKEN" ``` 確認伺服器已成功註冊: ```bash claude mcp list ``` 清單中出現 `hackmd` 後,所有 `claude` 工作階段都可以使用 HackMD 工具。 --- ### 其他 MCP 用戶端 任何支援 [Streamable HTTP 傳輸](https://modelcontextprotocol.io/docs/concepts/transports#streamable-http) 的用戶端皆可連線,使用以下通用設定: ```json { "mcpServers": { "hackmd": { "url": "https://mcp.hackmd.io", "headers": { "Authorization": "Bearer YOUR_API_TOKEN" } } } } ``` 不需要在本機執行任何程序或 Docker 容器,伺服器完全由雲端託管。 --- ## 可用工具 ### 筆記工具 | 工具 | 說明 | 參數 | |------|------|------| | `list-notes` | 列出你可存取的所有筆記。僅回傳筆記摘要,如需完整內容請使用 `get-note`。 | `page`(預設:1)、`limit`(預設:20,上限:50) | | `get-note` | 取得特定筆記的完整內容。 | `noteId`(筆記 ID 或短 ID) | | `create-note` | 建立一篇新的 Markdown 筆記。 | `content`(必填)、`title`(選填) | | `update-note` | 覆寫現有筆記的內容。 | `noteId`、`content` | | `delete-note` | 將筆記移至垃圾桶。 | `noteId` | | `get-history` | 取得最近瀏覽的筆記紀錄。僅回傳筆記摘要。 | `limit`(預設:20,上限:200) | ### 團隊工具 | 工具 | 說明 | 參數 | |------|------|------| | `list-teams` | 列出你所屬的所有團隊。 | — | | `get-team` | 取得特定團隊的詳細資訊。 | `teamPath` | | `list-team-notes` | 列出團隊中的所有筆記。僅回傳筆記摘要。 | `teamPath`、`page`(預設:1)、`limit`(預設:20,上限:50) | | `create-team-note` | 在團隊工作區建立一篇新筆記。 | `teamPath`、`content`(必填)、`title`(選填) | | `update-team-note` | 更新團隊中某篇筆記的內容。 | `teamPath`、`noteId`、`content` | | `delete-team-note` | 將團隊筆記移至垃圾桶。 | `teamPath`、`noteId` | ### 搜尋工具 | 工具 | 說明 | 參數 | |------|------|------| | `search-notes` | 依標題搜尋個人及團隊筆記。 | `query`、`limit`(預設:20,上限:50) | ### 使用者工具 | 工具 | 說明 | 參數 | |------|------|------| | `get-me` | 取得目前登入帳號的個人資料,包含團隊成員資格與方案狀態。 | — | --- ## 可用資源 資源提供透過 URI 直接讀取 HackMD 資料的方式。支援 MCP 資源的用戶端可直接透過 URI 載入,不需呼叫工具。 | URI | 說明 | MIME 類型 | |-----|------|-----------| | `hackmd://notes/{noteId}` | 筆記的原始 Markdown 內容 | `text/markdown` | | `hackmd://me` | 你的個人資料(JSON 格式) | `application/json` | | `hackmd://teams/{teamPath}` | 團隊詳細資訊(JSON 格式) | `application/json` | --- ## 疑難排解 **伺服器無法連線** - 確認 API 金鑰正確且仍有效(HackMD → 設定 → API)。 - 確認 `Authorization` 標頭的值以 `Bearer ` 開頭(注意空格)。 - 確認你的用戶端支援 Streamable HTTP 傳輸協定。 - 修改設定檔後,請完全重新啟動用戶端。 **每次呼叫工具都出現「Authentication required」** - API 金鑰可能已被撤銷或失效,請在 HackMD 設定中重新產生,並更新設定檔。 **設定後工具未出現** - Claude Desktop:完全關閉並重新開啟應用程式(選單列 → Quit,再重新啟動)。 - Cursor:完全關閉並重新啟動 Cursor——僅重新載入視窗對 `mcp-remote` 不夠。同時確認 `HMD_API_ACCESS_TOKEN` 已在 shell 設定檔中匯出,且在設定後有重新啟動 Cursor。 - Windsurf:在 MCP 面板中點擊 Refresh。 **`search-notes` 沒有回傳結果** - 搜尋只比對筆記**標題**,不搜尋內文。 - 嘗試使用更短或更通用的關鍵字。 **`get-history` 回傳「No notes in history」** - 瀏覽紀錄是在 HackMD 網頁介面或 API 中開啟筆記時才會建立。請先在 HackMD 中開啟幾篇筆記,再重試。 **存取團隊筆記時顯示「Permission denied」** - 確認你是該團隊的成員,且 API 金鑰屬於正確的帳號。 - 使用 `list-teams` 確認你的金鑰能存取哪些團隊。 --- 如需更多資訊,請參閱 [HackMD 開發者入口](https://hackmd.io/@hackmd-api/developer-portal) 及 [MCP 規格說明](https://modelcontextprotocol.io/specification)。 # 這篇教學對你有幫助嗎? <iframe src="https://tally.so/embed/warqZW?feature=mcp&hideTitle=1&dynamicHeight=1" loading="lazy" width="100%" height="200" frameborder="0" marginheight="0" marginwidth="0" title="Tutorial Book Feedback"></iframe><script>var d=document,w="https://tally.so/widgets/embed.js",v=function(){"undefined"!=typeof Tally?Tally.loadEmbeds():d.querySelectorAll("iframe\[data-tally-src\]:not(\[src\])").forEach((function(e){e.src=e.dataset.tallySrc}))};if("undefined"!=typeof Tally)v();else if(d.querySelector('script\[src="'+w+'"\]')==null){var s=d.createElement("script");s.src=w,s.onload=v,s.onerror=v,d.body.appendChild(s);}</script>