update_note
Modify existing notes in Flint Note by updating content, metadata, or both, supporting single or batch operations with optimistic locking.
Instructions
Update one or more existing notes
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| identifier | No | Note identifier in format "type/filename" or full path - only used for single note update | |
| content | No | New content for the note - only used for single note update | |
| content_hash | No | Content hash of the current note for optimistic locking - required for single note update | |
| metadata | No | Metadata fields to update - only used for single note update | |
| updates | No | Array of note updates (must specify content, metadata, or both) - used for batch updates | |
| vault_id | No | Optional vault ID to operate on. If not provided, uses the current active vault. |
Implementation Reference
- src/server/note-handlers.ts:188-248 (handler)MCP tool handler for 'update_note'. Validates args, handles single or batch updates by calling NoteManager methods (updateNote, updateNoteWithMetadata, batchUpdateNotes), returns JSON result.
handleUpdateNote = async (args: UpdateNoteArgs) => { // Validate arguments validateToolArgs('update_note', args); const { noteManager } = await this.resolveVaultContext(args.vault_id); // Handle batch updates if updates array is provided if (args.updates) { const result = await noteManager.batchUpdateNotes(args.updates); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } // Handle single note update // Note: validation already handled by validateToolArgs const identifier = args.identifier!; // Safe after validation const contentHash = args.content_hash!; // Safe after validation let result; if (args.content !== undefined && args.metadata !== undefined) { // Both content and metadata update result = await noteManager.updateNoteWithMetadata( identifier, args.content, args.metadata as NoteMetadata, contentHash ); } else if (args.content !== undefined) { // Content-only update result = await noteManager.updateNote(identifier, args.content, contentHash); } else if (args.metadata !== undefined) { // Metadata-only update const currentNote = await noteManager.getNote(identifier); if (!currentNote) { throw new Error(`Note '${identifier}' not found`); } result = await noteManager.updateNoteWithMetadata( identifier, currentNote.content, args.metadata as NoteMetadata, contentHash ); } else { throw new Error('Either content or metadata must be provided for update'); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; }; - src/server.ts:1246-1249 (registration)Registration of 'update_note' tool in MCP server's CallToolRequestSchema handler switch statement. Routes to NoteHandlers.handleUpdateNote.
case 'update_note': return await this.noteHandlers.handleUpdateNote( args as unknown as UpdateNoteArgs ); - src/server.ts:517-581 (schema)Input schema definition for 'update_note' tool advertised in ListToolsRequestSchema response.
{ name: 'update_note', description: 'Update one or more existing notes', inputSchema: { type: 'object', properties: { identifier: { type: 'string', description: 'Note identifier in format "type/filename" or full path - only used for single note update' }, content: { type: 'string', description: 'New content for the note - only used for single note update' }, content_hash: { type: 'string', description: 'Content hash of the current note for optimistic locking - required for single note update' }, metadata: { type: 'object', description: 'Metadata fields to update - only used for single note update', additionalProperties: true }, updates: { type: 'array', items: { type: 'object', properties: { identifier: { type: 'string', description: 'Note identifier in format "type/filename" or full path' }, content: { type: 'string', description: 'New content for the note' }, content_hash: { type: 'string', description: 'Content hash for optimistic locking' }, metadata: { type: 'object', description: 'Metadata fields to update', additionalProperties: true } }, required: ['identifier', 'content_hash'] }, description: 'Array of note updates (must specify content, metadata, or both) - used for batch updates' }, vault_id: { type: 'string', description: 'Optional vault ID to operate on. If not provided, uses the current active vault.' } }, required: [] } }, - src/server/types.ts:50-62 (schema)TypeScript interface UpdateNoteArgs defining the input parameters for the update_note tool handler.
export interface UpdateNoteArgs { identifier?: string; content?: string; metadata?: Record<string, unknown>; content_hash?: string; updates?: Array<{ identifier: string; content?: string; metadata?: Record<string, unknown>; content_hash: string; }>; vault_id?: string; } - src/core/notes.ts:517-570 (helper)Core NoteManager.updateNote method: handles content-only updates with optimistic locking via content hash, preserves metadata, updates search index.
async updateNote( identifier: string, newContent: string, contentHash: string ): Promise<UpdateResult> { try { if (!contentHash) { throw new MissingContentHashError('note update'); } const { typeName: _typeName, filename: _filename, notePath } = this.parseNoteIdentifier(identifier); // Check if note exists try { await fs.access(notePath); } catch { throw new Error(`Note '${identifier}' does not exist`); } // Read current content to preserve metadata const currentContent = await fs.readFile(notePath, 'utf-8'); const parsed = this.parseNoteContent(currentContent); // Validate content hash to prevent conflicts validateContentHash(parsed.content, contentHash); // Update the content while preserving metadata const updatedMetadata = { ...parsed.metadata, updated: new Date().toISOString() }; const updatedContent = this.formatUpdatedNoteContent(updatedMetadata, newContent); // Write updated content await fs.writeFile(notePath, updatedContent, 'utf-8'); // Update search index await this.updateSearchIndex(notePath, updatedContent); return { id: identifier, updated: true, timestamp: updatedMetadata.updated }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; throw new Error(`Failed to update note '${identifier}': ${errorMessage}`); } } - src/core/notes.ts:657-719 (helper)Core NoteManager.updateNoteWithMetadata: handles content + metadata updates, protects critical fields like title/filename unless bypassed.
async updateNoteWithMetadata( identifier: string, content: string, metadata: NoteMetadata, contentHash: string, bypassProtection: boolean = false ): Promise<UpdateResult> { try { if (!contentHash) { throw new MissingContentHashError('note metadata update'); } const { typeName: _typeName, filename: _filename, notePath } = this.parseNoteIdentifier(identifier); // Check if note exists try { await fs.access(notePath); } catch { throw new Error(`Note '${identifier}' does not exist`); } // Read current content to preserve existing metadata const currentContent = await fs.readFile(notePath, 'utf-8'); const parsed = this.parseNoteContent(currentContent); // Validate content hash to prevent conflicts validateContentHash(parsed.content, contentHash); // Check for protected fields unless bypassing protection if (!bypassProtection) { this.#validateNoProtectedFields(metadata); } // Merge metadata with existing metadata const updatedMetadata = { ...parsed.metadata, ...metadata, updated: new Date().toISOString() }; // Format content with metadata const formattedContent = this.formatUpdatedNoteContent(updatedMetadata, content); // Write updated content await fs.writeFile(notePath, formattedContent, 'utf-8'); // Update search index await this.updateSearchIndex(notePath, formattedContent); return { id: identifier, updated: true, timestamp: updatedMetadata.updated }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; throw new Error(`Failed to update note '${identifier}': ${errorMessage}`); } }