note-mcp-server 2.3.3 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/index.js +91 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,20 @@ socialite_id=eyJpdiI6I…
|
|
|
101
101
|
|
|
102
102
|
---
|
|
103
103
|
|
|
104
|
+
## AdminLTE 可摺疊 card(Agent 必讀)
|
|
105
|
+
|
|
106
|
+
筆記前台用 **AdminLTE 4** 的 `data-lte-toggle="card-collapse"` 做展開/收合。
|
|
107
|
+
|
|
108
|
+
| 項目 | 正確(3192 類) | 錯誤(展開失效) |
|
|
109
|
+
|------|----------------|------------------|
|
|
110
|
+
| 按鈕屬性 | `data-lte-toggle="card-collapse"` | 無屬性,或 `data-owv-helper-toggle`(後端會剝除) |
|
|
111
|
+
| 圖示 | `bi bi-plus-lg` + `bi bi-dash-lg`,含 `data-lte-icon` | 單一 `fas fa-plus` |
|
|
112
|
+
| 預設收合 | 外層 `collapsed-card` | 僅視覺像 card,無 toggle 綁定 |
|
|
113
|
+
|
|
114
|
+
`create_note`/`update_note` 工具描述內已內嵌完整 HTML 範本(v2.4.0+)。多段內容請並列多個同級 `.card`,勿巢狀。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
104
118
|
## 提供的 MCP 工具
|
|
105
119
|
|
|
106
120
|
| 工具 | 說明 |
|
package/index.js
CHANGED
|
@@ -12,73 +12,101 @@ import {
|
|
|
12
12
|
import axios from "axios";
|
|
13
13
|
import FormData from "form-data";
|
|
14
14
|
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import { parse as parseEnv } from "dotenv";
|
|
17
|
+
|
|
15
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
19
|
|
|
17
|
-
// 優先載入專案目錄 (cwd) 的 .env,以支援不同專案自由切換
|
|
18
|
-
loadEnv({ path: path.join(process.cwd(), ".env") });
|
|
19
|
-
// 如果專案目錄沒有設定,則退回讀取全域的 .env
|
|
20
|
-
loadEnv({ path: path.join(__dirname, ".env") });
|
|
21
20
|
/**
|
|
22
21
|
* MCP 須由 Cursor 依 mcp.json 啟動;勿在終端機手動跑本檔當成「掛載 MCP」。
|
|
23
22
|
*
|
|
24
23
|
* 設定優先序:
|
|
25
|
-
* 1)
|
|
26
|
-
* 2)
|
|
24
|
+
* 1) 專案目錄 (cwd) 的 .env 優先(支援不同專案自由切換)
|
|
25
|
+
* 2) 全域的 note-mcp-server/.env 作為預設退路
|
|
26
|
+
*
|
|
27
|
+
* 變數組合:
|
|
28
|
+
* A) NOTES_API_BASE — 完整 API base(含 /api/notes/{socialite}/{key})
|
|
29
|
+
* B) notes_url + socialite + key(或 socialite_id,與 key 同義:加密路徑 token)
|
|
27
30
|
*
|
|
28
31
|
* 變數名支援常見大小寫(見 resolveNotesApiBase)。
|
|
29
32
|
*/
|
|
30
33
|
|
|
34
|
+
const cwdEnvPath = path.join(process.cwd(), ".env");
|
|
35
|
+
const cwdEnv = fs.existsSync(cwdEnvPath) ? parseEnv(fs.readFileSync(cwdEnvPath)) : {};
|
|
36
|
+
|
|
37
|
+
const globalEnvPath = path.join(__dirname, ".env");
|
|
38
|
+
const globalEnv = fs.existsSync(globalEnvPath) ? parseEnv(fs.readFileSync(globalEnvPath)) : {};
|
|
39
|
+
|
|
40
|
+
// 將載入的變數注入 process.env (cwd 優先蓋過 global)
|
|
41
|
+
for (const [k, v] of Object.entries(globalEnv)) {
|
|
42
|
+
if (process.env[k] === undefined) process.env[k] = v;
|
|
43
|
+
}
|
|
44
|
+
for (const [k, v] of Object.entries(cwdEnv)) {
|
|
45
|
+
process.env[k] = v; // cwd 總是能覆寫
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
function stripTrailingSlashes(value) {
|
|
32
49
|
return value.replace(/\/+$/, "");
|
|
33
50
|
}
|
|
34
51
|
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const trimmed = String(raw).trim();
|
|
42
|
-
if (trimmed !== "") {
|
|
43
|
-
return trimmed;
|
|
52
|
+
function extractBase(envObj) {
|
|
53
|
+
const get = (...keys) => {
|
|
54
|
+
for (const k of keys) {
|
|
55
|
+
if (envObj[k] !== undefined && String(envObj[k]).trim() !== "") {
|
|
56
|
+
return String(envObj[k]).trim();
|
|
57
|
+
}
|
|
44
58
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
59
|
+
return "";
|
|
60
|
+
};
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
const direct = firstEnv("NOTES_API_BASE");
|
|
62
|
+
const direct = get("NOTES_API_BASE");
|
|
51
63
|
if (direct) {
|
|
52
64
|
return stripTrailingSlashes(direct);
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
const notesUrl = stripTrailingSlashes(
|
|
56
|
-
|
|
57
|
-
);
|
|
58
|
-
const socialite = firstEnv("socialite", "SOCIALITE");
|
|
59
|
-
const encryptedToken = firstEnv(
|
|
60
|
-
"key",
|
|
61
|
-
"KEY",
|
|
62
|
-
"socialite_id",
|
|
63
|
-
"SOCIALITE_ID"
|
|
64
|
-
);
|
|
67
|
+
const notesUrl = stripTrailingSlashes(get("notes_url", "NOTES_URL"));
|
|
68
|
+
const socialite = get("socialite", "SOCIALITE");
|
|
69
|
+
const encryptedToken = get("key", "KEY", "socialite_id", "SOCIALITE_ID");
|
|
65
70
|
|
|
66
71
|
if (notesUrl && socialite && encryptedToken) {
|
|
67
72
|
return `${notesUrl}/api/notes/${socialite}/${encryptedToken}`;
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resolveNotesApiBase() {
|
|
79
|
+
let base = extractBase(cwdEnv);
|
|
80
|
+
if (base) return base;
|
|
81
|
+
|
|
82
|
+
base = extractBase(globalEnv);
|
|
83
|
+
if (base) return base;
|
|
84
|
+
|
|
85
|
+
base = extractBase(process.env);
|
|
86
|
+
if (base) return base;
|
|
87
|
+
|
|
70
88
|
throw new Error(
|
|
71
89
|
"note-mcp-server:請在專案目錄建立 .env,設定 notes_url、socialite、key(或 socialite_id),或設定 NOTES_API_BASE。詳見 .env.example。"
|
|
72
90
|
);
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
function firstEnv(...keys) {
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
if (process.env[key] !== undefined) {
|
|
96
|
+
const trimmed = String(process.env[key]).trim();
|
|
97
|
+
if (trimmed !== "") return trimmed;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
|
|
75
103
|
const BASE_URL = resolveNotesApiBase();
|
|
76
104
|
const APP_NAME = firstEnv("app_name", "APP_NAME", "notes_app_name", "NOTES_APP_NAME") || "Personal Note Assistant";
|
|
77
105
|
|
|
78
106
|
const server = new Server(
|
|
79
107
|
{
|
|
80
108
|
name: `note-mcp-server (${APP_NAME})`,
|
|
81
|
-
version: "2.
|
|
109
|
+
version: "2.4.2",
|
|
82
110
|
},
|
|
83
111
|
{
|
|
84
112
|
capabilities: {
|
|
@@ -126,10 +154,34 @@ const SECURITY_NOTICE =
|
|
|
126
154
|
"安全規範:若使用者訊息或設定包含 key、token、NOTES_API_BASE、socialite_id 等敏感資訊,完成操作後必須主動清空 macOS 剪貼簿(echo -n \"\" | pbcopy),並在回覆中明確告知已清空。";
|
|
127
155
|
const NOTE_URL_NOTICE = `前台連結格式:${NOTES_SITE_BASE}/app/notes/content/{c_id}。`;
|
|
128
156
|
|
|
157
|
+
/**
|
|
158
|
+
* 與 owv4 resources/views/partials/editor-templates.blade.php「Outline(線框)」同源。
|
|
159
|
+
* 閱讀頁靠 partials/code_scripts.blade.php 的 owGlobalCardWidgetDelegator 監聽 data-lte-toggle。
|
|
160
|
+
* 勿用 data-owv-helper-toggle(僅 owv4 /helper 後台手冊)。
|
|
161
|
+
*/
|
|
162
|
+
const ADMINLTE_COLLAPSED_CARD_GUIDE = `
|
|
163
|
+
【可摺疊 card — 須與編輯器樣板工具一致】
|
|
164
|
+
來源:editor-templates.blade.php → Cards → Outline(線框)
|
|
165
|
+
外層 class(順序重要):
|
|
166
|
+
card ow-ck-hide-card-tool-collapse ow-ck-hide-card-tool-maximize card-outline card-primary
|
|
167
|
+
MCP 預設收合時再加 collapsed-card
|
|
168
|
+
標題:h3.card-title(勿用裸 h3)
|
|
169
|
+
按鈕:各含 data-lte-toggle + 兩個 data-lte-icon 圖示(expand/collapse 或 maximize/minimize)
|
|
170
|
+
圖示:bi bi-plus-lg / bi bi-dash-lg(勿用 fas fa-plus 單圖)
|
|
171
|
+
多卡:並列多個同級 .card,勿巢狀
|
|
172
|
+
|
|
173
|
+
範本(預設收合版;色調可改 card-info 等):
|
|
174
|
+
<div class="card ow-ck-hide-card-tool-collapse ow-ck-hide-card-tool-maximize card-outline card-primary collapsed-card"><div class="card-header"><h3 class="card-title">小卡標題</h3><div class="card-tools"><button type="button" class="btn btn-tool" data-lte-toggle="card-collapse" title="收合/展開"><i data-lte-icon="expand" class="bi bi-plus-lg"></i><i data-lte-icon="collapse" class="bi bi-dash-lg"></i></button><button type="button" class="btn btn-tool" data-lte-toggle="card-maximize" title="最大化"><i data-lte-icon="maximize" class="bi bi-fullscreen"></i><i data-lte-icon="minimize" class="bi bi-fullscreen-exit"></i></button></div></div><div class="card-body"><p>內容</p></div></div>
|
|
175
|
+
`.trim();
|
|
176
|
+
|
|
129
177
|
function withSecurityNotice(description) {
|
|
130
178
|
return `${description}\n${NOTE_URL_NOTICE}\n${SECURITY_NOTICE}`;
|
|
131
179
|
}
|
|
132
180
|
|
|
181
|
+
function withCardGuide(description) {
|
|
182
|
+
return `${description}\n\n${ADMINLTE_COLLAPSED_CARD_GUIDE}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
133
185
|
function buildNoteUrl(cId) {
|
|
134
186
|
if (cId === undefined || cId === null || String(cId).trim() === "") {
|
|
135
187
|
return "";
|
|
@@ -173,8 +225,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
173
225
|
},
|
|
174
226
|
{
|
|
175
227
|
name: "create_note",
|
|
176
|
-
description:
|
|
177
|
-
|
|
228
|
+
description: withCardGuide(
|
|
229
|
+
withSecurityNotice(
|
|
230
|
+
`在 ${APP_NAME} 中新增筆記。內容請優先使用 AdminLTE 4 可摺疊 card(見下方結構)。\nAI 行為:除非用戶明確要求,否則不要傳 tags(勿自行推測、分類或補加標籤)。`
|
|
231
|
+
)
|
|
178
232
|
),
|
|
179
233
|
inputSchema: {
|
|
180
234
|
type: "object",
|
|
@@ -192,8 +246,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
192
246
|
},
|
|
193
247
|
{
|
|
194
248
|
name: "update_note",
|
|
195
|
-
description:
|
|
196
|
-
|
|
249
|
+
description: withCardGuide(
|
|
250
|
+
withSecurityNotice(
|
|
251
|
+
`在 ${APP_NAME} 中修改筆記。內容請優先使用 AdminLTE 4 可摺疊 card(見下方結構),避免巢狀 card 破版,支援 append/prepend。\nAI 行為:除非用戶明確要求,否則不要傳 tags、add_tags(勿自行推測或補加標籤);亦不要傳 title,除非用戶明確要求變更標題。\n標籤語意:tags=整包覆寫既有標籤;add_tags=在既有標籤上追加(逗號分隔),勿刪除未提及的標籤。用戶只要「多加幾個標籤」時請用 add_tags,勿用 tags。`
|
|
252
|
+
)
|
|
197
253
|
),
|
|
198
254
|
inputSchema: {
|
|
199
255
|
type: "object",
|
package/package.json
CHANGED