bear-notes-mcp 2.0.0
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 +186 -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 +76 -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,186 @@
|
|
|
1
|
+
[](https://github.com/vasylenko/claude-desktop-extension-bear-notes/actions/workflows/workflow.yml)
|
|
2
|
+
[](https://snyk.io/test/github/vasylenko/claude-desktop-extension-bear-notes)
|
|
3
|
+
[](https://mseep.ai/app/34d7b12a-3983-40a3-876f-3cdd2ccfe3f2)
|
|
4
|
+
|
|
5
|
+
# Bear Notes Claude Extension (aka MCP Bundle)
|
|
6
|
+
|
|
7
|
+
Search, read, and update your Bear Notes directly from Claude conversations.
|
|
8
|
+
|
|
9
|
+
This Claude Desktop extension (bundled MCP server) provides seamless integration between AI assistance and your personal note-taking workflow with complete privacy: local-only operations, no external connections.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Staying Up To Date
|
|
14
|
+
|
|
15
|
+
Consider to subscribe to release announcements to know when I release a new version of the extenstion:
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
I also post to [reddit.com/r/bearapp/](https://www.reddit.com/r/bearapp/) when there's a new release.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
**Prerequisites**: [Bear app](https://bear.app/) must be installed and [Claude Desktop](https://claude.ai/download) must be installed.
|
|
24
|
+
|
|
25
|
+
1. Download the latest `bear-notes-mcpb.mcpb` extension from releases
|
|
26
|
+
2. Make sure your Claude Desktop is running (start if not)
|
|
27
|
+
3. Doubleclick on the extension file – Claude Desktop should show you the installation prompt
|
|
28
|
+
|
|
29
|
+
If doubleclick does not work for some reason, then open Claude -> Settings -> Extensions -> Advanced Settings -> click "Install Extension".
|
|
30
|
+
4. You're all set!
|
|
31
|
+
|
|
32
|
+
Ask Claude to search your Bear notes with a query like "Search my Bear notes for 'meeting'" - you should see your notes appear in the response!
|
|
33
|
+
|
|
34
|
+
## Standalone MCP Server Usage
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
**Requirements**: Node.js 22.13.0+
|
|
39
|
+
|
|
40
|
+
### Quick Start - Claude Code (One Command)
|
|
41
|
+
|
|
42
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
43
|
+
```bash
|
|
44
|
+
claude mcp add bear-notes --transport stdio -- npx -y bear-notes-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
48
|
+
```bash
|
|
49
|
+
claude mcp add bear-notes --transport stdio --env NODE_OPTIONS="--experimental-sqlite" -- npx -y bear-notes-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That's it! The server will be downloaded from npm and configured automatically.
|
|
53
|
+
|
|
54
|
+
### Quick Start - Other AI Assistants
|
|
55
|
+
|
|
56
|
+
**For Node.js 22.13.0+ / 23.4.0+ / 24.x+ / 25.x+ (recommended):**
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"bear-notes": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["-y", "bear-notes-mcp"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**For Node.js 22.5.0-22.12.x or 23.0.0-23.3.x (older versions):**
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"bear-notes": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": ["-y", "bear-notes-mcp"],
|
|
75
|
+
"env": {
|
|
76
|
+
"NODE_OPTIONS": "--experimental-sqlite"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Config file locations:**
|
|
84
|
+
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
85
|
+
- **Cline**: VS Code Settings → Extensions → Cline → MCP Settings
|
|
86
|
+
- **Continue**: `~/.continue/config.json`
|
|
87
|
+
- **Cursor**: Settings → Cursor Settings → MCP
|
|
88
|
+
|
|
89
|
+
**Check your Node.js version:** `node --version`
|
|
90
|
+
|
|
91
|
+
### Advanced: Local Development Build
|
|
92
|
+
|
|
93
|
+
If you want to contribute or modify the code:
|
|
94
|
+
|
|
95
|
+
**Step 1: Clone and build**
|
|
96
|
+
```bash
|
|
97
|
+
git clone https://github.com/vasylenko/claude-desktop-extension-bear-notes.git
|
|
98
|
+
cd claude-desktop-extension-bear-notes
|
|
99
|
+
npm install
|
|
100
|
+
npm run build
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Step 2: Configure with local path**
|
|
104
|
+
|
|
105
|
+
For Claude Code (Node.js 22.13.0+):
|
|
106
|
+
```bash
|
|
107
|
+
claude mcp add bear-notes --transport stdio -- node /absolute/path/to/dist/main.js
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For Claude Code (Node.js 22.5.0-22.12.x):
|
|
111
|
+
```bash
|
|
112
|
+
claude mcp add bear-notes --transport stdio -- node --experimental-sqlite /absolute/path/to/dist/main.js
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For other AI assistants (Node.js 22.13.0+):
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"mcpServers": {
|
|
119
|
+
"bear-notes": {
|
|
120
|
+
"command": "node",
|
|
121
|
+
"args": ["/absolute/path/to/dist/main.js"]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
For other AI assistants (Node.js 22.5.0-22.12.x):
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"mcpServers": {
|
|
131
|
+
"bear-notes": {
|
|
132
|
+
"command": "node",
|
|
133
|
+
"args": ["--experimental-sqlite", "/absolute/path/to/dist/main.js"]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## MCP Server Tools
|
|
140
|
+
|
|
141
|
+
- **`bear-search-notes`** - Find notes by text content or tags, returns list with IDs for further actions
|
|
142
|
+
- **`bear-open-note`** - Read full content of a specific note including text, formatting, and metadata
|
|
143
|
+
- **`bear-create-note`** - Create new notes with optional title, content, and tags
|
|
144
|
+
- **`bear-add-text-append`** - Add text to the end of existing notes or specific sections
|
|
145
|
+
- **`bear-add-text-prepend`** - Insert text at the beginning of existing notes or sections
|
|
146
|
+
- **`bear-add-file`** - Attach files (images, PDFs, spreadsheets, etc.) to existing notes
|
|
147
|
+
|
|
148
|
+
## Technical Details
|
|
149
|
+
|
|
150
|
+
This server reads your Bear Notes SQLite database directly for search/read operations and uses Bear's X-callback-URL API for write operations. All data processing happens locally on your machine with no external network calls.
|
|
151
|
+
|
|
152
|
+
### Platforms Supported
|
|
153
|
+
macOS only because Bear desktop works only on macOS.
|
|
154
|
+
|
|
155
|
+
### Logs
|
|
156
|
+
- MCP server logs go into `~/Library/Logs/Claude/main.log`, look for `bear-notes-mcp`
|
|
157
|
+
- MCP transport logs go to `~/Library/Logs/Claude/mcp-server-Bear\ Notes.log`
|
|
158
|
+
|
|
159
|
+
## FAQ
|
|
160
|
+
|
|
161
|
+
### Could this steal my data?
|
|
162
|
+
**No**. Extension only reads Bear's local database (same data Bear app shows you) and uses Bear's application native API to add text to the notes. No network transmission, no external servers.
|
|
163
|
+
|
|
164
|
+
### Why SQLite and not just a native Bear app's x-callback-url API?
|
|
165
|
+
|
|
166
|
+
For read operations (search/open), the x-callback-url API returns the note data in `x-success` response: that would require a server or custom binary to handle x-success responses - both risky and fragile. Direct SQLite read-only access is simpler and more reliable for searching and reading notes.
|
|
167
|
+
|
|
168
|
+
### Why experimental flag for nodejs?
|
|
169
|
+
|
|
170
|
+
This is to enable native SQLite support and avoid shipping an SQLite binary from third-party node packages, which poses supply chain risks and blocks the Claude extension from running on macOS.
|
|
171
|
+
|
|
172
|
+
Anthropic does not sign third-party SQLite binaries (obviously), causing macOS security systems to flag that the Claude process from a binary signed by Anthropic is trying to run another binary signed by a third party. As a result, Claude cannot run the extension.
|
|
173
|
+
|
|
174
|
+
### When I install the extension, I see a red warning: "Installing will grant access to everything on your computer." - what does this mean?
|
|
175
|
+
|
|
176
|
+
This is how Claude for Desktop reacts to the fact that this extension needs access to the Bear SQLite databse on your Mac.
|
|
177
|
+
|
|
178
|
+
Claude warning system does not distinguish between the need to access only one file (what the extension does) versus the need to access all files (this is NOT what the extention does).
|
|
179
|
+
|
|
180
|
+
One of the ways to validate this is asking your Claude to analyze the codebase (it is pretty small) before installing the extension and tell you.
|
|
181
|
+
|
|
182
|
+
### How can I report a bug or contribute?
|
|
183
|
+
|
|
184
|
+
Use issues or discussions! I'd be glad to see your feedback or suggestions, or your help to make this extension better! ❤️
|
|
185
|
+
|
|
186
|
+
[](https://mseep.ai/app/vasylenko-claude-desktop-extension-bear-notes)
|
|
@@ -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.0';
|
|
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
|
+
}
|