## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
15 KiB
Architecture - mcp-server (MCP Server)
Overview
Architecture documentation for the Memento MCP (Model Context Protocol) server, an Express-based microservice that provides AI assistant and automation integration for the Memento note-taking application.
Architecture Pattern: Microservice API Framework: Express.js 4.22.1 Protocol: MCP SDK 1.0.4 Language: JavaScript (ES modules) Database: Shared SQLite via Prisma
Technology Stack
Core
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 20+ | Runtime |
| Express.js | 4.22.1 | Web framework |
| MCP SDK | 1.0.4 | Model Context Protocol |
| Prisma | 5.22.0 | ORM |
Transport
- Primary: Stdio (standard input/output)
- Alternative: Server-Sent Events (SSE) via
index-sse.js
Database
- ORM: Prisma 5.22.0
- Database: SQLite (shared with keep-notes)
- File:
../keep-notes/prisma/dev.db
Architecture Pattern: Microservice
System Architecture
┌─────────────────┐
│ MCP Client │
│ (AI Assistant) │
│ N8N Workflow │
└────────┬────────┘
│ MCP Protocol
↓
┌─────────────────┐
│ mcp-server │
│ │
│ MCP Tools: │
│ - create_note │
│ - get_notes │
│ - search_notes │
│ - update_note │
│ - delete_note │
│ - toggle_pin │
│ - toggle_archive│
│ - get_labels │
└────────┬────────┘
│
↓
┌─────────────────┐
│ Prisma ORM │
│ │
│ Shared SQLite │
└─────────────────┘
Communication Flow
- MCP Client (AI assistant, N8N) connects via stdio
- Request: Tool invocation with parameters
- Processing: Server executes business logic
- Database: Prisma queries/updates
- Response: JSON result returned via stdio
Server Structure
File Organization
mcp-server/
├── index.js # Main MCP server (stdio transport)
├── index-sse.js # SSE variant (HTTP + SSE)
├── package.json # Dependencies
├── README.md # Server documentation
├── README-SSE.md # SSE documentation
├── N8N-CONFIG.md # N8N setup guide
└── prisma/ # Prisma client (shared)
├── schema.prisma # Schema reference
└── [generated client]
Entry Points
Primary: index.js
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
// ... tool definitions
const transport = new StdioServerTransport();
await server.connect(transport);
Alternative: index-sse.js
// HTTP server with Server-Sent Events
// For web-based MCP clients
MCP Tool Architecture
Tool Registration Pattern
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'create_note',
description: 'Create a new note in Memento',
inputSchema: {
type: 'object',
properties: { /* ... */ },
required: ['content']
}
},
// ... 7 more tools
]
}
});
Tool Execution Pattern
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'create_note': {
const note = await prisma.note.create({ /* ... */ });
return {
content: [{ type: 'text', text: JSON.stringify(parseNote(note)) }]
};
}
// ... handle other tools
}
} catch (error) {
throw new McpError(ErrorCode.InternalError, error.message);
}
});
Available MCP Tools
Note Management (6 tools)
| Tool | Purpose | Required Params |
|---|---|---|
| create_note | Create note | content |
| get_notes | List all notes | (optional) includeArchived, search |
| get_note | Get single note | id |
| update_note | Update note | id, (optional) fields to update |
| delete_note | Delete note | id |
| search_notes | Search notes | query |
Note Operations (2 tools)
| Tool | Purpose | Required Params |
|---|---|---|
| toggle_pin | Toggle pin status | id |
| toggle_archive | Toggle archive status | id |
Data Query (1 tool)
| Tool | Purpose | Required Params |
|---|---|---|
| get_labels | Get all unique labels | (none) |
Total Tools: 9 tools
Database Integration
Connection Details
const prisma = new PrismaClient({
datasources: {
db: {
url: `file:${join(__dirname, '../keep-notes/prisma/dev.db')}`
}
}
});
Path Resolution:
- From
mcp-server/index.js - To:
../keep-notes/prisma/dev.db - Absolute:
D:/dev_new_pc/Keep/keep-notes/prisma/dev.db
Database Access Pattern
Read Operations:
const notes = await prisma.note.findMany({
where: { /* conditions */ },
orderBy: [/* sorting */]
});
Write Operations:
const note = await prisma.note.create({
data: { /* note data */ }
});
Update Operations:
const note = await prisma.note.update({
where: { id: args.id },
data: { /* updates */ }
});
Delete Operations:
await prisma.note.delete({
where: { id: args.id }
});
Data Processing
JSON Field Parsing
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,
};
}
Purpose: Convert JSON strings from DB to JavaScript objects
Fields Parsed:
checkItems: Array of checklist itemslabels: Array of label namesimages: Array of image URLs/data
Error Handling
MCP Error Pattern
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
// Not found
throw new McpError(ErrorCode.InvalidRequest, 'Note not found');
// Server error
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error.message}`);
// Unknown tool
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
Error Types
| Error Code | Usage |
|---|---|
InvalidRequest |
Invalid parameters, resource not found |
MethodNotFound |
Unknown tool requested |
InternalError |
Server-side failures (DB, parsing, etc.) |
Tool Schemas
create_note
Input Schema:
{
"type": "object",
"properties": {
"title": { "type": "string" },
"content": { "type": "string" },
"color": { "type": "string", "enum": ["default","red",...] },
"type": { "type": "string", "enum": ["text","checklist"] },
"checkItems": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"text": { "type": "string" },
"checked": { "type": "boolean" }
},
"required": ["id","text","checked"]
}
},
"labels": { "type": "array", "items": { "type": "string" } },
"images": { "type": "array", "items": { "type": "string" } },
"isPinned": { "type": "boolean" },
"isArchived": { "type": "boolean" }
},
"required": ["content"]
}
update_note
Input Schema:
{
"type": "object",
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"content": { "type": "string" },
"color": { "type": "string" },
"checkItems": { "type": "array", "items": {/*...*/} },
"labels": { "type": "array", "items": { "type": "string" } },
"isPinned": { "type": "boolean" },
"isArchived": { "type": "boolean" },
"images": { "type": "array", "items": { "type": "string" } }
},
"required": ["id"]
}
Behavior: Only updates fields provided (partial update)
Transport Layer
Stdio Transport (Primary)
Implementation:
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const transport = new StdioServerTransport();
await server.connect(transport);
Use Cases:
- AI assistants (ChatGPT, Claude, etc.)
- N8N MCP integration
- Command-line tools
- Desktop applications
Communication:
- Input: STDIN (JSON-RPC messages)
- Output: STDOUT (JSON-RPC responses)
- Logging: STDERR (diagnostic messages)
SSE Transport (Alternative)
File: index-sse.js
Implementation: HTTP server with Server-Sent Events
Use Cases:
- Web-based clients
- Browser integrations
- Real-time notifications
- Long-running operations
Concurrency Model
Single-Threaded Event Loop
- Node.js event loop
- No parallel execution
- Sequential request handling
Database Concurrency
- SQLite handles concurrent reads
- Single writer limitation
- Prisma manages connection
Scalability
Current: Single instance Limitations:
- No load balancing
- No automatic failover
- Single point of failure
Future Enhancements:
- Multiple instances with connection pooling
- PostgreSQL for better concurrency
- Redis for session management
- Message queue for async operations
Security Architecture
Current: No Authentication
Rationale:
- Trusted environment (localhost)
- AI assistants need full access
- Simplified integration
Security Considerations
Risks:
- No access control
- No rate limiting
- No audit logging
- Direct database access
Recommendations for Production:
- Add API key authentication
- Implement rate limiting
- Add request logging
- Restrict to localhost or VPN
- Use reverse proxy (nginx) for SSL
Future Security
- JWT tokens for authentication
- Role-based access control
- IP whitelisting
- Request signing
- Audit logging
Performance Characteristics
Latency
- Direct DB access: ~1-5ms per query
- JSON parsing: ~0.1-0.5ms
- Total tool execution: ~5-20ms
Throughput
- Single-threaded: Limited by CPU
- SQLite: ~1000-5000 ops/sec
- Bottleneck: Database I/O
Optimization Opportunities
- Connection pooling: Reuse Prisma client
- Query optimization: Add indexes
- Caching: Redis for frequent queries
- Batching: Batch multiple operations
Monitoring & Observability
Current: Basic Logging
console.error('Memento MCP server running on stdio');
Logged to: STDERR (won't interfere with stdio transport)
Future Monitoring Needs
- Request Logging: Log all tool invocations
- Error Tracking: Sentry, Rollbar
- Performance Monitoring: Query latency
- Metrics: Tool usage statistics
- Health Checks:
/healthendpoint
Deployment Architecture
Development
cd mcp-server
npm install
npm start
# Connects via stdio
Production Options
Option 1: Standalone Process
node /path/to/mcp-server/index.js
Option 2: Docker Container
FROM node:20-alpine
COPY mcp-server/ /app
WORKDIR /app
RUN npm install
CMD ["node", "index.js"]
Option 3: Docker Compose
services:
mcp-server:
build: ./mcp-server
volumes:
- ./keep-notes/prisma:/app/db
Option 4: Process Manager
- PM2
- Systemd service
- Supervisord
Integration Patterns
N8N Workflow Integration
Setup: See N8N-CONFIG.md
Usage:
- Add MCP node in N8N
- Configure connection to mcp-server
- Select tools (create_note, search_notes, etc.)
- Build workflow
Example Workflow:
- Trigger: Webhook
- Tool: create_note
- Parameters: From webhook data
- Output: Created note
AI Assistant Integration
Supported Assistants:
- ChatGPT (via MCP plugin)
- Claude (via MCP plugin)
- Custom AI agents
Usage Pattern:
- User asks assistant: "Create a note about..."
- Assistant calls MCP tools
- Tools execute on Memento DB
- Results returned to assistant
- Assistant responds to user
Versioning & Compatibility
Current Version
Server: 1.0.0 MCP Protocol: 1.0.4
Backward Compatibility
- Tool schemas evolve
- New tools added (non-breaking)
- Existing tools maintained
Versioning Strategy
- Semantic versioning (MAJOR.MINOR.PATCH)
- MAJOR: Breaking changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes
Testing Strategy
Current: Manual Testing
- N8N workflow testing
- Direct stdio invocation
- SSE variant testing
Recommended Tests
- Unit Tests: Tool execution logic
- Integration Tests: Prisma operations
- E2E Tests: Full MCP protocol flow
- Load Tests: Concurrent tool execution
Test Tools
- Jest for unit tests
- Supertest for HTTP endpoints
- Playwright for E2E
- Artillery for load testing
Configuration Management
Environment Variables
Current: Hardcoded (dev.db path)
Recommended:
DATABASE_URL="file:../keep-notes/prisma/dev.db"
LOG_LEVEL="info"
PORT="3000" # For SSE variant
Configuration File
Future: config.json
{
"database": {
"url": "file:../keep-notes/prisma/dev.db"
},
"logging": {
"level": "info"
},
"server": {
"name": "memento-mcp-server",
"version": "1.0.0"
}
}
Maintenance & Operations
Startup
node index.js
# Output to stderr: "Memento MCP server running on stdio"
Shutdown
- Send SIGTERM (Ctrl+C)
- Graceful shutdown
- Close database connections
Health Checks
Future: /health endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime() })
})
Troubleshooting
Common Issues
1. Database Connection Failed
- Symptom: "Unable to connect to database"
- Cause: Incorrect path or missing DB file
- Fix: Verify
../keep-notes/prisma/dev.dbexists
2. Permission Denied
- Symptom: "EACCES: permission denied"
- Cause: File permissions on SQLite DB
- Fix: chmod 644 dev.db
3. Stdio Not Working
- Symptom: No response from server
- Cause: Client not connected to stdin/stdout
- Fix: Ensure proper stdio redirection
Future Enhancements
Short Term
- Add authentication
- Implement rate limiting
- Add request logging
- Health check endpoint
- Configuration file support
Long Term
- WebSocket support for real-time
- GraphQL integration
- Batch operations
- Transaction support
- Multi-database support
Summary
The mcp-server is a lightweight, focused microservice that:
- Exposes 9 MCP tools for note management
- Connects directly to SQLite via Prisma
- Uses stdio transport for AI/automation integration
- Provides N8N workflow integration
- Shares database with keep-notes web app
- Offers SSE variant for web clients
This architecture provides a clean separation of concerns while maintaining data consistency through the shared database layer.