org-mcp NEWS -- history of user-visible changes

* Changes in org-mcp 0.10.0 (unreleased)

** New Features

*** Added `org-get-agenda' tool for day/week/month agenda views
    Returns the plain-text Org agenda for a day, week, or month span
    from an optional reference date, built from the allowed files.
    Thanks to Collins Conover (@PC-LoadLetter).

*** Added `org-grep' tool for searching across Org files
    Search for a literal substring across one or all allowed files.
    Results are grouped by containing section and annotated with
    headline paths and resource URIs (readable via =resources/read=).

*** Added `org-archive-subtree' tool for archiving subtrees
    Archives a headline subtree to its configured location (the
    headline `ARCHIVE' property, the file's `#+ARCHIVE:', or
    `org-archive-location'), and returns the archive file path and the
    headline's `org-id://' URI.  Thanks to Michael Cordell (@mcordell).

*** Send MCP `initialize' instructions
    The server now emits an `instructions' string in the MCP
    `initialize' response, carrying the cross-cutting tool and
    resource guidance that was previously repeated in each
    description.

*** Added `position' parameter to `org-add-todo'
    Optional `position' ("start" or "end", default "end") selects
    placement: first-child / last-child under a child `parent_uri',
    or start-of-file / end-of-file under a top-level `parent_uri'.
    Cannot be combined with `after_uri'.

** Bug Fixes

*** Fixed `org-update-todo-state' failing on no-state heading with `current_state=""'
    `string=' treats nil and `""' as distinct, so the documented
    "use empty string for no TODO state" path errored with
    "State mismatch" while the undocumented JSON null path
    silently worked.  `""' now matches a heading with no TODO
    state, and `current_state' rejects JSON null at the
    tool-boundary validator.

*** Stopped `org-edit-body' from returning the URI of a non-target heading
    The returned `org-id://' URI previously identified the parent's
    first child, or a strictly-deeper heading inside the new body.

*** Stopped `org-edit-body' from concatenating new content with the preceding line
    With `old_body=""' on a heading whose body is empty in a file
    without a trailing newline, the inserted content previously ran
    onto the heading line (or onto the property drawer's `:END:').
    Now the content lands on its own line, and `org-edit-body'
    always leaves the file ending in a newline.

*** Returned new-heading URI when `org-add-todo' `body' contains deeper headlines
    `body' may contain headlines deeper than the new heading
    (legitimate child structure), but the returned `org-id://' URI
    previously pointed to the deepest such headline instead of the
    newly added one.

*** Rejected `after_uri' with a top-level `parent_uri' in `org-add-todo'
    A top-level `parent_uri' (no fragment) combined with `after_uri'
    is now rejected as a validation error.

*** Rejected `after_uri' equal to `parent_uri' in `org-add-todo'
    Passing an `after_uri' whose `:ID:' resolves to `parent_uri'
    itself is now rejected as a validation error.

*** Restricted `org-add-todo' `position="start"' top-level anchor to level-1 headings
    The anchor previously matched any-level heading at column 0;
    an orphan deeper heading before the first level-1 was silently
    reparented under the new heading.

*** Normalised redundant separators in `org-headline://' URIs
    `org-mcp--split-headline-uri' now collapses both an empty
    fragment (`org-headline://FILE#') and a single trailing `/' on
    the file path to the same `(FILE . nil)' pair as the bare
    `org-headline://FILE'.  All of `org-headline://file.org',
    `org-headline://file.org/', `org-headline://file.org#',
    `org-headline://file.org/#', `org-headline://file.org#H' and
    `org-headline://file.org/#H' now resolve to equivalent
    `(FILE . HEADLINE)' pairs.

*** Removed trailing blank line at end of file in `org-add-todo' with body
    Adding a TODO with a non-empty `body' under a parent heading
    (either via `after_uri' against the parent's last child, or with
    `position="end"' when the parent is the file's last subtree) to a
    file that does not end with a trailing newline now produces a
    file ending in exactly one newline.  Mid-file placements still
    leave a single blank line between the body and the next heading.

*** Treated empty `body' string as no-body in `org-add-todo'
    Passing `body=""' (an empty string) now produces the same file
    as omitting `body' entirely.

*** Cached ID-fallback resolution to avoid repeated full-file scans
    Successful ID-fallback scans now register the (id, file) pair
    in `org-id-locations'.  Gated on `org-id-track-globally'.

*** Routed read-only `org-id://' resolution through allowed-files fallback
    Read-only `org-id://' resolution (`org-read-by-id' and the
    `org-id://{uuid}' resource) now falls back to scanning allowed
    files when `org-id-find-id-file' misses, matching the modifying
    tools' behaviour.

*** Clarified `org-id://' error when the resolved file is not allowed
    The MCP error now reads `ID '<uuid>' resolves to a file not in
    the allowed list'.  The resolved file path is intentionally not
    surfaced to avoid disclosing files outside `org-mcp-allowed-files'.

*** Rejected malformed `org-id://' and `org-headline://' URIs at the dispatch boundary
    A URI whose suffix after the prefix carries no meaningful content
    (empty, whitespace-only, or NBSP-only) or itself contains another
    URI scheme separator (e.g. `org-id://org-headline://foo') is now
    rejected with `Invalid resource URI format' at the validation
    boundary, instead of dispatching to a no-meaningful-content
    lookup that surfaced as `Cannot find ID '<chars>'' or a
    downstream file-access error.

*** Rejected unterminated file-header drawer across modifying tools
    Every modifying MCP tool now rejects a file whose header block
    contains a `:NAME:' drawer opener with no matching `:END:'.

*** Recognised `#+BEGIN_*' and `#+BEGIN:' block pairs in the file-header walker
    A named block (`#+BEGIN_NAME ... #+END_NAME') or dynamic block
    (`#+BEGIN: ... #+END:') at the start of an Org file is now
    consumed as a single bounded element.  An unterminated block is
    rejected, mirroring unterminated drawers.

*** Rejected heading line inside file-header drawer
    A heading line between a drawer opener and its `:END:' in the
    file's header block is now rejected as a validation error,
    matching Org's parser.

*** Strict `:END:' drawer terminator recognition
    Only a line that consists of exactly `:END:' (optionally followed
    by spaces or tabs) counts as the drawer terminator.  A line such
    as `:END:typo' does not close the drawer.

*** Strict drawer opener recognition in the file-header walker
    Only a line that consists of exactly `:NAME:' (any drawer
    keyword, optionally followed by spaces or tabs) opens a drawer.
    A line such as `:PROPERTIES: stray text' at column 0 is treated
    as ordinary content terminating the file's header block,
    matching how Org's own parser classifies the line.

*** Leading whitespace accepted on file-header lines
    Drawer openers (`:NAME:'), drawer closers (`:END:'), and
    `#'-prefixed lines (`#+'-keywords, `# '-comments, `#hashtag'
    paragraphs) are now accepted by the file-header walker with
    optional leading whitespace (spaces or tabs), matching how Org's
    parser already accepts indented forms.  Headings themselves
    still require column 0 (Org rule).

*** Any drawer keyword recognised in the file-header walker
    The file-header walker now recognises any well-formed drawer
    opener -- `:PROPERTIES:', `:LOGBOOK:', or any custom `:NAME:'
    keyword -- and consumes it through its `:END:'.  A bare `:END:'
    with no preceding opener is still treated as ordinary content
    terminating the header.

*** Uniform type validation for wire-protocol string parameters
    Every MCP tool now rejects non-string values for string-typed
    parameters with a uniform `Field X must be a string, got: V
    (type: T)' error.

*** Fixed spurious blank line in `org-add-todo' when parent is followed by content
    When adding a child under a parent heading that has following
    content (a top-level heading or any parent-level sibling), the
    new heading now lands flush against the following content.

*** Fixed default placement for top-level `org-add-todo'
    When called at top level (parent URI with no fragment) on a file
    that already contains top-level headings, the default placement
    now appends the new heading at end of file, as the tool's
    description documents.

*** Rejected empty or whitespace-only `after_uri' in `org-add-todo'
    `after_uri=""' and any whitespace-only value (regular spaces,
    tabs, NBSP) are now validation errors.  Omit the JSON key
    entirely when not specifying a sibling.  The bare prefix
    `after_uri="org-id://"' (with no UUID after the scheme) is
    also rejected at the validation boundary.

*** Case-sensitive body matching in `org-edit-body'
    The MCP tool `org-edit-body' now matches the `old_body' parameter
    case-sensitively.

*** Marked `org-edit-body's `replace_all' as optional in the JSON Schema
    The published JSON Schema now reports `replace_all' as not
    required, matching the tool's documented default of `false'.

*** Fixed `org-edit-body' silently replacing all on `replace_all=false'
    The JSON boolean `false' is now recognised for the `replace_all'
    parameter (e.g. `{"replace_all": false}'), so the
    multiple-occurrence safety guard fires on ambiguous matches as
    documented.

** Incompatible Changes

*** Now requires `mcp-server-lib' 0.4.0
    The MCP `initialize' `serverInfo' now reports org-mcp's own name
    and version instead of the library's defaults.

* Changes in org-mcp 0.9.0 (2025-11-11)

** Initial release
