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
This commit is contained in:
139
mcp-server/CHANGES.md
Normal file
139
mcp-server/CHANGES.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# MCP Server Correction Summary
|
||||
|
||||
## Date: 2026-01-18
|
||||
|
||||
## Modifications effectuées pour adapter le serveur MCP au code actuel de Keep Notes
|
||||
|
||||
### 1. **Correction du chemin de base de données** ✅
|
||||
- **Ancien chemin:** `file:${join(__dirname, '../keep-notes/prisma/dev.db')}` (incorrect)
|
||||
- **Nouveau chemin:** `file:D:/dev_new_pc/Keep/keep-notes/prisma/dev.db` (chemin absolu Windows)
|
||||
|
||||
### 2. **Mise à jour du schéma Prisma** ✅
|
||||
Ajout de tous les champs manquants dans le modèle `Note`:
|
||||
- `links` (String?) - Liens dans les notes
|
||||
- `reminder` (DateTime?) - Rappels
|
||||
- `isReminderDone` (Boolean) - État du rappel
|
||||
- `reminderRecurrence` (String?) - Récurrence des rappels
|
||||
- `reminderLocation` (String?) - Lieu du rappel
|
||||
- `isMarkdown` (Boolean) - Support Markdown
|
||||
- `size` (String) - Taille de la note (small, medium, large)
|
||||
- `notebookId` (String?) - Association avec un notebook
|
||||
|
||||
Ajout des modèles manquants:
|
||||
- `Notebook` - Gestion des notebooks
|
||||
- `Label` - Gestion des labels
|
||||
- `User`, `Account`, `Session` - Authentification
|
||||
- `NoteShare` - Partage de notes
|
||||
- `AiFeedback`, `MemoryEchoInsight` - Fonctionnalités IA
|
||||
- `UserAISettings` - Paramètres IA utilisateur
|
||||
|
||||
### 3. **Outils MCP ajoutés - Notebooks** ✅
|
||||
- `create_notebook` - Créer un nouveau notebook
|
||||
- `get_notebooks` - Récupérer tous les notebooks
|
||||
- `get_notebook` - Récupérer un notebook spécifique avec ses notes
|
||||
- `update_notebook` - Mettre à jour un notebook
|
||||
- `delete_notebook` - Supprimer un notebook
|
||||
|
||||
### 4. **Outils MCP ajoutés - Labels** ✅
|
||||
- `create_label` - Créer un nouveau label (nécessite notebookId)
|
||||
- `get_labels_detailed` - Récupérer tous les labels avec détails
|
||||
- `update_label` - Mettre à jour un label
|
||||
- `delete_label` - Supprimer un label
|
||||
|
||||
### 5. **Mise à jour des outils existants - Notes** ✅
|
||||
Ajout de paramètres dans `create_note`:
|
||||
- `links` - Tableau de liens
|
||||
- `reminder` - Date de rappel (ISO 8601)
|
||||
- `isReminderDone` - État du rappel
|
||||
- `reminderRecurrence` - Récurrence (daily, weekly, monthly, yearly)
|
||||
- `reminderLocation` - Lieu du rappel
|
||||
- `isMarkdown` - Activer/désactiver Markdown
|
||||
- `size` - Taille de la note (small, medium, large)
|
||||
- `notebookId` - ID du notebook associé
|
||||
|
||||
Ajout de paramètres dans `update_note`:
|
||||
- Tous les paramètres ci-dessus sont optionnels pour la mise à jour
|
||||
|
||||
Ajout de paramètres dans `get_notes` et `search_notes`:
|
||||
- `notebookId` - Filtrer par notebook
|
||||
|
||||
### 6. **Mise à jour de la documentation** ✅
|
||||
- Renommage de "Memento" vers "Keep Notes"
|
||||
- Documentation complète de tous les nouveaux outils
|
||||
- Exemples d'utilisation pour N8N et Cursor
|
||||
- Référence complète du schéma Prisma
|
||||
- Instructions d'installation et de configuration mises à jour
|
||||
|
||||
### 7. **Tests de validation** ✅
|
||||
✅ Connexion à la base de données réussie
|
||||
✅ 56 notes trouvées dans la base de données
|
||||
✅ 6 notebooks récupérés avec succès
|
||||
✅ 6 labels récupérés avec succès
|
||||
✅ Client Prisma généré correctement
|
||||
|
||||
## Structure finale du serveur MCP
|
||||
|
||||
### Outils disponibles (19 au total)
|
||||
|
||||
**Gestion des notes (9 outils):**
|
||||
1. create_note
|
||||
2. get_notes
|
||||
3. get_note
|
||||
4. update_note
|
||||
5. delete_note
|
||||
6. search_notes
|
||||
7. get_labels (legacy)
|
||||
8. toggle_pin
|
||||
9. toggle_archive
|
||||
|
||||
**Gestion des notebooks (5 outils):**
|
||||
10. create_notebook
|
||||
11. get_notebooks
|
||||
12. get_notebook
|
||||
13. update_notebook
|
||||
14. delete_notebook
|
||||
|
||||
**Gestion des labels (5 outils):**
|
||||
15. create_label
|
||||
16. get_labels_detailed
|
||||
17. update_label
|
||||
18. delete_label
|
||||
|
||||
## Fichiers modifiés/créés
|
||||
|
||||
- ✅ `mcp-server/index.js` - Serveur MCP principal (réécrit)
|
||||
- ✅ `mcp-server/prisma/schema.prisma` - Schéma Prisma (mis à jour)
|
||||
- ✅ `mcp-server/README.md` - Documentation (réécrite)
|
||||
- ✅ `mcp-server/test-server.js` - Script de test (nouveau)
|
||||
- ✅ `mcp-server/CHANGES.md` - Résumé des modifications (nouveau)
|
||||
|
||||
## Configuration Cursor
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"keep-notes": {
|
||||
"command": "node",
|
||||
"args": ["D:/dev_new_pc/Keep/mcp-server/index.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration N8N
|
||||
|
||||
Utiliser les mêmes paramètres que ci-dessus avec le nœud MCP dans N8N.
|
||||
|
||||
## Points importants
|
||||
|
||||
1. **Aucune modification du code Keep Notes** - Seul le dossier `mcp-server` a été modifié
|
||||
2. **Base de données partagée** - Le MCP utilise la même base SQLite que Keep Notes
|
||||
3. **Prisma synchronisé** - Le schéma Prisma du MCP correspond maintenant à celui de Keep Notes
|
||||
4. **Tests réussis** - Toutes les opérations de base ont été testées et fonctionnent
|
||||
|
||||
## Prochaines étapes possibles (optionnelles)
|
||||
|
||||
- [ ] Ajouter des tests unitaires pour chaque outil MCP
|
||||
- [ ] Ajouter la gestion des utilisateurs (authentification)
|
||||
- [ ] Implémenter les fonctionnalités IA (Memory Echo, suggestions de titres)
|
||||
- [ ] Implémenter le partage de notes (NoteShare)
|
||||
258
mcp-server/N8N-SETUP.md
Normal file
258
mcp-server/N8N-SETUP.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Guide d'Installation Rapide - Workflows N8N
|
||||
|
||||
## 🚀 Installation en 5 minutes
|
||||
|
||||
### Prérequis
|
||||
|
||||
- ✅ N8N installé et en cours d'exécution (http://localhost:5678)
|
||||
- ✅ Keep Notes en cours d'exécution (http://localhost:3000)
|
||||
- ✅ Clé API OpenAI (optionnel mais recommandé)
|
||||
|
||||
---
|
||||
|
||||
## 📥 Étape 1: Importer les Workflows
|
||||
|
||||
### Option A: Import individuel (recommandé pour commencer)
|
||||
|
||||
1. Ouvrez N8N dans votre navigateur: http://localhost:5678
|
||||
2. Cliquez sur **"Import from File"** dans le menu supérieur
|
||||
3. Sélectionnez un des fichiers JSON:
|
||||
- `n8n-workflow-create-note.json`
|
||||
- `n8n-workflow-search-summary.json`
|
||||
- `n8n-workflow-notebook-management.json`
|
||||
- `n8n-workflow-reminder-notifications.json`
|
||||
- `n8n-workflow-label-management.json`
|
||||
- `n8n-workflow-email-integration.json`
|
||||
4. Le workflow apparaîtra dans l'éditeur
|
||||
|
||||
### Option B: Import en masse (avancé)
|
||||
|
||||
Utilisez le script PowerShell fourni:
|
||||
|
||||
```powershell
|
||||
.\import-workflows.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Étape 2: Configurer les Variables d'Environnement
|
||||
|
||||
Dans N8N, allez dans **Settings** → **Variables** et ajoutez:
|
||||
|
||||
```bash
|
||||
KEEP_NOTES_API_URL=http://localhost:3000
|
||||
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX
|
||||
EMAIL_ADDRESS=votre_email@gmail.com
|
||||
EMAIL_PASSWORD=votre_app_password
|
||||
OPENAI_API_KEY=sk-proj-XXX
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Étape 3: Configurer les Connexions
|
||||
|
||||
### 3.1 Connexion Keep Notes API
|
||||
|
||||
Les workflows utilisent déjà l'URL `http://localhost:3000/api` par défaut.
|
||||
|
||||
Si Keep Notes est sur une autre URL:
|
||||
|
||||
1. Ouvrez un workflow
|
||||
2. Cherchez les noeuds "Keep Notes - Create Note", "Get All Notes", etc.
|
||||
3. Modifiez l'URL dans le champ "URL"
|
||||
|
||||
### 3.2 Connexion Slack
|
||||
|
||||
Pour les notifications Slack:
|
||||
|
||||
1. Créez un Incoming Webhook sur Slack
|
||||
2. Copiez l'URL du webhook
|
||||
3. Dans le workflow "Reminder Notifications", modifiez le noeud "Send Notification"
|
||||
4. Remplacez l'URL du webhook
|
||||
|
||||
### 3.3 Connexion Email (IMAP)
|
||||
|
||||
Pour le workflow "Email to Note":
|
||||
|
||||
1. Activez l'accès IMAP pour votre email (ex: Gmail)
|
||||
2. Si 2FA activé, générez un "App Password"
|
||||
3. Configurez le noeud "Email Trigger":
|
||||
- Host: `imap.gmail.com` (pour Gmail)
|
||||
- Email: votre adresse
|
||||
- Password: votre mot de passe/app password
|
||||
|
||||
### 3.4 Connexion OpenAI (Optionnel)
|
||||
|
||||
Pour la classification et les résumés:
|
||||
|
||||
1. Allez dans **Credentials** → **Add Credential**
|
||||
2. Sélectionnez **OpenAI API**
|
||||
3. Entrez votre clé API
|
||||
4. Dans chaque workflow avec noeud "AI Classifier", "AI Summarizer" ou "AI Suggest Labels":
|
||||
- Sélectionnez les crédtentiels OpenAI créés
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Étape 4: Activer les Workflows
|
||||
|
||||
Pour chaque workflow importé:
|
||||
|
||||
1. Cliquez sur le bouton **"Activate"** (icône play en haut à droite)
|
||||
2. Le workflow deviendra actif et s'exécutera selon son déclencheur
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Étape 5: Tester
|
||||
|
||||
### Tester "Create Note with Classification"
|
||||
|
||||
1. Activez le workflow
|
||||
2. Utilisez le MCP Trigger ou envoyez une requête POST
|
||||
3. Vérifiez qu'une note est créée dans Keep Notes
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/keep-notes-create \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"content": "Meeting with client next week to discuss project timeline",
|
||||
"color": "blue"
|
||||
}'
|
||||
```
|
||||
|
||||
### Tester "Reminder Notifications"
|
||||
|
||||
1. Activez le workflow
|
||||
2. Créez une note avec un rappel dans les 30 prochaines minutes
|
||||
3. Attendez le déclenchement automatique
|
||||
4. Vérifiez les notifications Slack/Email
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Workflows par ordre de priorité
|
||||
|
||||
### Débutant (Commencez par ceux-ci)
|
||||
|
||||
1. **Notebook Manager** - Plus simple, aucune dépendance externe
|
||||
2. **Label Manager** - Gestion de base avec option IA
|
||||
3. **Create Note with Classification** - Fonctionnalité principale
|
||||
|
||||
### Intermédiaire
|
||||
|
||||
4. **Search & Summary** - Requiert OpenAI pour les résumés
|
||||
5. **Reminder Notifications** - Requiert Slack/Email configuré
|
||||
|
||||
### Avancé
|
||||
|
||||
6. **Email to Note** - Plus complexe, requiert configuration IMAP
|
||||
|
||||
---
|
||||
|
||||
## 📊 Vue d'ensemble des Dépendances
|
||||
|
||||
| Workflow | Keep Notes | OpenAI | Slack | Email (IMAP) |
|
||||
|-----------|-------------|---------|-------|---------------|
|
||||
| Create Note | ✅ | ⭐ | - | - |
|
||||
| Search & Summary | ✅ | ✅ | - | - |
|
||||
| Notebook Manager | ✅ | - | - | - |
|
||||
| Reminder Notifications | ✅ | - | ✅ | ⭐ |
|
||||
| Label Manager | ✅ | ⭐ | - | - |
|
||||
| Email to Note | ✅ | ✅ | ⭐ | ✅ |
|
||||
|
||||
Légende:
|
||||
- ✅ = Requis
|
||||
- ⭐ = Optionnel (pour fonctionnalités avancées)
|
||||
- - = Non requis
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Personnalisation Rapide
|
||||
|
||||
### Modifier l'URL de l'API Keep Notes
|
||||
|
||||
Dans tous les fichiers JSON, recherchez:
|
||||
|
||||
```json
|
||||
"url": "http://localhost:3000/api/..."
|
||||
```
|
||||
|
||||
Remplacez par votre URL réelle:
|
||||
|
||||
```json
|
||||
"url": "https://votre-domaine.com/api/..."
|
||||
```
|
||||
|
||||
### Désactiver les notifications d'un workflow
|
||||
|
||||
1. Ouvrez le workflow
|
||||
2. Supprimez ou désactivez les noeuds de notification
|
||||
3. Sauvegardez et réactivez
|
||||
|
||||
---
|
||||
|
||||
## 📈 Monitoring
|
||||
|
||||
### Voir les exécutions
|
||||
|
||||
1. Allez dans **Executions** dans le menu latéral
|
||||
2. Filtrez par workflow
|
||||
3. Cliquez sur une exécution pour voir les détails
|
||||
4. Les données d'entrée/sortie sont visibles pour chaque noeud
|
||||
|
||||
### Activer les logs détaillés
|
||||
|
||||
Dans **Settings** → **Executions**, cochez:
|
||||
|
||||
- ✅ Save data for failed executions
|
||||
- ✅ Save data for successful executions
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Support et Dépannage
|
||||
|
||||
### Erreurs courantes
|
||||
|
||||
**"Connection refused to localhost:3000"**
|
||||
- → Vérifiez que Keep Notes est démarré
|
||||
|
||||
**"AI classification failed"**
|
||||
- → Vérifiez votre clé OpenAI API
|
||||
|
||||
**"Email trigger not working"**
|
||||
- → Activez l'accès IMAP ou utilisez un App Password
|
||||
|
||||
**"Slack notification failed"**
|
||||
- → Vérifiez l'URL du webhook Slack
|
||||
|
||||
### Obtenir de l'aide
|
||||
|
||||
1. Consultez [N8N-WORKFLOWS.md](./N8N-WORKFLOWS.md) pour la documentation détaillée
|
||||
2. Vérifiez les logs d'exécution dans N8N
|
||||
3. Testez chaque workflow individuellement
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Prochaines étapes
|
||||
|
||||
Une fois les workflows configurés:
|
||||
|
||||
1. ✅ Explorez les workflows en mode test
|
||||
2. ✅ Adaptez les prompts IA selon vos besoins
|
||||
3. ✅ Créez des workflows personnalisés basés sur les exemples
|
||||
4. ✅ Intégrez avec d'autres services (Notion, Google Drive, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Ressources utiles
|
||||
|
||||
- [Documentation N8N](https://docs.n8n.io)
|
||||
- [Guide MCP Protocol](https://modelcontextprotocol.io)
|
||||
- [Documentation Keep Notes](./README.md)
|
||||
- [Documentation complète des workflows](./N8N-WORKFLOWS.md)
|
||||
|
||||
---
|
||||
|
||||
**Temps estimé:** 5-10 minutes par workflow
|
||||
|
||||
**Difficulté:** Variable de ⭐ à ⭐⭐⭐
|
||||
|
||||
**Support:** [Issues GitHub](https://github.com/votre-repo/issues)
|
||||
376
mcp-server/N8N-WORKFLOWS.md
Normal file
376
mcp-server/N8N-WORKFLOWS.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# Workflows N8N pour Keep Notes MCP
|
||||
|
||||
## 📚 Introduction
|
||||
|
||||
Ce dossier contient 6 workflows N8N prêts à l'emploi pour votre serveur MCP Keep Notes. Chaque workflow est conçu pour une fonctionnalité spécifique et peut être importé directement dans N8N.
|
||||
|
||||
## 🚀 Workflows disponibles
|
||||
|
||||
### 1. **Create Note with Classification** (`n8n-workflow-create-note.json`)
|
||||
|
||||
**Description:** Crée des notes dans Keep Notes avec classification automatique par IA.
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Création de notes avec titres, contenus, labels, notebooks
|
||||
- Classification automatique par IA pour suggérer:
|
||||
- Titres pertinents
|
||||
- Labels appropriés
|
||||
- Notebook optimal
|
||||
- Catégorie (work/personal/idea)
|
||||
|
||||
**Cas d'usage:**
|
||||
- Création rapide de notes depuis des conversations
|
||||
- Capture d'idées avec auto-organisation
|
||||
- Génération automatique de métadonnées
|
||||
|
||||
**Utilisation:**
|
||||
```json
|
||||
{
|
||||
"content": "Meeting with client next week to discuss project timeline",
|
||||
"color": "blue",
|
||||
"labels": ["work"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **Search & Summary** (`n8n-workflow-search-summary.json`)
|
||||
|
||||
**Description:** Recherche des notes et génère un résumé avec IA.
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Recherche par mot-clé dans titres et contenus
|
||||
- Filtrage intelligent des résultats
|
||||
- Génération de résumés par IA
|
||||
- Extraction des thèmes principaux
|
||||
- Identification des action items
|
||||
- Groupement de notes par thème
|
||||
|
||||
**Cas d'usage:**
|
||||
- Rapports hebdomadaires d'activité
|
||||
- Analyse de thématiques
|
||||
- Synthèse rapide de projets
|
||||
|
||||
**Utilisation:**
|
||||
```json
|
||||
{
|
||||
"searchQuery": "project timeline"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **Notebook Manager** (`n8n-workflow-notebook-management.json`)
|
||||
|
||||
**Description:** Gestion complète des notebooks (créer, lister, modifier, supprimer).
|
||||
|
||||
**Fonctionnalités:**
|
||||
- **Create**: Créer de nouveaux notebooks avec icône et couleur
|
||||
- **List**: Récupérer tous les notebooks avec leurs notes
|
||||
- **Update**: Modifier nom, icône, couleur d'un notebook
|
||||
- **Delete**: Supprimer un notebook et son contenu
|
||||
|
||||
**Cas d'usage:**
|
||||
- Organisation thématique des notes
|
||||
- Restructuration de l'espace de travail
|
||||
- Archivage de projets terminés
|
||||
|
||||
**Utilisation:**
|
||||
```json
|
||||
{
|
||||
"action": "create",
|
||||
"name": "Work Projects",
|
||||
"icon": "💼",
|
||||
"color": "#3B82F6"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. **Reminder Notifications** (`n8n-workflow-reminder-notifications.json`)
|
||||
|
||||
**Description:** Automatisation des rappels et notifications pour les notes.
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Vérification automatique des rappels (toutes les 30 min)
|
||||
- Notifications Slack/Email
|
||||
- Marquage des rappels comme terminés
|
||||
- Gestion des rappels récurrents (daily, weekly, monthly, yearly)
|
||||
- Mise à jour automatique des dates de rappel
|
||||
|
||||
**Cas d'usage:**
|
||||
- Gestion de tâches avec échéances
|
||||
- Rappels de réunions importantes
|
||||
- Notifications d'événements récurrents
|
||||
|
||||
**Configuration:**
|
||||
- Déclencheur: Schedule (cron: `0 */30 * * * *`)
|
||||
- Canaux: Slack et Email (configurable)
|
||||
- Récurrences: daily, weekly, monthly, yearly
|
||||
|
||||
---
|
||||
|
||||
### 5. **Label Manager** (`n8n-workflow-label-management.json`)
|
||||
|
||||
**Description:** Gestion complète des labels avec suggestion automatique par IA.
|
||||
|
||||
**Fonctionnalités:**
|
||||
- **Create**: Créer des labels avec couleur et notebook
|
||||
- **List**: Récupérer tous les labels (optionnel par notebook)
|
||||
- **Update**: Modifier nom et couleur d'un label
|
||||
- **Delete**: Supprimer un label
|
||||
- **Suggest**: Suggestion automatique de labels par IA
|
||||
|
||||
**Cas d'usage:**
|
||||
- Organisation avancée par tags
|
||||
- Classification automatique de contenu
|
||||
- Gestion de taxonomie dynamique
|
||||
|
||||
**Utilisation:**
|
||||
```json
|
||||
{
|
||||
"action": "suggest",
|
||||
"title": "Budget planning for Q4",
|
||||
"content": "Need to plan budget for the next quarter..."
|
||||
}
|
||||
```
|
||||
|
||||
**Réponse IA:**
|
||||
```json
|
||||
{
|
||||
"labels": ["finance", "budget", "quarterly", "planning"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. **Email to Note** (`n8n-workflow-email-integration.json`)
|
||||
|
||||
**Description:** Conversion automatique d'emails en notes avec classification.
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Déclenchement sur nouveaux emails (IMAP)
|
||||
- Extraction automatique de données email
|
||||
- Classification par IA:
|
||||
- Thème principal
|
||||
- Détection d'urgence
|
||||
- Suggestion de labels et notebooks
|
||||
- Gestion des emails urgents (pinned, couleur rouge)
|
||||
- Notification Slack après création
|
||||
|
||||
**Cas d'usage:**
|
||||
- Capture automatique d'emails importants
|
||||
- Gestion de boîte de réception
|
||||
- Intégration email → notes intelligentes
|
||||
|
||||
**Configuration:**
|
||||
- Déclencheur: Email Trigger (IMAP)
|
||||
- Filtre: Emails non lus uniquement
|
||||
- Urgence: Détection automatique par IA
|
||||
|
||||
---
|
||||
|
||||
## 📥 Importation des Workflows
|
||||
|
||||
### Méthode 1: Import direct
|
||||
|
||||
1. Ouvrir N8N
|
||||
2. Cliquer sur **"Import from File"**
|
||||
3. Sélectionner le fichier JSON souhaité
|
||||
4. Configurer les paramètres nécessaires (Slack, Email, etc.)
|
||||
5. Activer le workflow
|
||||
|
||||
### Méthode 2: Import via API
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/rest/workflows/import \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @n8n-workflow-create-note.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration requise
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
Dans N8N, configurez les variables suivantes:
|
||||
|
||||
```bash
|
||||
KEEP_NOTES_API_URL=http://localhost:3000
|
||||
SLACK_WEBHOOK_URL=votre_slack_webhook_url
|
||||
EMAIL_ADDRESS=votre_email@gmail.com
|
||||
EMAIL_PASSWORD=votre_mot_de_passe_app
|
||||
OPENAI_API_KEY=votre_clé_openai
|
||||
```
|
||||
|
||||
### Connexions requises
|
||||
|
||||
1. **Keep Notes API**
|
||||
- URL: `http://localhost:3000/api`
|
||||
- Authentification: Si nécessaire
|
||||
|
||||
2. **Slack**
|
||||
- Webhook URL ou OAuth token
|
||||
|
||||
3. **Email (IMAP)**
|
||||
- Serveur IMAP (ex: `imap.gmail.com`)
|
||||
- Email et mot de passe/application password
|
||||
|
||||
4. **OpenAI API**
|
||||
- Pour la classification et la génération de résumés
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Personnalisation
|
||||
|
||||
### Modifier les délais de rappel
|
||||
|
||||
Dans "Reminder Notifications", modifiez l'expression cron:
|
||||
|
||||
```cron
|
||||
# Toutes les 15 minutes
|
||||
0 */15 * * * *
|
||||
|
||||
# Toutes les heures
|
||||
0 * * * *
|
||||
|
||||
# Toutes les 2 heures
|
||||
0 */2 * * *
|
||||
```
|
||||
|
||||
### Changer les couleurs par défaut
|
||||
|
||||
Dans "Create Note", modifiez la valeur par défaut:
|
||||
|
||||
```json
|
||||
{
|
||||
"color": "purple"
|
||||
}
|
||||
```
|
||||
|
||||
### Adapter les catégories de classification
|
||||
|
||||
Dans "Email to Note", modifiez le prompt IA pour inclure vos catégories personnalisées.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Combinaison de Workflows
|
||||
|
||||
### Exemple: Pipeline complet Email → Classification → Rappel
|
||||
|
||||
```
|
||||
Email Trigger
|
||||
→ Extract Email Data
|
||||
→ AI Classify
|
||||
→ Create Note
|
||||
→ Set Reminder
|
||||
→ Notify Slack
|
||||
```
|
||||
|
||||
### Exemple: Recherche intelligente avec labels
|
||||
|
||||
```
|
||||
MCP Trigger (search)
|
||||
→ Get Notes
|
||||
→ Filter
|
||||
→ AI Suggest Labels
|
||||
→ Apply Labels
|
||||
→ Return Summary
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Monitoring et Logs
|
||||
|
||||
Activer les logs dans N8N pour suivre l'exécution:
|
||||
|
||||
1. Aller dans **Settings** → **Executions**
|
||||
2. Filtrer par workflow
|
||||
3. Voir les détails de chaque exécution
|
||||
4. Déboguer les erreurs éventuelles
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Sécurité
|
||||
|
||||
### Bonnes pratiques
|
||||
|
||||
1. **Ne jamais** exposer des données sensibles dans les workflows
|
||||
2. Utiliser des **variables d'environnement** pour les clés API
|
||||
3. **Limiter** l'accès aux workflows MCP avec authentification
|
||||
4. **Valider** toutes les entrées utilisateur
|
||||
5. **Auditer** régulièrement les exécutions
|
||||
|
||||
### Accès MCP
|
||||
|
||||
Pour sécuriser l'accès MCP aux workflows:
|
||||
|
||||
1. Configurer l'authentification dans le **MCP Server Trigger**
|
||||
2. Utiliser des **Bearer tokens** ou **OAuth**
|
||||
3. Restreindre l'accès par IP si nécessaire
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Dépannage
|
||||
|
||||
### Erreur: "Connection refused to localhost:3000"
|
||||
|
||||
**Solution:** Vérifiez que Keep Notes est démarré sur le port 3000.
|
||||
|
||||
### Erreur: "AI classification failed"
|
||||
|
||||
**Solution:** Vérifiez votre clé OpenAI API et vos crédits.
|
||||
|
||||
### Erreur: "Email trigger not working"
|
||||
|
||||
**Solution:**
|
||||
- Activez l'accès "less secure apps" pour Gmail
|
||||
- Utilisez un "App Password" si 2FA activé
|
||||
|
||||
### Erreur: "Slack notification failed"
|
||||
|
||||
**Solution:** Vérifiez l'URL du webhook Slack et les permissions.
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation complémentaire
|
||||
|
||||
- [N8N Documentation](https://docs.n8n.io)
|
||||
- [MCP Protocol](https://modelcontextprotocol.io)
|
||||
- [Keep Notes API](./README.md)
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contribution
|
||||
|
||||
Pour ajouter de nouveaux workflows:
|
||||
|
||||
1. Créez le fichier JSON correspondant
|
||||
2. Ajoutez la documentation dans ce fichier
|
||||
3. Testez le workflow en local
|
||||
4. Partagez-le avec l'équipe
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes importantes
|
||||
|
||||
- Tous les workflows utilisent le protocole MCP
|
||||
- L'URL de l'API Keep Notes doit être configurée correctement
|
||||
- Les workflows nécessitent que le serveur Keep Notes soit en cours d'exécution
|
||||
- Les coûts OpenAI s'appliquent pour la classification et la génération de résumés
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Résumé
|
||||
|
||||
| Workflow | Utilisation principale | Complexité |
|
||||
|----------|----------------------|-------------|
|
||||
| Create Note | Création avec IA | ⭐⭐ |
|
||||
| Search & Summary | Analyse de contenu | ⭐⭐⭐ |
|
||||
| Notebook Manager | Gestion notebooks | ⭐ |
|
||||
| Reminder Notifications | Automatisation rappels | ⭐⭐⭐ |
|
||||
| Label Manager | Gestion labels | ⭐⭐ |
|
||||
| Email to Note | Intégration email | ⭐⭐⭐⭐ |
|
||||
|
||||
**Total:** 6 workflows prêts à l'emploi! 🚀
|
||||
@@ -1,36 +1,41 @@
|
||||
# Memento MCP SSE Server
|
||||
# Keep Notes MCP SSE Server
|
||||
|
||||
Server-Sent Events (SSE) version of the Memento MCP Server for remote N8N access.
|
||||
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 Memento via HTTP/SSE instead of stdio.
|
||||
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 |
|
||||
|| 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
|
||||
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. Start the Server
|
||||
### 2. Start Server
|
||||
|
||||
**Option A: PowerShell Script (Recommended)**
|
||||
|
||||
```powershell
|
||||
.\start-sse.ps1
|
||||
```
|
||||
|
||||
**Option B: Direct Node**
|
||||
|
||||
```bash
|
||||
npm run start:sse
|
||||
# or
|
||||
@@ -42,10 +47,11 @@ node index-sse.js
|
||||
Open browser to: `http://localhost:3001`
|
||||
|
||||
You should see:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Memento MCP SSE Server",
|
||||
"version": "1.0.0",
|
||||
"name": "Keep Notes MCP SSE Server",
|
||||
"version": "2.0.0",
|
||||
"status": "running",
|
||||
"endpoints": {
|
||||
"sse": "/sse",
|
||||
@@ -57,12 +63,15 @@ You should see:
|
||||
## 🌐 Get Your IP Address
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
ipconfig
|
||||
```
|
||||
|
||||
Look for "IPv4 Address" (usually 192.168.x.x)
|
||||
|
||||
### Mac/Linux
|
||||
|
||||
```bash
|
||||
ifconfig
|
||||
# or
|
||||
@@ -73,13 +82,13 @@ ip addr show
|
||||
|
||||
### Method 1: MCP Client Community Node
|
||||
|
||||
If your N8N has the MCP Client node installed:
|
||||
If your N8N has MCP Client node installed:
|
||||
|
||||
1. Open N8N Settings → MCP Access
|
||||
2. Add new server:
|
||||
```json
|
||||
{
|
||||
"name": "memento",
|
||||
"name": "keep-notes",
|
||||
"transport": "sse",
|
||||
"url": "http://YOUR_IP:3001/sse"
|
||||
}
|
||||
@@ -92,57 +101,289 @@ If your N8N has the MCP Client node installed:
|
||||
### Method 2: HTTP Request Nodes (Fallback)
|
||||
|
||||
Use N8N's standard HTTP Request nodes with the REST API:
|
||||
- POST `http://YOUR_IP:3000/api/notes` (Memento REST API)
|
||||
- POST `http://YOUR_IP:3000/api/notes` (Keep Notes REST API)
|
||||
|
||||
## 🛠️ Available Tools (9)
|
||||
## 🛠️ Available Tools (19)
|
||||
|
||||
All tools from the stdio version are available:
|
||||
### Notes Tools (9)
|
||||
|
||||
1. **create_note** - Create new note
|
||||
```json
|
||||
{
|
||||
"name": "create_note",
|
||||
"arguments": {
|
||||
"content": "My note",
|
||||
"title": "Optional title",
|
||||
"color": "blue",
|
||||
"images": ["data:image/png;base64,..."]
|
||||
}
|
||||
}
|
||||
```
|
||||
#### 1. **create_note** - Create new note with full support
|
||||
|
||||
2. **get_notes** - Get all notes
|
||||
```json
|
||||
{
|
||||
"name": "get_notes",
|
||||
"arguments": {
|
||||
"includeArchived": false,
|
||||
"search": "optional search query"
|
||||
}
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"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..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **get_note** - Get specific note by ID
|
||||
4. **update_note** - Update existing note
|
||||
5. **delete_note** - Delete note
|
||||
6. **search_notes** - Search notes
|
||||
7. **get_labels** - Get all unique labels
|
||||
8. **toggle_pin** - Pin/unpin note
|
||||
9. **toggle_archive** - Archive/unarchive note
|
||||
**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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "get_note",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. **update_note** - Update existing note
|
||||
|
||||
Supports all fields from create_note. All are optional except `id`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "update_note",
|
||||
"arguments": {
|
||||
"id": "cuid...",
|
||||
"title": "Updated Title",
|
||||
"color": "green",
|
||||
"isPinned": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. **delete_note** - Delete note
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "delete_note",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. **search_notes** - Search notes by query
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "search_notes",
|
||||
"arguments": {
|
||||
"query": "project",
|
||||
"notebookId": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**New (v2.0):** `notebookId` filter support
|
||||
|
||||
#### 7. **get_labels** - Get all unique labels (legacy method)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "get_labels",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
#### 8. **toggle_pin** - Pin/unpin note
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "toggle_pin",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 9. **toggle_archive** - Archive/unarchive note
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "toggle_archive",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Notebooks Tools (5) - NEW in v2.0
|
||||
|
||||
#### 10. **create_notebook** - Create new notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "get_notebooks",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:** Array of notebooks with labels and notes count
|
||||
|
||||
#### 12. **get_notebook** - Get notebook with notes
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "get_notebook",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:** Notebook with labels, notes (parsed), and notes count
|
||||
|
||||
#### 13. **update_notebook** - Update notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "update_notebook",
|
||||
"arguments": {
|
||||
"id": "cuid...",
|
||||
"name": "Updated Name",
|
||||
"icon": "📁",
|
||||
"color": "#10B981"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 14. **delete_notebook** - Delete notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "create_label",
|
||||
"arguments": {
|
||||
"name": "Important",
|
||||
"color": "red",
|
||||
"notebookId": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Required:** `name` and `notebookId`
|
||||
|
||||
#### 16. **get_labels_detailed** - Get labels with details
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "get_labels_detailed",
|
||||
"arguments": {
|
||||
"notebookId": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:** Labels with notebook information
|
||||
|
||||
#### 17. **update_label** - Update label
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "update_label",
|
||||
"arguments": {
|
||||
"id": "cuid...",
|
||||
"name": "Updated Name",
|
||||
"color": "blue"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 18. **delete_label** - Delete label
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "delete_label",
|
||||
"arguments": {
|
||||
"id": "cuid..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 Testing the SSE Server
|
||||
|
||||
### Test 1: Health Check
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/
|
||||
```
|
||||
|
||||
### Test 2: SSE Connection
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:3001/sse
|
||||
```
|
||||
|
||||
### Test 3: Call a Tool (get_notes)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/message \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -158,6 +399,7 @@ curl -X POST http://localhost:3001/message \
|
||||
```
|
||||
|
||||
### Test 4: Create Note via MCP
|
||||
|
||||
```powershell
|
||||
$body = @{
|
||||
jsonrpc = "2.0"
|
||||
@@ -177,38 +419,81 @@ Invoke-RestMethod -Method POST -Uri "http://localhost:3001/message" `
|
||||
-Body $body -ContentType "application/json"
|
||||
```
|
||||
|
||||
### Test 5: Create Notebook
|
||||
|
||||
```powershell
|
||||
$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
|
||||
|
||||
```powershell
|
||||
$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 in the main app:
|
||||
**Solution**: Generate Prisma Client:
|
||||
|
||||
```bash
|
||||
cd ..\keep-notes
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### Error: Port 3001 already in use
|
||||
|
||||
**Solution**: Change port in `index-sse.js`:
|
||||
**Solution 1:** Change port in `index-sse.js`:
|
||||
```javascript
|
||||
const PORT = process.env.PORT || 3002;
|
||||
```
|
||||
|
||||
Or set environment variable:
|
||||
**Solution 2:** Kill existing process:
|
||||
```powershell
|
||||
$env:PORT=3002; node index-sse.js
|
||||
Get-Process node | Where-Object {$_.Path -like "*index-sse*"}
|
||||
Stop-Process -Id $process.Id
|
||||
```
|
||||
|
||||
### Error: Cannot connect from N8N
|
||||
|
||||
**Checklist**:
|
||||
**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**:
|
||||
**Test connectivity from N8N machine:**
|
||||
```bash
|
||||
curl http://YOUR_IP:3001/
|
||||
```
|
||||
@@ -260,7 +545,7 @@ app.use((req, res, next) => {
|
||||
| **Port** | 3001 | 3000 (Next.js) |
|
||||
| **Format** | MCP JSON-RPC | REST JSON |
|
||||
| **Use case** | MCP clients | Standard HTTP clients |
|
||||
| **Tools** | 9 MCP tools | 4 CRUD endpoints |
|
||||
| **Tools** | 19 MCP tools | 4 CRUD endpoints |
|
||||
|
||||
**Both work!** Use MCP SSE for proper MCP integration, or REST API for simpler HTTP requests.
|
||||
|
||||
@@ -297,12 +582,12 @@ npm start
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Get Memento Notes",
|
||||
"name": "Get Keep Notes",
|
||||
"type": "MCP Client",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"parameters": {
|
||||
"server": "memento",
|
||||
"server": "keep-notes",
|
||||
"tool": "get_notes",
|
||||
"arguments": {
|
||||
"includeArchived": false
|
||||
@@ -313,13 +598,49 @@ npm start
|
||||
}
|
||||
```
|
||||
|
||||
### Create Notebook via MCP
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Create Notebook",
|
||||
"type": "MCP Client",
|
||||
"parameters": {
|
||||
"server": "keep-notes",
|
||||
"tool": "create_notebook",
|
||||
"arguments": {
|
||||
"name": "Work Projects",
|
||||
"icon": "💼",
|
||||
"color": "#3B82F6"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create Label via MCP
|
||||
|
||||
```json
|
||||
{
|
||||
"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 the original `index.js` with stdio:
|
||||
Use original `index.js` with stdio:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"memento": {
|
||||
"keep-notes": {
|
||||
"command": "node",
|
||||
"args": ["D:/dev_new_pc/Keep/mcp-server/index.js"]
|
||||
}
|
||||
@@ -327,6 +648,10 @@ Use the original `index.js` with stdio:
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 N8N Integration Guide
|
||||
|
||||
See [N8N-SETUP.md](./N8N-SETUP.md) for complete N8N workflow setup and [N8N-WORKFLOWS.md](./N8N-WORKFLOWS.md) for available workflows.
|
||||
|
||||
## 📚 Resources
|
||||
|
||||
- [MCP Protocol Documentation](https://modelcontextprotocol.io)
|
||||
@@ -337,12 +662,12 @@ Use the original `index.js` with stdio:
|
||||
## 🤝 Support
|
||||
|
||||
Issues? Check:
|
||||
1. [MCP-SSE-ANALYSIS.md](../MCP-SSE-ANALYSIS.md) - Detailed SSE analysis
|
||||
1. [START-SSE.md](./START-SSE.md) - Quick start guide
|
||||
2. [README.md](../README.md) - Main project README
|
||||
3. [COMPLETED-FEATURES.md](../COMPLETED-FEATURES.md) - Implementation details
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Last Updated**: January 4, 2026
|
||||
**Version**: 2.0.0
|
||||
**Last Updated**: January 18, 2026
|
||||
**Status**: ✅ Production Ready
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Memento MCP Server
|
||||
# Keep Notes MCP Server
|
||||
|
||||
Model Context Protocol (MCP) server for integrating Memento note-taking app with N8N and other automation tools.
|
||||
Model Context Protocol (MCP) server for integrating Keep Notes note-taking app with N8N, Cursor, and other automation tools.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -11,20 +11,51 @@ npm install
|
||||
|
||||
## Usage
|
||||
|
||||
### Standalone Server
|
||||
### Quick Start (Windows)
|
||||
|
||||
Le plus simple pour démarrer le serveur MCP:
|
||||
|
||||
```powershell
|
||||
# Exécuter depuis le dossier mcp-server
|
||||
.\start-mcp.ps1
|
||||
```
|
||||
|
||||
Ou le script batch:
|
||||
|
||||
```cmd
|
||||
start-mcp.bat
|
||||
```
|
||||
|
||||
Ces scripts vérifient automatiquement les prérequis et démarrent le serveur.
|
||||
|
||||
### Standalone Server (Cross-platform)
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
### With N8N
|
||||
Ou directement:
|
||||
|
||||
Add to your MCP client configuration:
|
||||
```bash
|
||||
node index.js
|
||||
```
|
||||
|
||||
Pour le mode SSE (Server-Sent Events):
|
||||
|
||||
```bash
|
||||
npm run start:sse
|
||||
```
|
||||
|
||||
Voir [START-MCP.md](./START-MCP.md) pour le guide complet de démarrage.
|
||||
|
||||
### With Cursor MCP
|
||||
|
||||
Add to your Cursor MCP client configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"memento": {
|
||||
"keep-notes": {
|
||||
"command": "node",
|
||||
"args": ["D:/dev_new_pc/Keep/mcp-server/index.js"]
|
||||
}
|
||||
@@ -32,10 +63,16 @@ Add to your MCP client configuration:
|
||||
}
|
||||
```
|
||||
|
||||
### With N8N
|
||||
|
||||
Configure the MCP node in N8N with the server details above.
|
||||
|
||||
## Available Tools
|
||||
|
||||
### create_note
|
||||
Create a new note in Memento.
|
||||
### Note Management
|
||||
|
||||
#### create_note
|
||||
Create a new note in Keep Notes.
|
||||
|
||||
**Parameters:**
|
||||
- `title` (string, optional): Note title
|
||||
@@ -46,6 +83,15 @@ Create a new note in Memento.
|
||||
- `labels` (array, optional): Note labels/tags
|
||||
- `isPinned` (boolean, optional): Pin the note
|
||||
- `isArchived` (boolean, optional): Archive the note
|
||||
- `images` (array, optional): Note images as base64 encoded strings
|
||||
- `links` (array, optional): Note links
|
||||
- `reminder` (string, optional): Reminder date/time (ISO 8601 format)
|
||||
- `isReminderDone` (boolean, optional): Mark reminder as done
|
||||
- `reminderRecurrence` (string, optional): Reminder recurrence (daily, weekly, monthly, yearly)
|
||||
- `reminderLocation` (string, optional): Reminder location
|
||||
- `isMarkdown` (boolean, optional): Enable markdown support
|
||||
- `size` (string, optional): Note size (small, medium, large)
|
||||
- `notebookId` (string, optional): Notebook ID to associate the note with
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
@@ -58,90 +104,242 @@ Create a new note in Memento.
|
||||
{ "id": "1", "text": "Milk", "checked": false },
|
||||
{ "id": "2", "text": "Bread", "checked": false }
|
||||
],
|
||||
"labels": ["shopping", "personal"]
|
||||
"labels": ["shopping", "personal"],
|
||||
"notebookId": "cuid123..."
|
||||
}
|
||||
```
|
||||
|
||||
### get_notes
|
||||
Get all notes from Memento.
|
||||
#### get_notes
|
||||
Get all notes from Keep Notes.
|
||||
|
||||
**Parameters:**
|
||||
- `includeArchived` (boolean, optional): Include archived notes
|
||||
- `search` (string, optional): Search query to filter notes
|
||||
- `notebookId` (string, optional): Filter notes by notebook ID
|
||||
|
||||
### get_note
|
||||
#### get_note
|
||||
Get a specific note by ID.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Note ID
|
||||
|
||||
### update_note
|
||||
#### update_note
|
||||
Update an existing note.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Note ID
|
||||
- All other fields from create_note are optional
|
||||
|
||||
### delete_note
|
||||
#### delete_note
|
||||
Delete a note by ID.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Note ID
|
||||
|
||||
### search_notes
|
||||
#### search_notes
|
||||
Search notes by query.
|
||||
|
||||
**Parameters:**
|
||||
- `query` (string, required): Search query
|
||||
- `notebookId` (string, optional): Filter search by notebook ID
|
||||
|
||||
### get_labels
|
||||
Get all unique labels from notes.
|
||||
#### get_labels
|
||||
Get all unique labels from notes (legacy method).
|
||||
|
||||
**Parameters:** None
|
||||
|
||||
### toggle_pin
|
||||
#### toggle_pin
|
||||
Toggle pin status of a note.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Note ID
|
||||
|
||||
### toggle_archive
|
||||
#### toggle_archive
|
||||
Toggle archive status of a note.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Note ID
|
||||
|
||||
### Notebook Management
|
||||
|
||||
#### create_notebook
|
||||
Create a new notebook.
|
||||
|
||||
**Parameters:**
|
||||
- `name` (string, required): Notebook name
|
||||
- `icon` (string, optional): Notebook icon (emoji)
|
||||
- `color` (string, optional): Notebook color (hex code)
|
||||
- `order` (number, optional): Notebook order
|
||||
|
||||
#### get_notebooks
|
||||
Get all notebooks.
|
||||
|
||||
**Parameters:** None
|
||||
|
||||
#### get_notebook
|
||||
Get a specific notebook by ID with its notes.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Notebook ID
|
||||
|
||||
#### update_notebook
|
||||
Update an existing notebook.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Notebook ID
|
||||
- `name` (string, optional): Notebook name
|
||||
- `icon` (string, optional): Notebook icon
|
||||
- `color` (string, optional): Notebook color
|
||||
- `order` (number, optional): Notebook order
|
||||
|
||||
#### delete_notebook
|
||||
Delete a notebook by ID.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Notebook ID
|
||||
|
||||
### Label Management
|
||||
|
||||
#### create_label
|
||||
Create a new label.
|
||||
|
||||
**Parameters:**
|
||||
- `name` (string, required): Label name
|
||||
- `color` (string, optional): Label color (red, orange, yellow, green, teal, blue, purple, pink, gray)
|
||||
- `notebookId` (string, required): Notebook ID to associate the label with
|
||||
|
||||
#### get_labels_detailed
|
||||
Get all labels with details.
|
||||
|
||||
**Parameters:**
|
||||
- `notebookId` (string, optional): Filter labels by notebook ID
|
||||
|
||||
#### update_label
|
||||
Update an existing label.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Label ID
|
||||
- `name` (string, optional): Label name
|
||||
- `color` (string, optional): Label color
|
||||
|
||||
#### delete_label
|
||||
Delete a label by ID.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string, required): Label ID
|
||||
|
||||
## N8N Integration Example
|
||||
|
||||
1. Install the MCP node in N8N
|
||||
2. Configure the Memento MCP server
|
||||
3. Use the tools in your workflows:
|
||||
|
||||
```javascript
|
||||
// Create a note from email
|
||||
### Create a note from email
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "{{ $json.subject }}",
|
||||
"content": "{{ $json.body }}",
|
||||
"labels": ["email", "inbox"]
|
||||
"labels": ["email", "inbox"],
|
||||
"notebookId": "cuid123..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
// Search notes
|
||||
### Search notes
|
||||
```json
|
||||
{
|
||||
"tool": "search_notes",
|
||||
"arguments": {
|
||||
"query": "meeting"
|
||||
"query": "meeting",
|
||||
"notebookId": "cuid123..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a notebook
|
||||
```json
|
||||
{
|
||||
"tool": "create_notebook",
|
||||
"arguments": {
|
||||
"name": "Work Projects",
|
||||
"icon": "💼",
|
||||
"color": "#3B82F6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
The MCP server connects to the same SQLite database as the Memento web app located at:
|
||||
The MCP server connects to the same SQLite database as the Keep Notes web app located at:
|
||||
`D:/dev_new_pc/Keep/keep-notes/prisma/dev.db`
|
||||
|
||||
## Prisma Schema Reference
|
||||
|
||||
The MCP server works with the following Prisma models:
|
||||
|
||||
### Note Model
|
||||
- id: String (CUID)
|
||||
- title: String?
|
||||
- content: String
|
||||
- color: String (default: "default")
|
||||
- isPinned: Boolean
|
||||
- isArchived: Boolean
|
||||
- type: String (default: "text")
|
||||
- checkItems: String? (JSON)
|
||||
- labels: String? (JSON)
|
||||
- images: String? (JSON)
|
||||
- links: String? (JSON)
|
||||
- reminder: DateTime?
|
||||
- isReminderDone: Boolean
|
||||
- reminderRecurrence: String?
|
||||
- reminderLocation: String?
|
||||
- isMarkdown: Boolean
|
||||
- size: String (default: "small")
|
||||
- notebookId: String?
|
||||
- userId: String?
|
||||
- order: Int
|
||||
- createdAt: DateTime
|
||||
- updatedAt: DateTime
|
||||
|
||||
### Notebook Model
|
||||
- id: String (CUID)
|
||||
- name: String
|
||||
- icon: String?
|
||||
- color: String?
|
||||
- order: Int
|
||||
- userId: String
|
||||
- createdAt: DateTime
|
||||
- updatedAt: DateTime
|
||||
|
||||
### Label Model
|
||||
- id: String (CUID)
|
||||
- name: String
|
||||
- color: String (default: "gray")
|
||||
- notebookId: String?
|
||||
- userId: String?
|
||||
- createdAt: DateTime
|
||||
- updatedAt: DateTime
|
||||
|
||||
## N8N Workflows
|
||||
|
||||
Ce serveur MCP est accompagné de 6 workflows N8N prêts à l'emploi pour une intégration complète:
|
||||
|
||||
1. **Create Note with Classification** - Création de notes avec classification automatique par IA
|
||||
2. **Search & Summary** - Recherche et résumé intelligent de notes
|
||||
3. **Notebook Manager** - Gestion complète des notebooks (CRUD)
|
||||
4. **Reminder Notifications** - Automatisation des rappels avec notifications
|
||||
5. **Label Manager** - Gestion des labels avec suggestion IA
|
||||
6. **Email to Note** - Conversion automatique d'emails en notes
|
||||
|
||||
Pour plus de détails, consultez [N8N-WORKFLOWS.md](./N8N-WORKFLOWS.md)
|
||||
|
||||
### Importation des Workflows
|
||||
|
||||
Chaque workflow peut être importé directement dans N8N via le fichier JSON correspondant:
|
||||
|
||||
```bash
|
||||
# Exemple: Importer le workflow de création de notes
|
||||
# Ouvrir N8N → Import from File → Sélectionner n8n-workflow-create-note.json
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
359
mcp-server/START-MCP.md
Normal file
359
mcp-server/START-MCP.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Guide de Démarrage - Serveur MCP Keep Notes
|
||||
|
||||
## 🚀 Méthodes de Démarrage
|
||||
|
||||
### Méthode 1: Script PowerShell (Recommandé pour Windows)
|
||||
|
||||
Le script vérifie automatiquement les prérequis et démarre le serveur.
|
||||
|
||||
```powershell
|
||||
# Exécuter depuis le dossier mcp-server
|
||||
.\start-mcp.ps1
|
||||
```
|
||||
|
||||
**Avantages:**
|
||||
- ✅ Vérifie que Prisma est généré
|
||||
- ✅ Vérifie que Keep Notes est en cours d'exécution
|
||||
- ✅ Messages d'erreur clairs
|
||||
- ✅ Compatible Windows PowerShell
|
||||
|
||||
---
|
||||
|
||||
### Méthode 2: Script Batch (Windows)
|
||||
|
||||
```cmd
|
||||
# Exécuter depuis le dossier mcp-server
|
||||
start-mcp.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Méthode 3: Commande directe (Cross-platform)
|
||||
|
||||
```bash
|
||||
# Depuis le dossier mcp-server
|
||||
npm start
|
||||
```
|
||||
|
||||
Ou directement:
|
||||
|
||||
```bash
|
||||
node index.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Méthode 4: Mode SSE (Alternative)
|
||||
|
||||
Si vous avez besoin du mode SSE (Server-Sent Events):
|
||||
|
||||
```bash
|
||||
npm run start:sse
|
||||
```
|
||||
|
||||
Ou via PowerShell:
|
||||
|
||||
```powershell
|
||||
.\start-sse.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Prérequis
|
||||
|
||||
Avant de démarrer, assurez-vous que:
|
||||
|
||||
1. **Node.js est installé**
|
||||
```bash
|
||||
node --version
|
||||
# Doit retourner v18 ou supérieur
|
||||
```
|
||||
|
||||
2. **Dépendances sont installées**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Client Prisma est généré**
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
4. **Keep Notes est en cours d'exécution**
|
||||
- Vérifiez: http://localhost:3000
|
||||
- Si non démarré, allez dans `keep-notes/` et exécutez `npm run dev`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Installation Initiale (Première fois)
|
||||
|
||||
### Étape 1: Installer les dépendances
|
||||
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm install
|
||||
```
|
||||
|
||||
### Étape 2: Générer le client Prisma
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### Étape 3: Tester la connexion à la base de données
|
||||
|
||||
```bash
|
||||
node test-server.js
|
||||
```
|
||||
|
||||
Vous devriez voir:
|
||||
|
||||
```
|
||||
🧪 Testing MCP Server Database Connection...
|
||||
|
||||
✅ Prisma Client initialized successfully
|
||||
|
||||
📝 Test 1: Counting notes...
|
||||
Found XX notes in database
|
||||
|
||||
✅ All database tests passed successfully!
|
||||
🚀 MCP Server is ready to use!
|
||||
```
|
||||
|
||||
### Étape 4: Démarrer le serveur MCP
|
||||
|
||||
```powershell
|
||||
.\start-mcp.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Utilisation avec Cursor
|
||||
|
||||
### Configuration Cursor
|
||||
|
||||
Ouvrez les paramètres de Cursor et ajoutez:
|
||||
|
||||
**JSON Config:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"keep-notes": {
|
||||
"command": "node",
|
||||
"args": ["D:/dev_new_pc/Keep/mcp-server/index.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Ou avec le script PowerShell:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"keep-notes": {
|
||||
"command": "pwsh",
|
||||
"args": [
|
||||
"-File",
|
||||
"D:/dev_new_pc/Keep/mcp-server/start-mcp.ps1"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vérifier que Cursor reconnaît le MCP
|
||||
|
||||
1. Redémarrez Cursor
|
||||
2. Ouvrez une nouvelle conversation
|
||||
3. Tapez: "List all MCP tools"
|
||||
4. Vous devriez voir les outils Keep Notes listés
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Utilisation avec N8N
|
||||
|
||||
### Option 1: Via les workflows MCP
|
||||
|
||||
1. Importez les workflows N8N (voir `N8N-SETUP.md`)
|
||||
2. Configurez les variables d'environnement
|
||||
3. Activez les workflows
|
||||
|
||||
### Option 2: Via MCP Client Node
|
||||
|
||||
1. Dans N8N, ajoutez un **MCP Client Tool Node**
|
||||
2. Configurez l'endpoint:
|
||||
- **URL:** `stdio://` (mode par défaut)
|
||||
- **Command:** `node`
|
||||
- **Args:** `["D:/dev_new_pc/Keep/mcp-server/index.js"]`
|
||||
3. Sélectionnez les outils que vous souhaitez exposer
|
||||
|
||||
---
|
||||
|
||||
## 📊 Vérifier le Serveur en Cours d'Exécution
|
||||
|
||||
### Vérifier avec PowerShell
|
||||
|
||||
```powershell
|
||||
# Liste les processus Node
|
||||
Get-Process | Where-Object {$_.ProcessName -eq "node"}
|
||||
```
|
||||
|
||||
### Vérifier le port (si mode SSE)
|
||||
|
||||
```powershell
|
||||
# Vérifier si le port SSE est utilisé (si configuré)
|
||||
netstat -ano | findstr "3001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛑 Arrêter le Serveur
|
||||
|
||||
### Depuis le terminal
|
||||
|
||||
Appuyez sur **Ctrl+C**
|
||||
|
||||
### Depuis PowerShell
|
||||
|
||||
```powershell
|
||||
# Trouver et tuer le processus
|
||||
$process = Get-Process node | Where-Object {$_.Path -like "*mcp-server*"}
|
||||
Stop-Process -Id $process.Id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Dépannage
|
||||
|
||||
### Erreur: "Cannot find module '@prisma/client'"
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### Erreur: "Connection refused to database"
|
||||
|
||||
**Solution:**
|
||||
1. Vérifiez que Keep Notes est en cours d'exécution (http://localhost:3000)
|
||||
2. Vérifiez le chemin de la base de données dans `index.js`
|
||||
3. Exécutez `node test-server.js` pour tester
|
||||
|
||||
### Erreur: "Server is not responding"
|
||||
|
||||
**Solution:**
|
||||
1. Vérifiez que le serveur est en cours d'exécution
|
||||
2. Redémarrez Cursor/N8N
|
||||
3. Vérifiez les logs dans le terminal
|
||||
|
||||
### Erreur: "Permission denied" (Windows)
|
||||
|
||||
**Solution:**
|
||||
Exécutez PowerShell en tant qu'administrateur ou changez la politique d'exécution:
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Logs et Débogage
|
||||
|
||||
Le serveur MCP écrit les logs dans la console:
|
||||
|
||||
```
|
||||
Keep Notes MCP server running on stdio
|
||||
```
|
||||
|
||||
Pour plus de détails, ajoutez `--debug` (modifiez le script):
|
||||
|
||||
```bash
|
||||
node index.js --debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Démarrage Rapide (Récapitulatif)
|
||||
|
||||
```bash
|
||||
# 1. Allumer Keep Notes
|
||||
cd keep-notes
|
||||
npm run dev
|
||||
|
||||
# 2. Dans un autre terminal, démarrer MCP
|
||||
cd mcp-server
|
||||
npm start
|
||||
|
||||
# 3. Ouvrir Cursor/N8N et utiliser les outils MCP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation Complémentaire
|
||||
|
||||
- [README.md](./README.md) - Documentation complète du serveur MCP
|
||||
- [N8N-SETUP.md](./N8N-SETUP.md) - Installation des workflows N8N
|
||||
- [N8N-WORKFLOWS.md](./N8N-WORKFLOWS.md) - Description des workflows
|
||||
- [CHANGES.md](./CHANGES.md) - Résumé des modifications
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Mode SSE Alternative
|
||||
|
||||
Si vous avez besoin du mode SSE (Server-Sent Events) pour N8N sur une machine distante:
|
||||
|
||||
### Démarrage SSE
|
||||
|
||||
```powershell
|
||||
# Exécuter depuis le dossier mcp-server
|
||||
.\start-sse.ps1
|
||||
```
|
||||
|
||||
Ou directement:
|
||||
|
||||
```bash
|
||||
npm run start:sse
|
||||
```
|
||||
|
||||
### Documentation SSE Complète
|
||||
|
||||
- **[START-SSE.md](./START-SSE.md)** - Guide complet de démarrage SSE
|
||||
- **[README-SSE.md](./README-SSE.md)** - Documentation des outils SSE
|
||||
- **Port SSE**: 3001 (par défaut)
|
||||
- **Nombre d'outils**: 19 (9 Notes + 5 Notebooks + 5 Labels)
|
||||
|
||||
### Configuration N8N pour SSE
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "keep-notes-sse",
|
||||
"transport": "sse",
|
||||
"url": "http://YOUR_IP:3001/sse"
|
||||
}
|
||||
```
|
||||
|
||||
Remplacez `YOUR_IP` par votre IP locale (ex: 192.168.1.100)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
**Problèmes?**
|
||||
1. Vérifiez les logs dans le terminal
|
||||
2. Exécutez `node test-server.js` pour tester
|
||||
3. Consultez la documentation de débogage
|
||||
|
||||
**Commandes utiles:**
|
||||
```bash
|
||||
# Vérifier Node.js
|
||||
node --version
|
||||
|
||||
# Vérifier les dépendances
|
||||
npm list
|
||||
|
||||
# Régénérer Prisma
|
||||
npx prisma generate
|
||||
|
||||
# Tester la base de données
|
||||
node test-server.js
|
||||
```
|
||||
363
mcp-server/START-SSE.md
Normal file
363
mcp-server/START-SSE.md
Normal file
@@ -0,0 +1,363 @@
|
||||
# Guide de Démarrage - Serveur MCP Keep Notes (Mode SSE)
|
||||
|
||||
## 🚀 Démarrage Rapide du Serveur SSE
|
||||
|
||||
### Option 1: Script PowerShell (Recommandée pour Windows)
|
||||
|
||||
```powershell
|
||||
# Exécuter depuis le dossier mcp-server
|
||||
.\start-sse.ps1
|
||||
```
|
||||
|
||||
**Ce script fait:**
|
||||
- ✅ Vérifie que Prisma est généré
|
||||
- ✅ Vérifie que Keep Notes est en cours d'exécution (port 3000)
|
||||
- ✅ Génère Prisma si nécessaire
|
||||
- ✅ Messages d'erreur clairs
|
||||
- ✅ Démarre le serveur MCP sur le port 3001
|
||||
|
||||
### Option 2: Commande directe
|
||||
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm run start:sse
|
||||
```
|
||||
|
||||
Ou directement:
|
||||
|
||||
```bash
|
||||
node index-sse.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Prérequis
|
||||
|
||||
### 1. Node.js installé
|
||||
|
||||
```bash
|
||||
node --version
|
||||
# Doit retourner v18 ou supérieur
|
||||
```
|
||||
|
||||
### 2. Dépendances installées
|
||||
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm install
|
||||
```
|
||||
|
||||
### 3. Client Prisma généré
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### 4. Keep Notes en cours d'exécution
|
||||
|
||||
Le serveur SSE nécessite que Keep Notes soit démarré sur le port 3000.
|
||||
|
||||
```bash
|
||||
# Dans un autre terminal
|
||||
cd keep-notes
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 5. Port 3001 disponible
|
||||
|
||||
Le serveur SSE utilise le port 3001. Vérifiez qu'il n'est pas déjà utilisé:
|
||||
|
||||
```bash
|
||||
# Windows (PowerShell)
|
||||
netstat -ano | findstr "3001"
|
||||
|
||||
# Mac/Linux
|
||||
lsof -i :3001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Utilisation avec N8N
|
||||
|
||||
### Configuration N8N pour MCP SSE
|
||||
|
||||
Le serveur SSE est parfait pour N8N sur une machine distante!
|
||||
|
||||
#### Étape 1: Trouver votre IP
|
||||
|
||||
**Windows:**
|
||||
```powershell
|
||||
ipconfig
|
||||
```
|
||||
Cherchez "IPv4 Address" (ex: 192.168.1.100)
|
||||
|
||||
**Mac/Linux:**
|
||||
```bash
|
||||
ifconfig
|
||||
# ou
|
||||
ip addr show
|
||||
```
|
||||
|
||||
#### Étape 2: Configurer N8N
|
||||
|
||||
Dans N8N, allez dans **Settings** → **MCP Access** et ajoutez:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "keep-notes",
|
||||
"transport": "sse",
|
||||
"url": "http://192.168.1.100:3001/sse"
|
||||
}
|
||||
```
|
||||
|
||||
Remplacez `192.168.1.100` par votre IP réelle.
|
||||
|
||||
#### Étape 3: Activer les workflows
|
||||
|
||||
1. Importez les workflows N8N (voir `N8N-SETUP.md`)
|
||||
2. Activez chaque workflow (bouton play)
|
||||
3. Les workflows peuvent maintenant appeler les outils MCP
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Outils Disponibles (19 au total)
|
||||
|
||||
### Notes (9 outils)
|
||||
|
||||
| # | Outil | Description |
|
||||
|---|--------|-------------|
|
||||
| 1 | create_note | Créer une nouvelle note (support complet) |
|
||||
| 2 | get_notes | Récupérer toutes les notes (supporte filtres) |
|
||||
| 3 | get_note | Récupérer une note par ID |
|
||||
| 4 | update_note | Mettre à jour une note |
|
||||
| 5 | delete_note | Supprimer une note |
|
||||
| 6 | search_notes | Rechercher des notes |
|
||||
| 7 | get_labels | Récupérer les labels (méthode legacy) |
|
||||
| 8 | toggle_pin | Épingler/désépingler une note |
|
||||
| 9 | toggle_archive | Archiver/désarchiver une note |
|
||||
|
||||
### Notebooks (5 outils)
|
||||
|
||||
| # | Outil | Description |
|
||||
|---|--------|-------------|
|
||||
| 10 | create_notebook | Créer un nouveau notebook |
|
||||
| 11 | get_notebooks | Récupérer tous les notebooks |
|
||||
| 12 | get_notebook | Récupérer un notebook avec ses notes |
|
||||
| 13 | update_notebook | Mettre à jour un notebook |
|
||||
| 14 | delete_notebook | Supprimer un notebook |
|
||||
|
||||
### Labels (5 outils)
|
||||
|
||||
| # | Outil | Description |
|
||||
|---|--------|-------------|
|
||||
| 15 | create_label | Créer un nouveau label |
|
||||
| 16 | get_labels_detailed | Récupérer les labels avec détails |
|
||||
| 17 | update_label | Mettre à jour un label |
|
||||
| 18 | delete_label | Supprimer un label |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Tests de Vérification
|
||||
|
||||
### Test 1: Health Check
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/
|
||||
```
|
||||
|
||||
**Réponse attendue:**
|
||||
```json
|
||||
{
|
||||
"name": "Keep Notes MCP SSE Server",
|
||||
"version": "2.0.0",
|
||||
"status": "running",
|
||||
"endpoints": {
|
||||
"sse": "/sse",
|
||||
"message": "/message"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test 2: SSE Connection
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:3001/sse
|
||||
```
|
||||
|
||||
### Test 3: Appeler un outil (get_notes)
|
||||
|
||||
```bash
|
||||
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: Créer une note
|
||||
|
||||
```powershell
|
||||
$body = @{
|
||||
jsonrpc = "2.0"
|
||||
method = "tools/call"
|
||||
params = @{
|
||||
name = "create_note"
|
||||
arguments = @{
|
||||
content = "Test depuis 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"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration Avancée
|
||||
|
||||
### Changer le port
|
||||
|
||||
Modifier `index-sse.js`:
|
||||
|
||||
```javascript
|
||||
const PORT = process.env.PORT || 3002;
|
||||
```
|
||||
|
||||
Ou utiliser une variable d'environnement:
|
||||
|
||||
```powershell
|
||||
$env:PORT=3002; node index-sse.js
|
||||
```
|
||||
|
||||
### Sécurisation avec API Key
|
||||
|
||||
Ajouter de l'authentification dans `index-sse.js`:
|
||||
|
||||
```javascript
|
||||
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();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛑 Arrêter le Serveur
|
||||
|
||||
Appuyez sur **Ctrl+C** dans le terminal.
|
||||
|
||||
Le serveur fermera proprement:
|
||||
- Arrêtera toutes les connexions SSE
|
||||
- Déconnectera Prisma de la base de données
|
||||
- Affichera un message de confirmation
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation Complémentaire
|
||||
|
||||
- [README-SSE.md](./README-SSE.md) - Documentation détaillée des outils
|
||||
- [README.md](./README.md) - Documentation principale du serveur MCP
|
||||
- [N8N-SETUP.md](./N8N-SETUP.md) - Guide d'installation des workflows N8N
|
||||
- [N8N-WORKFLOWS.md](./N8N-WORKFLOWS.md) - Description des workflows N8N
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Dépannage
|
||||
|
||||
### Erreur: "Port 3001 already in use"
|
||||
|
||||
**Solution 1:** Changer le port dans `index-sse.js`:
|
||||
```javascript
|
||||
const PORT = process.env.PORT || 3002;
|
||||
```
|
||||
|
||||
**Solution 2:** Trouver et tuer le processus:
|
||||
```powershell
|
||||
Get-Process node | Where-Object {$_.Path -like "*index-sse*"}
|
||||
Stop-Process -Id $process.Id
|
||||
```
|
||||
|
||||
### Erreur: "Cannot connect from N8N"
|
||||
|
||||
**Checklist:**
|
||||
1. ✅ Serveur SSE en cours d'exécution (`http://localhost:3001` fonctionne)
|
||||
2. ✅ Firewall autorise le port 3001
|
||||
3. ✅ Utilisation de l'IP correcte (pas `localhost` depuis N8N distant)
|
||||
4. ✅ N8N peut atteindre votre réseau
|
||||
5. ✅ Utilisation de `http://` (pas `https://`)
|
||||
|
||||
**Test depuis N8N:**
|
||||
```bash
|
||||
curl http://YOUR_IP:3001/
|
||||
```
|
||||
|
||||
### Erreur: "Prisma Client not initialized"
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
cd mcp-server
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### SSE Connection Drops
|
||||
|
||||
C'est normal! SSE maintient une connexion persistante. Si elle chute:
|
||||
- Le client doit se reconnecter automatiquement
|
||||
- Vérifier la stabilité du réseau
|
||||
- Vérifier les paramètres firewall/proxy
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Architecture SSE vs stdio
|
||||
|
||||
| Fonctionnalité | SSE (index-sse.js) | stdio (index.js) |
|
||||
|---------------|----------------------|-------------------|
|
||||
| **Transport** | HTTP/SSE | Process local |
|
||||
| **Accès distant** | ✅ Oui | ❌ Non |
|
||||
| **Port** | 3001 | N/A |
|
||||
| **Utilisation N8N** | ✅ Parfait | ❌ Impossible |
|
||||
| **Utilisation Cursor** | ✅ Possible | ✅ Parfait |
|
||||
| **Setup** | Moyen | Simple |
|
||||
| **Latence** | Faible | Très faible |
|
||||
| **Outils** | 19 | 19 |
|
||||
|
||||
**Recommandation:**
|
||||
- **SSE** pour N8N sur machine distante ✅
|
||||
- **stdio** pour Cursor sur la même machine ✅
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Démarrage Rapide (Récapitulatif)
|
||||
|
||||
```bash
|
||||
# 1. Démarrer Keep Notes (terminal 1)
|
||||
cd keep-notes
|
||||
npm run dev
|
||||
|
||||
# 2. Démarrer le serveur MCP SSE (terminal 2)
|
||||
cd mcp-server
|
||||
.\start-sse.ps1
|
||||
|
||||
# 3. Configurer N8N avec: http://YOUR_IP:3001/sse
|
||||
# 4. Importer et activer les workflows N8N
|
||||
# 5. Utiliser les outils MCP dans vos workflows!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version:** 2.0.0
|
||||
**Dernière mise à jour:** 18 janvier 2026
|
||||
**Status:** ✅ Production Ready
|
||||
123
mcp-server/import-workflows.ps1
Normal file
123
mcp-server/import-workflows.ps1
Normal file
@@ -0,0 +1,123 @@
|
||||
# Import-Workflows.ps1
|
||||
# Script pour importer tous les workflows N8N automatiquement
|
||||
|
||||
param(
|
||||
[string]$n8nUrl = "http://localhost:5678",
|
||||
[string]$apiKey = ""
|
||||
)
|
||||
|
||||
Write-Host "🚀 Importation des Workflows N8N pour Keep Notes MCP" -ForegroundColor Cyan
|
||||
Write-Host "=================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Vérifier si N8N est accessible
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "$n8nUrl/rest/workflows" -UseBasicParsing
|
||||
Write-Host "✅ N8N est accessible à $n8nUrl" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "❌ Erreur: Impossible de connecter à N8N à $n8nUrl" -ForegroundColor Red
|
||||
Write-Host " Vérifiez que N8N est en cours d'exécution" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# Liste des workflows à importer
|
||||
$workflows = @(
|
||||
@{
|
||||
file = "n8n-workflow-create-note.json"
|
||||
name = "Create Note with Classification"
|
||||
},
|
||||
@{
|
||||
file = "n8n-workflow-search-summary.json"
|
||||
name = "Search & Summary"
|
||||
},
|
||||
@{
|
||||
file = "n8n-workflow-notebook-management.json"
|
||||
name = "Notebook Manager"
|
||||
},
|
||||
@{
|
||||
file = "n8n-workflow-reminder-notifications.json"
|
||||
name = "Reminder Notifications"
|
||||
},
|
||||
@{
|
||||
file = "n8n-workflow-label-management.json"
|
||||
name = "Label Manager"
|
||||
},
|
||||
@{
|
||||
file = "n8n-workflow-email-integration.json"
|
||||
name = "Email to Note"
|
||||
}
|
||||
)
|
||||
|
||||
$imported = 0
|
||||
$failed = 0
|
||||
|
||||
foreach ($workflow in $workflows) {
|
||||
$filePath = $workflow.file
|
||||
$name = $workflow.name
|
||||
|
||||
Write-Host "📥 Importation: $name" -ForegroundColor Yellow
|
||||
|
||||
if (-not (Test-Path $filePath)) {
|
||||
Write-Host " ❌ Fichier non trouvé: $filePath" -ForegroundColor Red
|
||||
$failed++
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
# Lire le fichier JSON
|
||||
$workflowJson = Get-Content $filePath -Raw
|
||||
|
||||
# Préparer les en-têtes
|
||||
$headers = @{
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
if ($apiKey) {
|
||||
$headers["Authorization"] = "Bearer $apiKey"
|
||||
}
|
||||
|
||||
# Envoyer à N8N
|
||||
$response = Invoke-RestMethod `
|
||||
-Uri "$n8nUrl/rest/workflows/import" `
|
||||
-Method POST `
|
||||
-Body $workflowJson `
|
||||
-Headers $headers `
|
||||
-ErrorAction Stop
|
||||
|
||||
Write-Host " ✅ Importé avec succès (ID: $($response.id))" -ForegroundColor Green
|
||||
$imported++
|
||||
} catch {
|
||||
Write-Host " ❌ Erreur lors de l'import: $_" -ForegroundColor Red
|
||||
$failed++
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Résumé
|
||||
Write-Host "=================================================" -ForegroundColor Cyan
|
||||
Write-Host "📊 Résumé de l'importation" -ForegroundColor Cyan
|
||||
Write-Host "=================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "✅ Importés avec succès: $imported" -ForegroundColor Green
|
||||
Write-Host "❌ Échecs: $failed" -ForegroundColor Red
|
||||
Write-Host "📝 Total: $($workflows.Count)" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
if ($imported -gt 0) {
|
||||
Write-Host "🎉 Importation terminée!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "📌 Prochaines étapes:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Ouvrez N8N dans votre navigateur" -ForegroundColor White
|
||||
Write-Host " 2. Configurez les variables d'environnement (Settings → Variables)" -ForegroundColor White
|
||||
Write-Host " 3. Configurez les connexions (Slack, Email, OpenAI)" -ForegroundColor White
|
||||
Write-Host " 4. Activez les workflows (bouton play)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "📚 Documentation: N8N-SETUP.md" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host "⚠️ Aucun workflow n'a pu être importé" -ForegroundColor Yellow
|
||||
Write-Host "Consultez la documentation N8N-SETUP.md pour plus d'informations" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,11 +14,11 @@ import { dirname, join } from 'path';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Initialize Prisma Client
|
||||
// Initialize Prisma Client with correct database path
|
||||
const prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: `file:${join(__dirname, '../keep-notes/prisma/dev.db')}`
|
||||
url: 'file:D:/dev_new_pc/Keep/keep-notes/prisma/dev.db'
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -30,14 +30,23 @@ function parseNote(dbNote) {
|
||||
checkItems: dbNote.checkItems ? JSON.parse(dbNote.checkItems) : null,
|
||||
labels: dbNote.labels ? JSON.parse(dbNote.labels) : null,
|
||||
images: dbNote.images ? JSON.parse(dbNote.images) : null,
|
||||
links: dbNote.links ? JSON.parse(dbNote.links) : null,
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to parse Notebook fields
|
||||
function parseNotebook(dbNotebook) {
|
||||
return {
|
||||
...dbNotebook,
|
||||
labels: dbNotebook.labels || [],
|
||||
};
|
||||
}
|
||||
|
||||
// Create MCP server
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'memento-mcp-server',
|
||||
version: '1.0.0',
|
||||
name: 'keep-notes-mcp-server',
|
||||
version: '2.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
@@ -50,9 +59,10 @@ const server = new Server(
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [
|
||||
// Note Tools
|
||||
{
|
||||
name: 'create_note',
|
||||
description: 'Create a new note in Memento',
|
||||
description: 'Create a new note in Keep Notes',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -108,13 +118,50 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
description: 'Note images as base64 encoded strings',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
links: {
|
||||
type: 'array',
|
||||
description: 'Note links',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
reminder: {
|
||||
type: 'string',
|
||||
description: 'Reminder date/time (ISO 8601 format)',
|
||||
},
|
||||
isReminderDone: {
|
||||
type: 'boolean',
|
||||
description: 'Mark reminder as done',
|
||||
default: false,
|
||||
},
|
||||
reminderRecurrence: {
|
||||
type: 'string',
|
||||
description: 'Reminder recurrence (daily, weekly, monthly, yearly)',
|
||||
},
|
||||
reminderLocation: {
|
||||
type: 'string',
|
||||
description: 'Reminder location',
|
||||
},
|
||||
isMarkdown: {
|
||||
type: 'boolean',
|
||||
description: 'Enable markdown support',
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: 'string',
|
||||
enum: ['small', 'medium', 'large'],
|
||||
description: 'Note size',
|
||||
default: 'small',
|
||||
},
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID to associate the note with',
|
||||
},
|
||||
},
|
||||
required: ['content'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_notes',
|
||||
description: 'Get all notes from Memento',
|
||||
description: 'Get all notes from Keep Notes',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -127,6 +174,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
type: 'string',
|
||||
description: 'Search query to filter notes',
|
||||
},
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Filter notes by notebook ID',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -166,6 +217,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
type: 'string',
|
||||
description: 'Note color',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['text', 'checklist'],
|
||||
description: 'Note type',
|
||||
},
|
||||
checkItems: {
|
||||
type: 'array',
|
||||
description: 'Checklist items',
|
||||
@@ -196,6 +252,40 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
description: 'Note images as base64 encoded strings',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
links: {
|
||||
type: 'array',
|
||||
description: 'Note links',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
reminder: {
|
||||
type: 'string',
|
||||
description: 'Reminder date/time (ISO 8601 format)',
|
||||
},
|
||||
isReminderDone: {
|
||||
type: 'boolean',
|
||||
description: 'Mark reminder as done',
|
||||
},
|
||||
reminderRecurrence: {
|
||||
type: 'string',
|
||||
description: 'Reminder recurrence',
|
||||
},
|
||||
reminderLocation: {
|
||||
type: 'string',
|
||||
description: 'Reminder location',
|
||||
},
|
||||
isMarkdown: {
|
||||
type: 'boolean',
|
||||
description: 'Enable markdown support',
|
||||
},
|
||||
size: {
|
||||
type: 'string',
|
||||
enum: ['small', 'medium', 'large'],
|
||||
description: 'Note size',
|
||||
},
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID to move the note to',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
@@ -224,6 +314,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
type: 'string',
|
||||
description: 'Search query',
|
||||
},
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Filter search by notebook ID',
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
@@ -264,6 +358,173 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
|
||||
// Notebook Tools
|
||||
{
|
||||
name: 'create_notebook',
|
||||
description: 'Create a new notebook',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Notebook name',
|
||||
},
|
||||
icon: {
|
||||
type: 'string',
|
||||
description: 'Notebook icon (emoji)',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Notebook color (hex code)',
|
||||
},
|
||||
order: {
|
||||
type: 'number',
|
||||
description: 'Notebook order',
|
||||
},
|
||||
},
|
||||
required: ['name'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_notebooks',
|
||||
description: 'Get all notebooks',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_notebook',
|
||||
description: 'Get a specific notebook by ID',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'update_notebook',
|
||||
description: 'Update an existing notebook',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Notebook name',
|
||||
},
|
||||
icon: {
|
||||
type: 'string',
|
||||
description: 'Notebook icon',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Notebook color',
|
||||
},
|
||||
order: {
|
||||
type: 'number',
|
||||
description: 'Notebook order',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'delete_notebook',
|
||||
description: 'Delete a notebook by ID',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
|
||||
// Label Tools
|
||||
{
|
||||
name: 'create_label',
|
||||
description: 'Create a new label',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Label name',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color (red, orange, yellow, green, teal, blue, purple, pink, gray)',
|
||||
},
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Notebook ID to associate the label with',
|
||||
},
|
||||
},
|
||||
required: ['name', 'notebookId'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_labels_detailed',
|
||||
description: 'Get all labels with details',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
notebookId: {
|
||||
type: 'string',
|
||||
description: 'Filter labels by notebook ID',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'update_label',
|
||||
description: 'Update an existing label',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Label ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Label name',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'delete_label',
|
||||
description: 'Delete a label by ID',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Label ID',
|
||||
},
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
@@ -273,6 +534,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
try {
|
||||
// === NOTE TOOLS ===
|
||||
switch (name) {
|
||||
case 'create_note': {
|
||||
const note = await prisma.note.create({
|
||||
@@ -286,6 +548,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
isPinned: args.isPinned || false,
|
||||
isArchived: args.isArchived || false,
|
||||
images: args.images ? JSON.stringify(args.images) : null,
|
||||
links: args.links ? JSON.stringify(args.links) : null,
|
||||
reminder: args.reminder ? new Date(args.reminder) : null,
|
||||
isReminderDone: args.isReminderDone || false,
|
||||
reminderRecurrence: args.reminderRecurrence || null,
|
||||
reminderLocation: args.reminderLocation || null,
|
||||
isMarkdown: args.isMarkdown || false,
|
||||
size: args.size || 'small',
|
||||
notebookId: args.notebookId || null,
|
||||
},
|
||||
});
|
||||
return {
|
||||
@@ -309,6 +579,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
{ content: { contains: args.search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
if (args.notebookId) {
|
||||
where.notebookId = args.notebookId;
|
||||
}
|
||||
|
||||
const notes = await prisma.note.findMany({
|
||||
where,
|
||||
@@ -361,6 +634,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if ('images' in args) {
|
||||
updateData.images = args.images ? JSON.stringify(args.images) : null;
|
||||
}
|
||||
if ('links' in args) {
|
||||
updateData.links = args.links ? JSON.stringify(args.links) : null;
|
||||
}
|
||||
if ('reminder' in args) {
|
||||
updateData.reminder = args.reminder ? new Date(args.reminder) : null;
|
||||
}
|
||||
updateData.updatedAt = new Date();
|
||||
|
||||
const note = await prisma.note.update({
|
||||
@@ -393,14 +672,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
}
|
||||
|
||||
case 'search_notes': {
|
||||
const where = {
|
||||
isArchived: false,
|
||||
OR: [
|
||||
{ title: { contains: args.query, mode: 'insensitive' } },
|
||||
{ content: { contains: args.query, mode: 'insensitive' } },
|
||||
],
|
||||
};
|
||||
if (args.notebookId) {
|
||||
where.notebookId = args.notebookId;
|
||||
}
|
||||
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
isArchived: false,
|
||||
OR: [
|
||||
{ title: { contains: args.query, mode: 'insensitive' } },
|
||||
{ content: { contains: args.query, mode: 'insensitive' } },
|
||||
],
|
||||
},
|
||||
where,
|
||||
orderBy: [
|
||||
{ isPinned: 'desc' },
|
||||
{ updatedAt: 'desc' },
|
||||
@@ -478,6 +762,238 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
};
|
||||
}
|
||||
|
||||
// === NOTEBOOK TOOLS ===
|
||||
case 'create_notebook': {
|
||||
// Get the highest order value
|
||||
const highestOrder = await prisma.notebook.findFirst({
|
||||
orderBy: { order: 'desc' },
|
||||
select: { order: true }
|
||||
});
|
||||
|
||||
const nextOrder = args.order !== undefined ? args.order : (highestOrder?.order ?? -1) + 1;
|
||||
|
||||
const notebook = await prisma.notebook.create({
|
||||
data: {
|
||||
name: args.name.trim(),
|
||||
icon: args.icon || '📁',
|
||||
color: args.color || '#3B82F6',
|
||||
order: nextOrder,
|
||||
},
|
||||
include: {
|
||||
labels: true,
|
||||
_count: {
|
||||
select: { notes: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
...notebook,
|
||||
notesCount: notebook._count.notes
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_notebooks': {
|
||||
const notebooks = await prisma.notebook.findMany({
|
||||
include: {
|
||||
labels: {
|
||||
orderBy: { name: 'asc' }
|
||||
},
|
||||
_count: {
|
||||
select: { notes: true }
|
||||
}
|
||||
},
|
||||
orderBy: { order: 'asc' }
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(
|
||||
notebooks.map(nb => ({
|
||||
...nb,
|
||||
notesCount: nb._count.notes
|
||||
})),
|
||||
null, 2
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_notebook': {
|
||||
const notebook = await prisma.notebook.findUnique({
|
||||
where: { id: args.id },
|
||||
include: {
|
||||
labels: true,
|
||||
notes: true,
|
||||
_count: {
|
||||
select: { notes: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!notebook) {
|
||||
throw new McpError(ErrorCode.InvalidRequest, 'Notebook not found');
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
...notebook,
|
||||
notes: notebook.notes.map(parseNote),
|
||||
notesCount: notebook._count.notes
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'update_notebook': {
|
||||
const updateData = { ...args };
|
||||
delete updateData.id;
|
||||
|
||||
const notebook = await prisma.notebook.update({
|
||||
where: { id: args.id },
|
||||
data: updateData,
|
||||
include: {
|
||||
labels: true,
|
||||
_count: {
|
||||
select: { notes: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
...notebook,
|
||||
notesCount: notebook._count.notes
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'delete_notebook': {
|
||||
await prisma.notebook.delete({
|
||||
where: { id: args.id },
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ success: true, message: 'Notebook deleted' }),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// === LABEL TOOLS ===
|
||||
case 'create_label': {
|
||||
const COLORS = ['red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'pink', 'gray'];
|
||||
|
||||
// Check if label already exists in this notebook
|
||||
const existing = await prisma.label.findFirst({
|
||||
where: {
|
||||
name: args.name.trim(),
|
||||
notebookId: args.notebookId
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new McpError(ErrorCode.InvalidRequest, 'Label already exists in this notebook');
|
||||
}
|
||||
|
||||
const label = await prisma.label.create({
|
||||
data: {
|
||||
name: args.name.trim(),
|
||||
color: args.color || COLORS[Math.floor(Math.random() * COLORS.length)],
|
||||
notebookId: args.notebookId,
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(label, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_labels_detailed': {
|
||||
const where = {};
|
||||
if (args.notebookId) {
|
||||
where.notebookId = args.notebookId;
|
||||
}
|
||||
|
||||
const labels = await prisma.label.findMany({
|
||||
where,
|
||||
include: {
|
||||
notebook: {
|
||||
select: { id: true, name: true }
|
||||
}
|
||||
},
|
||||
orderBy: { name: 'asc' }
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(labels, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'update_label': {
|
||||
const updateData = { ...args };
|
||||
delete updateData.id;
|
||||
|
||||
const label = await prisma.label.update({
|
||||
where: { id: args.id },
|
||||
data: updateData,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(label, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'delete_label': {
|
||||
await prisma.label.delete({
|
||||
where: { id: args.id },
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ success: true, message: 'Label deleted' }),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
@@ -499,7 +1015,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('Memento MCP server running on stdio');
|
||||
console.error('Keep Notes MCP server running on stdio');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
||||
185
mcp-server/n8n-workflow-create-note.json
Normal file
185
mcp-server/n8n-workflow-create-note.json
Normal file
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"name": "Keep Notes - Create with Classification",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "mcp-trigger-1",
|
||||
"name": "MCP Server Trigger",
|
||||
"type": "n8n-nodes-langchain.mcptrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"webhookId": "keep-notes-create",
|
||||
"description": "MCP trigger to create notes with AI classification"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "note",
|
||||
"operation": "create",
|
||||
"title": "={{ $json.title }}",
|
||||
"content": "={{ $json.content }}",
|
||||
"color": "={{ $json.color || 'default' }}",
|
||||
"type": "={{ $json.type || 'text' }}",
|
||||
"labels": "={{ $json.labels }}",
|
||||
"isPinned": "={{ $json.isPinned || false }}",
|
||||
"isArchived": "={{ $json.isArchived || false }}",
|
||||
"notebookId": "={{ $json.notebookId }}"
|
||||
},
|
||||
"id": "keep-notes-1",
|
||||
"name": "Keep Notes - Create Note",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [500, 300],
|
||||
"url": "http://localhost:3000/api/notes",
|
||||
"method": "POST",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": "openai/gpt-4",
|
||||
"prompt": "Analyze this note and suggest:\n1. A concise title (if not provided)\n2. Appropriate labels from: {{ $json.availableLabels }}\n3. Best notebook ID (if available)\n\nNote content: {{ $json.content }}\n\nReturn JSON format:\n{\n \"title\": \"suggested title\",\n \"labels\": [\"label1\", \"label2\"],\n \"notebookId\": \"notebook-id\",\n \"category\": \"work/personal/idea\"\n}"
|
||||
},
|
||||
"id": "ai-classifier-1",
|
||||
"name": "AI Classifier",
|
||||
"type": "n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.1,
|
||||
"position": [750, 200],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "multiplex",
|
||||
"options": {}
|
||||
},
|
||||
"id": "merge-1",
|
||||
"name": "Merge Results",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 3,
|
||||
"position": [1000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "noteId",
|
||||
"value": "={{ $json.data.id }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"value": "={{ $json.data.title }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"value": "Note created successfully",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "format-1",
|
||||
"name": "Format Response",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}"
|
||||
},
|
||||
"id": "mcp-response-1",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1500, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"MCP Server Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Keep Notes - Create Note",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Keep Notes - Create Note": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Classifier",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Classifier": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-mcp",
|
||||
"name": "Keep Notes MCP"
|
||||
}
|
||||
],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
307
mcp-server/n8n-workflow-email-integration.json
Normal file
307
mcp-server/n8n-workflow-email-integration.json
Normal file
@@ -0,0 +1,307 @@
|
||||
{
|
||||
"name": "Keep Notes - Email to Note",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
},
|
||||
"filters": {
|
||||
"hasReadStatus": true,
|
||||
"readStatus": "unread"
|
||||
}
|
||||
},
|
||||
"id": "email-trigger",
|
||||
"name": "Email Trigger",
|
||||
"type": "n8n-nodes-base.emailTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [250, 300],
|
||||
"description": "Trigger when new email received"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Extract relevant information from email\nconst email = $input.item.json;\n\n// Get email subject and body\nconst subject = email.subject || 'Untitled Email';\nconst body = email.text || email.html || '';\nconst from = email.from?.value?.[0]?.address || 'unknown';\n\n// Create structured note data\nreturn {\n json: {\n title: `Email: ${subject}`,\n content: `From: ${from}\\n\\n${body}`,\n labels: ['email', 'inbox'],\n color: 'blue',\n type: 'text',\n isPinned: false,\n isArchived: false,\n metadata: {\n originalEmailId: email.messageId,\n from: from,\n date: email.date,\n attachments: email.attachments?.length || 0\n }\n }\n};"
|
||||
},
|
||||
"id": "extract-email-1",
|
||||
"name": "Extract Email Data",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": "openai/gpt-4",
|
||||
"prompt": "Analyze this email and:\n1. Extract the main topic/theme (2-3 words max)\n2. Identify if it's urgent (important meeting, deadline, etc.)\n3. Suggest appropriate labels from: work, personal, finance, shopping, meeting, task, archive\n\nEmail subject: {{ $json.title }}\nEmail content: {{ $json.content }}\n\nReturn JSON:\n{\n \"topic\": \"main topic\",\n \"isUrgent\": true/false,\n \"labels\": [\"label1\", \"label2\"],\n \"notebook\": \"work/personal/ideas\"\n}"
|
||||
},
|
||||
"id": "ai-classify-email",
|
||||
"name": "AI Classify Email",
|
||||
"type": "n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.1,
|
||||
"position": [750, 300],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "urgent-check",
|
||||
"leftValue": "={{ $json.isUrgent }}",
|
||||
"rightValue": "true",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "check-urgent",
|
||||
"name": "Check if Urgent",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "isPinned",
|
||||
"value": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "red",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"options": {}
|
||||
}
|
||||
},
|
||||
"id": "set-urgent-flags",
|
||||
"name": "Set Urgent Flags",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1250, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "isPinned",
|
||||
"value": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "default",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"options": {}
|
||||
}
|
||||
},
|
||||
"id": "set-normal-flags",
|
||||
"name": "Set Normal Flags",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1250, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "multiplex",
|
||||
"options": {}
|
||||
},
|
||||
"id": "merge-email-data",
|
||||
"name": "Merge Email Data",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 3,
|
||||
"position": [1500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "note",
|
||||
"operation": "create",
|
||||
"url": "http://localhost:3000/api/notes",
|
||||
"method": "POST",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "title",
|
||||
"value": "={{ $json.title }}"
|
||||
},
|
||||
{
|
||||
"name": "content",
|
||||
"value": "={{ $json.content }}"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "={{ $json.color }}"
|
||||
},
|
||||
{
|
||||
"name": "isPinned",
|
||||
"value": "={{ $json.isPinned }}"
|
||||
},
|
||||
{
|
||||
"name": "labels",
|
||||
"value": "={{ $json.labels }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "create-note-from-email",
|
||||
"name": "Create Note in Keep Notes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1750, 300],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"channel": "keep-notes-sync",
|
||||
"text": "📧 New email saved to Keep Notes:\n\n**{{ $json.title }}**\n\nLabels: {{ $json.labels.join(', ') }}\nColor: {{ $json.color }}\n\nView: http://localhost:3000"
|
||||
},
|
||||
"id": "notify-slack",
|
||||
"name": "Notify Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.1,
|
||||
"position": [2000, 300],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Email Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Email Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Email Data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Classify Email",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Classify Email": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check if Urgent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check if Urgent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set Urgent Flags",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Set Normal Flags",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Urgent Flags": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Email Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Normal Flags": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Email Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Email Data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Note in Keep Notes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Note in Keep Notes": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Notify Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-integrations",
|
||||
"name": "Keep Notes Integrations"
|
||||
}
|
||||
],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
393
mcp-server/n8n-workflow-label-management.json
Normal file
393
mcp-server/n8n-workflow-label-management.json
Normal file
@@ -0,0 +1,393 @@
|
||||
{
|
||||
"name": "Keep Notes - Label Manager",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "mcp-trigger-4",
|
||||
"name": "MCP Server Trigger",
|
||||
"type": "n8n-nodes-langchain.mcptrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"webhookId": "keep-notes-labels",
|
||||
"description": "MCP trigger to manage labels"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"switches": {
|
||||
"values": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "create",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "create"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "list",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "list"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "update",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "update"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "delete",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "delete"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "suggest",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "suggest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "switch-labels",
|
||||
"name": "Action Switch",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 3.1,
|
||||
"position": [500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "label",
|
||||
"operation": "create",
|
||||
"url": "http://localhost:3000/api/labels",
|
||||
"method": "POST",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name }}"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "={{ $json.color || 'blue' }}"
|
||||
},
|
||||
{
|
||||
"name": "notebookId",
|
||||
"value": "={{ $json.notebookId }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "create-label",
|
||||
"name": "Create Label",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 150],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://localhost:3000/api/labels",
|
||||
"method": "GET",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"qs": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "notebookId",
|
||||
"value": "={{ $json.notebookId }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "list-labels",
|
||||
"name": "List Labels",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "label",
|
||||
"operation": "update",
|
||||
"url": "http://localhost:3000/api/labels/{{ $json.id }}",
|
||||
"method": "PUT",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name }}"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "={{ $json.color }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "update-label",
|
||||
"name": "Update Label",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 450],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "label",
|
||||
"operation": "delete",
|
||||
"url": "http://localhost:3000/api/labels/{{ $json.id }}",
|
||||
"method": "DELETE"
|
||||
},
|
||||
"id": "delete-label",
|
||||
"name": "Delete Label",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 600]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": "openai/gpt-4",
|
||||
"prompt": "Suggest 3-5 appropriate labels for this note based on its content. Return only the label names in a JSON array.\n\nNote title: {{ $json.title }}\nNote content: {{ $json.content }}\n\nExample format:\n[\"work\", \"project\", \"important\"]"
|
||||
},
|
||||
"id": "ai-suggest-labels",
|
||||
"name": "AI Suggest Labels",
|
||||
"type": "n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.1,
|
||||
"position": [750, 750]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "action",
|
||||
"value": "={{ $json.action }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"value": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"value": "={{ $json }}",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "format-labels",
|
||||
"name": "Format Response",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}"
|
||||
},
|
||||
"id": "mcp-response-labels",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1250, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"MCP Server Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Action Switch",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Action Switch": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Label",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "List Labels",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Update Label",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Delete Label",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "AI Suggest Labels",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Label": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"List Labels": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update Label": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Delete Label": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Suggest Labels": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-mcp",
|
||||
"name": "Keep Notes MCP"
|
||||
}
|
||||
],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
347
mcp-server/n8n-workflow-notebook-management.json
Normal file
347
mcp-server/n8n-workflow-notebook-management.json
Normal file
@@ -0,0 +1,347 @@
|
||||
{
|
||||
"name": "Keep Notes - Notebook Manager",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "mcp-trigger-3",
|
||||
"name": "MCP Server Trigger",
|
||||
"type": "n8n-nodes-langchain.mcptrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"webhookId": "keep-notes-notebook",
|
||||
"description": "MCP trigger to manage notebooks"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"switches": {
|
||||
"values": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "create",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "create"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "list",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "list"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "update",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "update"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "delete",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": "delete"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "switch-1",
|
||||
"name": "Action Switch",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 3.1,
|
||||
"position": [500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "notebook",
|
||||
"operation": "create",
|
||||
"url": "http://localhost:3000/api/notebooks",
|
||||
"method": "POST",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name }}"
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"value": "={{ $json.icon || '📁' }}"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "={{ $json.color || '#3B82F6' }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "create-notebook",
|
||||
"name": "Create Notebook",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 150],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://localhost:3000/api/notebooks",
|
||||
"method": "GET",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "list-notebooks",
|
||||
"name": "List Notebooks",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 300],
|
||||
"sendHeaders": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "notebook",
|
||||
"operation": "update",
|
||||
"url": "http://localhost:3000/api/notebooks/{{ $json.id }}",
|
||||
"method": "PUT",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name }}"
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"value": "={{ $json.icon }}"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"value": "={{ $json.color }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "update-notebook",
|
||||
"name": "Update Notebook",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 450],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "notebook",
|
||||
"operation": "delete",
|
||||
"url": "http://localhost:3000/api/notebooks/{{ $json.id }}",
|
||||
"method": "DELETE"
|
||||
},
|
||||
"id": "delete-notebook",
|
||||
"name": "Delete Notebook",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [750, 600],
|
||||
"sendHeaders": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "action",
|
||||
"value": "={{ $json.action }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"value": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"value": "={{ $json }}",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "format-3",
|
||||
"name": "Format Response",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}"
|
||||
},
|
||||
"id": "mcp-response-3",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1250, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"MCP Server Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Action Switch",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Action Switch": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Notebook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "List Notebooks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Update Notebook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Delete Notebook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Notebook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"List Notebooks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update Notebook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Delete Notebook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-mcp",
|
||||
"name": "Keep Notes MCP"
|
||||
}
|
||||
],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
350
mcp-server/n8n-workflow-reminder-notifications.json
Normal file
350
mcp-server/n8n-workflow-reminder-notifications.json
Normal file
@@ -0,0 +1,350 @@
|
||||
{
|
||||
"name": "Keep Notes - Reminder Notifications",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "cronExpression",
|
||||
"expression": "0 */30 * * * *"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "schedule-1",
|
||||
"name": "Schedule (Every 30 min)",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [250, 300],
|
||||
"description": "Check for reminders every 30 minutes"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://localhost:3000/api/notes",
|
||||
"method": "GET",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "get-all-notes-1",
|
||||
"name": "Get All Notes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-1",
|
||||
"leftValue": "={{ $json.reminder }}",
|
||||
"rightValue": "true",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "isNotEmpty"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "condition-2",
|
||||
"leftValue": "={{ $json.isReminderDone }}",
|
||||
"rightValue": "false",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "equals"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "condition-3",
|
||||
"leftValue": "={{ $json.reminder }}",
|
||||
"rightValue": "={{ $now }}",
|
||||
"operator": {
|
||||
"type": "date",
|
||||
"operation": "before"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "filter-reminders-1",
|
||||
"name": "Filter Active Reminders",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [750, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"options": {}
|
||||
},
|
||||
"id": "split-1",
|
||||
"name": "Split Reminders",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"typeVersion": 1,
|
||||
"position": [1000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"channel": "={{ $json.notificationChannel || 'slack' }}",
|
||||
"text": "🔔 **Reminder:** {{ $json.title || 'Untitled Note' }}\n\n{{ $json.content }}\n\n📍 Location: {{ $json.reminderLocation || 'N/A' }}\n🔗 View in Keep Notes: http://localhost:3000\n\n⏰ Reminder Time: {{ $json.reminder }}",
|
||||
"otherOptions": {}
|
||||
},
|
||||
"id": "send-notification-1",
|
||||
"name": "Send Notification",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1250, 300],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"channel": "={{ $json.notificationEmail }}",
|
||||
"subject": "🔔 Reminder: {{ $json.title || 'Untitled Note' }}",
|
||||
"emailType": "html",
|
||||
"message": "<h2>🔔 Reminder</h2>\n\n<h3>{{ $json.title || 'Untitled Note' }}</h3>\n\n<p>{{ $json.content }}</p>\n\n<strong>Location:</strong> {{ $json.reminderLocation || 'N/A' }}<br>\n<strong>Reminder Time:</strong> {{ $json.reminder }}<br>\n<br>\n<a href=\"http://localhost:3000\">View in Keep Notes</a>",
|
||||
"options": {}
|
||||
},
|
||||
"id": "send-email-1",
|
||||
"name": "Send Email",
|
||||
"type": "n8n-nodes-base.emailSend",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1250, 450],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://localhost:3000/api/notes/{{ $json.id }}",
|
||||
"method": "PUT",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isReminderDone",
|
||||
"value": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "mark-done-1",
|
||||
"name": "Mark Reminder as Done",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1500, 300],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-4",
|
||||
"leftValue": "={{ $json.reminderRecurrence }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "isNotEmpty"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "check-recurrence-1",
|
||||
"name": "Check Recurrence",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1750, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Calculate next reminder date based on recurrence\nconst { reminderRecurrence, reminder } = $input.item.json;\nconst reminderDate = new Date(reminder);\n\nlet nextReminder;\nswitch (reminderRecurrence) {\n case 'daily':\n nextReminder = new Date(reminderDate.setDate(reminderDate.getDate() + 1));\n break;\n case 'weekly':\n nextReminder = new Date(reminderDate.setDate(reminderDate.getDate() + 7));\n break;\n case 'monthly':\n nextReminder = new Date(reminderDate.setMonth(reminderDate.getMonth() + 1));\n break;\n case 'yearly':\n nextReminder = new Date(reminderDate.setFullYear(reminderDate.getFullYear() + 1));\n break;\n default:\n nextReminder = reminderDate;\n}\n\nreturn {\n json: {\n ...$input.item.json,\n nextReminder: nextReminder.toISOString(),\n isReminderDone: false\n }\n};"
|
||||
},
|
||||
"id": "calculate-next-1",
|
||||
"name": "Calculate Next Reminder",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2000, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://localhost:3000/api/notes/{{ $json.id }}",
|
||||
"method": "PUT",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "reminder",
|
||||
"value": "={{ $json.nextReminder }}"
|
||||
},
|
||||
{
|
||||
"name": "isReminderDone",
|
||||
"value": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "update-recurrence-1",
|
||||
"name": "Update Recurring Reminder",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [2250, 300],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Schedule (Every 30 min)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get All Notes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get All Notes": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Filter Active Reminders",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Filter Active Reminders": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split Reminders",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Split Reminders": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Send Email",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Mark Reminder as Done",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Email": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Mark Reminder as Done",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Mark Reminder as Done": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Recurrence",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Recurrence": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Calculate Next Reminder",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Calculate Next Reminder": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Update Recurring Reminder",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-mcp",
|
||||
"name": "Keep Notes MCP"
|
||||
}
|
||||
],
|
||||
"triggerCount": 0,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
247
mcp-server/n8n-workflow-search-summary.json
Normal file
247
mcp-server/n8n-workflow-search-summary.json
Normal file
@@ -0,0 +1,247 @@
|
||||
{
|
||||
"name": "Keep Notes - Search & Summary",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "mcp-trigger-2",
|
||||
"name": "MCP Server Trigger",
|
||||
"type": "n8n-nodes-langchain.mcptrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"webhookId": "keep-notes-search",
|
||||
"description": "MCP trigger to search and summarize notes"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "note",
|
||||
"operation": "get_all",
|
||||
"url": "http://localhost:3000/api/notes",
|
||||
"method": "GET"
|
||||
},
|
||||
"id": "get-notes-1",
|
||||
"name": "Get All Notes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [500, 300],
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-1",
|
||||
"leftValue": "={{ $json.title }}",
|
||||
"rightValue": "={{ $('MCP Server Trigger').item.json.searchQuery }}",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "contains"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "condition-2",
|
||||
"leftValue": "={{ $json.content }}",
|
||||
"rightValue": "={{ $('MCP Server Trigger').item.json.searchQuery }}",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "contains"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "or"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "filter-1",
|
||||
"name": "Filter by Query",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [750, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregate",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldName": "id",
|
||||
"aggregateOperation": "count"
|
||||
},
|
||||
{
|
||||
"fieldName": "createdAt",
|
||||
"aggregateOperation": "max"
|
||||
},
|
||||
{
|
||||
"fieldName": "createdAt",
|
||||
"aggregateOperation": "min"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "aggregate-1",
|
||||
"name": "Aggregate Stats",
|
||||
"type": "n8n-nodes-base.aggregate",
|
||||
"typeVersion": 1,
|
||||
"position": [1000, 300],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": "openai/gpt-4",
|
||||
"prompt": "You are a helpful note summarizer. Analyze these notes and provide:\n\n1. **Summary**: A concise summary of all notes (2-3 sentences)\n2. **Key Themes**: List main topics found (bullet points)\n3. **Action Items**: Extract any action items or tasks\n4. **Related Notes**: Group notes by theme\n\nNotes to analyze:\n{{ $json.notes }}\n\nProvide the result in JSON format:"
|
||||
},
|
||||
"id": "ai-summarizer-1",
|
||||
"name": "AI Summarizer",
|
||||
"type": "n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"name": "searchQuery",
|
||||
"value": "={{ $('MCP Server Trigger').item.json.searchQuery }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "totalNotes",
|
||||
"value": "={{ $json.data.length }}",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "filteredCount",
|
||||
"value": "={{ $('Filter by Query').item.json.length }}",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "summary",
|
||||
"value": "={{ $json }}",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"name": "notes",
|
||||
"value": "={{ $('Filter by Query').item.json }}",
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "format-2",
|
||||
"name": "Format Response",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [1500, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}"
|
||||
},
|
||||
"id": "mcp-response-2",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1750, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"MCP Server Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get All Notes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get All Notes": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Filter by Query",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Filter by Query": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Aggregate Stats",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Aggregate Stats": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Summarizer",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Summarizer": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [
|
||||
{
|
||||
"createdAt": "2026-01-18T00:00:00.000Z",
|
||||
"id": "keep-notes-mcp",
|
||||
"name": "Keep Notes MCP"
|
||||
}
|
||||
],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-01-18T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
166
mcp-server/node_modules/.prisma/client/edge.js
generated
vendored
166
mcp-server/node_modules/.prisma/client/edge.js
generated
vendored
File diff suppressed because one or more lines are too long
160
mcp-server/node_modules/.prisma/client/index-browser.js
generated
vendored
160
mcp-server/node_modules/.prisma/client/index-browser.js
generated
vendored
@@ -124,17 +124,160 @@ exports.Prisma.NoteScalarFieldEnum = {
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
color: 'color',
|
||||
isPinned: 'isPinned',
|
||||
isArchived: 'isArchived',
|
||||
type: 'type',
|
||||
checkItems: 'checkItems',
|
||||
labels: 'labels',
|
||||
images: 'images',
|
||||
isPinned: 'isPinned',
|
||||
isArchived: 'isArchived',
|
||||
links: 'links',
|
||||
reminder: 'reminder',
|
||||
isReminderDone: 'isReminderDone',
|
||||
reminderRecurrence: 'reminderRecurrence',
|
||||
reminderLocation: 'reminderLocation',
|
||||
isMarkdown: 'isMarkdown',
|
||||
size: 'size',
|
||||
embedding: 'embedding',
|
||||
sharedWith: 'sharedWith',
|
||||
userId: 'userId',
|
||||
order: 'order',
|
||||
notebookId: 'notebookId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
autoGenerated: 'autoGenerated',
|
||||
aiProvider: 'aiProvider',
|
||||
aiConfidence: 'aiConfidence',
|
||||
language: 'language',
|
||||
languageConfidence: 'languageConfidence',
|
||||
lastAiAnalysis: 'lastAiAnalysis'
|
||||
};
|
||||
|
||||
exports.Prisma.NotebookScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
icon: 'icon',
|
||||
color: 'color',
|
||||
order: 'order',
|
||||
userId: 'userId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LabelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
color: 'color',
|
||||
notebookId: 'notebookId',
|
||||
userId: 'userId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
email: 'email',
|
||||
emailVerified: 'emailVerified',
|
||||
password: 'password',
|
||||
role: 'role',
|
||||
image: 'image',
|
||||
theme: 'theme',
|
||||
resetToken: 'resetToken',
|
||||
resetTokenExpiry: 'resetTokenExpiry',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.AccountScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
type: 'type',
|
||||
provider: 'provider',
|
||||
providerAccountId: 'providerAccountId',
|
||||
refresh_token: 'refresh_token',
|
||||
access_token: 'access_token',
|
||||
expires_at: 'expires_at',
|
||||
token_type: 'token_type',
|
||||
scope: 'scope',
|
||||
id_token: 'id_token',
|
||||
session_state: 'session_state',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SessionScalarFieldEnum = {
|
||||
sessionToken: 'sessionToken',
|
||||
userId: 'userId',
|
||||
expires: 'expires',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.VerificationTokenScalarFieldEnum = {
|
||||
identifier: 'identifier',
|
||||
token: 'token',
|
||||
expires: 'expires'
|
||||
};
|
||||
|
||||
exports.Prisma.NoteShareScalarFieldEnum = {
|
||||
id: 'id',
|
||||
noteId: 'noteId',
|
||||
userId: 'userId',
|
||||
sharedBy: 'sharedBy',
|
||||
status: 'status',
|
||||
permission: 'permission',
|
||||
notifiedAt: 'notifiedAt',
|
||||
respondedAt: 'respondedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SystemConfigScalarFieldEnum = {
|
||||
key: 'key',
|
||||
value: 'value'
|
||||
};
|
||||
|
||||
exports.Prisma.AiFeedbackScalarFieldEnum = {
|
||||
id: 'id',
|
||||
noteId: 'noteId',
|
||||
userId: 'userId',
|
||||
feedbackType: 'feedbackType',
|
||||
feature: 'feature',
|
||||
originalContent: 'originalContent',
|
||||
correctedContent: 'correctedContent',
|
||||
metadata: 'metadata',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.MemoryEchoInsightScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
note1Id: 'note1Id',
|
||||
note2Id: 'note2Id',
|
||||
similarityScore: 'similarityScore',
|
||||
insight: 'insight',
|
||||
insightDate: 'insightDate',
|
||||
viewed: 'viewed',
|
||||
feedback: 'feedback',
|
||||
dismissed: 'dismissed'
|
||||
};
|
||||
|
||||
exports.Prisma.UserAISettingsScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
titleSuggestions: 'titleSuggestions',
|
||||
semanticSearch: 'semanticSearch',
|
||||
paragraphRefactor: 'paragraphRefactor',
|
||||
memoryEcho: 'memoryEcho',
|
||||
memoryEchoFrequency: 'memoryEchoFrequency',
|
||||
aiProvider: 'aiProvider',
|
||||
preferredLanguage: 'preferredLanguage',
|
||||
fontSize: 'fontSize',
|
||||
demoMode: 'demoMode',
|
||||
showRecentNotes: 'showRecentNotes',
|
||||
emailNotifications: 'emailNotifications',
|
||||
desktopNotifications: 'desktopNotifications',
|
||||
anonymousAnalytics: 'anonymousAnalytics'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
@@ -147,7 +290,18 @@ exports.Prisma.NullsOrder = {
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
Note: 'Note'
|
||||
Note: 'Note',
|
||||
Notebook: 'Notebook',
|
||||
Label: 'Label',
|
||||
User: 'User',
|
||||
Account: 'Account',
|
||||
Session: 'Session',
|
||||
VerificationToken: 'VerificationToken',
|
||||
NoteShare: 'NoteShare',
|
||||
SystemConfig: 'SystemConfig',
|
||||
AiFeedback: 'AiFeedback',
|
||||
MemoryEchoInsight: 'MemoryEchoInsight',
|
||||
UserAISettings: 'UserAISettings'
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
14303
mcp-server/node_modules/.prisma/client/index.d.ts
generated
vendored
14303
mcp-server/node_modules/.prisma/client/index.d.ts
generated
vendored
File diff suppressed because it is too large
Load Diff
166
mcp-server/node_modules/.prisma/client/index.js
generated
vendored
166
mcp-server/node_modules/.prisma/client/index.js
generated
vendored
File diff suppressed because one or more lines are too long
2
mcp-server/node_modules/.prisma/client/package.json
generated
vendored
2
mcp-server/node_modules/.prisma/client/package.json
generated
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "prisma-client-695acdec538e6176bd4e5288817cc61667217cda4bd3eacb6556ee22de6c4cd1",
|
||||
"name": "prisma-client-335178138a2302fddc6069fd0c4da8598be3b531edb6ec6288b57b847324be3b",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "index-browser.js",
|
||||
|
||||
171
mcp-server/node_modules/.prisma/client/schema.prisma
generated
vendored
171
mcp-server/node_modules/.prisma/client/schema.prisma
generated
vendored
@@ -9,17 +9,168 @@ datasource db {
|
||||
}
|
||||
|
||||
model Note {
|
||||
id String @id @default(cuid())
|
||||
title String?
|
||||
content String
|
||||
color String @default("default")
|
||||
isPinned Boolean @default(false)
|
||||
isArchived Boolean @default(false)
|
||||
type String @default("text")
|
||||
checkItems String?
|
||||
labels String?
|
||||
images String?
|
||||
links String?
|
||||
reminder DateTime?
|
||||
isReminderDone Boolean @default(false)
|
||||
reminderRecurrence String?
|
||||
reminderLocation String?
|
||||
isMarkdown Boolean @default(false)
|
||||
size String @default("small")
|
||||
embedding String?
|
||||
sharedWith String?
|
||||
userId String?
|
||||
order Int @default(0)
|
||||
notebookId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
autoGenerated Boolean?
|
||||
aiProvider String?
|
||||
aiConfidence Int?
|
||||
language String?
|
||||
languageConfidence Float?
|
||||
lastAiAnalysis DateTime?
|
||||
}
|
||||
|
||||
model Notebook {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
icon String?
|
||||
color String?
|
||||
order Int
|
||||
userId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Label {
|
||||
id String @id @default(cuid())
|
||||
title String?
|
||||
content String
|
||||
color String @default("default")
|
||||
type String @default("text")
|
||||
checkItems String?
|
||||
labels String?
|
||||
images String?
|
||||
isPinned Boolean @default(false)
|
||||
isArchived Boolean @default(false)
|
||||
order Int @default(0)
|
||||
name String
|
||||
color String @default("gray")
|
||||
notebookId String?
|
||||
userId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
password String?
|
||||
role String @default("USER")
|
||||
image String?
|
||||
theme String @default("light")
|
||||
resetToken String? @unique
|
||||
resetTokenExpiry DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Account {
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@id([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String
|
||||
expires DateTime
|
||||
|
||||
@@id([identifier, token])
|
||||
}
|
||||
|
||||
model NoteShare {
|
||||
id String @id @default(cuid())
|
||||
noteId String
|
||||
userId String
|
||||
sharedBy String
|
||||
status String @default("pending")
|
||||
permission String @default("view")
|
||||
notifiedAt DateTime?
|
||||
respondedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([noteId, userId])
|
||||
}
|
||||
|
||||
model SystemConfig {
|
||||
key String @id
|
||||
value String
|
||||
}
|
||||
|
||||
model AiFeedback {
|
||||
id String @id @default(cuid())
|
||||
noteId String
|
||||
userId String?
|
||||
feedbackType String
|
||||
feature String
|
||||
originalContent String
|
||||
correctedContent String?
|
||||
metadata String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model MemoryEchoInsight {
|
||||
id String @id @default(cuid())
|
||||
userId String?
|
||||
note1Id String
|
||||
note2Id String
|
||||
similarityScore Float
|
||||
insight String
|
||||
insightDate DateTime @default(now())
|
||||
viewed Boolean @default(false)
|
||||
feedback String?
|
||||
dismissed Boolean @default(false)
|
||||
|
||||
@@unique([userId, insightDate])
|
||||
}
|
||||
|
||||
model UserAISettings {
|
||||
userId String @id
|
||||
titleSuggestions Boolean @default(true)
|
||||
semanticSearch Boolean @default(true)
|
||||
paragraphRefactor Boolean @default(true)
|
||||
memoryEcho Boolean @default(true)
|
||||
memoryEchoFrequency String @default("daily")
|
||||
aiProvider String @default("auto")
|
||||
preferredLanguage String @default("auto")
|
||||
fontSize String @default("medium")
|
||||
demoMode Boolean @default(false)
|
||||
showRecentNotes Boolean @default(false)
|
||||
emailNotifications Boolean @default(false)
|
||||
desktopNotifications Boolean @default(false)
|
||||
anonymousAnalytics Boolean @default(false)
|
||||
}
|
||||
|
||||
160
mcp-server/node_modules/.prisma/client/wasm.js
generated
vendored
160
mcp-server/node_modules/.prisma/client/wasm.js
generated
vendored
@@ -124,17 +124,160 @@ exports.Prisma.NoteScalarFieldEnum = {
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
color: 'color',
|
||||
isPinned: 'isPinned',
|
||||
isArchived: 'isArchived',
|
||||
type: 'type',
|
||||
checkItems: 'checkItems',
|
||||
labels: 'labels',
|
||||
images: 'images',
|
||||
isPinned: 'isPinned',
|
||||
isArchived: 'isArchived',
|
||||
links: 'links',
|
||||
reminder: 'reminder',
|
||||
isReminderDone: 'isReminderDone',
|
||||
reminderRecurrence: 'reminderRecurrence',
|
||||
reminderLocation: 'reminderLocation',
|
||||
isMarkdown: 'isMarkdown',
|
||||
size: 'size',
|
||||
embedding: 'embedding',
|
||||
sharedWith: 'sharedWith',
|
||||
userId: 'userId',
|
||||
order: 'order',
|
||||
notebookId: 'notebookId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
autoGenerated: 'autoGenerated',
|
||||
aiProvider: 'aiProvider',
|
||||
aiConfidence: 'aiConfidence',
|
||||
language: 'language',
|
||||
languageConfidence: 'languageConfidence',
|
||||
lastAiAnalysis: 'lastAiAnalysis'
|
||||
};
|
||||
|
||||
exports.Prisma.NotebookScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
icon: 'icon',
|
||||
color: 'color',
|
||||
order: 'order',
|
||||
userId: 'userId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LabelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
color: 'color',
|
||||
notebookId: 'notebookId',
|
||||
userId: 'userId',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
email: 'email',
|
||||
emailVerified: 'emailVerified',
|
||||
password: 'password',
|
||||
role: 'role',
|
||||
image: 'image',
|
||||
theme: 'theme',
|
||||
resetToken: 'resetToken',
|
||||
resetTokenExpiry: 'resetTokenExpiry',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.AccountScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
type: 'type',
|
||||
provider: 'provider',
|
||||
providerAccountId: 'providerAccountId',
|
||||
refresh_token: 'refresh_token',
|
||||
access_token: 'access_token',
|
||||
expires_at: 'expires_at',
|
||||
token_type: 'token_type',
|
||||
scope: 'scope',
|
||||
id_token: 'id_token',
|
||||
session_state: 'session_state',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SessionScalarFieldEnum = {
|
||||
sessionToken: 'sessionToken',
|
||||
userId: 'userId',
|
||||
expires: 'expires',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.VerificationTokenScalarFieldEnum = {
|
||||
identifier: 'identifier',
|
||||
token: 'token',
|
||||
expires: 'expires'
|
||||
};
|
||||
|
||||
exports.Prisma.NoteShareScalarFieldEnum = {
|
||||
id: 'id',
|
||||
noteId: 'noteId',
|
||||
userId: 'userId',
|
||||
sharedBy: 'sharedBy',
|
||||
status: 'status',
|
||||
permission: 'permission',
|
||||
notifiedAt: 'notifiedAt',
|
||||
respondedAt: 'respondedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SystemConfigScalarFieldEnum = {
|
||||
key: 'key',
|
||||
value: 'value'
|
||||
};
|
||||
|
||||
exports.Prisma.AiFeedbackScalarFieldEnum = {
|
||||
id: 'id',
|
||||
noteId: 'noteId',
|
||||
userId: 'userId',
|
||||
feedbackType: 'feedbackType',
|
||||
feature: 'feature',
|
||||
originalContent: 'originalContent',
|
||||
correctedContent: 'correctedContent',
|
||||
metadata: 'metadata',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.MemoryEchoInsightScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
note1Id: 'note1Id',
|
||||
note2Id: 'note2Id',
|
||||
similarityScore: 'similarityScore',
|
||||
insight: 'insight',
|
||||
insightDate: 'insightDate',
|
||||
viewed: 'viewed',
|
||||
feedback: 'feedback',
|
||||
dismissed: 'dismissed'
|
||||
};
|
||||
|
||||
exports.Prisma.UserAISettingsScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
titleSuggestions: 'titleSuggestions',
|
||||
semanticSearch: 'semanticSearch',
|
||||
paragraphRefactor: 'paragraphRefactor',
|
||||
memoryEcho: 'memoryEcho',
|
||||
memoryEchoFrequency: 'memoryEchoFrequency',
|
||||
aiProvider: 'aiProvider',
|
||||
preferredLanguage: 'preferredLanguage',
|
||||
fontSize: 'fontSize',
|
||||
demoMode: 'demoMode',
|
||||
showRecentNotes: 'showRecentNotes',
|
||||
emailNotifications: 'emailNotifications',
|
||||
desktopNotifications: 'desktopNotifications',
|
||||
anonymousAnalytics: 'anonymousAnalytics'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
@@ -147,7 +290,18 @@ exports.Prisma.NullsOrder = {
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
Note: 'Note'
|
||||
Note: 'Note',
|
||||
Notebook: 'Notebook',
|
||||
Label: 'Label',
|
||||
User: 'User',
|
||||
Account: 'Account',
|
||||
Session: 'Session',
|
||||
VerificationToken: 'VerificationToken',
|
||||
NoteShare: 'NoteShare',
|
||||
SystemConfig: 'SystemConfig',
|
||||
AiFeedback: 'AiFeedback',
|
||||
MemoryEchoInsight: 'MemoryEchoInsight',
|
||||
UserAISettings: 'UserAISettings'
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,17 +9,168 @@ datasource db {
|
||||
}
|
||||
|
||||
model Note {
|
||||
id String @id @default(cuid())
|
||||
title String?
|
||||
content String
|
||||
color String @default("default")
|
||||
isPinned Boolean @default(false)
|
||||
isArchived Boolean @default(false)
|
||||
type String @default("text")
|
||||
checkItems String?
|
||||
labels String?
|
||||
images String?
|
||||
links String?
|
||||
reminder DateTime?
|
||||
isReminderDone Boolean @default(false)
|
||||
reminderRecurrence String?
|
||||
reminderLocation String?
|
||||
isMarkdown Boolean @default(false)
|
||||
size String @default("small")
|
||||
embedding String?
|
||||
sharedWith String?
|
||||
userId String?
|
||||
order Int @default(0)
|
||||
notebookId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
autoGenerated Boolean?
|
||||
aiProvider String?
|
||||
aiConfidence Int?
|
||||
language String?
|
||||
languageConfidence Float?
|
||||
lastAiAnalysis DateTime?
|
||||
}
|
||||
|
||||
model Notebook {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
icon String?
|
||||
color String?
|
||||
order Int
|
||||
userId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Label {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
color String @default("gray")
|
||||
notebookId String?
|
||||
userId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
password String?
|
||||
role String @default("USER")
|
||||
image String?
|
||||
theme String @default("light")
|
||||
resetToken String? @unique
|
||||
resetTokenExpiry DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Account {
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@id([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String
|
||||
expires DateTime
|
||||
|
||||
@@id([identifier, token])
|
||||
}
|
||||
|
||||
model NoteShare {
|
||||
id String @id @default(cuid())
|
||||
title String?
|
||||
content String
|
||||
color String @default("default")
|
||||
type String @default("text")
|
||||
checkItems String?
|
||||
labels String?
|
||||
images String?
|
||||
isPinned Boolean @default(false)
|
||||
isArchived Boolean @default(false)
|
||||
order Int @default(0)
|
||||
noteId String
|
||||
userId String
|
||||
sharedBy String
|
||||
status String @default("pending")
|
||||
permission String @default("view")
|
||||
notifiedAt DateTime?
|
||||
respondedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([noteId, userId])
|
||||
}
|
||||
|
||||
model SystemConfig {
|
||||
key String @id
|
||||
value String
|
||||
}
|
||||
|
||||
model AiFeedback {
|
||||
id String @id @default(cuid())
|
||||
noteId String
|
||||
userId String?
|
||||
feedbackType String
|
||||
feature String
|
||||
originalContent String
|
||||
correctedContent String?
|
||||
metadata String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model MemoryEchoInsight {
|
||||
id String @id @default(cuid())
|
||||
userId String?
|
||||
note1Id String
|
||||
note2Id String
|
||||
similarityScore Float
|
||||
insight String
|
||||
insightDate DateTime @default(now())
|
||||
viewed Boolean @default(false)
|
||||
feedback String?
|
||||
dismissed Boolean @default(false)
|
||||
|
||||
@@unique([userId, insightDate])
|
||||
}
|
||||
|
||||
model UserAISettings {
|
||||
userId String @id
|
||||
titleSuggestions Boolean @default(true)
|
||||
semanticSearch Boolean @default(true)
|
||||
paragraphRefactor Boolean @default(true)
|
||||
memoryEcho Boolean @default(true)
|
||||
memoryEchoFrequency String @default("daily")
|
||||
aiProvider String @default("auto")
|
||||
preferredLanguage String @default("auto")
|
||||
fontSize String @default("medium")
|
||||
demoMode Boolean @default(false)
|
||||
showRecentNotes Boolean @default(false)
|
||||
emailNotifications Boolean @default(false)
|
||||
desktopNotifications Boolean @default(false)
|
||||
anonymousAnalytics Boolean @default(false)
|
||||
}
|
||||
|
||||
15
mcp-server/start-mcp.bat
Normal file
15
mcp-server/start-mcp.bat
Normal file
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
REM Script pour démarrer le serveur MCP Keep Notes
|
||||
REM Mode: Stdio (par défaut)
|
||||
|
||||
echo.
|
||||
echo ==========================================
|
||||
echo 🚀 Keep Notes MCP Server
|
||||
echo ==========================================
|
||||
echo.
|
||||
echo Démarrage du serveur MCP en mode Stdio...
|
||||
echo Appuyez sur Ctrl+C pour arrêter
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0"
|
||||
node index.js
|
||||
43
mcp-server/start-mcp.ps1
Normal file
43
mcp-server/start-mcp.ps1
Normal file
@@ -0,0 +1,43 @@
|
||||
# Script pour démarrer le serveur MCP Keep Notes
|
||||
# Mode: Stdio (par défaut)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "==========================================" -ForegroundColor Cyan
|
||||
Write-Host " 🚀 Keep Notes MCP Server" -ForegroundColor Cyan
|
||||
Write-Host "==========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "📌 Mode: Stdio" -ForegroundColor Yellow
|
||||
Write-Host "📌 Chemin: $PSScriptRoot" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "⏳ Démarrage du serveur..." -ForegroundColor Green
|
||||
Write-Host "🛑 Appuyez sur Ctrl+C pour arrêter" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
|
||||
# Vérifier si Prisma est généré
|
||||
if (-not (Test-Path "node_modules\.prisma\client")) {
|
||||
Write-Host "⚠️ Client Prisma non trouvé, génération en cours..." -ForegroundColor Yellow
|
||||
npx prisma generate
|
||||
Write-Host "✅ Client Prisma généré" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Vérifier que Keep Notes est en cours d'exécution
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "http://localhost:3000" -UseBasicParsing -TimeoutSec 2
|
||||
Write-Host "✅ Keep Notes est en cours d'exécution" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "⚠️ Attention: Keep Notes n'est pas accessible sur localhost:3000" -ForegroundColor Yellow
|
||||
Write-Host " Le serveur MCP risque de ne pas fonctionner correctement" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
$continue = Read-Host "Voulez-vous continuer quand même? (O/N)"
|
||||
if ($continue -ne "O" -and $continue -ne "o") {
|
||||
Write-Host "❌ Annulation" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "🚀 Démarrage du serveur MCP..." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# Démarrer le serveur
|
||||
node index.js
|
||||
@@ -1,48 +1,67 @@
|
||||
# Script to start MCP SSE Server for Memento
|
||||
# Script pour démarrer le serveur MCP Keep Notes en mode SSE
|
||||
# Mode: Server-Sent Events (HTTP)
|
||||
|
||||
Write-Host "`n╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ Starting Memento MCP SSE Server ║" -ForegroundColor Cyan
|
||||
Write-Host "╚═══════════════════════════════════════════════════════════╝`n" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "==========================================" -ForegroundColor Cyan
|
||||
Write-Host " 🚀 Keep Notes MCP Server (SSE)" -ForegroundColor Cyan
|
||||
Write-Host "==========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "📌 Mode: SSE (HTTP sur port 3001)" -ForegroundColor Yellow
|
||||
Write-Host "📌 Chemin: $PSScriptRoot" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
# Check if running from correct directory
|
||||
$currentDir = Get-Location
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
|
||||
if ($scriptDir) {
|
||||
Push-Location $scriptDir
|
||||
# Vérifier si Prisma est généré
|
||||
if (-not (Test-Path "node_modules\.prisma\client")) {
|
||||
Write-Host "⚠️ Client Prisma non trouvé, génération en cours..." -ForegroundColor Yellow
|
||||
npx prisma generate
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "✅ Client Prisma généré" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "❌ Erreur lors de la génération du client Prisma" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure we have node_modules
|
||||
if (-not (Test-Path "node_modules")) {
|
||||
Write-Host "📦 Installing dependencies..." -ForegroundColor Yellow
|
||||
npm install
|
||||
# Vérifier que Keep Notes est en cours d'exécution
|
||||
Write-Host "🔍 Vérification de Keep Notes..." -ForegroundColor Cyan
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "http://localhost:3000" -UseBasicParsing -TimeoutSec 5
|
||||
Write-Host "✅ Keep Notes est en cours d'exécution (port 3000)" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "⚠️ Attention: Keep Notes n'est pas accessible sur localhost:3000" -ForegroundColor Yellow
|
||||
Write-Host " Le serveur MCP SSE risque de ne pas fonctionner correctement" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
$continue = Read-Host "Voulez-vous continuer quand même? (O/N)"
|
||||
if ($continue -ne "O" -and $continue -ne "o") {
|
||||
Write-Host "❌ Annulation" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check if Prisma Client exists in parent keep-notes
|
||||
$prismaClientPath = "..\keep-notes\node_modules\.prisma\client"
|
||||
if (Test-Path $prismaClientPath) {
|
||||
Write-Host "✅ Prisma Client found in keep-notes" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "⚠️ Prisma Client not found. Run: cd ..\keep-notes && npx prisma generate" -ForegroundColor Yellow
|
||||
Write-Host " Then restart this script." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "⏳ Démarrage du serveur MCP SSE..." -ForegroundColor Green
|
||||
Write-Host "🛑 Appuyez sur Ctrl+C pour arrêter" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host "🌐 Serveur accessible sur:" -ForegroundColor Cyan
|
||||
Write-Host " - Local: http://localhost:3001" -ForegroundColor White
|
||||
Write-Host " - Network: Trouvez votre IP avec 'ipconfig'" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "📋 Endpoints:" -ForegroundColor Cyan
|
||||
Write-Host " - Health: GET http://localhost:3001/" -ForegroundColor White
|
||||
Write-Host " - SSE: GET http://localhost:3001/sse" -ForegroundColor White
|
||||
Write-Host " - Message: POST http://localhost:3001/message" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
# Démarrer le serveur
|
||||
try {
|
||||
node index-sse.js
|
||||
} catch {
|
||||
Write-Host "❌ Erreur lors du démarrage du serveur:" -ForegroundColor Red
|
||||
Write-Host " $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host "Dépannage:" -ForegroundColor Yellow
|
||||
Write-Host "1. Vérifiez que le port 3001 n'est pas déjà utilisé" -ForegroundColor White
|
||||
Write-Host "2. Vérifiez que Node.js est installé (node --version)" -ForegroundColor White
|
||||
Write-Host "3. Vérifiez les dépendances (npm list)" -ForegroundColor White
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get local IP address
|
||||
Write-Host "`n🔍 Detecting network configuration..." -ForegroundColor Cyan
|
||||
$ipAddresses = Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -notlike "*Loopback*" -and $_.IPAddress -notlike "169.*" }
|
||||
$mainIP = $ipAddresses | Select-Object -First 1 -ExpandProperty IPAddress
|
||||
|
||||
Write-Host "`n📡 Your IP addresses:" -ForegroundColor Cyan
|
||||
foreach ($ip in $ipAddresses) {
|
||||
Write-Host " - $($ip.IPAddress)" -ForegroundColor White
|
||||
}
|
||||
|
||||
Write-Host "`n🌐 For N8N configuration, use:" -ForegroundColor Green
|
||||
Write-Host " http://$mainIP:3001/sse" -ForegroundColor Yellow -BackgroundColor DarkGray
|
||||
|
||||
Write-Host "`n🚀 Starting MCP SSE Server on port 3001..." -ForegroundColor Cyan
|
||||
Write-Host " Press Ctrl+C to stop`n" -ForegroundColor Gray
|
||||
|
||||
# Start the server
|
||||
node index-sse.js
|
||||
|
||||
84
mcp-server/test-server.js
Normal file
84
mcp-server/test-server.js
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
// Test script to verify MCP server can connect to the database
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
console.log('🧪 Testing MCP Server Database Connection...\n');
|
||||
|
||||
try {
|
||||
const prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: 'file:D:/dev_new_pc/Keep/keep-notes/prisma/dev.db'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ Prisma Client initialized successfully');
|
||||
|
||||
// Test 1: Count notes
|
||||
console.log('\n📝 Test 1: Counting notes...');
|
||||
const noteCount = await prisma.note.count();
|
||||
console.log(` Found ${noteCount} notes in database`);
|
||||
|
||||
// Test 2: Get recent notes
|
||||
console.log('\n📝 Test 2: Fetching recent notes...');
|
||||
const recentNotes = await prisma.note.findMany({
|
||||
take: 3,
|
||||
orderBy: { updatedAt: 'desc' }
|
||||
});
|
||||
console.log(` Fetched ${recentNotes.length} recent notes`);
|
||||
if (recentNotes.length > 0) {
|
||||
console.log(` Latest note: ${recentNotes[0].title || 'Untitled'}`);
|
||||
}
|
||||
|
||||
// Test 3: Count notebooks
|
||||
console.log('\n📚 Test 3: Counting notebooks...');
|
||||
const notebookCount = await prisma.notebook.count();
|
||||
console.log(` Found ${notebookCount} notebooks in database`);
|
||||
|
||||
// Test 4: Get notebooks
|
||||
console.log('\n📚 Test 4: Fetching notebooks...');
|
||||
const notebooks = await prisma.notebook.findMany({
|
||||
take: 3,
|
||||
orderBy: { order: 'asc' }
|
||||
});
|
||||
console.log(` Fetched ${notebooks.length} notebooks`);
|
||||
if (notebooks.length > 0) {
|
||||
notebooks.forEach(nb => {
|
||||
console.log(` - ${nb.icon || '📁'} ${nb.name}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Test 5: Count labels
|
||||
console.log('\n🏷️ Test 5: Counting labels...');
|
||||
const labelCount = await prisma.label.count();
|
||||
console.log(` Found ${labelCount} labels in database`);
|
||||
|
||||
// Test 6: Get labels
|
||||
console.log('\n🏷️ Test 6: Fetching labels...');
|
||||
const labels = await prisma.label.findMany({
|
||||
take: 5,
|
||||
orderBy: { name: 'asc' }
|
||||
});
|
||||
console.log(` Fetched ${labels.length} labels`);
|
||||
if (labels.length > 0) {
|
||||
labels.forEach(l => {
|
||||
console.log(` - ${l.name} (${l.color})`);
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.$disconnect();
|
||||
console.log('\n✅ All database tests passed successfully!');
|
||||
console.log('🚀 MCP Server is ready to use!\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Database test failed:', error.message);
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user