brain_create_note
Create new notes with markdown content and frontmatter in an Obsidian vault, organizing them in designated folders for knowledge management.
Instructions
Create a new note in the vault.
By default notes are created in Notes/ per the vault workflow standard. Agent-created notes should always go to Notes/ first; promotion happens during manual vault review.
Args: params: Title, content (full markdown with frontmatter), and target folder.
Returns: Confirmation with the path of the created note, or an error if the note already exists.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Implementation Reference
- brain_mcp.py:483-518 (handler)The async handler function that implements the brain_create_note tool. It creates a new note file in the vault with the provided title, content, and folder, validates the path doesn't escape vault root, and returns JSON confirmation or error message.
async def brain_create_note(params: CreateNoteInput) -> str: """Create a new note in the vault. By default notes are created in Notes/ per the vault workflow standard. Agent-created notes should always go to Notes/ first; promotion happens during manual vault review. Args: params: Title, content (full markdown with frontmatter), and target folder. Returns: Confirmation with the path of the created note, or an error if the note already exists. """ try: folder = _resolve_path(params.folder) folder.mkdir(parents=True, exist_ok=True) filename = params.title if params.title.endswith(".md") else f"{params.title}.md" target = folder / filename if target.exists(): return f"Error: Note already exists at {_vault_relative(target)}. Use brain_update_note to modify it." target.write_text(params.content, encoding="utf-8") logger.info("Created note: %s", _vault_relative(target)) return json.dumps({ "status": "created", "path": _vault_relative(target), "title": target.stem, }, indent=2) except ValueError as e: return f"Error: {e}" except OSError as e: return f"Error creating note: {e}" - brain_mcp.py:277-295 (schema)Pydantic BaseModel defining the input schema for brain_create_note. Includes title (1-200 chars), content (full markdown with frontmatter), and folder (defaults to 'Notes'). Uses ConfigDict for string stripping.
class CreateNoteInput(BaseModel): """Input for creating a new note. Notes are created in Notes/ by default.""" model_config = ConfigDict(str_strip_whitespace=True) title: str = Field( ..., description="Note title (becomes the filename, e.g., '2026-03-12 - My Topic')", min_length=1, max_length=200, ) content: str = Field( ..., description="Full markdown content including frontmatter (use --- delimiters)", ) folder: str = Field( default="Notes", description="Vault-relative folder. Defaults to Notes/ per vault workflow.", ) - brain_mcp.py:473-482 (registration)Registration of the brain_create_note tool with the MCP server using the @mcp.tool decorator. Includes metadata annotations for title and behavior hints (not read-only, not destructive, not idempotent, closed world).
@mcp.tool( name="brain_create_note", annotations={ "title": "Create a New Note", "readOnlyHint": False, "destructiveHint": False, "idempotentHint": False, "openWorldHint": False, }, ) - brain_mcp.py:77-91 (helper)Helper utilities used by brain_create_note: _resolve_path validates and resolves vault-relative paths while preventing directory traversal attacks, and _vault_relative converts absolute paths back to vault-relative strings.
def _resolve_path(relative: str) -> Path: """Resolve a vault-relative path, preventing directory traversal.""" cleaned = relative.lstrip("/").lstrip("\\") full = (VAULT_ROOT / cleaned).resolve() if not str(full).startswith(str(VAULT_ROOT.resolve())): raise ValueError(f"Path escapes vault root: {relative}") return full def _vault_relative(absolute: Path) -> str: """Return vault-relative path string.""" try: return str(absolute.resolve().relative_to(VAULT_ROOT.resolve())) except ValueError: return str(absolute)