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