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
TableJSON 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}`); } }