bear-notes-mcp-xq7k 2.0.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/LICENSE.md +21 -0
- package/README.md +109 -0
- package/README.npm.md +109 -0
- package/dist/bear-urls.js +87 -0
- package/dist/config.js +11 -0
- package/dist/database.js +40 -0
- package/dist/main.js +422 -0
- package/dist/notes.js +224 -0
- package/dist/tags.js +170 -0
- package/dist/types.js +1 -0
- package/dist/utils.js +184 -0
- package/package.json +78 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Serhii Vasylenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Bear Notes MCP Server
|
|
2
|
+
|
|
3
|
+
Search, read, and update your Bear Notes
|
|
4
|
+
|
|
5
|
+
Want to use this Bear Notes MCP server with Claude Code, Cursor, Codex, or other AI assistants? You can run it as a standalone MCP server.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
**Requirements**: Node.js 22.13.0+
|
|
9
|
+
|
|
10
|
+
### Quick Start - Claude Code (One Command)
|
|
11
|
+
|
|
12
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
13
|
+
```bash
|
|
14
|
+
claude mcp add bear-notes --transport stdio -- npx -y bear-notes-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Read more here -- [README.npm.md](./README.npm.md)
|
|
18
|
+
|
|
19
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
20
|
+
```bash
|
|
21
|
+
claude mcp add bear-notes --transport stdio --env NODE_OPTIONS="--experimental-sqlite" -- npx -y bear-notes-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's it! The server will be downloaded from npm and configured automatically.
|
|
25
|
+
|
|
26
|
+
### Quick Start - Other AI Assistants
|
|
27
|
+
|
|
28
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"bear-notes": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "bear-notes-mcp"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"bear-notes": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "bear-notes-mcp"],
|
|
47
|
+
"env": {
|
|
48
|
+
"NODE_OPTIONS": "--experimental-sqlite"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Config file locations:**
|
|
56
|
+
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
57
|
+
- **Cline**: VS Code Settings → Extensions → Cline → MCP Settings
|
|
58
|
+
- **Continue**: `~/.continue/config.json`
|
|
59
|
+
- **Cursor**: Settings → Cursor Settings → MCP
|
|
60
|
+
|
|
61
|
+
**Check your Node.js version:** `node --version`
|
|
62
|
+
|
|
63
|
+
### Advanced: Local Development Build
|
|
64
|
+
|
|
65
|
+
If you want to contribute or modify the code:
|
|
66
|
+
|
|
67
|
+
**Step 1: Clone and build**
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/vasylenko/claude-desktop-extension-bear-notes.git
|
|
70
|
+
cd claude-desktop-extension-bear-notes
|
|
71
|
+
npm install
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Step 2: Configure with local path**
|
|
76
|
+
|
|
77
|
+
For Claude Code (Node.js 22.13.0+):
|
|
78
|
+
```bash
|
|
79
|
+
claude mcp add bear-notes --transport stdio -- node /absolute/path/to/dist/main.js
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For Claude Code (Node.js 22.5.0-22.12.x):
|
|
83
|
+
```bash
|
|
84
|
+
claude mcp add bear-notes --transport stdio -- node --experimental-sqlite /absolute/path/to/dist/main.js
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For other AI assistants (Node.js 22.13.0+):
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"bear-notes": {
|
|
92
|
+
"command": "node",
|
|
93
|
+
"args": ["/absolute/path/to/dist/main.js"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For other AI assistants (Node.js 22.5.0-22.12.x):
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"bear-notes": {
|
|
104
|
+
"command": "node",
|
|
105
|
+
"args": ["--experimental-sqlite", "/absolute/path/to/dist/main.js"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
package/README.npm.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Bear Notes MCP Server
|
|
2
|
+
|
|
3
|
+
Search, read, and update your Bear Notes
|
|
4
|
+
|
|
5
|
+
Want to use this Bear Notes MCP server with Claude Code, Cursor, Codex, or other AI assistants? You can run it as a standalone MCP server.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
**Requirements**: Node.js 22.13.0+
|
|
9
|
+
|
|
10
|
+
### Quick Start - Claude Code (One Command)
|
|
11
|
+
|
|
12
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
13
|
+
```bash
|
|
14
|
+
claude mcp add bear-notes --transport stdio -- npx -y bear-notes-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Read more here -- [README.npm.md](./README.npm.md)
|
|
18
|
+
|
|
19
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
20
|
+
```bash
|
|
21
|
+
claude mcp add bear-notes --transport stdio --env NODE_OPTIONS="--experimental-sqlite" -- npx -y bear-notes-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's it! The server will be downloaded from npm and configured automatically.
|
|
25
|
+
|
|
26
|
+
### Quick Start - Other AI Assistants
|
|
27
|
+
|
|
28
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"bear-notes": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "bear-notes-mcp"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"bear-notes": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "bear-notes-mcp"],
|
|
47
|
+
"env": {
|
|
48
|
+
"NODE_OPTIONS": "--experimental-sqlite"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Config file locations:**
|
|
56
|
+
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
57
|
+
- **Cline**: VS Code Settings → Extensions → Cline → MCP Settings
|
|
58
|
+
- **Continue**: `~/.continue/config.json`
|
|
59
|
+
- **Cursor**: Settings → Cursor Settings → MCP
|
|
60
|
+
|
|
61
|
+
**Check your Node.js version:** `node --version`
|
|
62
|
+
|
|
63
|
+
### Advanced: Local Development Build
|
|
64
|
+
|
|
65
|
+
If you want to contribute or modify the code:
|
|
66
|
+
|
|
67
|
+
**Step 1: Clone and build**
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/vasylenko/claude-desktop-extension-bear-notes.git
|
|
70
|
+
cd claude-desktop-extension-bear-notes
|
|
71
|
+
npm install
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Step 2: Configure with local path**
|
|
76
|
+
|
|
77
|
+
For Claude Code (Node.js 22.13.0+):
|
|
78
|
+
```bash
|
|
79
|
+
claude mcp add bear-notes --transport stdio -- node /absolute/path/to/dist/main.js
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For Claude Code (Node.js 22.5.0-22.12.x):
|
|
83
|
+
```bash
|
|
84
|
+
claude mcp add bear-notes --transport stdio -- node --experimental-sqlite /absolute/path/to/dist/main.js
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For other AI assistants (Node.js 22.13.0+):
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"bear-notes": {
|
|
92
|
+
"command": "node",
|
|
93
|
+
"args": ["/absolute/path/to/dist/main.js"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For other AI assistants (Node.js 22.5.0-22.12.x):
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"bear-notes": {
|
|
104
|
+
"command": "node",
|
|
105
|
+
"args": ["--experimental-sqlite", "/absolute/path/to/dist/main.js"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { BEAR_URL_SCHEME } from './config.js';
|
|
3
|
+
import { logAndThrow, logger } from './utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds a Bear x-callback-url for various actions with proper parameter encoding.
|
|
6
|
+
* Includes required UX parameters for optimal user experience.
|
|
7
|
+
*
|
|
8
|
+
* @param action - Bear API action (e.g., 'create', 'add-text')
|
|
9
|
+
* @param params - Parameters for the specific action
|
|
10
|
+
* @returns Properly encoded x-callback-url string
|
|
11
|
+
*/
|
|
12
|
+
export function buildBearUrl(action, params = {}) {
|
|
13
|
+
logger.debug(`Building Bear URL for action: ${action}`);
|
|
14
|
+
if (!action || typeof action !== 'string' || !action.trim()) {
|
|
15
|
+
logAndThrow('Bear URL error: Action parameter is required and must be a non-empty string');
|
|
16
|
+
}
|
|
17
|
+
const baseUrl = `${BEAR_URL_SCHEME}${action.trim()}`;
|
|
18
|
+
const urlParams = new URLSearchParams();
|
|
19
|
+
// Add provided parameters with proper encoding
|
|
20
|
+
const stringParams = ['title', 'text', 'tags', 'id', 'header', 'file', 'filename'];
|
|
21
|
+
for (const key of stringParams) {
|
|
22
|
+
const value = params[key];
|
|
23
|
+
if (value !== undefined && value.trim()) {
|
|
24
|
+
urlParams.set(key, value.trim());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (params.mode !== undefined) {
|
|
28
|
+
urlParams.set('mode', params.mode);
|
|
29
|
+
}
|
|
30
|
+
// UX params with defaults
|
|
31
|
+
urlParams.set('open_note', params.open_note ?? 'yes');
|
|
32
|
+
urlParams.set('new_window', params.new_window ?? 'no');
|
|
33
|
+
urlParams.set('show_window', params.show_window ?? 'yes');
|
|
34
|
+
// Add required Bear API parameters for add-text action
|
|
35
|
+
if (action === 'add-text') {
|
|
36
|
+
urlParams.set('new_line', 'yes'); // Ensures text appears on new line
|
|
37
|
+
}
|
|
38
|
+
// Convert URLSearchParams to proper URL encoding (Bear expects %20 not +)
|
|
39
|
+
const queryString = urlParams.toString().replace(/\+/g, '%20');
|
|
40
|
+
const finalUrl = `${baseUrl}?${queryString}`;
|
|
41
|
+
logger.debug(`Built Bear URL: ${finalUrl}`);
|
|
42
|
+
return finalUrl;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Executes a Bear x-callback-url using macOS subprocess execution.
|
|
46
|
+
* Platform-specific function that requires macOS with Bear Notes installed.
|
|
47
|
+
*
|
|
48
|
+
* @param url - The x-callback-url to execute
|
|
49
|
+
* @returns Promise that resolves when the command completes successfully
|
|
50
|
+
* @throws Error if platform is not macOS or subprocess execution fails
|
|
51
|
+
*/
|
|
52
|
+
export function executeBearXCallbackApi(url) {
|
|
53
|
+
logger.debug('Executing Bear x-callback-url');
|
|
54
|
+
if (!url || typeof url !== 'string' || !url.trim()) {
|
|
55
|
+
logAndThrow('Bear URL error: URL parameter is required and must be a non-empty string');
|
|
56
|
+
}
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
logger.debug('Launching Bear Notes via x-callback-url');
|
|
59
|
+
const child = spawn('open', ['-g', url.trim()], {
|
|
60
|
+
stdio: 'pipe',
|
|
61
|
+
detached: false,
|
|
62
|
+
});
|
|
63
|
+
let errorOutput = '';
|
|
64
|
+
if (child.stderr) {
|
|
65
|
+
child.stderr.on('data', (data) => {
|
|
66
|
+
errorOutput += data.toString();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
child.on('close', (code) => {
|
|
70
|
+
if (code === 0) {
|
|
71
|
+
logger.debug('Bear x-callback-url executed successfully');
|
|
72
|
+
resolve();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const errorMessage = `Bear URL error: Failed to execute x-callback-url (exit code: ${code})`;
|
|
76
|
+
const fullError = errorOutput ? `${errorMessage}. Error: ${errorOutput}` : errorMessage;
|
|
77
|
+
logger.error(fullError);
|
|
78
|
+
reject(new Error(fullError));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
child.on('error', (error) => {
|
|
82
|
+
const errorMessage = `Bear URL error: Failed to spawn subprocess: ${error.message}`;
|
|
83
|
+
logger.error(errorMessage);
|
|
84
|
+
reject(new Error(errorMessage));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const APP_VERSION = '2.0.1';
|
|
2
|
+
export const BEAR_URL_SCHEME = 'bear://x-callback-url/';
|
|
3
|
+
export const CORE_DATA_EPOCH_OFFSET = 978307200; // 2001-01-01 to Unix epoch
|
|
4
|
+
export const DEFAULT_SEARCH_LIMIT = 50;
|
|
5
|
+
export const BEAR_DATABASE_PATH = 'Library/Group Containers/9K33E3U3T4.net.shinyfrog.bear/Application Data/database.sqlite';
|
|
6
|
+
export const ERROR_MESSAGES = {
|
|
7
|
+
BEAR_DATABASE_NOT_FOUND: 'Bear database not found. Please ensure Bear Notes is installed and has been opened at least once.',
|
|
8
|
+
MISSING_SEARCH_PARAM: 'Please provide either a search term or a tag to search for notes.',
|
|
9
|
+
MISSING_NOTE_ID: 'Please provide a note identifier. Use bear-search-notes first to find the note ID.',
|
|
10
|
+
MISSING_TEXT_PARAM: 'Text input parameter is required and must be a non-empty string',
|
|
11
|
+
};
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { BEAR_DATABASE_PATH, ERROR_MESSAGES } from './config.js';
|
|
6
|
+
import { logAndThrow, logger } from './utils.js';
|
|
7
|
+
function getBearDatabasePath() {
|
|
8
|
+
// Environment override for testing
|
|
9
|
+
const envPath = process.env.BEAR_DB_PATH;
|
|
10
|
+
if (envPath) {
|
|
11
|
+
logger.debug(`Using environment override database path: ${envPath}`);
|
|
12
|
+
return envPath;
|
|
13
|
+
}
|
|
14
|
+
const defaultPath = join(homedir(), BEAR_DATABASE_PATH);
|
|
15
|
+
if (!existsSync(defaultPath)) {
|
|
16
|
+
logger.error(`Bear database not found at: ${defaultPath}`);
|
|
17
|
+
logAndThrow(`Database error: ${ERROR_MESSAGES.BEAR_DATABASE_NOT_FOUND}`);
|
|
18
|
+
}
|
|
19
|
+
logger.debug(`Using default Bear database path: ${defaultPath}`);
|
|
20
|
+
return defaultPath;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Opens a read-only connection to Bear's SQLite database.
|
|
24
|
+
*
|
|
25
|
+
* @returns DatabaseSync instance for querying Bear notes
|
|
26
|
+
* @throws Error if Bear database is not found or cannot be opened
|
|
27
|
+
*/
|
|
28
|
+
export function openBearDatabase() {
|
|
29
|
+
const databasePath = getBearDatabasePath();
|
|
30
|
+
logger.info(`Opening Bear database at: ${databasePath}`);
|
|
31
|
+
try {
|
|
32
|
+
const db = new DatabaseSync(databasePath);
|
|
33
|
+
logger.debug('Bear database opened successfully');
|
|
34
|
+
return db;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.error(`Failed to open Bear database: ${error}`);
|
|
38
|
+
logAndThrow(`Database error: Failed to open Bear database: ${error instanceof Error ? error.message : String(error)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|