Keep/docs/api-contracts-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

453 lines
10 KiB
Markdown

# API Contracts - mcp-server (Memento MCP Server)
## Overview
The mcp-server provides a Model Context Protocol (MCP) interface for integrating Memento with AI assistants and automation tools like N8N. It exposes tools (functions) that can be called via the MCP protocol.
**Protocol:** Model Context Protocol (MCP) SDK v1.0.4
**Transport:** Stdio (standard input/output)
**Type:** JavaScript ES modules
**Database:** Shared Prisma SQLite database (connects to `keep-notes/prisma/dev.db`)
---
## MCP Tools
The server exposes the following MCP tools that can be invoked by MCP clients:
---
### create_note
Create a new note in Memento.
**Parameters:**
```json
{
"title": "string (optional) - Note title",
"content": "string (required) - Note content",
"color": "string (optional, default: 'default') - Note color: default|red|orange|yellow|green|teal|blue|purple|pink|gray",
"type": "string (optional, default: 'text') - Note type: 'text' or 'checklist'",
"checkItems": "array (optional) - Checklist items (if type is 'checklist')",
"labels": "array (optional) - Note labels/tags",
"isPinned": "boolean (optional, default: false) - Pin the note",
"isArchived": "boolean (optional, default: false) - Archive the note",
"images": "array (optional) - Note images as base64 encoded strings"
}
```
**Response (text content):**
```json
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"isPinned": boolean,
"isArchived": boolean,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
```
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### get_notes
Get all notes from Memento with optional filtering.
**Parameters:**
```json
{
"includeArchived": "boolean (optional, default: false) - Include archived notes",
"search": "string (optional) - Search query to filter notes (case-insensitive)"
}
```
**Response (text content):**
```json
[
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"isPinned": boolean,
"isArchived": boolean,
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"order": number,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
]
```
**Ordering:** `isPinned DESC`, `order ASC`, `updatedAt DESC`
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### get_note
Get a specific note by ID.
**Parameters:**
```json
{
"id": "string (required) - Note ID"
}
```
**Response (text content):**
```json
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"isPinned": boolean,
"isArchived": boolean,
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"reminder": "ISO8601 datetime" | null,
"isReminderDone": boolean,
"reminderRecurrence": "string" | null,
"reminderLocation": "string" | null,
"isMarkdown": boolean,
"size": "small|medium|large",
"embedding": "JSON string" | null,
"userId": "string" | null,
"order": number,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
```
**Error Response:**
- Throws `McpError` with `ErrorCode.InvalidRequest` if note not found
- Throws `McpError` with `ErrorCode.InternalError` on other failures
---
### update_note
Update an existing note.
**Parameters:**
```json
{
"id": "string (required) - Note ID",
"title": "string (optional) - Note title",
"content": "string (optional) - Note content",
"color": "string (optional) - Note color",
"checkItems": "array (optional) - Checklist items",
"labels": "array (optional) - Note labels",
"isPinned": "boolean (optional) - Pin status",
"isArchived": "boolean (optional) - Archive status",
"images": "array (optional) - Note images as base64 encoded strings"
}
```
**Response (text content):**
```json
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"isPinned": boolean,
"isArchived": boolean,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
```
**Notes:**
- Automatically updates `updatedAt` timestamp
- Arrays (checkItems, labels, images) are JSON-encoded before storage
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### delete_note
Delete a note by ID.
**Parameters:**
```json
{
"id": "string (required) - Note ID"
}
```
**Response (text content):**
```json
{
"success": true,
"message": "Note deleted"
}
```
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### search_notes
Search notes by query.
**Parameters:**
```json
{
"query": "string (required) - Search query"
}
```
**Response (text content):**
```json
[
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"isPinned": boolean,
"isArchived": boolean,
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
]
```
**Search Logic:**
- Only searches non-archived notes (`isArchived: false`)
- Case-insensitive search across `title` and `content` fields
- Uses SQL `LIKE` operator (contains matching)
**Ordering:** `isPinned DESC`, `updatedAt DESC`
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### get_labels
Get all unique labels from all notes.
**Parameters:**
```json
{}
```
**Response (text content):**
```json
[
"label1",
"label2",
"label3"
]
```
**Notes:**
- Extracts labels from all notes in the database
- Labels are stored as JSON arrays in the `labels` field
- Returns unique, sorted list of all label strings
- Does not create or use the Label table (reads directly from Note.labels)
**Error Response:**
- Throws `McpError` with `ErrorCode.InternalError` on failure
---
### toggle_pin
Toggle pin status of a note.
**Parameters:**
```json
{
"id": "string (required) - Note ID"
}
```
**Response (text content):**
```json
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"isPinned": boolean, // Toggled value
"isArchived": boolean,
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
```
**Error Response:**
- Throws `McpError` with `ErrorCode.InvalidRequest` if note not found
- Throws `McpError` with `ErrorCode.InternalError` on other failures
---
### toggle_archive
Toggle archive status of a note.
**Parameters:**
```json
{
"id": "string (required) - Note ID"
}
```
**Response (text content):**
```json
{
"id": "clxxxxxxx",
"title": "string or null",
"content": "string",
"color": "string",
"isPinned": boolean,
"isArchived": boolean, // Toggled value
"type": "text|checklist",
"checkItems": [...] | null,
"labels": [...] | null,
"images": [...] | null,
"createdAt": "ISO8601 datetime",
"updatedAt": "ISO8601 datetime"
}
```
**Error Response:**
- Throws `McpError` with `ErrorCode.InvalidRequest` if note not found
- Throws `McpError` with `ErrorCode.InternalError` on other failures
---
## Server Information
**Name:** `memento-mcp-server`
**Version:** `1.0.0`
**Capabilities:** Tools only (no resources or prompts)
## Connection Details
**Transport:** Stdio (Standard Input/Output)
**Entry Point:** `index.js`
**Alternative SSE Endpoint:** `index-sse.js` (Server-Sent Events variant)
## Database Connection
```javascript
const prisma = new PrismaClient({
datasources: {
db: {
url: `file:${join(__dirname, '../keep-notes/prisma/dev.db')}`
}
}
});
```
**Important:** The MCP server connects directly to the keep-notes SQLite database file, sharing the same data as the web application.
## Error Handling
All errors are returned as MCP errors with appropriate error codes:
- `ErrorCode.InvalidRequest`: Invalid parameters or resource not found
- `ErrorCode.MethodNotFound`: Unknown tool requested
- `ErrorCode.InternalError`: Server-side errors (database, parsing, etc.)
## Usage Example
With N8N or MCP-compatible client:
```javascript
// List available tools
server.tools()
// Create a note
server.callTool('create_note', {
title: 'Meeting Notes',
content: 'Discussed Q1 roadmap...',
labels: ['work', 'planning'],
color: 'blue'
})
// Search notes
server.callTool('search_notes', {
query: 'roadmap'
})
// Toggle pin
server.callTool('toggle_pin', {
id: 'clxxxxxxx'
})
```
---
## Helper Functions
### parseNote(dbNote)
Internal helper to parse JSON fields from database:
```javascript
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,
};
}
```
This function is called on all note objects returned to clients to ensure JSON arrays are properly parsed from their string representation in the database.
---
## Integration with N8N
The MCP server is designed to work with N8N workflows:
1. Install N8N MCP integration
2. Configure connection to `memento-mcp-server`
3. Use tools in N8N nodes:
- Create notes from external sources
- Search and retrieve notes
- Update notes based on triggers
- Archive/delete old notes
- Extract labels for categorization
---
## Notes
- **No Authentication:** MCP server runs with direct database access (no auth layer)
- **Shared Database:** Uses same SQLite file as keep-notes web app
- **JSON Encoding:** Array fields (checkItems, labels, images) stored as JSON strings
- **Idempotent Operations:** Toggle operations (pin, archive) are idempotent
- **Case-Insensitive Search:** Search operations use `mode: 'insensitive'`
- **Ordering:** Results sorted by relevance (pinned first, then by date)