fix: Add debounced Undo/Redo system to avoid character-by-character history
- Add debounced state updates for title and content (500ms delay) - Immediate UI updates with delayed history saving - Prevent one-letter-per-undo issue - Add cleanup for debounce timers on unmount
This commit is contained in:
324
MCP-SSE-ANALYSIS.md
Normal file
324
MCP-SSE-ANALYSIS.md
Normal file
@@ -0,0 +1,324 @@
|
||||
# MCP et SSE (Server-Sent Events) - Analyse
|
||||
|
||||
## Question
|
||||
**Peut-on utiliser le MCP en SSE?**
|
||||
|
||||
## Réponse: OUI ✅ (avec nuances)
|
||||
|
||||
Le SDK MCP (@modelcontextprotocol/sdk) supporte **plusieurs transports**, dont certains utilisent SSE.
|
||||
|
||||
---
|
||||
|
||||
## Transports MCP Disponibles
|
||||
|
||||
### 1. **stdio** (Actuellement utilisé) ✅
|
||||
```javascript
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
```
|
||||
|
||||
**Avantages**:
|
||||
- Simple, direct, pas de réseau
|
||||
- Idéal pour processus locaux
|
||||
- Utilisé par Claude Desktop, Cline, etc.
|
||||
|
||||
**Inconvénients**:
|
||||
- ❌ Nécessite accès fichier local
|
||||
- ❌ Pas adapté pour N8N sur machine distante
|
||||
- ❌ N8N doit avoir accès à `D:/dev_new_pc/Keep/mcp-server/index.js`
|
||||
|
||||
---
|
||||
|
||||
### 2. **SSE via HTTP** ✅ (RECOMMANDÉ pour N8N distant!)
|
||||
```javascript
|
||||
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
||||
```
|
||||
|
||||
**Avantages**:
|
||||
- ✅ Fonctionne sur le réseau (parfait pour N8N distant!)
|
||||
- ✅ Connexion persistante unidirectionnelle
|
||||
- ✅ Standard HTTP/HTTPS
|
||||
- ✅ Pas de WebSocket nécessaire
|
||||
- ✅ Compatible avec la plupart des firewalls
|
||||
|
||||
**Comment ça marche**:
|
||||
1. Client (N8N) se connecte à `http://your-ip:3001/sse`
|
||||
2. Serveur maintient la connexion ouverte
|
||||
3. Envoie des événements SSE au format `data: {...}\n\n`
|
||||
4. Client peut envoyer des requêtes via POST sur `/message`
|
||||
|
||||
---
|
||||
|
||||
### 3. **HTTP avec WebSockets** (Alternative)
|
||||
```javascript
|
||||
// Pas officiellement documenté dans SDK 1.0.4
|
||||
// Mais possible avec implémentation custom
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration SSE pour Memento MCP Server
|
||||
|
||||
### Option A: Créer un serveur SSE séparé
|
||||
|
||||
**Fichier**: `mcp-server/index-sse.js`
|
||||
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
||||
import express from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const app = express();
|
||||
const PORT = 3001;
|
||||
|
||||
// Initialize Prisma
|
||||
const prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: `file:${join(__dirname, '../keep-notes/prisma/dev.db')}`
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function
|
||||
function parseNote(dbNote) {
|
||||
return {
|
||||
...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,
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize MCP Server
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'memento-mcp-server',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Register all tools (same as stdio version)
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [
|
||||
// ... tous les tools comme create_note, get_notes, etc.
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
// ... même logique que stdio version
|
||||
});
|
||||
|
||||
// SSE Endpoint
|
||||
app.get('/sse', async (req, res) => {
|
||||
const transport = new SSEServerTransport('/message', res);
|
||||
await server.connect(transport);
|
||||
});
|
||||
|
||||
// Message Endpoint
|
||||
app.post('/message', express.json(), async (req, res) => {
|
||||
// Handled by SSEServerTransport
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`MCP SSE Server running on http://localhost:${PORT}`);
|
||||
console.log(`SSE endpoint: http://localhost:${PORT}/sse`);
|
||||
});
|
||||
```
|
||||
|
||||
**Dépendances à ajouter**:
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm install express
|
||||
```
|
||||
|
||||
**Démarrage**:
|
||||
```bash
|
||||
node mcp-server/index-sse.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Option B: Utiliser Next.js API Route avec SSE
|
||||
|
||||
**Fichier**: `keep-notes/app/api/mcp/route.ts`
|
||||
|
||||
```typescript
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
const mcpServer = new Server(
|
||||
{
|
||||
name: 'memento-mcp-server',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Register tools...
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const encoder = new TextEncoder();
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const transport = new SSEServerTransport('/api/mcp/message', {
|
||||
write: (data) => controller.enqueue(encoder.encode(data)),
|
||||
});
|
||||
await mcpServer.connect(transport);
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
// Handle messages
|
||||
const body = await req.json();
|
||||
// Process via mcpServer
|
||||
return Response.json({ success: true });
|
||||
}
|
||||
```
|
||||
|
||||
**Avantage**: Même serveur Next.js, pas de port séparé!
|
||||
|
||||
---
|
||||
|
||||
## Configuration N8N avec SSE
|
||||
|
||||
### Méthode 1: MCP Client Community Node (si supporte SSE)
|
||||
|
||||
**Configuration**:
|
||||
```json
|
||||
{
|
||||
"name": "memento-sse",
|
||||
"transport": "sse",
|
||||
"url": "http://YOUR_IP:3001/sse"
|
||||
}
|
||||
```
|
||||
|
||||
### Méthode 2: HTTP Request Nodes (Fallback)
|
||||
|
||||
Si le MCP Client ne supporte pas SSE, utiliser les nodes HTTP Request standards de N8N avec l'API REST existante (comme actuellement).
|
||||
|
||||
---
|
||||
|
||||
## Comparaison: stdio vs SSE
|
||||
|
||||
| Feature | stdio | SSE |
|
||||
|---------|-------|-----|
|
||||
| **Connexion** | Process local | HTTP réseau |
|
||||
| **N8N distant** | ❌ Non | ✅ Oui |
|
||||
| **Latence** | Très faible | Faible |
|
||||
| **Setup** | Simple | Moyen |
|
||||
| **Firewall** | N/A | Standard HTTP |
|
||||
| **Scaling** | 1 instance | Multiple clients |
|
||||
| **Debugging** | Console logs | Network logs + curl |
|
||||
|
||||
---
|
||||
|
||||
## Recommandation pour ton cas
|
||||
|
||||
### Contexte
|
||||
- N8N déployé sur une **machine séparée**
|
||||
- Besoin d'accès distant au MCP server
|
||||
- MCP Client Community Node installé
|
||||
|
||||
### Solution: **SSE via Express** ✅
|
||||
|
||||
**Pourquoi**:
|
||||
1. stdio nécessite accès fichier local (impossible si N8N sur autre machine)
|
||||
2. SSE fonctionne sur HTTP standard (réseau)
|
||||
3. Facile à tester avec curl
|
||||
4. Compatible avec la plupart des clients MCP
|
||||
|
||||
**Étapes**:
|
||||
1. Créer `mcp-server/index-sse.js` avec Express + SSE
|
||||
2. Ajouter `express` aux dépendances
|
||||
3. Démarrer sur port 3001 (ou autre)
|
||||
4. Trouver l'IP de ta machine Windows: `ipconfig`
|
||||
5. Configurer N8N MCP Client avec `http://YOUR_IP:3001/sse`
|
||||
6. Tester avec N8N workflows
|
||||
|
||||
---
|
||||
|
||||
## Test SSE sans N8N
|
||||
|
||||
### Avec curl:
|
||||
```bash
|
||||
# Test connexion SSE
|
||||
curl -N http://localhost:3001/sse
|
||||
|
||||
# Test appel d'un tool
|
||||
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
|
||||
}'
|
||||
```
|
||||
|
||||
### Avec JavaScript (test rapide):
|
||||
```javascript
|
||||
const eventSource = new EventSource('http://localhost:3001/sse');
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
console.log('Received:', event.data);
|
||||
};
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
console.error('SSE Error:', error);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prochaines étapes
|
||||
|
||||
1. ✅ **Je peux créer le serveur SSE si tu veux**
|
||||
2. ✅ **Tester localement avec curl**
|
||||
3. ✅ **Configurer N8N pour pointer vers SSE endpoint**
|
||||
4. ✅ **Vérifier que MCP Client Community Node supporte SSE**
|
||||
|
||||
**Tu veux que je crée le serveur SSE maintenant?**
|
||||
|
||||
---
|
||||
|
||||
## Ressources
|
||||
|
||||
- [MCP SDK Documentation](https://github.com/modelcontextprotocol/sdk)
|
||||
- [SSE Specification](https://html.spec.whatwg.org/multipage/server-sent-events.html)
|
||||
- [N8N MCP Integration](https://community.n8n.io/)
|
||||
|
||||
---
|
||||
|
||||
**Conclusion**: Oui, MCP peut utiliser SSE! C'est même **recommandé** pour ton cas avec N8N sur une machine distante. Le transport stdio actuel ne fonctionne que pour processus locaux.
|
||||
Reference in New Issue
Block a user