sharedoc-mcp
v2.2.0
Published
Share agent-generated Markdown as links — GitHub gists today, your own server tomorrow. An MCP server.
Readme
sharedoc-mcp
Agent 產出的 Markdown → 一條可以交給任何人的連結。今天用 GitHub gist,明天用你自己的 server。
English | 繁體中文
一個 MCP stdio server——可用於 Claude Code、Codex CLI 與任何 MCP client——給你的 agent 9 個工具:發佈、更新、搜尋、撤銷分享文件。同一組介面、兩個可切換後端:gist(零設定,搭你已登入的 gh CLI)與 selfhost(SQLite 存你機器上,支援密碼與強制期限)。
後端不支援某參數時(如 gist 收到
password)會回明確錯誤,不會靜默忽略。
為什麼?
AI agent 整天在產 Markdown——報告、研究摘要、會議記錄。要交給另一個人,通常得把一大面文字牆貼進聊天視窗。
沒有 sharedoc-mcp 有 sharedoc-mcp
───────────────── ────────────────
複製一大段文字貼進聊天 「把這個做成分享文件」
每多一個人就再貼一次 一條連結給所有人
內容埋在聊天記錄裡 事後可撤銷/延長/追加
「可以加密碼嗎?」……不行 selfhost 後端:bcrypt + 期限這些 Markdown 摘要哪來的?常常是另一個 skill 產的——例如 audio-tldr 把影片和 podcast 變成 Markdown 摘要, sharedoc-mcp 再把摘要變成連結。
特色
- ✓ 9 個 MCP 工具:建立/追加/取代內容/延長/改密碼/改標題/撤銷/刪除/搜尋
- ✓
sharedoc-mcp servedaemon 模式——MCP client 關掉後 selfhost 連結照樣活著 - ✓ 內容搜尋:用文件裡寫了什麼找回舊連結,不只靠標題
- ✓
GET /healthz——帶身分識別的健檢端點,外部監控/自動重啟直接掛 - ✓ 兩個後端、同一組介面——一個環境變數切換,工具 schema 完全相同
- ✓ Gist 後端(預設):secret gist 走你已登入的
ghCLI——不用管 token、不用架任何東西 - ✓ Selfhost 後端:文件留在你機器上(內建
node:sqlite——零原生模組) - ✓ Server 端密碼驗證(bcrypt)+ 錯誤嘗試限流——5 次/分鐘,HTTP 429,計數存 SQLite、重啟不歸零(selfhost)
- ✓ 強制期限(410)與撤銷 7 天內容清除緩衝(selfhost);惰性過期清理(gist)
- ✓ Markdown 經
marked+sanitize-html渲染——分享內容中的 script、事件屬性、javascript:連結都會被剝除 - ✓ Viewer 只 bind 127.0.0.1,所有回應帶完整安全 headers(CSP
default-src 'none'、nosniff、禁 iframe、no-referrer、no-store)——對外曝光交給你自己控制的 tunnel(食譜見下) - ✓ 本地索引支援
search_shared_docs與建立去重(5 分鐘內相同的無保護重試回同一 URL;補加密碼/期限的重試一律建新文件) - ✓
search_shared_docs支援 offset 分頁(回應含hasMore),selfhost 另有瀏覽統計(viewCount/lastViewedAt,僅成功渲染才計入) - ✓ Docker 一行指令跑 standalone
servedaemon,預設跟其他地方一樣只 bind 127.0.0.1 - ✓ 兩個 MCP client 可共用同一資料目錄:SQLite WAL + busy timeout、埠衝突優雅共存
- ✓ 105 個離線測試;乾淨 checkout
npm test直接綠
安裝
需要 Node.js ≥ 22.13.0。gist 後端另需已登入的 GitHub CLI(gh auth login)。
方式 A — Claude Code(一行):
claude mcp add sharedoc --scope user -- npx -y sharedoc-mcp@^2方式 B — Codex CLI(~/.codex/config.toml):
[mcp_servers.sharedoc]
command = "npx"
args = ["-y", "sharedoc-mcp@^2"]**方式 C — Cursor(一鍵):**點 Add to Cursor,或把設定併進 ~/.cursor/mcp.json:
{ "mcpServers": { "sharedoc": { "command": "npx", "args": ["-y", "sharedoc-mcp@^2"] } } }**方式 D — VS Code(一鍵):**點 Install in VS Code,或在終端機執行:
code --add-mcp '{"name":"sharedoc","command":"npx","args":["-y","sharedoc-mcp@^2"]}'**方式 E — 其他 MCP client:**以 stdio server 執行 npx -y sharedoc-mcp@^2。
為什麼用
@^2?裸的npx -y sharedoc-mcp每次冷啟動解析最新已發佈版本——未來的 3.0 可能在你不知情下改變行為(甚至移除工具)。@^2跟得上 2.x 修正、但永不跨 breaking 大版;想零漂移就釘精確版本(@2.1.0)。
選後端
| | 🅰 gist(預設) | 🅱 selfhost |
|---|---|---|
| 設定 | 無——用你已登入的 gh CLI | 無額外設定——資料留在你機器上 |
| 文件放在 | GitHub(secret gist) | 你的機器(SQLite) |
| 連結可達性 | 任何地方、立即 | localhost——對外分享請接 tunnel |
| 密碼 | ✗(secret URL 本身就是保護) | ✓ server 端驗證(bcrypt)+ 限流 |
| 期限 | 惰性——過期 gist 於下次使用時刪除 | 強制——過期連結回 410 |
| 撤銷 | gist 立即刪除、不可逆 | 立即 410,內容 7 天緩衝後清除 |
| 瀏覽統計 | ✗(GitHub gist API 不提供瀏覽次數資料) | ✓ viewCount + lastViewedAt,僅成功渲染才計入 |
Gist 快速開始
跟 agent 說「把這個做成分享文件」——它呼叫 create_shared_doc 回傳 secret gist URL。Secret gist 不會被公開列出、網址無法猜測,但拿到連結的任何人都能讀——這就是此後端的完整安全模型。需要密碼請用 selfhost。
本地索引(~/.config/sharedoc-mcp/index.json)記錄分享過的內容,供搜尋與過期清理。此後端的期限是惰性的:過期 gist 於下次任一工具執行時刪除,不是到期那一刻。
Selfhost 快速開始
claude mcp add sharedoc --scope user --env SHAREDOC_BACKEND=selfhost -- npx -y sharedoc-mcp@^2文件存在 ~/.local/share/sharedoc-mcp/ 的 SQLite;viewer 於 http://127.0.0.1:8377 服務。要分享到機器之外,前面接一個 tunnel 並設定 SHAREDOC_PUBLIC_URL:
**讓連結活得比編輯器久:**MCP 模式下 viewer 跟著 MCP client 一起關——關掉 Claude Code,selfhost 連結就暫時打不開(資料安全存在 SQLite,下次開就恢復)。要連結全天候在線,跑獨立 daemon:
npx -y sharedoc-mcp@^2 serve # 只跑 viewer、共用同一個 DB——用 launchd/systemd/tmux 常駐(Windows:工作排程器或 NSSM)MCP client 偵測到 daemon 已佔埠就直接沿用它。
**什麼時候該設:**第一次把連結交給別人的那一刻——跟 tunnel 一起設(兩者都該常駐,如 launchd/systemd)。在那之前 MCP 模式的 viewer 就夠用;gist 後端使用者則永遠不需要。
| 食譜 | 適合 | 設定 |
|---|---|---|
| Tailscale 私有連線(推薦) | 收件人是自己的裝置/可邀進 tailnet 的人 | tailscale serve --bg 8377 → https://<機器>.<tailnet>.ts.net,只有 tailnet 內可達——完全不暴露到公網 |
| Tailscale Funnel | 要分享給任何人、沒網域 | tailscale funnel 8377 → 同一條固定網址,但公開 |
| Cloudflare named tunnel | 有自己的網域 | 網域掛 Cloudflare,cloudflared tunnel create + 主機名 route 到 http://127.0.0.1:8377 |
| cloudflared quick tunnel | 臨時分享 | cloudflared tunnel --url http://127.0.0.1:8377 → 隨機網址,每次重啟會變 |
有自己的網域?Cloudflare named tunnel 逐步版
品牌化的固定分享網址,例如 https://docs.example.com/docs/<uuid>——TLS 由 Cloudflare 處理、機器在 NAT 後面也通:
# 一次性設定(網域已掛進 Cloudflare——免費方案就夠)
cloudflared tunnel login
cloudflared tunnel create sharedoc
cloudflared tunnel route dns sharedoc docs.example.com~/.cloudflared/config.yml:
tunnel: sharedoc
credentials-file: ~/.cloudflared/<tunnel-id>.json
ingress:
- hostname: docs.example.com
service: http://127.0.0.1:8377
- service: http_status:404跑 cloudflared tunnel run sharedoc(要常駐就裝成 service),MCP 註冊時帶上公開網址:
claude mcp add sharedoc --scope user \
--env SHAREDOC_BACKEND=selfhost \
--env SHAREDOC_PUBLIC_URL=https://docs.example.com \
-- npx -y sharedoc-mcp順帶解鎖:Cloudflare 的 DDoS 防護免費附送;可疊 WAF 規則,或在分享路徑以外套 Cloudflare Access(SSO)——變成「內部走 SSO、對外分享靠密碼」的雙層結構。
**另一種情境——不想依賴家裡機器常開:**把 sharedoc-mcp 跑在 VPS 上(agent 也在那執行),nginx/caddy 反代 127.0.0.1:8377 配網域與自動 TLS 即可,不需要 tunnel。
Docker
跟上面一樣是跑 standalone 的 serve daemon,只是包成容器:
docker build -t sharedoc-mcp .
docker run -d --name sharedoc \
-p 8377:8377 \
-e SHAREDOC_BIND_HOST=0.0.0.0 \
-v sharedoc-data:/data \
sharedoc-mcp-v sharedoc-data:/data把docs.db存進 named volume——容器重建文件不會不見。SHAREDOC_BIND_HOST=0.0.0.0是連上容器的必要條件。 viewer 預設只 bind127.0.0.1——跟本文件其他部署方式一樣——但在容器內這代表透過docker run -p完全連不到,因為-p轉發到容器的網路介面,不是它的 loopback。沒設這個環境變數的話,docker logs會顯示 viewer 正常在聽,但對外映射的 host port 會一律拒絕連線。- 設成
0.0.0.0代表任何連得到容器對外埠的人都連得到 viewer,光靠網路位置不做任何驗證——跟在容器裡跑任何沒驗證機制的 app、卻沒在前面擋一層是一樣的風險。請像本文件其他 selfhost 情境一樣在前面加一層(host 上的 reverse proxy、Tailscale sidecar、Cloudflare tunnel),不要直接把-p 8377:8377對外網公開。單篇文件的密碼保護(這個後端內建的功能)不能替代這一層。 - 如果收件人實際會用的網址跟
http://<host>:8377不同(reverse proxy、網域、tunnel),記得也設SHAREDOC_PUBLIC_URL——容器沒辦法自己推斷。 - MCP stdio server 本身不適合跑在 Docker 裡——它需要一個綁定 MCP client stdin/stdout 的本機 process。MCP client 一樣照常指向 host 上的
npx -y sharedoc-mcp;只有 standalone viewer daemon 適合放進容器。
環境變數:
| 變數 | 預設 | 意義 |
|---|---|---|
| SHAREDOC_BACKEND | gist | gist 或 selfhost |
| SHAREDOC_PORT | 8377 | viewer 埠(selfhost) |
| SHAREDOC_BIND_HOST | 127.0.0.1 | viewer bind 位址(selfhost)——要從 Docker 容器外連進來設 0.0.0.0;改之前先看上面 Docker 段落的曝險取捨 |
| SHAREDOC_PUBLIC_URL | http://127.0.0.1:<port> | 分享連結的網址前綴——設成你的 tunnel 主機名 |
| SHAREDOC_DATA_DIR | ~/.local/share/sharedoc-mcp | SQLite 位置(selfhost) |
| SHAREDOC_INDEX_PATH | ~/.config/sharedoc-mcp/index.json | 本地索引(gist) |
| MCP_CALLER | — | 建立文件的預設作者歸因 |
9 個工具
| 工具 | 功能 |
|---|---|
| create_shared_doc | 標題 + Markdown(+ 選填密碼 / expires_in_hours / 作者)→ 分享 URL |
| append_to_shared_doc | 尾端追加 Markdown(非冪等——重試會加兩次) |
| update_shared_doc_content | 取代整份內容(標題/密碼/期限不變)——冪等,可安全重試 |
| extend_shared_doc | 延長期限 N 小時 |
| reset_shared_doc_password | 設定/更換/移除(null)密碼(僅 selfhost) |
| update_shared_doc_title | 改標題 |
| revoke_shared_doc | 撤銷連結、保留紀錄(語意見後端對照表) |
| delete_shared_doc | 連結失效+紀錄整個消失——不可逆;需帶 confirm: true(agent 應先取得使用者明確同意) |
| search_shared_docs | 不帶參數=列出最新連結;標題子字串、內文搜尋(selfhost 全文;gist 僅開頭摘要)、狀態篩選、offset 分頁(回應含 hasMore)、selfhost 瀏覽統計 |
隱私
各後端的資料流:
- Gist 後端:文件內容以 secret gist 上傳到你 GitHub 帳號下——適用 GitHub 的條款與保存政策。本地索引留在
~/.config/sharedoc-mcp/——存標題、URL、時間戳與每份文件的前 200 字元(供本機內容搜尋);不存完整內容。除了經你自己的ghCLI 送 GitHub 之外,不送任何地方。 - Selfhost 後端:內容不離開你的機器,除非你接了 tunnel——那之後就是「拿到連結的人 + 中繼流量的 tunnel 供應商」可及。密碼只以 bcrypt 雜湊儲存。
- sharedoc-mcp 本身無遙測、不呼叫任何自己的第三方服務。
安全語意(誠實版)
- Gist 連結即權限:拿到 URL 就能讀。撤銷=立即刪除 gist、不可逆。
- Selfhost 密碼於 server 端驗證通過才吐內容;只有「錯誤」嘗試會計入限流(每來源+文件 5 次/分鐘;解鎖成功即清空計數),計數存 SQLite——重啟 server 不會歸零。經 tunnel 時所有外部訪客共用同一來源位址,實際效果是每份文件 5 次/分鐘——比逐訪客更嚴格;一個人打錯幾次會讓該文件對其他人短暫鎖定。
- 刻意沒有檔案分享工具:可傳任意路徑的「分享這個檔案」工具是 prompt injection 的洩密面(
.env、金鑰)——被劫持的 agent 可以直接把機敏檔發佈出去。與其做 allowlist 不如整個拿掉。 - Viewer 永遠只 bind 127.0.0.1。它是否、如何觸及網際網路,完全由你的 tunnel 設定決定。
開發
git clone https://github.com/AugustusW/sharedoc-mcp.git
cd sharedoc-mcp
npm install
npm test # 先 build 再跑 105 個離線測試——gh CLI 以 mock 替身,HTTP 測試只打 127.0.0.1版本規則:每次釋出 bump package.json 的 version、加一筆 CHANGELOG、打 git tag 發 GitHub Release + npm。
想收到更新通知:Watch 本 repo(Custom → Releases)。npx -y 每次冷啟動會抓最新已發佈版本;你的索引與文件 DB 都在套件外——更新永遠不會動到它們。
狀態
v2.1.0(CHANGELOG)——核心邏輯有 105 個離線單元/整合測試(gh CLI 以 mock 模擬;HTTP 測試只打 127.0.0.1;不需網路)。完整流程於 2026-07-25 人工驗證(經 built server 走 stdio JSON-RPC 實建 secret gist 的建立/索引/刪除,以及 selfhost 密碼流程端到端——表單 → 錯密碼 401 → 對密碼 200 → 限流 429 → 撤銷 410——並以 lsof 確認僅 bind 127.0.0.1),環境:
- macOS(Apple Silicon)、Node v25——gist + selfhost 兩後端
Tunnel 食譜依各工具的標準行為撰寫;Windows/Linux 與真實 tunnel 端到端尚未驗證——歡迎回報。
授權
MIT © AugustusW
