#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import chalk from 'chalk';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { BearDB } from './bearDB.js';
import {
tools,
handleOpenNote,
handleSearchNotes,
handleGetTags,
handleOpenTag,
handleGetRecentNotes,
handleGetPinnedNotes,
handleGetNoteStats,
handleGetNotesByDateRange,
handleCreateNote,
handleAddText,
handleTrashNote,
handleRenameTag,
handleDeleteTag
} from './tools/index.js';
// Parse command line arguments
const argv = yargs(hideBin(process.argv))
.option('db-path', {
alias: 'd',
type: 'string',
description: 'Path to Bear SQLite database'
})
.help()
.parseSync();
// Banner
console.error(chalk.blue(`
╭─────────────────────────────╮
│ Bear MCP Server v0.1.0 │
│ Bear notes access │
╰─────────────────────────────╯
`));
class BearMCPServer {
private bearDB: BearDB;
private server: Server;
constructor(dbPath?: string) {
try {
this.bearDB = new BearDB(dbPath);
} catch (error) {
console.error(error instanceof Error ? error.message : 'Failed to initialize Bear database');
process.exit(1);
}
this.server = new Server(
{
name: "bear-mcp-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
this.setupHandlers();
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: tools,
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
const args = request.params.arguments || {};
try {
switch (toolName) {
case "open_note":
return await handleOpenNote(this.bearDB, args);
case "search_notes":
return await handleSearchNotes(this.bearDB, args);
case "get_tags":
return await handleGetTags(this.bearDB);
case "open_tag":
return await handleOpenTag(this.bearDB, args);
case "get_recent_notes":
return await handleGetRecentNotes(this.bearDB, args);
case "get_pinned_notes":
return await handleGetPinnedNotes(this.bearDB, args);
case "get_note_stats":
return await handleGetNoteStats(this.bearDB, args);
case "get_notes_by_date":
return await handleGetNotesByDateRange(this.bearDB, args);
case "create_note":
return await handleCreateNote(args);
case "add_text":
return await handleAddText(args);
case "trash_note":
return await handleTrashNote(args);
case "rename_tag":
return await handleRenameTag(args);
case "delete_tag":
return await handleDeleteTag(args);
default:
return {
content: [{
type: "text",
text: `Unknown tool: ${toolName}`
}]
};
}
} catch (error) {
console.error(chalk.red(`Error executing tool ${toolName}:`), error);
return {
content: [{
type: "text",
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
}]
};
}
});
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error(chalk.green('✓ Bear MCP Server is running'));
console.error(chalk.gray('Available tools:'));
tools.forEach(tool => {
console.error(chalk.gray(` - ${tool.name}: ${tool.description}`));
});
}
cleanup() {
this.bearDB.close();
}
}
// Main execution
async function main() {
const server = new BearMCPServer(argv['db-path'] as string | undefined);
// Handle graceful shutdown
process.on('SIGINT', () => {
console.error(chalk.yellow('\n✓ Shutting down Bear MCP Server...'));
server.cleanup();
process.exit(0);
});
process.on('SIGTERM', () => {
server.cleanup();
process.exit(0);
});
try {
await server.start();
} catch (error) {
console.error(chalk.red('Failed to start server:'), error);
server.cleanup();
process.exit(1);
}
}
// Run the server
main().catch((error) => {
console.error(chalk.red('Fatal error:'), error);
process.exit(1);
});