feat(mcp): add all 4 note types, translate N8N docs to English, add N8N workflow examples
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 55s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 55s
- tools.js: expose type enum ['text','markdown','richtext','checklist'] in create_note & update_note - default changed from 'text' to 'richtext' (matches Prisma schema) - isMarkdown marked as deprecated in favour of type='markdown' - N8N-CONFIG.md: full French → English translation - N8N-WORKFLOWS.md: full French → English translation - N8N-EXAMPLES.md: new comprehensive examples for all 22 MCP tools + workflow patterns - n8n-workflow-mcp-reminder-bot.json: cron → get_due_reminders → Telegram → mark done - n8n-workflow-mcp-email-to-note.json: IMAP → create_note → urgent Slack alert - n8n-workflow-mcp-daily-digest.json: 8AM cron → notes + reminders digest → save + Slack - n8n-workflow-mcp-webhook-to-note.json: universal webhook → create_note → respond - notebooks-list.tsx: fix truncated notebook names (pe-24→pe-14), replace hover overlay with Tooltip
This commit is contained in:
@@ -1,34 +1,34 @@
|
||||
# Configuration N8N - Memento MCP Server
|
||||
# N8N Configuration — Memento MCP Server
|
||||
|
||||
## Configuration MCP Client dans N8N
|
||||
## MCP Client Setup in N8N
|
||||
|
||||
Le serveur MCP utilise le transport **Streamable HTTP** (remplace l'ancien SSE).
|
||||
The MCP server uses **Streamable HTTP** transport (replaces the legacy SSE transport).
|
||||
|
||||
### 1. Generer une cle API
|
||||
### 1. Generate an API Key
|
||||
|
||||
Dans Memento : **Parametres > MCP > Generer une cle**
|
||||
In Memento: **Settings > MCP > Generate a key**
|
||||
|
||||
La cle a le format `mcp_sk_...` et est associee a votre compte utilisateur. Seules vos notes seront accessibles.
|
||||
The key has the format `mcp_sk_...` and is scoped to your user account. Only your notes will be accessible.
|
||||
|
||||
### 2. Configurer le noeud MCP Client dans N8N
|
||||
### 2. Configure the MCP Client Node in N8N
|
||||
|
||||
1. Ajouter un noeud **MCP Client** dans votre workflow
|
||||
2. **Server Transport** : `Streamable HTTP`
|
||||
3. **MCP Endpoint URL** : `http://memento-mcp:3001/mcp` (Docker) ou `http://VOTRE_IP:3001/mcp`
|
||||
4. **Authentication** : Header Auth
|
||||
- Header Name : `x-api-key`
|
||||
- Header Value : votre cle API (`mcp_sk_...`)
|
||||
1. Add an **MCP Client** node to your workflow
|
||||
2. **Server Transport**: `Streamable HTTP`
|
||||
3. **MCP Endpoint URL**: `http://memento-mcp:3001/mcp` (Docker) or `http://YOUR_IP:3001/mcp`
|
||||
4. **Authentication**: Header Auth
|
||||
- Header Name: `x-api-key`
|
||||
- Header Value: your API key (`mcp_sk_...`)
|
||||
|
||||
### Alternative : curl
|
||||
### Alternative: curl
|
||||
|
||||
```bash
|
||||
# Health check
|
||||
curl -H "x-api-key: mcp_sk_votrecle" http://localhost:3001/
|
||||
curl -H "x-api-key: mcp_sk_yourkey" http://localhost:3001/
|
||||
|
||||
# Lister les outils
|
||||
# List available tools
|
||||
curl -X POST http://localhost:3001/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: mcp_sk_votrecle" \
|
||||
-H "x-api-key: mcp_sk_yourkey" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
@@ -36,10 +36,10 @@ curl -X POST http://localhost:3001/mcp \
|
||||
"params": {}
|
||||
}'
|
||||
|
||||
# Creer une note
|
||||
# Create a note
|
||||
curl -X POST http://localhost:3001/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: mcp_sk_votrecle" \
|
||||
-H "x-api-key: mcp_sk_yourkey" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
@@ -47,72 +47,81 @@ curl -X POST http://localhost:3001/mcp \
|
||||
"params": {
|
||||
"name": "create_note",
|
||||
"arguments": {
|
||||
"title": "Ma note",
|
||||
"content": "Contenu de la note"
|
||||
"title": "My note",
|
||||
"content": "Note content here"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Outils disponibles (22)
|
||||
## Available Tools (22)
|
||||
|
||||
### Notes (11)
|
||||
|
||||
| Outil | Description |
|
||||
|-------|-------------|
|
||||
| `create_note` | Creer une note |
|
||||
| `get_notes` | Lister les notes |
|
||||
| `get_note` | Recuperer une note par ID |
|
||||
| `update_note` | Modifier une note |
|
||||
| `delete_note` | Supprimer une note |
|
||||
| `search_notes` | Rechercher par mot-cle |
|
||||
| `move_note` | Deplacer vers un notebook |
|
||||
| `toggle_pin` | Epingler/Depingler |
|
||||
| `toggle_archive` | Archiver/Desarchiver |
|
||||
| `export_notes` | Exporter en JSON |
|
||||
| `import_notes` | Importer depuis JSON |
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `create_note` | Create a note |
|
||||
| `get_notes` | List notes |
|
||||
| `get_note` | Get a note by ID |
|
||||
| `update_note` | Update a note |
|
||||
| `delete_note` | Delete a note |
|
||||
| `search_notes` | Search by keyword |
|
||||
| `move_note` | Move to a notebook |
|
||||
| `toggle_pin` | Pin / unpin |
|
||||
| `toggle_archive` | Archive / unarchive |
|
||||
| `export_notes` | Export as JSON |
|
||||
| `import_notes` | Import from JSON |
|
||||
|
||||
#### Note Types
|
||||
|
||||
| `type` value | Description |
|
||||
|--------------|-------------|
|
||||
| `richtext` | Rich text editor — **default** |
|
||||
| `markdown` | Markdown rendered (use instead of `isMarkdown`) |
|
||||
| `text` | Plain text |
|
||||
| `checklist` | Interactive checklist with `checkItems` |
|
||||
|
||||
### Notebooks (6)
|
||||
|
||||
| Outil | Description |
|
||||
|-------|-------------|
|
||||
| `create_notebook` | Creer un notebook |
|
||||
| `get_notebooks` | Lister les notebooks |
|
||||
| `get_notebook` | Details d'un notebook |
|
||||
| `update_notebook` | Modifier un notebook |
|
||||
| `delete_notebook` | Supprimer un notebook |
|
||||
| `reorder_notebooks` | Reordonner |
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `create_notebook` | Create a notebook |
|
||||
| `get_notebooks` | List all notebooks |
|
||||
| `get_notebook` | Get notebook details |
|
||||
| `update_notebook` | Update a notebook |
|
||||
| `delete_notebook` | Delete a notebook |
|
||||
| `reorder_notebooks` | Reorder notebooks |
|
||||
|
||||
### Labels (4)
|
||||
|
||||
| Outil | Description |
|
||||
|-------|-------------|
|
||||
| `create_label` | Creer un label |
|
||||
| `get_labels` | Lister les labels |
|
||||
| `update_label` | Modifier un label |
|
||||
| `delete_label` | Supprimer un label |
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `create_label` | Create a label |
|
||||
| `get_labels` | List labels |
|
||||
| `update_label` | Update a label |
|
||||
| `delete_label` | Delete a label |
|
||||
|
||||
### Rappels (1)
|
||||
### Reminders (1)
|
||||
|
||||
| Outil | Description |
|
||||
|-------|-------------|
|
||||
| `get_due_reminders` | Recuperer les rappels dus |
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `get_due_reminders` | Get due reminders |
|
||||
|
||||
## Endpoints HTTP
|
||||
## HTTP Endpoints
|
||||
|
||||
| Endpoint | Methode | Description |
|
||||
|----------|---------|-------------|
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/` | GET | Health check |
|
||||
| `/mcp` | GET/POST | Endpoint MCP principal |
|
||||
| `/sse` | GET/POST | Legacy (redirige vers `/mcp`) |
|
||||
| `/sessions` | GET | Sessions actives |
|
||||
| `/mcp` | GET/POST | Main MCP endpoint |
|
||||
| `/sse` | GET/POST | Legacy (redirects to `/mcp`) |
|
||||
| `/sessions` | GET | Active sessions |
|
||||
|
||||
## Securite
|
||||
## Security
|
||||
|
||||
- **Authentification obligatoire** en production (`MCP_REQUIRE_AUTH=true` dans Docker)
|
||||
- Les cles API sont gerees depuis **Parametres > MCP** dans Memento
|
||||
- Chaque cle est scopee a un utilisateur : seules ses notes sont accessibles
|
||||
- Les cles sont hashees en base (SHA256), seul le raw key est montre a la creation
|
||||
- **Authentication required** in production (`MCP_REQUIRE_AUTH=true` in Docker)
|
||||
- API keys are managed from **Settings > MCP** in Memento
|
||||
- Each key is scoped to a single user: only their notes are accessible
|
||||
- Keys are hashed in the database (SHA256); the raw key is only shown once at creation
|
||||
|
||||
## Ports
|
||||
|
||||
|
||||
571
mcp-server/N8N-EXAMPLES.md
Normal file
571
mcp-server/N8N-EXAMPLES.md
Normal file
@@ -0,0 +1,571 @@
|
||||
# N8N Examples — Memento MCP Server
|
||||
|
||||
Practical, copy-paste-ready N8N workflow snippets for every Memento MCP tool.
|
||||
All examples assume an MCP Client node configured with Streamable HTTP and `x-api-key` authentication.
|
||||
|
||||
## Note Types
|
||||
|
||||
| `type` value | Description |
|
||||
|--------------|-------------|
|
||||
| `richtext` | Rich text editor — **default** |
|
||||
| `markdown` | Markdown rendered (preferred over deprecated `isMarkdown`) |
|
||||
| `text` | Plain text |
|
||||
| `checklist` | Interactive checklist with `checkItems` |
|
||||
|
||||
> **Note**: `isMarkdown: true` is deprecated. Use `"type": "markdown"` instead.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
### Create a simple text note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Quick idea",
|
||||
"content": "Explore serverless architecture for the new API gateway.",
|
||||
"color": "yellow",
|
||||
"labels": ["idea", "architecture"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a pinned markdown note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Project README",
|
||||
"content": "# My Project\n\nThis is the main documentation.\n\n## Getting Started\n\n```bash\nnpm install\nnpm run dev\n```",
|
||||
"isMarkdown": true,
|
||||
"isPinned": true,
|
||||
"color": "blue",
|
||||
"size": "large"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a checklist note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Weekly tasks",
|
||||
"content": "Tasks for this week",
|
||||
"type": "checklist",
|
||||
"checkItems": [
|
||||
{ "id": "1", "text": "Review pull requests", "checked": false },
|
||||
{ "id": "2", "text": "Update deployment docs", "checked": false },
|
||||
{ "id": "3", "text": "Team standup notes", "checked": true }
|
||||
],
|
||||
"color": "teal"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a note with a reminder
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Dentist appointment",
|
||||
"content": "Dr. Martin — 10:30 AM. Bring insurance card.",
|
||||
"reminder": "2025-06-15T10:00:00.000Z",
|
||||
"reminderRecurrence": "yearly",
|
||||
"color": "red",
|
||||
"labels": ["health", "personal"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a note in a specific notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Sprint planning notes",
|
||||
"content": "**Goal**: Ship user authentication by Friday.\n\n- Design review at 2PM\n- Backend API ready by Wednesday",
|
||||
"isMarkdown": true,
|
||||
"notebookId": "{{ $json.notebookId }}",
|
||||
"labels": ["sprint", "planning"],
|
||||
"color": "purple"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a note with external links
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_note",
|
||||
"arguments": {
|
||||
"title": "Research resources",
|
||||
"content": "Curated links for the ML paper review.",
|
||||
"links": [
|
||||
"https://arxiv.org/abs/2304.15004",
|
||||
"https://huggingface.co/papers",
|
||||
"https://paperswithcode.com"
|
||||
],
|
||||
"labels": ["research", "ml"],
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get all notes (lightweight)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notes",
|
||||
"arguments": {
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get notes from a specific notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notes",
|
||||
"arguments": {
|
||||
"notebookId": "{{ $json.notebookId }}",
|
||||
"limit": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get unfiled notes (Inbox)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notes",
|
||||
"arguments": {
|
||||
"notebookId": "inbox",
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get all notes including archived
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notes",
|
||||
"arguments": {
|
||||
"includeArchived": true,
|
||||
"limit": 200,
|
||||
"fullDetails": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get a single note by ID
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Search notes by keyword
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "search_notes",
|
||||
"arguments": {
|
||||
"query": "deployment kubernetes"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Search in a specific notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "search_notes",
|
||||
"arguments": {
|
||||
"query": "{{ $json.searchTerm }}",
|
||||
"notebookId": "{{ $json.notebookId }}",
|
||||
"includeArchived": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update a note's content
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"content": "{{ $json.newContent }}",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mark a checklist item as done
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"checkItems": [
|
||||
{ "id": "1", "text": "Review pull requests", "checked": true },
|
||||
{ "id": "2", "text": "Update deployment docs", "checked": false }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add labels to an existing note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"labels": ["ai-generated", "reviewed", "{{ $json.category }}"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mark a reminder as done
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"isReminderDone": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Move a note to a notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "move_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"notebookId": "{{ $json.targetNotebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Move a note to Inbox (no notebook)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "move_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}",
|
||||
"notebookId": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pin a note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "toggle_pin",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Archive a note
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "toggle_archive",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete a note permanently
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "delete_note",
|
||||
"arguments": {
|
||||
"id": "{{ $json.noteId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Export all notes as JSON backup
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "export_notes",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Import notes from a JSON backup
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "import_notes",
|
||||
"arguments": {
|
||||
"data": "{{ $json.exportPayload }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notebooks
|
||||
|
||||
### Create a notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_notebook",
|
||||
"arguments": {
|
||||
"name": "Work Projects",
|
||||
"icon": "💼",
|
||||
"color": "#3B82F6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a notebook for a team/topic
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_notebook",
|
||||
"arguments": {
|
||||
"name": "Machine Learning Research",
|
||||
"icon": "🤖",
|
||||
"color": "#8B5CF6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### List all notebooks
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notebooks",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Get a notebook with its notes
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_notebook",
|
||||
"arguments": {
|
||||
"id": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rename a notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_notebook",
|
||||
"arguments": {
|
||||
"id": "{{ $json.notebookId }}",
|
||||
"name": "Q3 2025 Projects",
|
||||
"color": "#10B981"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reorder notebooks
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "reorder_notebooks",
|
||||
"arguments": {
|
||||
"notebookIds": [
|
||||
"{{ $json.ids[0] }}",
|
||||
"{{ $json.ids[1] }}",
|
||||
"{{ $json.ids[2] }}"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete a notebook (notes go to Inbox)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "delete_notebook",
|
||||
"arguments": {
|
||||
"id": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Labels
|
||||
|
||||
### Create a label inside a notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_label",
|
||||
"arguments": {
|
||||
"name": "urgent",
|
||||
"color": "red",
|
||||
"notebookId": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create common project labels
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_label",
|
||||
"arguments": {
|
||||
"name": "in-progress",
|
||||
"color": "orange",
|
||||
"notebookId": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "create_label",
|
||||
"arguments": {
|
||||
"name": "done",
|
||||
"color": "green",
|
||||
"notebookId": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### List labels in a notebook
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_labels",
|
||||
"arguments": {
|
||||
"notebookId": "{{ $json.notebookId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### List all labels (global)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_labels",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Rename a label
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "update_label",
|
||||
"arguments": {
|
||||
"id": "{{ $json.labelId }}",
|
||||
"name": "high-priority",
|
||||
"color": "red"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete a label
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "delete_label",
|
||||
"arguments": {
|
||||
"id": "{{ $json.labelId }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reminders
|
||||
|
||||
### Get all due reminders (for cron automation)
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "get_due_reminders",
|
||||
"arguments": {}
|
||||
}
|
||||
```
|
||||
|
||||
> **Tip**: Schedule this every 30 minutes with a cron trigger (`0 */30 * * * *`), then loop over results and send notifications via Slack, Telegram, or email.
|
||||
|
||||
---
|
||||
|
||||
## Common Workflow Patterns
|
||||
|
||||
### Pattern: Email → Note
|
||||
1. **Email Trigger (IMAP)** — fires on new email
|
||||
2. **MCP Client** → `create_note`
|
||||
```json
|
||||
{
|
||||
"title": "{{ $json.subject }}",
|
||||
"content": "**From**: {{ $json.from }}\n\n{{ $json.text }}",
|
||||
"labels": ["email"],
|
||||
"color": "blue",
|
||||
"isMarkdown": true
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: Reminder Notifications
|
||||
1. **Schedule Trigger** — every 30 minutes
|
||||
2. **MCP Client** → `get_due_reminders`
|
||||
3. **IF** — results not empty
|
||||
4. **Loop over items** → send Slack/Telegram message per reminder
|
||||
5. **MCP Client** → `update_note` with `isReminderDone: true`
|
||||
|
||||
### Pattern: AI Summarization
|
||||
1. **MCP Client** → `search_notes` with a topic query
|
||||
2. **OpenAI / Anthropic node** — summarize the returned notes
|
||||
3. **MCP Client** → `create_note` with the summary as content, labeled `ai-summary`
|
||||
|
||||
### Pattern: Daily Digest
|
||||
1. **Schedule Trigger** — every day at 8 AM
|
||||
2. **MCP Client** → `get_notes` with `limit: 20` (most recent)
|
||||
3. **AI node** — generate a brief digest
|
||||
4. **Email / Slack node** — send the digest
|
||||
5. *(Optional)* **MCP Client** → `create_note` to archive the digest
|
||||
|
||||
### Pattern: Webhook → Structured Note
|
||||
1. **Webhook Trigger** — receives JSON payload (e.g., from a form or CI/CD pipeline)
|
||||
2. **Code node** — format the payload into markdown
|
||||
3. **MCP Client** → `create_note`
|
||||
```json
|
||||
{
|
||||
"title": "Build #{{ $json.buildNumber }} — {{ $json.status }}",
|
||||
"content": "{{ $node['Code'].json.markdown }}",
|
||||
"isMarkdown": true,
|
||||
"color": "{{ $json.status === 'success' ? 'green' : 'red' }}",
|
||||
"labels": ["ci-cd", "{{ $json.repo }}"]
|
||||
}
|
||||
```
|
||||
@@ -1,24 +1,24 @@
|
||||
# Workflows N8N pour Memento MCP
|
||||
# N8N Workflows for Memento MCP
|
||||
|
||||
## Introduction
|
||||
|
||||
Exemples de workflows N8N utilisant le serveur MCP de Memento via le transport **Streamable HTTP**.
|
||||
Example N8N workflows using the Memento MCP server via **Streamable HTTP** transport.
|
||||
|
||||
**Prerequis** : une cle API generee depuis **Parametres > MCP** dans Memento.
|
||||
**Prerequisite**: an API key generated from **Settings > MCP** in Memento.
|
||||
|
||||
## Configuration du noeud MCP Client
|
||||
## MCP Client Node Configuration
|
||||
|
||||
Dans chaque workflow, le noeud MCP Client doit etre configure ainsi :
|
||||
In every workflow, the MCP Client node must be configured as follows:
|
||||
|
||||
- **Server Transport** : `Streamable HTTP`
|
||||
- **MCP Endpoint URL** : `http://memento-mcp:3001/mcp` (Docker) ou `http://VOTRE_IP:3001/mcp`
|
||||
- **Authentication** : Header Auth avec `x-api-key` = votre cle API
|
||||
- **Server Transport**: `Streamable HTTP`
|
||||
- **MCP Endpoint URL**: `http://memento-mcp:3001/mcp` (Docker) or `http://YOUR_IP:3001/mcp`
|
||||
- **Authentication**: Header Auth with `x-api-key` = your API key
|
||||
|
||||
## Workflows disponibles
|
||||
## Available Workflows
|
||||
|
||||
### 1. Create Note (`n8n-workflow-create-note.json`)
|
||||
|
||||
Cree des notes dans Memento avec classification par IA.
|
||||
Creates notes in Memento with AI-based classification.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -34,7 +34,7 @@ Cree des notes dans Memento avec classification par IA.
|
||||
|
||||
### 2. Search & Summary (`n8n-workflow-search-summary.json`)
|
||||
|
||||
Recherche des notes et genere un resume.
|
||||
Searches notes and generates a summary.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -47,7 +47,7 @@ Recherche des notes et genere un resume.
|
||||
|
||||
### 3. Notebook Manager (`n8n-workflow-notebook-management.json`)
|
||||
|
||||
CRUD complet des notebooks.
|
||||
Full CRUD for notebooks.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -62,14 +62,14 @@ CRUD complet des notebooks.
|
||||
|
||||
### 4. Reminder Notifications (`n8n-workflow-reminder-notifications.json`)
|
||||
|
||||
Automatisation des rappels avec notifications.
|
||||
Automates reminders with notifications.
|
||||
|
||||
- Declencheur : Schedule (cron: `0 */30 * * * *`)
|
||||
- Appelle `get_due_reminders` et envoie les notifications
|
||||
- Trigger: Schedule (cron: `0 */30 * * * *`)
|
||||
- Calls `get_due_reminders` and sends notifications
|
||||
|
||||
### 5. Label Manager (`n8n-workflow-label-management.json`)
|
||||
|
||||
Gestion des labels.
|
||||
Label management.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -84,20 +84,20 @@ Gestion des labels.
|
||||
|
||||
### 6. Email to Note (`n8n-workflow-email-integration.json`)
|
||||
|
||||
Conversion automatique d'emails en notes.
|
||||
Automatically converts emails into notes.
|
||||
|
||||
- Declencheur : Email Trigger (IMAP)
|
||||
- Cree une note avec le contenu de l'email
|
||||
- Trigger: Email Trigger (IMAP)
|
||||
- Creates a note from the email content
|
||||
|
||||
## Importation
|
||||
## Import Instructions
|
||||
|
||||
1. Ouvrir N8N
|
||||
2. **Import from File** -> selectionner le fichier JSON
|
||||
3. Configurer le noeud MCP Client (URL + cle API)
|
||||
4. Activer le workflow
|
||||
1. Open N8N
|
||||
2. **Import from File** → select the JSON file
|
||||
3. Configure the MCP Client node (URL + API key)
|
||||
4. Activate the workflow
|
||||
|
||||
## Securite
|
||||
## Security
|
||||
|
||||
- Toujours utiliser une cle API dediee par workflow
|
||||
- Ne jamais exposer la cle dans les logs
|
||||
- Restreindre l'acces reseau au port 3001 si possible
|
||||
- Always use a dedicated API key per workflow
|
||||
- Never expose the key in logs
|
||||
- Restrict network access to port 3001 when possible
|
||||
|
||||
137
mcp-server/n8n-workflow-mcp-daily-digest.json
Normal file
137
mcp-server/n8n-workflow-mcp-daily-digest.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"name": "Memento MCP — Daily Digest",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "cronExpression",
|
||||
"expression": "0 0 8 * * 1-5"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "schedule-trigger",
|
||||
"name": "Daily 8AM (Mon-Fri)",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [240, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "x-api-key", "value": "={{ $vars.MEMENTO_API_KEY }}" }
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"get_notes\",\n \"arguments\": {\n \"limit\": 30,\n \"includeArchived\": false\n }\n }\n}"
|
||||
},
|
||||
"id": "mcp-get-notes",
|
||||
"name": "MCP — Get Recent Notes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "x-api-key", "value": "={{ $vars.MEMENTO_API_KEY }}" }
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 2,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"get_due_reminders\",\n \"arguments\": {}\n }\n}"
|
||||
},
|
||||
"id": "mcp-get-reminders",
|
||||
"name": "MCP — Get Due Reminders",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [480, 480]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const notesResponse = $('MCP — Get Recent Notes').item.json;\nconst remindersResponse = $('MCP — Get Due Reminders').item.json;\n\nconst notes = JSON.parse(notesResponse.result?.content?.[0]?.text || '[]');\nconst reminders = JSON.parse(remindersResponse.result?.content?.[0]?.text || '[]');\n\nconst today = new Date().toLocaleDateString('en-GB', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });\n\nconst pinnedNotes = notes.filter(n => n.isPinned).slice(0, 5);\nconst recentNotes = notes.filter(n => !n.isPinned).slice(0, 10);\n\nlet digest = `# 📋 Daily Digest — ${today}\\n\\n`;\n\nif (reminders.length > 0) {\n digest += `## ⏰ Reminders Due (${reminders.length})\\n\\n`;\n reminders.forEach(r => {\n const time = new Date(r.reminder).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });\n digest += `- **${r.title || 'Untitled'}** @ ${time}\\n`;\n });\n digest += '\\n';\n}\n\nif (pinnedNotes.length > 0) {\n digest += `## 📌 Pinned Notes (${pinnedNotes.length})\\n\\n`;\n pinnedNotes.forEach(n => {\n digest += `- **${n.title || 'Untitled'}**\\n`;\n });\n digest += '\\n';\n}\n\ndigest += `## 📝 Recent Notes (${recentNotes.length})\\n\\n`;\nrecentNotes.forEach(n => {\n const date = new Date(n.updatedAt).toLocaleDateString('en-GB');\n digest += `- ${n.title || 'Untitled'} *(${date})*\\n`;\n});\n\nreturn [{ json: { digest, noteCount: notes.length, reminderCount: reminders.length, today } }];"
|
||||
},
|
||||
"id": "build-digest",
|
||||
"name": "Build Digest",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [720, 390]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "x-api-key", "value": "={{ $vars.MEMENTO_API_KEY }}" }
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"id\": 3,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"create_note\",\n \"arguments\": {\n \"title\": \"Daily Digest — {{ $json.today }}\",\n \"content\": {{ JSON.stringify($json.digest) }},\n \"isMarkdown\": true,\n \"color\": \"teal\",\n \"isPinned\": false,\n \"labels\": [\"digest\", \"auto\"]\n }\n }\n}"
|
||||
},
|
||||
"id": "mcp-save-digest",
|
||||
"name": "MCP — Save Digest as Note",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [960, 390]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"channel": "general",
|
||||
"text": "📋 *Daily Digest ready!*\n\n📝 {{ $('Build Digest').item.json.noteCount }} notes · ⏰ {{ $('Build Digest').item.json.reminderCount }} reminders\n\nView in Memento: http://memento:3000",
|
||||
"additionalFields": { "parse_mode": "Markdown" }
|
||||
},
|
||||
"id": "slack-digest",
|
||||
"name": "Slack — Share Digest",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1200, 390],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Daily 8AM (Mon-Fri)": {
|
||||
"main": [
|
||||
[
|
||||
{ "node": "MCP — Get Recent Notes", "type": "main", "index": 0 },
|
||||
{ "node": "MCP — Get Due Reminders", "type": "main", "index": 0 }
|
||||
]
|
||||
]
|
||||
},
|
||||
"MCP — Get Recent Notes": {
|
||||
"main": [[{ "node": "Build Digest", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"MCP — Get Due Reminders": {
|
||||
"main": [[{ "node": "Build Digest", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Build Digest": {
|
||||
"main": [[{ "node": "MCP — Save Digest as Note", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"MCP — Save Digest as Note": {
|
||||
"main": [[{ "node": "Slack — Share Digest", "type": "main", "index": 0 }]]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": { "executionOrder": "v1" },
|
||||
"staticData": null,
|
||||
"tags": [{ "id": "memento-mcp", "name": "Memento MCP" }],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-05-03T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
109
mcp-server/n8n-workflow-mcp-email-to-note.json
Normal file
109
mcp-server/n8n-workflow-mcp-email-to-note.json
Normal file
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "Memento MCP — Email to Note",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"pollTimes": {
|
||||
"item": [{ "mode": "everyMinute" }]
|
||||
},
|
||||
"filters": {
|
||||
"hasReadStatus": true,
|
||||
"readStatus": "unread"
|
||||
}
|
||||
},
|
||||
"id": "email-trigger",
|
||||
"name": "Email Trigger (IMAP)",
|
||||
"type": "n8n-nodes-base.emailTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [240, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const email = $input.item.json;\nconst subject = email.subject || 'Email (no subject)';\nconst from = email.from?.value?.[0]?.address || email.from || 'unknown';\nconst body = email.text || email.html?.replace(/<[^>]+>/g, '') || '';\nconst date = email.date ? new Date(email.date).toISOString() : new Date().toISOString();\n\nconst isUrgent = /(urgent|asap|important|deadline|critical)/i.test(subject + body);\n\nreturn [{\n json: {\n title: `📧 ${subject}`,\n content: `**From:** ${from}\\n**Date:** ${new Date(date).toLocaleString('en-GB')}\\n\\n---\\n\\n${body.trim().substring(0, 5000)}`,\n isMarkdown: true,\n color: isUrgent ? 'red' : 'blue',\n isPinned: isUrgent,\n labels: isUrgent ? ['email', 'urgent'] : ['email'],\n isUrgent\n }\n}];"
|
||||
},
|
||||
"id": "format-email",
|
||||
"name": "Format Email as Note",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "x-api-key", "value": "={{ $vars.MEMENTO_API_KEY }}" }
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"create_note\",\n \"arguments\": {\n \"title\": \"{{ $json.title }}\",\n \"content\": \"{{ $json.content }}\",\n \"isMarkdown\": {{ $json.isMarkdown }},\n \"color\": \"{{ $json.color }}\",\n \"isPinned\": {{ $json.isPinned }},\n \"labels\": {{ JSON.stringify($json.labels) }}\n }\n }\n}"
|
||||
},
|
||||
"id": "mcp-create-note",
|
||||
"name": "MCP — Create Note",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [720, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"conditions": [
|
||||
{
|
||||
"id": "urgent",
|
||||
"leftValue": "={{ $('Format Email as Note').item.json.isUrgent }}",
|
||||
"rightValue": true,
|
||||
"operator": { "type": "boolean", "operation": "equals" }
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "if-urgent",
|
||||
"name": "Urgent?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [960, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"channel": "alerts",
|
||||
"text": "🚨 *Urgent email saved to Memento!*\n\n{{ $('Format Email as Note').item.json.title }}\n\nOpen: http://memento:3000",
|
||||
"additionalFields": { "parse_mode": "Markdown" }
|
||||
},
|
||||
"id": "slack-alert",
|
||||
"name": "Slack Alert",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1200, 200],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Email Trigger (IMAP)": {
|
||||
"main": [[{ "node": "Format Email as Note", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Format Email as Note": {
|
||||
"main": [[{ "node": "MCP — Create Note", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"MCP — Create Note": {
|
||||
"main": [[{ "node": "Urgent?", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Urgent?": {
|
||||
"main": [
|
||||
[{ "node": "Slack Alert", "type": "main", "index": 0 }],
|
||||
[]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": { "executionOrder": "v1" },
|
||||
"staticData": null,
|
||||
"tags": [{ "id": "memento-mcp", "name": "Memento MCP" }],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-05-03T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
155
mcp-server/n8n-workflow-mcp-reminder-bot.json
Normal file
155
mcp-server/n8n-workflow-mcp-reminder-bot.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"name": "Memento MCP — Reminder Bot",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "cronExpression",
|
||||
"expression": "0 */30 * * * *"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "schedule-trigger",
|
||||
"name": "Every 30 Minutes",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [240, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "x-api-key",
|
||||
"value": "={{ $vars.MEMENTO_API_KEY }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"get_due_reminders\",\n \"arguments\": {}\n }\n}"
|
||||
},
|
||||
"id": "mcp-get-reminders",
|
||||
"name": "MCP — Get Due Reminders",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Parse MCP response\nconst response = $input.item.json;\nconst result = JSON.parse(response.result?.content?.[0]?.text || '[]');\n\nif (!Array.isArray(result) || result.length === 0) {\n return [];\n}\n\nreturn result.map(note => ({\n json: {\n noteId: note.id,\n title: note.title || 'Untitled reminder',\n reminder: note.reminder,\n reminderFormatted: new Date(note.reminder).toLocaleString('en-GB'),\n content: (note.content || '').substring(0, 200),\n labels: Array.isArray(note.labels) ? note.labels.join(', ') : ''\n }\n}));"
|
||||
},
|
||||
"id": "parse-reminders",
|
||||
"name": "Parse Reminders",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [720, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": { "caseSensitive": true },
|
||||
"conditions": [
|
||||
{
|
||||
"id": "has-items",
|
||||
"leftValue": "={{ $items().length }}",
|
||||
"rightValue": 0,
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "gt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "check-has-reminders",
|
||||
"name": "Has Reminders?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.1,
|
||||
"position": [960, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "={{ $vars.TELEGRAM_CHAT_ID }}",
|
||||
"text": "⏰ *Reminder — Memento*\n\n📌 *{{ $json.title }}*\n🕐 {{ $json.reminderFormatted }}\n\n{{ $json.content }}{{ $json.labels ? '\n🏷️ ' + $json.labels : '' }}",
|
||||
"additionalFields": {
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
},
|
||||
"id": "send-telegram",
|
||||
"name": "Send Telegram Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [1200, 200],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "x-api-key",
|
||||
"value": "={{ $vars.MEMENTO_API_KEY }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"id\": 2,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"update_note\",\n \"arguments\": {\n \"id\": \"{{ $json.noteId }}\",\n \"isReminderDone\": true\n }\n }\n}"
|
||||
},
|
||||
"id": "mcp-mark-done",
|
||||
"name": "MCP — Mark Reminder Done",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1440, 200],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Every 30 Minutes": {
|
||||
"main": [[{ "node": "MCP — Get Due Reminders", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"MCP — Get Due Reminders": {
|
||||
"main": [[{ "node": "Parse Reminders", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Reminders": {
|
||||
"main": [[{ "node": "Has Reminders?", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Has Reminders?": {
|
||||
"main": [
|
||||
[{ "node": "Send Telegram Notification", "type": "main", "index": 0 }],
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Send Telegram Notification": {
|
||||
"main": [[{ "node": "MCP — Mark Reminder Done", "type": "main", "index": 0 }]]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": { "executionOrder": "v1" },
|
||||
"staticData": null,
|
||||
"tags": [{ "id": "memento-mcp", "name": "Memento MCP" }],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-05-03T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
92
mcp-server/n8n-workflow-mcp-webhook-to-note.json
Normal file
92
mcp-server/n8n-workflow-mcp-webhook-to-note.json
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "Memento MCP — Webhook to Note",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "memento-note",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"id": "webhook-trigger",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [240, 300],
|
||||
"webhookId": "memento-create-note"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Flexible input: accept anything and build a note from it\nconst body = $input.item.json.body || $input.item.json;\n\nconst title = body.title || body.subject || body.name || null;\nconst content = body.content || body.text || body.message || body.description || JSON.stringify(body, null, 2);\nconst color = body.color || 'default';\nconst labels = Array.isArray(body.labels) ? body.labels : body.labels ? [body.labels] : [];\nconst notebookId = body.notebookId || body.notebook_id || null;\nconst isPinned = body.isPinned === true || body.pinned === true;\nconst isMarkdown = body.isMarkdown === true || body.markdown === true;\nconst reminder = body.reminder || body.dueDate || body.due_date || null;\nconst color_valid = ['default','red','orange','yellow','green','teal','blue','purple','pink','gray'].includes(color) ? color : 'default';\n\nreturn [{ json: { title, content, color: color_valid, labels, notebookId, isPinned, isMarkdown, reminder } }];"
|
||||
},
|
||||
"id": "prepare-note",
|
||||
"name": "Prepare Note Data",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://memento-mcp:3001/mcp",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "x-api-key", "value": "={{ $vars.MEMENTO_API_KEY }}" }
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"create_note\",\n \"arguments\": {\n \"title\": {{ $json.title ? JSON.stringify($json.title) : 'null' }},\n \"content\": {{ JSON.stringify($json.content) }},\n \"color\": \"{{ $json.color }}\",\n \"labels\": {{ JSON.stringify($json.labels) }},\n \"isPinned\": {{ $json.isPinned }},\n \"isMarkdown\": {{ $json.isMarkdown }}\n {{ $json.notebookId ? ', \"notebookId\": \"' + $json.notebookId + '\"' : '' }}\n {{ $json.reminder ? ', \"reminder\": \"' + $json.reminder + '\"' : '' }}\n }\n }\n}"
|
||||
},
|
||||
"id": "mcp-create-note",
|
||||
"name": "MCP — Create Note",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [720, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const response = $input.item.json;\nconst result = JSON.parse(response.result?.content?.[0]?.text || '{}');\n\nreturn [{\n json: {\n success: true,\n noteId: result.id,\n title: result.title,\n createdAt: result.createdAt\n }\n}];"
|
||||
},
|
||||
"id": "format-response",
|
||||
"name": "Format Response",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [960, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}"
|
||||
},
|
||||
"id": "webhook-response",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1200, 300]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [[{ "node": "Prepare Note Data", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Prepare Note Data": {
|
||||
"main": [[{ "node": "MCP — Create Note", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"MCP — Create Note": {
|
||||
"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": [{ "id": "memento-mcp", "name": "Memento MCP" }],
|
||||
"triggerCount": 1,
|
||||
"updatedAt": "2026-05-03T00:00:00.000Z",
|
||||
"versionId": "1"
|
||||
}
|
||||
@@ -78,10 +78,15 @@ const toolDefinitions = [
|
||||
title: { type: 'string', description: 'Note title' },
|
||||
content: { type: 'string', description: 'Note body text (required)' },
|
||||
color: { type: 'string', description: `Color: ${NOTE_COLORS}`, default: 'default' },
|
||||
type: { type: 'string', enum: ['text', 'checklist'], description: 'Note type', default: 'text' },
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['text', 'markdown', 'richtext', 'checklist'],
|
||||
description: 'Note type. "text" = plain text, "markdown" = Markdown rendered, "richtext" = rich text editor (default), "checklist" = interactive checklist',
|
||||
default: 'richtext',
|
||||
},
|
||||
checkItems: {
|
||||
type: 'array',
|
||||
description: 'Checklist items (when type=list)',
|
||||
description: 'Checklist items (when type=checklist)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: { id: { type: 'string' }, text: { type: 'string' }, checked: { type: 'boolean' } },
|
||||
@@ -97,7 +102,7 @@ const toolDefinitions = [
|
||||
isReminderDone: { type: 'boolean', default: false },
|
||||
reminderRecurrence: { type: 'string', description: 'daily, weekly, monthly, yearly' },
|
||||
reminderLocation: { type: 'string', description: 'Location string' },
|
||||
isMarkdown: { type: 'boolean', description: 'Render as markdown', default: false },
|
||||
isMarkdown: { type: 'boolean', description: '(Deprecated — use type="markdown" instead) Render as markdown', default: false },
|
||||
size: { type: 'string', enum: ['small', 'medium', 'large'], default: 'small' },
|
||||
notebookId: { type: 'string', description: 'Assign to notebook' },
|
||||
},
|
||||
@@ -137,7 +142,11 @@ const toolDefinitions = [
|
||||
title: { type: 'string' },
|
||||
content: { type: 'string' },
|
||||
color: { type: 'string', description: `One of: ${NOTE_COLORS}` },
|
||||
type: { type: 'string', enum: ['text', 'checklist'] },
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['text', 'markdown', 'richtext', 'checklist'],
|
||||
description: 'Note type. "text" = plain text, "markdown" = Markdown rendered, "richtext" = rich text editor, "checklist" = interactive checklist',
|
||||
},
|
||||
checkItems: {
|
||||
type: 'array',
|
||||
items: {
|
||||
@@ -154,7 +163,7 @@ const toolDefinitions = [
|
||||
isReminderDone: { type: 'boolean' },
|
||||
reminderRecurrence: { type: 'string' },
|
||||
reminderLocation: { type: 'string' },
|
||||
isMarkdown: { type: 'boolean' },
|
||||
isMarkdown: { type: 'boolean', description: '(Deprecated — use type="markdown" instead)' },
|
||||
size: { type: 'string', enum: ['small', 'medium', 'large'] },
|
||||
notebookId: { type: 'string' },
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from 'next/link'
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { StickyNote, Plus, Tag, Folder, ChevronDown, ChevronRight } from 'lucide-react'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useNotebooks } from '@/context/notebooks-context'
|
||||
import { useNotebookDrag } from '@/context/notebook-drag-context'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -245,23 +246,30 @@ export function NotebooksList() {
|
||||
isDragOver && "ring-2 ring-blue-500 ring-dashed rounded-e-full me-2"
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={() => handleSelectNotebook(notebook.id)}
|
||||
className={cn(
|
||||
"pointer-events-auto flex items-center gap-4 px-6 py-3 rounded-e-full me-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pe-24",
|
||||
isDragOver && "opacity-50"
|
||||
)}
|
||||
>
|
||||
<NotebookIcon className="w-5 h-5 flex-shrink-0" />
|
||||
<NotebookName name={notebook.name}>
|
||||
<span className="text-[15px] font-medium tracking-wide text-start">
|
||||
<TooltipProvider delayDuration={600}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => handleSelectNotebook(notebook.id)}
|
||||
className={cn(
|
||||
"pointer-events-auto flex items-center gap-4 px-6 py-3 rounded-e-full me-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pe-14",
|
||||
isDragOver && "opacity-50"
|
||||
)}
|
||||
>
|
||||
<NotebookIcon className="w-5 h-5 flex-shrink-0" />
|
||||
<span className="text-[15px] font-medium tracking-wide text-start truncate min-w-0 flex-1">
|
||||
{notebook.name}
|
||||
</span>
|
||||
{(notebook as any).notesCount > 0 && (
|
||||
<span className="text-xs text-gray-400 ms-auto flex-shrink-0">({new Intl.NumberFormat(language).format((notebook as any).notesCount)})</span>
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="text-sm font-medium">
|
||||
{notebook.name}
|
||||
</span>
|
||||
</NotebookName>
|
||||
{(notebook as any).notesCount > 0 && (
|
||||
<span className="text-xs text-gray-400 ms-2 flex-shrink-0">({new Intl.NumberFormat(language).format((notebook as any).notesCount)})</span>
|
||||
)}
|
||||
</button>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
{/* Actions + expand on the right — always rendered, visible on hover */}
|
||||
<div className="absolute end-3 top-1/2 -translate-y-1/2 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
|
||||
Reference in New Issue
Block a user