Keep/mcp-server/README-SSE.md
sepehr ddb67ba9e5 fix: unify theme system - fix theme switching persistence
- Unified localStorage key to 'theme-preference' across all components
- Fixed header.tsx using wrong localStorage key ('theme' instead of 'theme-preference')
- Added localStorage hybrid persistence for instant theme changes
- Removed router.refresh() which was causing stale data revert
- Replaced Blue theme with Sepia
- Consolidated auth() calls to prevent race conditions
- Updated UserSettingsData types to include all themes
2026-01-18 22:33:41 +01:00

12 KiB

Keep Notes MCP SSE Server

Server-Sent Events (SSE) version of Keep Notes MCP Server for remote N8N access.

🎯 Purpose

This SSE server allows N8N (or other MCP clients) running on remote machines to connect to Keep Notes via HTTP/SSE instead of stdio.

stdio vs SSE

|| Feature | stdio (index.js) | SSE (index-sse.js) | ||---------|-------------------|---------------------| || Connection | Local process | Network HTTP | || Remote access | No | Yes | || Use case | Claude Desktop, local tools | N8N on remote machine | || Port | N/A | 3001 | || Version | 2.0.0 | 2.0.0 | || Tools | 19 | 19 |

🚀 Quick Start

1. Install Dependencies

cd mcp-server
npm install

2. Start Server

Option A: PowerShell Script (Recommended)

.\start-sse.ps1

Option B: Direct Node

npm run start:sse
# or
node index-sse.js

3. Verify Server is Running

Open browser to: http://localhost:3001

You should see:

{
  "name": "Keep Notes MCP SSE Server",
  "version": "2.0.0",
  "status": "running",
  "endpoints": {
    "sse": "/sse",
    "message": "/message"
  }
}

🌐 Get Your IP Address

Windows

ipconfig

Look for "IPv4 Address" (usually 192.168.x.x)

Mac/Linux

ifconfig
# or
ip addr show

🔌 N8N Configuration

Method 1: MCP Client Community Node

If your N8N has MCP Client node installed:

  1. Open N8N Settings → MCP Access

  2. Add new server:

    {
      "name": "keep-notes",
      "transport": "sse",
      "url": "http://YOUR_IP:3001/sse"
    }
    

    Replace YOUR_IP with your machine's IP (e.g., 192.168.1.100)

  3. Enable "Available in MCP" for your workflow

  4. Use MCP Client node to call tools

Method 2: HTTP Request Nodes (Fallback)

Use N8N's standard HTTP Request nodes with the REST API:

  • POST http://YOUR_IP:3000/api/notes (Keep Notes REST API)

🛠️ Available Tools (19)

Notes Tools (9)

1. create_note - Create new note with full support

{
  "name": "create_note",
  "arguments": {
    "title": "My Note",
    "content": "Note content",
    "color": "blue",
    "type": "text",
    "checkItems": [{"id": "1", "text": "Task", "checked": false}],
    "labels": ["work", "important"],
    "isPinned": false,
    "isArchived": false,
    "images": ["data:image/png;base64,..."],
    "links": ["https://example.com"],
    "reminder": "2026-01-20T10:00:00Z",
    "isReminderDone": false,
    "reminderRecurrence": "daily",
    "reminderLocation": "Office",
    "isMarkdown": false,
    "size": "medium",
    "notebookId": "cuid..."
  }
}

New Fields (v2.0):

  • links - Note links as array
  • reminder - Reminder date/time (ISO 8601)
  • isReminderDone - Mark reminder as done
  • reminderRecurrence - Reminder recurrence (daily, weekly, monthly, yearly)
  • reminderLocation - Reminder location
  • isMarkdown - Enable markdown support
  • size - Note size (small, medium, large)
  • notebookId - Associate note with notebook

2. get_notes - Get all notes (supports filters)

{
  "name": "get_notes",
  "arguments": {
    "includeArchived": false,
    "search": "optional search query",
    "notebookId": "cuid...",
    "fullDetails": false
  }
}

New Filters (v2.0):

  • notebookId - Filter by notebook
  • fullDetails - Return full details including images (warning: large payload)

3. get_note - Get specific note by ID

{
  "name": "get_note",
  "arguments": {
    "id": "cuid..."
  }
}

4. update_note - Update existing note

Supports all fields from create_note. All are optional except id.

{
  "name": "update_note",
  "arguments": {
    "id": "cuid...",
    "title": "Updated Title",
    "color": "green",
    "isPinned": true
  }
}

5. delete_note - Delete note

{
  "name": "delete_note",
  "arguments": {
    "id": "cuid..."
  }
}

6. search_notes - Search notes by query

{
  "name": "search_notes",
  "arguments": {
    "query": "project",
    "notebookId": "cuid..."
  }
}

New (v2.0): notebookId filter support

7. get_labels - Get all unique labels (legacy method)

{
  "name": "get_labels",
  "arguments": {}
}

8. toggle_pin - Pin/unpin note

{
  "name": "toggle_pin",
  "arguments": {
    "id": "cuid..."
  }
}

9. toggle_archive - Archive/unarchive note

{
  "name": "toggle_archive",
  "arguments": {
    "id": "cuid..."
  }
}

Notebooks Tools (5) - NEW in v2.0

10. create_notebook - Create new notebook

{
  "name": "create_notebook",
  "arguments": {
    "name": "Work Projects",
    "icon": "💼",
    "color": "#3B82F6",
    "order": 1
  }
}

Returns: Notebook with labels and notes count

11. get_notebooks - Get all notebooks

{
  "name": "get_notebooks",
  "arguments": {}
}

Returns: Array of notebooks with labels and notes count

12. get_notebook - Get notebook with notes

{
  "name": "get_notebook",
  "arguments": {
    "id": "cuid..."
  }
}

Returns: Notebook with labels, notes (parsed), and notes count

13. update_notebook - Update notebook

{
  "name": "update_notebook",
  "arguments": {
    "id": "cuid...",
    "name": "Updated Name",
    "icon": "📁",
    "color": "#10B981"
  }
}

14. delete_notebook - Delete notebook

{
  "name": "delete_notebook",
  "arguments": {
    "id": "cuid..."
  }
}

Warning: Deletes all notes in the notebook

Labels Tools (5) - NEW in v2.0

15. create_label - Create new label

{
  "name": "create_label",
  "arguments": {
    "name": "Important",
    "color": "red",
    "notebookId": "cuid..."
  }
}

Required: name and notebookId

16. get_labels_detailed - Get labels with details

{
  "name": "get_labels_detailed",
  "arguments": {
    "notebookId": "cuid..."
  }
}

Returns: Labels with notebook information

17. update_label - Update label

{
  "name": "update_label",
  "arguments": {
    "id": "cuid...",
    "name": "Updated Name",
    "color": "blue"
  }
}

18. delete_label - Delete label

{
  "name": "delete_label",
  "arguments": {
    "id": "cuid..."
  }
}

🧪 Testing the SSE Server

Test 1: Health Check

curl http://localhost:3001/

Test 2: SSE Connection

curl -N http://localhost:3001/sse

Test 3: Call a Tool (get_notes)

curl -X POST http://localhost:3001/message \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "get_notes",
      "arguments": {}
    },
    "id": 1
  }'

Test 4: Create Note via MCP

$body = @{
    jsonrpc = "2.0"
    method = "tools/call"
    params = @{
        name = "create_note"
        arguments = @{
            content = "Test from MCP SSE"
            title = "SSE Test"
            color = "green"
        }
    }
    id = 1
} | ConvertTo-Json -Depth 5

Invoke-RestMethod -Method POST -Uri "http://localhost:3001/message" `
  -Body $body -ContentType "application/json"

Test 5: Create Notebook

$body = @{
    jsonrpc = "2.0"
    method = "tools/call"
    params = @{
        name = "create_notebook"
        arguments = @{
            name = "Test Notebook"
            icon = "📁"
            color = "#3B82F6"
        }
    }
    id = 1
} | ConvertTo-Json -Depth 5

Invoke-RestMethod -Method POST -Uri "http://localhost:3001/message" `
  -Body $body -ContentType "application/json"

Test 6: Create Label

$body = @{
    jsonrpc = "2.0"
    method = "tools/call"
    params = @{
        name = "create_label"
        arguments = @{
            name = "Test Label"
            color = "blue"
            notebookId = "YOUR_NOTEBOOK_ID"
        }
    }
    id = 1
} | ConvertTo-Json -Depth 5

Invoke-RestMethod -Method POST -Uri "http://localhost:3001/message" `
  -Body $body -ContentType "application/json"

🔥 Troubleshooting

Error: Prisma Client not initialized

Solution: Generate Prisma Client:

npx prisma generate

Error: Port 3001 already in use

Solution 1: Change port in index-sse.js:

const PORT = process.env.PORT || 3002;

Solution 2: Kill existing process:

Get-Process node | Where-Object {$_.Path -like "*index-sse*"}
Stop-Process -Id $process.Id

Error: Cannot connect from N8N

Checklist:

  1. Server is running (http://localhost:3001 works locally)
  2. Firewall allows port 3001
  3. Using correct IP address (not localhost)
  4. N8N can reach your network
  5. Using http:// not https://

Test connectivity from N8N machine:

curl http://YOUR_IP:3001/

SSE Connection Keeps Dropping

This is normal! SSE maintains a persistent connection. If it drops:

  • Client should automatically reconnect
  • Check network stability
  • Verify firewall/proxy settings

🔒 Security Notes

⚠️ This server has NO AUTHENTICATION!

For production use:

  1. Add API key authentication
  2. Use HTTPS with SSL certificates
  3. Restrict CORS origins
  4. Use environment variables for secrets
  5. Deploy behind a reverse proxy (nginx, Caddy)

Add Basic API Key (Example)

// In index-sse.js, add middleware:
app.use((req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (apiKey !== process.env.MCP_API_KEY) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
});

📊 Endpoints

Method Path Description
GET / Health check
GET /sse SSE connection endpoint
POST /message MCP message handler

🆚 Comparison with REST API

Feature MCP SSE REST API
Protocol SSE (MCP) HTTP JSON
Port 3001 3000 (Next.js)
Format MCP JSON-RPC REST JSON
Use case MCP clients Standard HTTP clients
Tools 19 MCP tools 4 CRUD endpoints

Both work! Use MCP SSE for proper MCP integration, or REST API for simpler HTTP requests.

🔄 Development Workflow

Running Both Servers

Terminal 1: Next.js + REST API

cd keep-notes
npm run dev
# Runs on http://localhost:3000

Terminal 2: MCP SSE Server

cd mcp-server
npm run start:sse
# Runs on http://localhost:3001

Terminal 3: MCP stdio (for Claude Desktop)

cd mcp-server
npm start
# Runs as stdio process

📝 Configuration Examples

N8N Workflow (MCP Client)

{
  "nodes": [
    {
      "name": "Get Keep Notes",
      "type": "MCP Client",
      "typeVersion": 1,
      "position": [250, 300],
      "parameters": {
        "server": "keep-notes",
        "tool": "get_notes",
        "arguments": {
          "includeArchived": false
        }
      }
    }
  ]
}

Create Notebook via MCP

{
  "name": "Create Notebook",
  "type": "MCP Client",
  "parameters": {
    "server": "keep-notes",
    "tool": "create_notebook",
    "arguments": {
      "name": "Work Projects",
      "icon": "💼",
      "color": "#3B82F6"
    }
  }
}

Create Label via MCP

{
  "name": "Create Label",
  "type": "MCP Client",
  "parameters": {
    "server": "keep-notes",
    "tool": "create_label",
    "arguments": {
      "name": "Important",
      "color": "red",
      "notebookId": "WORKBOOK_ID"
    }
  }
}

Claude Desktop Config (stdio)

Use original index.js with stdio:

{
  "mcpServers": {
    "keep-notes": {
      "command": "node",
      "args": ["D:/dev_new_pc/Keep/mcp-server/index.js"]
    }
  }
}

🚀 N8N Integration Guide

See N8N-SETUP.md for complete N8N workflow setup and N8N-WORKFLOWS.md for available workflows.

📚 Resources

🤝 Support

Issues? Check:

  1. START-SSE.md - Quick start guide
  2. README.md - Main project README
  3. COMPLETED-FEATURES.md - Implementation details

Version: 2.0.0 Last Updated: January 18, 2026 Status: Production Ready