reflect-mcp 1.0.7 → 1.0.8

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.
Files changed (2) hide show
  1. package/dist/tools/index.js +110 -0
  2. package/package.json +1 -1
@@ -509,6 +509,116 @@ export function registerTools(server, dbPath) {
509
509
  }
510
510
  },
511
511
  });
512
+ // Tool: Search notes by subject (fuzzy search with multi-word support)
513
+ server.addTool({
514
+ name: "search_notes",
515
+ description: "Search for notes by subject/title using fuzzy matching. Returns notes whose subjects match the search query.",
516
+ parameters: z.object({
517
+ query: z.string().describe("The search query to match against note subjects"),
518
+ graph_id: z.string().default("rapheal-brain").describe("The graph ID to search in"),
519
+ limit: z.number().default(10).describe("Maximum number of notes to return"),
520
+ include_content: z.boolean().default(false).describe("Whether to search in note content as well as subjects"),
521
+ }),
522
+ execute: async (args) => {
523
+ const { query, graph_id, limit, include_content } = args;
524
+ try {
525
+ const dbFile = resolvedDbPath;
526
+ const db = new Database(dbFile, { readonly: true });
527
+ // Split query into terms, treating dots, dashes, underscores as word separators
528
+ // Filter out empty strings and very short terms
529
+ const searchTerms = query
530
+ .toLowerCase()
531
+ .split(/[\s.,\-_\/\\]+/)
532
+ .filter(term => term.length >= 2);
533
+ if (searchTerms.length === 0) {
534
+ db.close();
535
+ return {
536
+ content: [
537
+ {
538
+ type: "text",
539
+ text: JSON.stringify({ error: "Search query too short or empty", query }),
540
+ },
541
+ ],
542
+ };
543
+ }
544
+ // Build dynamic WHERE clause - notes must contain ALL search terms
545
+ // in either subject or content (if include_content is true)
546
+ const whereConditions = [];
547
+ const params = [];
548
+ for (const term of searchTerms) {
549
+ if (include_content) {
550
+ whereConditions.push(`(LOWER(subject) LIKE ? OR LOWER(documentText) LIKE ?)`);
551
+ params.push(`%${term}%`, `%${term}%`);
552
+ }
553
+ else {
554
+ whereConditions.push(`LOWER(subject) LIKE ?`);
555
+ params.push(`%${term}%`);
556
+ }
557
+ }
558
+ // Build relevance scoring - higher score for more matches in subject
559
+ const relevanceParts = [];
560
+ for (const term of searchTerms) {
561
+ relevanceParts.push(`CASE WHEN LOWER(subject) LIKE '%${term}%' THEN 20 ELSE 0 END`);
562
+ if (include_content) {
563
+ relevanceParts.push(`CASE WHEN LOWER(documentText) LIKE '%${term}%' THEN 5 ELSE 0 END`);
564
+ }
565
+ }
566
+ // Bonus for exact phrase match
567
+ const fullQuery = query.toLowerCase();
568
+ relevanceParts.push(`CASE WHEN LOWER(subject) = '${fullQuery}' THEN 100 ELSE 0 END`);
569
+ relevanceParts.push(`CASE WHEN LOWER(subject) LIKE '${fullQuery}%' THEN 50 ELSE 0 END`);
570
+ relevanceParts.push(`CASE WHEN LOWER(subject) LIKE '%${fullQuery}%' THEN 30 ELSE 0 END`);
571
+ const relevanceExpr = relevanceParts.join(' + ');
572
+ const sql = `
573
+ SELECT id, subject, documentText, tags, editedAt, createdAt, isDaily,
574
+ (${relevanceExpr}) as relevance
575
+ FROM notes
576
+ WHERE isDeleted = 0
577
+ AND graphId = ?
578
+ AND (${whereConditions.join(' AND ')})
579
+ ORDER BY relevance DESC, editedAt DESC
580
+ LIMIT ?
581
+ `;
582
+ const stmt = db.prepare(sql);
583
+ const results = stmt.all(graph_id, ...params, limit);
584
+ db.close();
585
+ const notes = results.map((row) => ({
586
+ id: row.id,
587
+ subject: row.subject,
588
+ preview: row.documentText?.slice(0, 200) || "",
589
+ tags: row.tags ? JSON.parse(row.tags) : [],
590
+ editedAt: formatDate(row.editedAt),
591
+ isDaily: row.isDaily === 1,
592
+ relevance: row.relevance,
593
+ }));
594
+ return {
595
+ content: [
596
+ {
597
+ type: "text",
598
+ text: JSON.stringify({
599
+ query,
600
+ searchTerms,
601
+ graph_id,
602
+ searchedContent: include_content,
603
+ count: notes.length,
604
+ notes
605
+ }, null, 2),
606
+ },
607
+ ],
608
+ };
609
+ }
610
+ catch (e) {
611
+ return {
612
+ content: [
613
+ {
614
+ type: "text",
615
+ text: JSON.stringify({ error: String(e) }),
616
+ },
617
+ ],
618
+ };
619
+ }
620
+ },
621
+ });
512
622
  // Tool: Create a new note in Reflect via API
513
623
  server.addTool({
514
624
  name: "create_note",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflect-mcp",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "MCP server for Reflect Notes - connect your notes to Claude Desktop. Just run: npx reflect-mcp",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",