Keep/docs/architecture-mcp-server.md
sepehr 640fcb26f7 fix: improve note interactions and markdown LaTeX support
## 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
2026-01-09 22:13:49 +01:00

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

  1. MCP Client (AI assistant, N8N) connects via stdio
  2. Request: Tool invocation with parameters
  3. Processing: Server executes business logic
  4. Database: Prisma queries/updates
  5. 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 items
  • labels: Array of label names
  • images: 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:

  1. Add API key authentication
  2. Implement rate limiting
  3. Add request logging
  4. Restrict to localhost or VPN
  5. 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

  1. Connection pooling: Reuse Prisma client
  2. Query optimization: Add indexes
  3. Caching: Redis for frequent queries
  4. 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

  1. Request Logging: Log all tool invocations
  2. Error Tracking: Sentry, Rollbar
  3. Performance Monitoring: Query latency
  4. Metrics: Tool usage statistics
  5. Health Checks: /health endpoint

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:

  1. Add MCP node in N8N
  2. Configure connection to mcp-server
  3. Select tools (create_note, search_notes, etc.)
  4. 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:

  1. User asks assistant: "Create a note about..."
  2. Assistant calls MCP tools
  3. Tools execute on Memento DB
  4. Results returned to assistant
  5. 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
  1. Unit Tests: Tool execution logic
  2. Integration Tests: Prisma operations
  3. E2E Tests: Full MCP protocol flow
  4. 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.db exists

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

  1. Add authentication
  2. Implement rate limiting
  3. Add request logging
  4. Health check endpoint
  5. Configuration file support

Long Term

  1. WebSocket support for real-time
  2. GraphQL integration
  3. Batch operations
  4. Transaction support
  5. 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.