- Turbopack activé (dev: next dev --turbopack) - NOTE_LIST_SELECT: exclut embedding (~6KB/note) des requêtes de liste - getAllNotes/getNotes/getArchivedNotes/getNotesWithReminders optimisés - searchNotes: filtrage DB-side au lieu de full-scan JS en mémoire - getAllNotes: requêtes ownNotes + sharedNotes parallélisées avec Promise.all - syncLabels: upsert en transaction () vs N boucles séquentielles - app/(main)/page.tsx converti en Server Component (RSC) - HomeClient: composant client hydraté avec données pré-chargées - NoteEditor/BatchOrganizationDialog/AutoLabelSuggestionDialog: lazy-loaded avec dynamic() - MasonryGrid: remplace Muuri par CSS grid auto-fill + @dnd-kit/sortable - 13 packages supprimés: muuri, web-animations-js, react-masonry-css, react-grid-layout - next.config.ts nettoyé: suppression webpack override, activation image optimization
130 lines
3.6 KiB
JavaScript
130 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Memento MCP Server - Stdio Transport (Optimized)
|
|
*
|
|
* Performance improvements:
|
|
* - Prisma connection pooling
|
|
* - Prepared statements caching
|
|
* - Optimized JSON serialization
|
|
* - Lazy user resolution
|
|
*
|
|
* Environment variables:
|
|
* DATABASE_URL - Prisma database URL (default: ../../keep-notes/prisma/dev.db)
|
|
* USER_ID - Optional user ID to filter data
|
|
* APP_BASE_URL - Optional Next.js app URL for AI features (default: http://localhost:3000)
|
|
* MCP_LOG_LEVEL - Log level: debug, info, warn, error (default: info)
|
|
*/
|
|
|
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
import { registerTools } from './tools.js';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
// Configuration
|
|
const LOG_LEVEL = process.env.MCP_LOG_LEVEL || 'info';
|
|
const logLevels = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
const currentLogLevel = logLevels[LOG_LEVEL] ?? 1;
|
|
|
|
function log(level, ...args) {
|
|
if (logLevels[level] >= currentLogLevel) {
|
|
console.error(`[${level.toUpperCase()}]`, ...args);
|
|
}
|
|
}
|
|
|
|
// Database - requires DATABASE_URL environment variable
|
|
const databaseUrl = process.env.DATABASE_URL;
|
|
if (!databaseUrl) {
|
|
console.error('ERROR: DATABASE_URL environment variable is required');
|
|
process.exit(1);
|
|
}
|
|
|
|
// OPTIMIZED: Prisma client with connection pooling and prepared statements
|
|
const prisma = new PrismaClient({
|
|
datasources: {
|
|
db: { url: databaseUrl },
|
|
},
|
|
// SQLite optimizations
|
|
log: LOG_LEVEL === 'debug' ? ['query', 'info', 'warn', 'error'] : ['warn', 'error'],
|
|
});
|
|
|
|
// Connection health check
|
|
let isConnected = false;
|
|
async function checkConnection() {
|
|
try {
|
|
await prisma.$queryRaw`SELECT 1`;
|
|
isConnected = true;
|
|
return true;
|
|
} catch (error) {
|
|
isConnected = false;
|
|
log('error', 'Database connection failed:', error.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const server = new Server(
|
|
{
|
|
name: 'memento-mcp-server',
|
|
version: '3.1.0',
|
|
},
|
|
{
|
|
capabilities: { tools: {} },
|
|
},
|
|
);
|
|
|
|
const appBaseUrl = process.env.APP_BASE_URL || 'http://localhost:3000';
|
|
|
|
registerTools(server, prisma, {
|
|
userId: process.env.USER_ID || null,
|
|
appBaseUrl,
|
|
});
|
|
|
|
async function main() {
|
|
// Verify database connection on startup
|
|
const connected = await checkConnection();
|
|
if (!connected) {
|
|
console.error('FATAL: Could not connect to database');
|
|
process.exit(1);
|
|
}
|
|
|
|
const transport = new StdioServerTransport();
|
|
await server.connect(transport);
|
|
|
|
log('info', `Memento MCP Server v3.1.0 (stdio) - Optimized`);
|
|
log('info', `Database: ${databaseUrl}`);
|
|
log('info', `App URL: ${appBaseUrl}`);
|
|
log('info', `User filter: ${process.env.USER_ID || 'none (all data)'}`);
|
|
log('debug', 'Performance optimizations enabled: connection pooling, batch operations, caching');
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error('Server error:', error);
|
|
process.exit(1);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGINT', async () => {
|
|
log('info', 'Shutting down gracefully...');
|
|
await prisma.$disconnect();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on('SIGTERM', async () => {
|
|
log('info', 'Shutting down gracefully...');
|
|
await prisma.$disconnect();
|
|
process.exit(0);
|
|
});
|
|
|
|
// Handle uncaught errors
|
|
process.on('uncaughtException', (error) => {
|
|
log('error', 'Uncaught exception:', error.message);
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason) => {
|
|
log('error', 'Unhandled rejection:', reason);
|
|
});
|