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 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
+ [![Supply Chain](https://github.com/vasylenko/claude-desktop-extension-bear-notes/actions/workflows/workflow.yml/badge.svg)](https://github.com/vasylenko/claude-desktop-extension-bear-notes/actions/workflows/workflow.yml)
2
+ [![Snyk](https://snyk.io/test/github/vasylenko/claude-desktop-extension-bear-notes/badge.svg)](https://snyk.io/test/github/vasylenko/claude-desktop-extension-bear-notes)
3
+ [![Verified on MseeP](https://mseep.ai/badge.svg)](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
+ ![](./docs/demo.gif)
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
+ ![](./docs/stay-updated.png)
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
+ [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/vasylenko-claude-desktop-extension-bear-notes-badge.png)](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
+ };
@@ -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
+ }