bear_open_note
Open notes in Bear app using ID, title, or search terms to access and edit content directly from AI assistants.
Instructions
Open a note in Bear by ID or title
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Note unique identifier | |
| title | No | Note title | |
| header | No | Header inside the note | |
| exclude_trashed | No | Exclude trashed notes | |
| new_window | No | Open in external window (macOS only) | |
| edit | No | Place cursor in note editor | |
| selected | No | Selected text in note | |
| pin | No | Pin note to top of list | |
| float | No | Float note window | |
| show_window | No | Show Bear window | |
| open_note | No | Open note after command | |
| search | No | Search term within note |
Implementation Reference
- src/index.ts:752-779 (handler)The handler function that implements the core logic for the 'bear_open_note' tool. It maps input arguments to Bear URL parameters, executes the 'open-note' action via x-callback-url with callback handling, and returns the note data from the callback.private async openNote(args: any) { const params: Record<string, string | boolean> = {}; if (args.id) params.id = args.id; if (args.title) params.title = args.title; if (args.header) params.header = args.header; if (args.exclude_trashed) params.exclude_trashed = "yes"; if (args.new_window) params.new_window = "yes"; if (args.edit) params.edit = "yes"; if (args.selected) params.selected = args.selected; if (args.pin) params.pin = "yes"; if (args.float) params.float = "yes"; if (args.show_window) params.show_window = "yes"; if (args.open_note) params.open_note = "yes"; if (args.search) params.search = args.search; // Set up a temporary HTTP server to capture the x-success callback const noteData = await this.executeWithCallback("open-note", params); return { content: [ { type: "text", text: JSON.stringify(noteData, null, 2) } ] }; }
- src/index.ts:198-254 (schema)The tool definition including name, description, and input schema for 'bear_open_note' registered in the ListTools response.{ name: "bear_open_note", description: "Open a note in Bear by ID or title", inputSchema: { type: "object", properties: { id: { type: "string", description: "Note unique identifier" }, title: { type: "string", description: "Note title" }, header: { type: "string", description: "Header inside the note" }, exclude_trashed: { type: "boolean", description: "Exclude trashed notes" }, new_window: { type: "boolean", description: "Open in external window (macOS only)" }, edit: { type: "boolean", description: "Place cursor in note editor" }, selected: { type: "string", description: "Selected text in note" }, pin: { type: "boolean", description: "Pin note to top of list" }, float: { type: "boolean", description: "Float note window" }, show_window: { type: "boolean", description: "Show Bear window" }, open_note: { type: "boolean", description: "Open note after command" }, search: { type: "string", description: "Search term within note" } } } },
- src/index.ts:705-706 (registration)The switch case in the CallToolRequest handler that dispatches to the openNote handler for 'bear_open_note'.case "bear_open_note": return await this.openNote(args);
- src/index.ts:89-190 (helper)Helper method used by the handler to execute Bear x-callback-url actions that return data via HTTP callback, parsing the response parameters dynamically.private async executeWithCallback(action: string, params: Record<string, string | boolean> = {}): Promise<any> { return new Promise((resolve, reject) => { // Create a temporary HTTP server to receive the callback const server = createServer((req, res) => { if (req.url) { const url = new URL(req.url, 'http://localhost'); const searchParams = url.searchParams; try { // Extract all callback parameters dynamically const callbackData: Record<string, any> = {}; for (const [key, value] of searchParams.entries()) { if (key === 'notes' || key === 'tags') { // Handle JSON array parameters try { callbackData[key] = JSON.parse(value); } catch { // If JSON parsing fails, treat as regular string callbackData[key] = value; } } else if (key === 'tags' && !callbackData[key]) { // Handle comma-separated tags for open-note callbackData[key] = value ? value.split(',').filter(Boolean) : []; } else if (key === 'is_trashed' || key === 'pin') { // Handle boolean parameters callbackData[key] = value === 'yes'; } else { // Handle regular string parameters callbackData[key] = value; } } // Send HTML response that immediately closes the browser window res.writeHead(200, { 'Content-Type': 'text/html', 'X-Frame-Options': 'DENY', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); const closeHtml = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="refresh" content="0; url=about:blank"> <title>Bear MCP Callback</title> <style>body { display: none; }</style> </head> <body> <script> // Multiple methods to close the window immediately try { window.close(); window.open('', '_self', ''); window.close(); setTimeout(() => window.close(), 1); setTimeout(() => history.back(), 10); } catch(e) {} </script> </body> </html> `; res.end(closeHtml); server.close(); resolve(callbackData); } catch (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error parsing callback data'); server.close(); reject(new Error(`Failed to parse callback data: ${error instanceof Error ? error.message : String(error)}`)); } } }); // Start server on a random available port server.listen(0, () => { const address = server.address(); if (address && typeof address === 'object') { const callbackUrl = `http://localhost:${address.port}/callback`; // Add x-success callback URL to params params['x-success'] = callbackUrl; // Build and execute Bear URL const bearUrl = this.buildBearURL(action, params); this.executeURL(bearUrl).catch(reject); // Set timeout to avoid hanging forever setTimeout(() => { server.close(); reject(new Error('Callback timeout')); }, 10000); } else { reject(new Error('Failed to start callback server')); } }); });
- src/index.ts:73-87 (helper)Helper method used to construct the Bear x-callback-url from action and parameters, URL-encoding query parts.private buildBearURL(action: string, params: Record<string, string | boolean> = {}): string { const baseURL = `bear://x-callback-url/${action}`; const queryParts: string[] = []; for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== null) { const encodedKey = encodeURIComponent(key); const encodedValue = encodeURIComponent(String(value)); queryParts.push(`${encodedKey}=${encodedValue}`); } } const queryString = queryParts.join('&'); return queryString ? `${baseURL}?${queryString}` : baseURL; }