Modify Tags
tags.modifyModify the frontmatter tags list of a note in an Obsidian vault using add, remove, replace, or merge operations. Idempotent, returns the new tag list. Use with vault-relative paths.
Instructions
Mutate the frontmatter tags list of a single note. Four ops are supported: add unions the incoming tags with the existing list (duplicates dropped); remove drops any incoming tag currently present; replace overwrites the list entirely; merge is an alias for add. Leading # on incoming tags is stripped automatically. This tool only touches the frontmatter block — inline #tag occurrences in the body are left untouched. Idempotent: repeated calls with the same op and tags converge on the same result. Returns {changed, target, summary, op, tagsAfter}.
Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins.
Examples:
Example 1 — Add two tags to a note (idempotent):
{
"path": "Projects/Alpha.md",
"op": "add",
"tags": [
"in-progress",
"priority/high"
]
}Example 2 — Replace a note's entire tag set:
{
"path": "Inbox/today.md",
"op": "replace",
"tags": [
"processed"
]
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Vault-relative note path to mutate. | |
| op | Yes | The mutation to apply to the note's frontmatter `tags` field: - `add` — union existing + incoming (duplicates dropped). - `remove` — drop any incoming tag that currently exists. - `replace` — overwrite the tag list entirely with `tags`. - `merge` — alias for `add` (kept for naming clarity; behaves identically). | |
| tags | Yes | Tags to add/remove/replace with. Leading `#` is stripped automatically. Max 50 per call. | |
| vaultPath | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| changed | Yes | ||
| target | Yes | ||
| summary | Yes | ||
| op | Yes | The mutation to apply to the note's frontmatter `tags` field: - `add` — union existing + incoming (duplicates dropped). - `remove` — drop any incoming tag that currently exists. - `replace` — overwrite the tag list entirely with `tags`. - `merge` — alias for `add` (kept for naming clarity; behaves identically). | |
| tagsBefore | No | ||
| tagsAfter | Yes | The full tag list after the mutation. |
Implementation Reference
- src/server/tools/tags.ts:36-49 (handler)Main handler for 'tags.modify' tool. Parses args, dispatches to addTags/removeTags/updateTags based on the 'op' field, and returns the result with tagsAfter.
handler: async (context, rawArgs) => { const args = tagsModifyArgsSchema.parse(rawArgs) as TagsModifyArgs; const mutationArgs = { path: args.path, tags: args.tags, vaultPath: args.vaultPath }; if (args.op === "add" || args.op === "merge") { const result = await addTags(context, mutationArgs); return { ...result, op: args.op, tagsAfter: result.allTags }; } if (args.op === "remove") { const result = await removeTags(context, mutationArgs); return { ...result, op: args.op, tagsAfter: result.remainingTags }; } const result = await updateTags(context, { ...mutationArgs, merge: false }); return { ...result, op: args.op, tagsAfter: result.newTags }; }, - src/schema/tags.ts:17-28 (schema)Input schema for tags.modify: path, op (add/remove/replace/merge), tags array, optional vaultPath.
export const tagsModifyArgsSchema = z .object({ path: notePathSchema.describe("Vault-relative note path to mutate."), op: tagModifyOpSchema, tags: tagsSchema.describe( "Tags to add/remove/replace with. Leading `#` is stripped automatically. Max 50 per call.", ), vaultPath: z.string().optional(), }) .strict() .describe("Arguments for `tags.modify`."); export type TagsModifyArgs = z.input<typeof tagsModifyArgsSchema>; - src/schema/tags.ts:64-73 (schema)Output schema for tags.modify: changed, target, summary, op, tagsBefore (optional), tagsAfter.
export const tagsModifyOutputSchema = z .object({ changed: z.boolean(), target: z.string(), summary: z.string(), op: tagModifyOpSchema, tagsBefore: z.array(z.string()).optional(), tagsAfter: z.array(z.string()).describe("The full tag list after the mutation."), }) .passthrough(); - src/schema/tags.ts:4-14 (schema)Enum schema for the operation type: add, remove, replace, merge.
export const tagModifyOpSchema = z .enum(["add", "remove", "replace", "merge"]) .describe( [ "The mutation to apply to the note's frontmatter `tags` field:", "- `add` — union existing + incoming (duplicates dropped).", "- `remove` — drop any incoming tag that currently exists.", "- `replace` — overwrite the tag list entirely with `tags`.", "- `merge` — alias for `add` (kept for naming clarity; behaves identically).", ].join(" "), ); - src/server/registry.ts:18-34 (registration)Registers tagTools (including tags.modify) into the tool registry via spread operator.
export const toolRegistry: ToolDefinition[] = [ ...vaultTools, ...noteTools, ...tagTools, ...linkTools, ...analyticsTools, ...taskTools, ...dataviewTools, ...blocksTools, ...marpTools, ...kanbanTools, ...canvasTools, ...templateTools, ...apiTools, ...wikiTools, ...systemTools, ];