@leeoohoo/ui-apps-devkit 0.1.0 → 0.1.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 +75 -60
- package/bin/chatos-uiapp.js +4 -4
- package/package.json +26 -20
- package/src/cli.js +53 -53
- package/src/commands/dev.js +14 -14
- package/src/commands/init.js +131 -129
- package/src/commands/install.js +47 -46
- package/src/commands/pack.js +72 -72
- package/src/commands/validate.js +138 -80
- package/src/lib/args.js +49 -49
- package/src/lib/config.js +29 -29
- package/src/lib/fs.js +78 -78
- package/src/lib/path-boundary.js +16 -16
- package/src/lib/plugin.js +45 -45
- package/src/lib/state-constants.js +2 -0
- package/src/lib/template.js +172 -168
- package/src/sandbox/server.js +1957 -692
- package/templates/basic/README.md +78 -54
- package/templates/basic/chatos.config.json +5 -5
- package/templates/basic/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +214 -181
- package/templates/basic/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
- package/templates/basic/docs/CHATOS_UI_APPS_HOST_API.md +136 -123
- package/templates/basic/docs/CHATOS_UI_APPS_OVERVIEW.md +112 -107
- package/templates/basic/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +242 -227
- package/templates/basic/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -0
- package/templates/basic/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -0
- package/templates/basic/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
- package/templates/basic/plugin/apps/app/compact.mjs +41 -0
- package/templates/basic/plugin/apps/app/index.mjs +287 -263
- package/templates/basic/plugin/apps/app/mcp-prompt.en.md +7 -7
- package/templates/basic/plugin/apps/app/mcp-prompt.zh.md +7 -7
- package/templates/basic/plugin/apps/app/mcp-server.mjs +15 -15
- package/templates/basic/plugin/backend/index.mjs +37 -37
- package/templates/basic/template.json +7 -7
- package/templates/notepad/README.md +55 -24
- package/templates/notepad/chatos.config.json +4 -4
- package/templates/notepad/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +214 -181
- package/templates/notepad/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
- package/templates/notepad/docs/CHATOS_UI_APPS_HOST_API.md +136 -123
- package/templates/notepad/docs/CHATOS_UI_APPS_OVERVIEW.md +112 -107
- package/templates/notepad/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +242 -227
- package/templates/notepad/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -0
- package/templates/notepad/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
- package/templates/notepad/plugin/apps/app/api.mjs +30 -30
- package/templates/notepad/plugin/apps/app/compact.mjs +41 -0
- package/templates/notepad/plugin/apps/app/dom.mjs +14 -14
- package/templates/notepad/plugin/apps/app/ds-tree.mjs +35 -35
- package/templates/notepad/plugin/apps/app/index.mjs +1056 -1056
- package/templates/notepad/plugin/apps/app/layers.mjs +338 -338
- package/templates/notepad/plugin/apps/app/markdown.mjs +120 -120
- package/templates/notepad/plugin/apps/app/mcp-prompt.en.md +22 -22
- package/templates/notepad/plugin/apps/app/mcp-prompt.zh.md +22 -22
- package/templates/notepad/plugin/apps/app/mcp-server.mjs +206 -199
- package/templates/notepad/plugin/apps/app/styles.mjs +355 -355
- package/templates/notepad/plugin/apps/app/tags.mjs +21 -21
- package/templates/notepad/plugin/apps/app/ui.mjs +280 -280
- package/templates/notepad/plugin/backend/index.mjs +99 -99
- package/templates/notepad/plugin/plugin.json +23 -23
- package/templates/notepad/plugin/shared/notepad-paths.mjs +59 -41
- package/templates/notepad/plugin/shared/notepad-store.mjs +765 -765
- package/templates/notepad/template.json +8 -8
|
@@ -1,200 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server entry.
|
|
3
|
+
*
|
|
4
|
+
* Note: ChatOS import excludes `node_modules/`, so if you import third-party
|
|
5
|
+
* deps (e.g. @modelcontextprotocol/sdk, zod), you must bundle this file into
|
|
6
|
+
* a single output (or vendor deps into the plugin directory).
|
|
7
|
+
*/
|
|
1
8
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
import { createNotepadStore } from '../../shared/notepad-store.mjs';
|
|
5
|
-
import { resolveUiAppDataDir } from '../../shared/notepad-paths.mjs';
|
|
6
|
-
|
|
7
|
-
const PLUGIN_ID = '__PLUGIN_ID__';
|
|
8
|
-
const SERVER_NAME = '__PLUGIN_ID__.__APP_ID__';
|
|
9
|
-
|
|
10
|
-
const store = createNotepadStore({ dataDir: resolveUiAppDataDir({ pluginId: PLUGIN_ID }) });
|
|
11
|
-
|
|
12
|
-
const server = new McpServer({
|
|
13
|
-
name: SERVER_NAME,
|
|
14
|
-
version: '0.1.0',
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const toText = (payload) => ({
|
|
18
|
-
content: [
|
|
19
|
-
{
|
|
20
|
-
type: 'text',
|
|
21
|
-
text: JSON.stringify(payload, null, 2),
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
server.registerTool(
|
|
27
|
-
'init',
|
|
28
|
-
{
|
|
29
|
-
title: 'Init Notepad Storage',
|
|
30
|
-
description: 'Initialize the Notepad storage (data directory, index, notes root).',
|
|
31
|
-
inputSchema: z.object({}).optional(),
|
|
32
|
-
},
|
|
33
|
-
async () => toText(await store.init())
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
server.registerTool(
|
|
37
|
-
'list_folders',
|
|
38
|
-
{
|
|
39
|
-
title: 'List Folders',
|
|
40
|
-
description: 'List all folders (categories) under Notepad notes root.',
|
|
41
|
-
inputSchema: z.object({}).optional(),
|
|
42
|
-
},
|
|
43
|
-
async () => toText(await store.listFolders())
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
server.registerTool(
|
|
47
|
-
'create_folder',
|
|
48
|
-
{
|
|
49
|
-
title: 'Create Folder',
|
|
50
|
-
description: 'Create a folder (category). Supports nested paths like "work/ideas".',
|
|
51
|
-
inputSchema: z.object({
|
|
52
|
-
folder: z.string().min(1).describe('Folder path, relative to notes root (e.g., "work/ideas")'),
|
|
53
|
-
}),
|
|
54
|
-
},
|
|
55
|
-
async ({ folder }) => toText(await store.createFolder({ folder }))
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
server.registerTool(
|
|
59
|
-
'rename_folder',
|
|
60
|
-
{
|
|
61
|
-
title: 'Rename Folder',
|
|
62
|
-
description: 'Rename/move a folder (and updates indexed notes folder paths).',
|
|
63
|
-
inputSchema: z.object({
|
|
64
|
-
from: z.string().min(1).describe('Source folder path'),
|
|
65
|
-
to: z.string().min(1).describe('Target folder path'),
|
|
66
|
-
}),
|
|
67
|
-
},
|
|
68
|
-
async ({ from, to }) => toText(await store.renameFolder({ from, to }))
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
server.registerTool(
|
|
72
|
-
'delete_folder',
|
|
73
|
-
{
|
|
74
|
-
title: 'Delete Folder',
|
|
75
|
-
description: 'Delete a folder. If recursive=true, deletes all notes under it and removes them from index.',
|
|
76
|
-
inputSchema: z.object({
|
|
77
|
-
folder: z.string().min(1).describe('Folder path to delete'),
|
|
78
|
-
recursive: z.boolean().optional().describe('Delete folder recursively'),
|
|
79
|
-
}),
|
|
80
|
-
},
|
|
81
|
-
async ({ folder, recursive } = {}) => toText(await store.deleteFolder({ folder, recursive: recursive === true }))
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
server.registerTool(
|
|
85
|
-
'list_notes',
|
|
86
|
-
{
|
|
87
|
-
title: 'List Notes',
|
|
88
|
-
description: 'List notes with optional folder/tags/title filtering.',
|
|
89
|
-
inputSchema: z.object({
|
|
90
|
-
folder: z.string().optional().describe('Folder path; empty means all'),
|
|
91
|
-
recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
|
|
92
|
-
tags: z.array(z.string()).optional().describe('Filter notes by tags'),
|
|
93
|
-
match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
|
|
94
|
-
query: z.string().optional().describe('Substring filter for title/folder'),
|
|
95
|
-
limit: z.number().int().min(1).max(500).optional().describe('Max notes to return'),
|
|
96
|
-
}),
|
|
97
|
-
},
|
|
98
|
-
async ({ folder, recursive, tags, match, query, limit } = {}) =>
|
|
99
|
-
toText(await store.listNotes({ folder, recursive, tags, match, query, limit }))
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
server.registerTool(
|
|
103
|
-
'create_note',
|
|
104
|
-
{
|
|
105
|
-
title: 'Create Note',
|
|
106
|
-
description: 'Create a new markdown note under a folder with optional title/content/tags.',
|
|
107
|
-
inputSchema: z.object({
|
|
108
|
-
folder: z.string().optional().describe('Folder path to create note in'),
|
|
109
|
-
title: z.string().optional().describe('Note title'),
|
|
110
|
-
content: z.string().optional().describe('Markdown content (optional)'),
|
|
111
|
-
tags: z.array(z.string()).optional().describe('Tags (optional)'),
|
|
112
|
-
}),
|
|
113
|
-
},
|
|
114
|
-
async ({ folder, title, content, tags } = {}) => toText(await store.createNote({ folder, title, content, tags }))
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
server.registerTool(
|
|
118
|
-
'read_note',
|
|
119
|
-
{
|
|
120
|
-
title: 'Read Note',
|
|
121
|
-
description: 'Read a note by id (returns metadata and markdown content).',
|
|
122
|
-
inputSchema: z.object({
|
|
123
|
-
id: z.string().min(1).describe('Note id'),
|
|
124
|
-
}),
|
|
125
|
-
},
|
|
126
|
-
async ({ id }) => toText(await store.getNote({ id }))
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
server.registerTool(
|
|
130
|
-
'update_note',
|
|
131
|
-
{
|
|
132
|
-
title: 'Update Note',
|
|
133
|
-
description: 'Update note metadata/content by id. You can also move it by changing folder.',
|
|
134
|
-
inputSchema: z.object({
|
|
135
|
-
id: z.string().min(1).describe('Note id'),
|
|
136
|
-
title: z.string().optional().describe('New title'),
|
|
137
|
-
content: z.string().optional().describe('New markdown content'),
|
|
138
|
-
folder: z.string().optional().describe('New folder path'),
|
|
139
|
-
tags: z.array(z.string()).optional().describe('Replace tags'),
|
|
140
|
-
}),
|
|
141
|
-
},
|
|
142
|
-
async ({ id, title, content, folder, tags } = {}) => toText(await store.updateNote({ id, title, content, folder, tags }))
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
server.registerTool(
|
|
146
|
-
'delete_note',
|
|
147
|
-
{
|
|
148
|
-
title: 'Delete Note',
|
|
149
|
-
description: 'Delete a note by id (removes file and index entry).',
|
|
150
|
-
inputSchema: z.object({
|
|
151
|
-
id: z.string().min(1).describe('Note id'),
|
|
152
|
-
}),
|
|
153
|
-
},
|
|
154
|
-
async ({ id }) => toText(await store.deleteNote({ id }))
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
server.registerTool(
|
|
158
|
-
'list_tags',
|
|
159
|
-
{
|
|
160
|
-
title: 'List Tags',
|
|
161
|
-
description: 'List all tags with usage counts.',
|
|
162
|
-
inputSchema: z.object({}).optional(),
|
|
163
|
-
},
|
|
164
|
-
async () => toText(await store.listTags())
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
server.registerTool(
|
|
168
|
-
'search_notes',
|
|
169
|
-
{
|
|
170
|
-
title: 'Search Notes',
|
|
171
|
-
description: 'Search notes by query, optionally filtered by folder/tags; can include content search.',
|
|
172
|
-
inputSchema: z.object({
|
|
173
|
-
query: z.string().min(1).describe('Search keyword'),
|
|
174
|
-
folder: z.string().optional().describe('Folder path filter'),
|
|
175
|
-
recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
|
|
176
|
-
tags: z.array(z.string()).optional().describe('Tag filter'),
|
|
177
|
-
match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
|
|
178
|
-
includeContent: z.boolean().optional().describe('Search in content (default true)'),
|
|
179
|
-
limit: z.number().int().min(1).max(200).optional().describe('Max results'),
|
|
180
|
-
}),
|
|
181
|
-
},
|
|
182
|
-
async ({ query, folder, recursive, tags, match, includeContent, limit } = {}) =>
|
|
183
|
-
toText(await store.searchNotes({ query, folder, recursive, tags, match, includeContent: includeContent !== false, limit }))
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
async function main() {
|
|
187
|
-
const initRes = await store.init();
|
|
188
|
-
if (!initRes?.ok) {
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
|
-
console.error('Notepad MCP init failed:', initRes?.message || initRes);
|
|
191
|
-
}
|
|
192
|
-
const transport = new StdioServerTransport();
|
|
193
|
-
await server.connect(transport);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
main().catch((error) => {
|
|
197
|
-
// eslint-disable-next-line no-console
|
|
198
|
-
console.error('MCP server error:', error);
|
|
199
|
-
process.exit(1);
|
|
200
|
-
});
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { createNotepadStore } from '../../shared/notepad-store.mjs';
|
|
12
|
+
import { resolveUiAppDataDir } from '../../shared/notepad-paths.mjs';
|
|
13
|
+
|
|
14
|
+
const PLUGIN_ID = '__PLUGIN_ID__';
|
|
15
|
+
const SERVER_NAME = '__PLUGIN_ID__.__APP_ID__';
|
|
16
|
+
|
|
17
|
+
const store = createNotepadStore({ dataDir: resolveUiAppDataDir({ pluginId: PLUGIN_ID }) });
|
|
18
|
+
|
|
19
|
+
const server = new McpServer({
|
|
20
|
+
name: SERVER_NAME,
|
|
21
|
+
version: '0.1.0',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const toText = (payload) => ({
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: JSON.stringify(payload, null, 2),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
server.registerTool(
|
|
34
|
+
'init',
|
|
35
|
+
{
|
|
36
|
+
title: 'Init Notepad Storage',
|
|
37
|
+
description: 'Initialize the Notepad storage (data directory, index, notes root).',
|
|
38
|
+
inputSchema: z.object({}).optional(),
|
|
39
|
+
},
|
|
40
|
+
async () => toText(await store.init())
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
server.registerTool(
|
|
44
|
+
'list_folders',
|
|
45
|
+
{
|
|
46
|
+
title: 'List Folders',
|
|
47
|
+
description: 'List all folders (categories) under Notepad notes root.',
|
|
48
|
+
inputSchema: z.object({}).optional(),
|
|
49
|
+
},
|
|
50
|
+
async () => toText(await store.listFolders())
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
server.registerTool(
|
|
54
|
+
'create_folder',
|
|
55
|
+
{
|
|
56
|
+
title: 'Create Folder',
|
|
57
|
+
description: 'Create a folder (category). Supports nested paths like "work/ideas".',
|
|
58
|
+
inputSchema: z.object({
|
|
59
|
+
folder: z.string().min(1).describe('Folder path, relative to notes root (e.g., "work/ideas")'),
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
async ({ folder }) => toText(await store.createFolder({ folder }))
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.registerTool(
|
|
66
|
+
'rename_folder',
|
|
67
|
+
{
|
|
68
|
+
title: 'Rename Folder',
|
|
69
|
+
description: 'Rename/move a folder (and updates indexed notes folder paths).',
|
|
70
|
+
inputSchema: z.object({
|
|
71
|
+
from: z.string().min(1).describe('Source folder path'),
|
|
72
|
+
to: z.string().min(1).describe('Target folder path'),
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
async ({ from, to }) => toText(await store.renameFolder({ from, to }))
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
server.registerTool(
|
|
79
|
+
'delete_folder',
|
|
80
|
+
{
|
|
81
|
+
title: 'Delete Folder',
|
|
82
|
+
description: 'Delete a folder. If recursive=true, deletes all notes under it and removes them from index.',
|
|
83
|
+
inputSchema: z.object({
|
|
84
|
+
folder: z.string().min(1).describe('Folder path to delete'),
|
|
85
|
+
recursive: z.boolean().optional().describe('Delete folder recursively'),
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
async ({ folder, recursive } = {}) => toText(await store.deleteFolder({ folder, recursive: recursive === true }))
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
server.registerTool(
|
|
92
|
+
'list_notes',
|
|
93
|
+
{
|
|
94
|
+
title: 'List Notes',
|
|
95
|
+
description: 'List notes with optional folder/tags/title filtering.',
|
|
96
|
+
inputSchema: z.object({
|
|
97
|
+
folder: z.string().optional().describe('Folder path; empty means all'),
|
|
98
|
+
recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
|
|
99
|
+
tags: z.array(z.string()).optional().describe('Filter notes by tags'),
|
|
100
|
+
match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
|
|
101
|
+
query: z.string().optional().describe('Substring filter for title/folder'),
|
|
102
|
+
limit: z.number().int().min(1).max(500).optional().describe('Max notes to return'),
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
105
|
+
async ({ folder, recursive, tags, match, query, limit } = {}) =>
|
|
106
|
+
toText(await store.listNotes({ folder, recursive, tags, match, query, limit }))
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
server.registerTool(
|
|
110
|
+
'create_note',
|
|
111
|
+
{
|
|
112
|
+
title: 'Create Note',
|
|
113
|
+
description: 'Create a new markdown note under a folder with optional title/content/tags.',
|
|
114
|
+
inputSchema: z.object({
|
|
115
|
+
folder: z.string().optional().describe('Folder path to create note in'),
|
|
116
|
+
title: z.string().optional().describe('Note title'),
|
|
117
|
+
content: z.string().optional().describe('Markdown content (optional)'),
|
|
118
|
+
tags: z.array(z.string()).optional().describe('Tags (optional)'),
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
async ({ folder, title, content, tags } = {}) => toText(await store.createNote({ folder, title, content, tags }))
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
server.registerTool(
|
|
125
|
+
'read_note',
|
|
126
|
+
{
|
|
127
|
+
title: 'Read Note',
|
|
128
|
+
description: 'Read a note by id (returns metadata and markdown content).',
|
|
129
|
+
inputSchema: z.object({
|
|
130
|
+
id: z.string().min(1).describe('Note id'),
|
|
131
|
+
}),
|
|
132
|
+
},
|
|
133
|
+
async ({ id }) => toText(await store.getNote({ id }))
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
server.registerTool(
|
|
137
|
+
'update_note',
|
|
138
|
+
{
|
|
139
|
+
title: 'Update Note',
|
|
140
|
+
description: 'Update note metadata/content by id. You can also move it by changing folder.',
|
|
141
|
+
inputSchema: z.object({
|
|
142
|
+
id: z.string().min(1).describe('Note id'),
|
|
143
|
+
title: z.string().optional().describe('New title'),
|
|
144
|
+
content: z.string().optional().describe('New markdown content'),
|
|
145
|
+
folder: z.string().optional().describe('New folder path'),
|
|
146
|
+
tags: z.array(z.string()).optional().describe('Replace tags'),
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
async ({ id, title, content, folder, tags } = {}) => toText(await store.updateNote({ id, title, content, folder, tags }))
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
server.registerTool(
|
|
153
|
+
'delete_note',
|
|
154
|
+
{
|
|
155
|
+
title: 'Delete Note',
|
|
156
|
+
description: 'Delete a note by id (removes file and index entry).',
|
|
157
|
+
inputSchema: z.object({
|
|
158
|
+
id: z.string().min(1).describe('Note id'),
|
|
159
|
+
}),
|
|
160
|
+
},
|
|
161
|
+
async ({ id }) => toText(await store.deleteNote({ id }))
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
server.registerTool(
|
|
165
|
+
'list_tags',
|
|
166
|
+
{
|
|
167
|
+
title: 'List Tags',
|
|
168
|
+
description: 'List all tags with usage counts.',
|
|
169
|
+
inputSchema: z.object({}).optional(),
|
|
170
|
+
},
|
|
171
|
+
async () => toText(await store.listTags())
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
server.registerTool(
|
|
175
|
+
'search_notes',
|
|
176
|
+
{
|
|
177
|
+
title: 'Search Notes',
|
|
178
|
+
description: 'Search notes by query, optionally filtered by folder/tags; can include content search.',
|
|
179
|
+
inputSchema: z.object({
|
|
180
|
+
query: z.string().min(1).describe('Search keyword'),
|
|
181
|
+
folder: z.string().optional().describe('Folder path filter'),
|
|
182
|
+
recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
|
|
183
|
+
tags: z.array(z.string()).optional().describe('Tag filter'),
|
|
184
|
+
match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
|
|
185
|
+
includeContent: z.boolean().optional().describe('Search in content (default true)'),
|
|
186
|
+
limit: z.number().int().min(1).max(200).optional().describe('Max results'),
|
|
187
|
+
}),
|
|
188
|
+
},
|
|
189
|
+
async ({ query, folder, recursive, tags, match, includeContent, limit } = {}) =>
|
|
190
|
+
toText(await store.searchNotes({ query, folder, recursive, tags, match, includeContent: includeContent !== false, limit }))
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
const initRes = await store.init();
|
|
195
|
+
if (!initRes?.ok) {
|
|
196
|
+
// eslint-disable-next-line no-console
|
|
197
|
+
console.error('Notepad MCP init failed:', initRes?.message || initRes);
|
|
198
|
+
}
|
|
199
|
+
const transport = new StdioServerTransport();
|
|
200
|
+
await server.connect(transport);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main().catch((error) => {
|
|
204
|
+
// eslint-disable-next-line no-console
|
|
205
|
+
console.error('MCP server error:', error);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
});
|