reflect-mcp 1.0.1 → 1.0.3

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.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # Reflect MCP Server
2
+
3
+ Connect your [Reflect](https://reflect.app) notes to Claude Desktop.
4
+
5
+ ## Prerequisites
6
+
7
+ Before installing, make sure you have:
8
+
9
+ - **Reflect Desktop** - Must be installed (but does not need to be running)
10
+ - Download from [reflect.app](https://reflect.app/download)
11
+
12
+ - **Claude Desktop** - Required to use MCP servers
13
+ - Download from [claude.ai](https://claude.com/download)
14
+
15
+ - **Node.js** - Version 18 or higher recommended
16
+ - Download from [nodejs.org](https://nodejs.org)
17
+
18
+ ## Quick Start
19
+
20
+
21
+ **1. Install the `reflect-mcp` package:**
22
+
23
+ ```bash
24
+ npm install reflect-mcp
25
+ ```
26
+
27
+ **2. Install the server:**
28
+
29
+ ```bash
30
+ npx reflect-mcp install
31
+ ```
32
+
33
+ **3. Add to Claude Desktop config** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "reflect": {
39
+ "command": "npx",
40
+ "args": ["-y", "mcp-remote", "http://localhost:3000/mcp"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ **4. Restart Claude Desktop**
47
+
48
+ That's it! First time you use a Reflect tool, your browser will open to authenticate.
49
+
50
+ > **Note:** If you see auth errors, try restarting Claude Desktop one more time.
51
+
52
+
53
+ ## Usage Examples
54
+
55
+ Once installed, you can ask Claude to read and write your notes:
56
+
57
+ - "Read all my notes tagged #spanish and create a study guide note with my biggest gaps"
58
+
59
+ - "Read my last 3 daily notes and create a weekly summary note tagged #reflection"
60
+
61
+ - "Look at notes tagged #work. Create a 'Career Development Plan' note based on what I'm learning and struggling with"
62
+
63
+ - "Read my 1:1 meeting notes with [[manager]] and create a performance review prep note "
64
+
65
+ ## Commands
66
+
67
+ ```bash
68
+ reflect-mcp install [db-path] # Install as auto-start service
69
+ reflect-mcp uninstall # Remove auto-start service
70
+ reflect-mcp status # Check service status
71
+ reflect-mcp [db-path] # Run server manually
72
+ ```
73
+
74
+ ## Options
75
+
76
+ | Option | Description | Default |
77
+ |--------|-------------|---------|
78
+ | `db-path` | Path to Reflect SQLite database | `~/Library/Application Support/Reflect/File System/000/t/00/00000000` |
79
+ | `--port <port>` | Server port | `3000` |
80
+
81
+ ## Examples
82
+
83
+ ```bash
84
+ # Install with default settings
85
+ npx reflect-mcp install
86
+
87
+ # Install with custom database path
88
+ npx reflect-mcp install ~/custom/path/to/reflect/db
89
+
90
+ # Install with custom port
91
+ npx reflect-mcp install --port 4000
92
+
93
+ # Check if service is running
94
+ npx reflect-mcp status
95
+
96
+ # Remove auto-start
97
+ npx reflect-mcp uninstall
98
+ ```
99
+
100
+ ## Tools Available
101
+
102
+ - `get_graphs` - List all Reflect graphs
103
+ - `get_backlinks` - Get backlinks for a note
104
+ - `get_daily_notes` - Get recent daily notes
105
+ - `get_daily_note_by_date` - Get daily note for specific date
106
+ - `get_backlinked_notes` - Get notes with most backlinks
107
+ - `get_tags` - Get all tags with usage counts
108
+ - `get_notes_with_tag` - Get notes with a specific tag
109
+ - `get_note` - Get a note by title
110
+ - `create_note` - Create a new note
111
+
112
+ ## Troubleshooting
113
+
114
+ **Server won't start**
115
+ - Check if port 3000 is available: `lsof -i :3000`
116
+ - Try a different port: `npx reflect-mcp install --port 4000`
117
+
118
+ **OAuth not working**
119
+ - Restart Claude Desktop after installation
120
+ - Check server is running: `npx reflect-mcp status`
121
+ - Try uninstalling and reinstalling: `npx reflect-mcp uninstall && npx reflect-mcp install`
122
+
123
+ **Database not found**
124
+ - Ensure Reflect Desktop is installed
125
+ - Verify database path exists at default location
126
+ - Try specifying custom path: `npx reflect-mcp install /path/to/db`
127
+
128
+ ## Demo:
129
+ https://www.loom.com/share/455b1d3eb7184bdea1ae4e8d5904fc53
130
+ ## License
131
+
132
+ MIT
@@ -11,7 +11,7 @@ export function registerTools(server, dbPath) {
11
11
  // Tool: Get all Reflect graphs
12
12
  server.addTool({
13
13
  name: "get_graphs",
14
- description: "Get a list of all Reflect graphs accessible with the current access token",
14
+ description: "Get a list of all Reflect graphs.",
15
15
  parameters: z.object({}),
16
16
  execute: async (_args, { session }) => {
17
17
  if (!session) {
@@ -59,7 +59,7 @@ export function registerTools(server, dbPath) {
59
59
  // Tool: Get backlinks for a note from local Reflect SQLite database
60
60
  server.addTool({
61
61
  name: "get_backlinks",
62
- description: "Get backlinks for a note from the local Reflect database. Returns notes that link to the specified note.",
62
+ description: "Get backlinks for a note from Reflect. Use this tool to get more context about a note after calling the get_note tool.",
63
63
  parameters: z.object({
64
64
  subject: z.string().describe("The subject/title of the note to get backlinks for"),
65
65
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
@@ -111,7 +111,7 @@ export function registerTools(server, dbPath) {
111
111
  // Tool: Get recent daily notes
112
112
  server.addTool({
113
113
  name: "get_daily_notes",
114
- description: "Get the most recent daily notes from the local Reflect database",
114
+ description: "Get the most recent daily notes from Reflect.",
115
115
  parameters: z.object({
116
116
  limit: z.number().default(5).describe("Number of recent daily notes to return"),
117
117
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
@@ -163,7 +163,7 @@ export function registerTools(server, dbPath) {
163
163
  // Tool: Get daily note by date
164
164
  server.addTool({
165
165
  name: "get_daily_note_by_date",
166
- description: "Get the daily note for a specific date from the local Reflect database",
166
+ description: "Get the daily note for a specific date.",
167
167
  parameters: z.object({
168
168
  date: z.string().describe("The date in YYYY-MM-DD format"),
169
169
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
@@ -225,7 +225,7 @@ export function registerTools(server, dbPath) {
225
225
  // Tool: Get notes with most backlinks
226
226
  server.addTool({
227
227
  name: "get_backlinked_notes",
228
- description: "Get notes that have at least a minimum number of backlinks from the local Reflect database",
228
+ description: "Get notes that have at least a minimum number of backlinks from Reflect.",
229
229
  parameters: z.object({
230
230
  minBacklinks: z.number().default(5).describe("Minimum number of backlinks a note must have"),
231
231
  limit: z.number().default(10).describe("Maximum number of notes to return"),
@@ -278,7 +278,7 @@ export function registerTools(server, dbPath) {
278
278
  // Tool: Get all tags with usage counts
279
279
  server.addTool({
280
280
  name: "get_tags",
281
- description: "Get all unique tags with their usage counts from the local Reflect database",
281
+ description: "Get all unique tags with their usage counts from Reflect.",
282
282
  parameters: z.object({
283
283
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
284
284
  limit: z.number().default(50).describe("Maximum number of tags to return"),
@@ -334,7 +334,7 @@ export function registerTools(server, dbPath) {
334
334
  // Tool: Get notes with a specific tag
335
335
  server.addTool({
336
336
  name: "get_notes_with_tag",
337
- description: "Get notes that have a specific tag from the local Reflect database",
337
+ description: "Get notes that have a specific tag from Reflect.",
338
338
  parameters: z.object({
339
339
  tag: z.string().describe("The tag to search for"),
340
340
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
@@ -383,49 +383,116 @@ export function registerTools(server, dbPath) {
383
383
  }
384
384
  },
385
385
  });
386
- // Tool: Get a note by title
386
+ // Tool: Get a note by title (exact match first, then fuzzy fallback)
387
387
  server.addTool({
388
388
  name: "get_note",
389
- description: "Get a note by its title (subject) from the local Reflect database",
389
+ description: "Get a note by its title (subject) from Reflect.",
390
390
  parameters: z.object({
391
391
  title: z.string().describe("The title/subject of the note to retrieve"),
392
392
  graphId: z.string().default("rapheal-brain").describe("The graph ID to search in"),
393
393
  }),
394
394
  execute: async (args) => {
395
395
  const { title, graphId } = args;
396
+ const FUZZY_LIMIT = 3;
396
397
  try {
397
398
  const dbFile = resolvedDbPath;
398
399
  const db = new Database(dbFile, { readonly: true });
399
- const stmt = db.prepare(`
400
+ // Try exact match first
401
+ const exactStmt = db.prepare(`
400
402
  SELECT id, subject, documentText, tags, editedAt, createdAt
401
403
  FROM notes
402
404
  WHERE isDeleted = 0 AND graphId = ? AND subject = ?
403
405
  `);
404
- const result = stmt.get(graphId, title);
406
+ const exactResult = exactStmt.get(graphId, title);
407
+ if (exactResult) {
408
+ db.close();
409
+ const note = {
410
+ id: exactResult.id,
411
+ subject: exactResult.subject,
412
+ documentText: exactResult.documentText,
413
+ tags: exactResult.tags ? JSON.parse(exactResult.tags) : [],
414
+ editedAt: formatDate(exactResult.editedAt),
415
+ createdAt: formatDate(exactResult.createdAt),
416
+ };
417
+ return {
418
+ content: [
419
+ {
420
+ type: "text",
421
+ text: JSON.stringify({ title, graphId, note }, null, 2),
422
+ },
423
+ ],
424
+ };
425
+ }
426
+ // No exact match - try fuzzy search
427
+ const searchTerm = title.toLowerCase();
428
+ const fuzzyStmt = db.prepare(`
429
+ SELECT id, subject, documentText, tags, editedAt, createdAt,
430
+ CASE
431
+ WHEN LOWER(subject) LIKE ? THEN 2
432
+ WHEN LOWER(subject) LIKE ? THEN 1
433
+ ELSE 0
434
+ END as relevance
435
+ FROM notes
436
+ WHERE isDeleted = 0
437
+ AND graphId = ?
438
+ AND (LOWER(subject) LIKE ? OR LOWER(subject) LIKE ?)
439
+ ORDER BY relevance DESC, editedAt DESC
440
+ LIMIT ?
441
+ `);
442
+ const fuzzyResults = fuzzyStmt.all(`${searchTerm}%`, // starts with (score 2)
443
+ `%${searchTerm}%`, // contains (score 1)
444
+ graphId, `${searchTerm}%`, // WHERE starts with
445
+ `%${searchTerm}%`, // WHERE contains
446
+ FUZZY_LIMIT);
405
447
  db.close();
406
- if (!result) {
448
+ if (fuzzyResults.length === 0) {
407
449
  return {
408
450
  content: [
409
451
  {
410
452
  type: "text",
411
- text: JSON.stringify({ error: `Note '${title}' not found`, title, graphId }),
453
+ text: JSON.stringify({
454
+ error: `No notes found matching '${title}'`,
455
+ query: title,
456
+ graphId
457
+ }),
412
458
  },
413
459
  ],
414
460
  };
415
461
  }
416
- const note = {
462
+ const notes = fuzzyResults.map((result) => ({
417
463
  id: result.id,
418
464
  subject: result.subject,
419
465
  documentText: result.documentText,
420
466
  tags: result.tags ? JSON.parse(result.tags) : [],
421
467
  editedAt: formatDate(result.editedAt),
422
468
  createdAt: formatDate(result.createdAt),
423
- };
469
+ }));
470
+ // If only one fuzzy match, return it directly
471
+ if (notes.length === 1) {
472
+ return {
473
+ content: [
474
+ {
475
+ type: "text",
476
+ text: JSON.stringify({
477
+ query: title,
478
+ graphId,
479
+ note: notes[0],
480
+ matchType: "fuzzy"
481
+ }, null, 2),
482
+ },
483
+ ],
484
+ };
485
+ }
424
486
  return {
425
487
  content: [
426
488
  {
427
489
  type: "text",
428
- text: JSON.stringify({ title, graphId, note }, null, 2),
490
+ text: JSON.stringify({
491
+ query: title,
492
+ graphId,
493
+ matchCount: notes.length,
494
+ notes
495
+ }, null, 2),
429
496
  },
430
497
  ],
431
498
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflect-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
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",
@@ -15,7 +15,8 @@
15
15
  "build": "tsc",
16
16
  "dev": "tsx src/cli.ts",
17
17
  "start": "node dist/cli.js",
18
- "prepublishOnly": "npm run build"
18
+ "prepublishOnly": "npm run build",
19
+ "postinstall": "npm rebuild better-sqlite3 || true"
19
20
  },
20
21
  "keywords": [
21
22
  "mcp",
@@ -36,7 +37,7 @@
36
37
  },
37
38
  "dependencies": {
38
39
  "@modelcontextprotocol/sdk": "^1.25.1",
39
- "better-sqlite3": "^11.0.0",
40
+ "better-sqlite3": "^11.10.0",
40
41
  "fastmcp": "^3.25.4",
41
42
  "zod": "^4.1.13"
42
43
  },