Skip to main content
Glama
bigjeager

Bear App MCP Server

by bigjeager

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
NameRequiredDescriptionDefault
idNoNote unique identifier
titleNoNote title
headerNoHeader inside the note
exclude_trashedNoExclude trashed notes
new_windowNoOpen in external window (macOS only)
editNoPlace cursor in note editor
selectedNoSelected text in note
pinNoPin note to top of list
floatNoFloat note window
show_windowNoShow Bear window
open_noteNoOpen note after command
searchNoSearch term within note

Implementation Reference

  • 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) } ] }; }
  • 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);
  • 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')); } }); });
  • 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; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bigjeager/bear-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server