bear-notes-mcp 2.4.0 → 2.4.1
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/dist/config.js +1 -1
- package/dist/database.js +1 -1
- package/dist/notes.js +27 -12
- package/dist/tags.js +1 -0
- package/package.json +4 -4
package/dist/config.js
CHANGED
package/dist/database.js
CHANGED
|
@@ -29,7 +29,7 @@ export function openBearDatabase() {
|
|
|
29
29
|
const databasePath = getBearDatabasePath();
|
|
30
30
|
logger.info(`Opening Bear database at: ${databasePath}`);
|
|
31
31
|
try {
|
|
32
|
-
const db = new DatabaseSync(databasePath);
|
|
32
|
+
const db = new DatabaseSync(databasePath, { readOnly: true });
|
|
33
33
|
logger.debug('Bear database opened successfully');
|
|
34
34
|
return db;
|
|
35
35
|
}
|
package/dist/notes.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { DEFAULT_SEARCH_LIMIT } from './config.js';
|
|
2
2
|
import { convertCoreDataTimestamp, convertDateToCoreDataTimestamp, logAndThrow, logger, parseDateString, } from './utils.js';
|
|
3
3
|
import { openBearDatabase } from './database.js';
|
|
4
|
+
// SQL equivalent of decodeTagName() in tags.ts — both MUST apply the same transformations
|
|
5
|
+
const DECODED_TAG_TITLE = "LOWER(TRIM(REPLACE(t.ZTITLE, '+', ' ')))";
|
|
6
|
+
/**
|
|
7
|
+
* Builds a SQL WHERE clause that matches a tag exactly or its nested children.
|
|
8
|
+
* Escapes LIKE wildcards (%, _) in the tag name to prevent unintended pattern matching.
|
|
9
|
+
*/
|
|
10
|
+
function buildTagMatchClause(tag) {
|
|
11
|
+
const normalizedTag = tag.trim().toLowerCase();
|
|
12
|
+
const escapedTag = normalizedTag.replace(/[%_\\]/g, '\\$&');
|
|
13
|
+
return {
|
|
14
|
+
sql: ` AND (
|
|
15
|
+
${DECODED_TAG_TITLE} = ?
|
|
16
|
+
OR ${DECODED_TAG_TITLE} LIKE ? || '/%' ESCAPE '\\'
|
|
17
|
+
)`,
|
|
18
|
+
params: [normalizedTag, escapedTag],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
4
21
|
function formatBearNote(row) {
|
|
5
22
|
const title = row.title || 'Untitled';
|
|
6
23
|
const identifier = row.identifier;
|
|
@@ -150,6 +167,11 @@ export function searchNotes(searchTerm, tag, limit, dateFilter, pinned) {
|
|
|
150
167
|
innerQuery += `
|
|
151
168
|
JOIN Z_5PINNEDINTAGS pt ON pt.Z_5PINNEDNOTES = note.Z_PK
|
|
152
169
|
JOIN ZSFNOTETAG t ON t.Z_PK = pt.Z_13PINNEDINTAGS`;
|
|
170
|
+
}
|
|
171
|
+
else if (hasTag) {
|
|
172
|
+
innerQuery += `
|
|
173
|
+
JOIN Z_5TAGS nt ON nt.Z_5NOTES = note.Z_PK
|
|
174
|
+
JOIN ZSFNOTETAG t ON t.Z_PK = nt.Z_13TAGS`;
|
|
153
175
|
}
|
|
154
176
|
innerQuery += `
|
|
155
177
|
WHERE note.ZARCHIVED = 0
|
|
@@ -162,24 +184,17 @@ export function searchNotes(searchTerm, tag, limit, dateFilter, pinned) {
|
|
|
162
184
|
innerQuery += ' AND (note.ZTITLE LIKE ? OR note.ZTEXT LIKE ? OR f.ZSEARCHTEXT LIKE ?)';
|
|
163
185
|
queryParams.push(searchPattern, searchPattern, searchPattern);
|
|
164
186
|
}
|
|
165
|
-
//
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
queryParams.push(tagPattern);
|
|
187
|
+
// Tag clause applies to both pinned+tag and tag-only paths (JOINs differ above)
|
|
188
|
+
if (hasTag) {
|
|
189
|
+
const tagClause = buildTagMatchClause(tag);
|
|
190
|
+
innerQuery += tagClause.sql;
|
|
191
|
+
queryParams.push(...tagClause.params);
|
|
171
192
|
}
|
|
172
193
|
else if (hasPinnedFilter) {
|
|
173
194
|
// All pinned notes: globally pinned OR pinned in any tag (matches Bear's "Pinned" section)
|
|
174
195
|
innerQuery +=
|
|
175
196
|
' AND (note.ZPINNED = 1 OR EXISTS (SELECT 1 FROM Z_5PINNEDINTAGS pt WHERE pt.Z_5PINNEDNOTES = note.Z_PK))';
|
|
176
197
|
}
|
|
177
|
-
else if (hasTag) {
|
|
178
|
-
// Text-based tag search
|
|
179
|
-
const tagPattern = `%#${tag.trim()}%`;
|
|
180
|
-
innerQuery += ' AND note.ZTEXT LIKE ?';
|
|
181
|
-
queryParams.push(tagPattern);
|
|
182
|
-
}
|
|
183
198
|
// Add date filtering
|
|
184
199
|
if (hasDateFilter && dateFilter) {
|
|
185
200
|
if (dateFilter.createdAfter) {
|
package/dist/tags.js
CHANGED
|
@@ -5,6 +5,7 @@ import { openBearDatabase } from './database.js';
|
|
|
5
5
|
* - Replaces '+' with spaces (Bear's URL encoding)
|
|
6
6
|
* - Converts to lowercase (matches Bear UI behavior)
|
|
7
7
|
* - Trims whitespace
|
|
8
|
+
* Keep in sync with DECODED_TAG_TITLE in notes.ts — both MUST apply the same transformations.
|
|
8
9
|
*/
|
|
9
10
|
function decodeTagName(encodedName) {
|
|
10
11
|
return encodedName.replace(/\+/g, ' ').trim().toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bear-notes-mcp",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Bear Notes MCP server with TypeScript and native SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"@modelcontextprotocol/inspector": "^0.20.0",
|
|
31
31
|
"@types/debug": "^4.1.12",
|
|
32
32
|
"@types/node": "^24.10.13",
|
|
33
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
34
|
-
"@typescript-eslint/parser": "^8.
|
|
35
|
-
"eslint": "^9.39.
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
34
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
35
|
+
"eslint": "^9.39.3",
|
|
36
36
|
"eslint-plugin-import": "^2.32.0",
|
|
37
37
|
"prettier": "^3.8.1",
|
|
38
38
|
"tsx": "^4.21.0",
|