Skip to main content
Glama

write_note

Save or update notes in your personal knowledge management system by specifying a path and content, and optionally adding tags for better organization.

Instructions

Create a new note or overwrite an existing note with content. Path should be relative to your notes directory. Optionally include tags that will be merged with any existing tags in the note.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYesContent to write to the note
pathYesPath where the note should be saved, relative to notes directory
tagsNoTags must follow these rules: - Can contain letters, numbers, underscore (_), hyphen (-), and forward slash (/) - Must contain at least one non-numerical character - Cannot contain spaces (use camelCase, PascalCase, snake_case, or kebab-case) - Are case-insensitive (stored as lowercase) Tags to add to the note's frontmatter. Will be merged with existing tags if present.

Implementation Reference

  • Main handler logic for the 'write_note' tool. Validates input, creates and saves a Note object with content and tags, and optionally appends a link to the daily log.
    case "write_note": {
      try {
        const writeNoteArgs = args as WriteNoteArgs;
        
        // Validate parameters
        if (!writeNoteArgs.path) {
          throw new Error("'path' parameter is required");
        }
        
        if (writeNoteArgs.content === undefined) {
          throw new Error("'content' parameter is required");
        }
        
        try {
          // Create a Note instance with content only
          const note = new Note(notesPath, writeNoteArgs.path, {
            content: writeNoteArgs.content
          });
          
          // Add tags separately to handle validation errors
          if (writeNoteArgs.tags && writeNoteArgs.tags.length > 0) {
            note.addTags(writeNoteArgs.tags);
          }
          
          // Save the note
          const result = await note.save();
        
        if (!result.success) {
          return {
            content: [{ type: "text", text: `Error writing note: ${result.error}` }],
            isError: true
          };
        }
        
        // After successfully saving the note, append an entry to the daily log
        try {
          // Create a LogNote instance for today
          const logNote = new LogNote(notesPath);
          
          // Load existing content if available
          await logNote.load();
          
          // Format the note path for wikilink
          // Remove file extension if present and get the base name
          const notePath = writeNoteArgs.path;
          const noteBaseName = path.basename(notePath, path.extname(notePath));
          
          // Create wikilink using Obsidian convention
          const wikilink = `[[${noteBaseName}]]`;
          
          // Create log entry with the wikilink
          const logEntry = `Created note: ${wikilink}`;
          
          // Append the entry to the log
          await logNote.appendEntry(logEntry);
          
          return {
            content: [{ 
              type: "text", 
              text: `Successfully wrote note to: ${writeNoteArgs.path} and updated daily log with link` 
            }]
          };
        } catch (logError) {
          const errorMessage = logError instanceof Error ? logError.message : String(logError);
          console.error("Error updating log with note link:", logError);
          
          // Still return success for the note creation even if log update fails
          return {
            content: [{ 
              type: "text", 
              text: `Successfully wrote note to: ${writeNoteArgs.path} (but failed to update daily log: ${errorMessage})` 
            }]
          };
        }
        } catch (tagError) {
          const errorMessage = tagError instanceof Error ? tagError.message : String(tagError);
          return {
            content: [{ type: "text", text: `Error writing note: ${errorMessage}` }],
            isError: true
          };
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          content: [{ type: "text", text: `Error writing note: ${errorMessage}` }],
          isError: true
        };
      }
    }
  • TypeScript interface defining the expected input arguments for the write_note tool.
    interface WriteNoteArgs {
      path: string;
      content: string;
      tags?: string[];
    }
  • Tool registration in getToolDefinitions(), including name, description, and JSON input schema.
    {
      name: "write_note",
      description: "Create a new note or overwrite an existing note with content. " +
        "Path should be relative to your notes directory. " +
        "Optionally include tags that will be merged with any existing tags in the note.",
      inputSchema: {
        type: "object",
        properties: {
          path: { 
            type: "string",
            description: "Path where the note should be saved, relative to notes directory" 
          },
          content: {
            type: "string",
            description: "Content to write to the note"
          },
          tags: {
            type: "array",
            items: { type: "string" },
            description: `${TAG_VALIDATION_DESCRIPTION}
            
            Tags to add to the note's frontmatter. Will be merged with existing tags if present.`
          }
        },
        required: ["path", "content"]
      },
    },
  • The Note.save() method called by the handler, which generates frontmatter (with created date and tags), ensures the directory exists, and writes the file.
    async save(): Promise<SaveResult> {
      try {
        // Ensure directory exists
        await this._ensureDirectoryExists();
        
        // Create content with frontmatter
        const frontmatter = this._createFrontmatter();
        const finalContent = frontmatter + this.content;
        
        // Write to file
        await fs.writeFile(this.fullPath, finalContent, 'utf8');
        
        return {
          success: true,
          path: this.relativePath,
          content: finalContent
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          success: false,
          error: errorMessage
        };
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but only partially discloses behavior. It states the tool can create or overwrite notes and merge tags, but doesn't cover permissions, error handling, or response format. This is inadequate for a mutation tool with zero annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and efficiently covers key points in two sentences without redundancy. Every sentence adds necessary information, making it appropriately sized and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description is incomplete. It lacks details on permissions, error cases, response format, and how overwriting interacts with existing data beyond tags, leaving significant gaps for agent understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents parameters. The description adds minimal value by clarifying tag merging behavior and path relativity, but doesn't provide additional syntax or format details beyond what the schema already specifies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Create a new note or overwrite an existing note') and resource ('note with content'), distinguishing it from read operations like read_note. However, it doesn't explicitly differentiate from other write-related siblings like create_directory or log, which slightly reduces specificity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives is provided. The description mentions path relativity and tag merging, but lacks context on prerequisites, error conditions, or comparisons to siblings like create_directory or log, leaving usage decisions ambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/markacianfrani/mcp-notes'

If you have feedback or need assistance with the MCP directory API, please join our Discord server