Revision history for MCP::Server::Tool::FileSystem

0.2.2  2026-08-21T21:05:33+01:00
    - Require MCP::Server 0.6.0, the release that actually ships what 0.2.1
      leans on: tool annotations in the tools/list catalog, the annotated-batch
      concurrent scheduler, and the bridge's content-only rendering. The old
      0.4.0+ pin let a resolver hand this pack a server that silently dropped
      its annotations.

0.2.1  2026-08-21T20:36:03+01:00
    - The six reading tools (read, list, glob, stat, grep, map) now declare the
      MCP annotations readOnlyHint and idempotentHint, so a host may run a
      batch of them side by side and execute identical calls once. The claim
      is about the handlers as much as the intent: the observation tools take
      this instance's mutation lock and the map cache is behind its own, so
      several reads on ONE pack instance see coherent state. The five mutating
      tools deliberately declare nothing.

0.2.0  2026-08-20T22:49:48+01:00
    - New reading tool: map(path, query, focus, max-chars). Returns a ranked
      skeleton of the code below a directory — each file as its path plus the
      definition lines of its most-referenced symbols, never bodies — so a
      model can orient in an unfamiliar tree with one call instead of a dozen
      speculative reads. Ranking is a personalized PageRank over the
      "file A refers to something file B defines" graph; query re-centres it on
      matching identifiers and focus on named paths. Registered on read-only
      kits too.
    - map understands C, C++, Go, Java, JavaScript, Python, Rust, TypeScript
      and TSX through TreeSitter::Native, and Raku through a heuristic scanner
      (no tree-sitter grammar for Raku exists), where `use Foo::Bar` resolves
      to the file that is Foo::Bar. The pack ships supplemental tree-sitter
      queries for C and C++ (whose vendored tags.scm files tag no references at
      all) and for TypeScript/TSX (whose vendored file tags only signature
      declarations, missing every function with a body).
    - map picks a file's representative definitions by how much the tree refers
      to them, sharing a name's references among the files that define it and
      setting aside names most of the map refers to. Matched-by-name references
      cannot tell whose `new` they mean, and counting them naively made
      "method new" the single line printed for three unrelated files.
    - map's Raku scanner skips declarator documentation in every form, the
      bracketed `#|( ... )` and `#=( ... )` blocks included. Their continuation
      lines carry no `#`, so prose about code — which says "sub", "class" and
      "constant" constantly — was being read as code and could outrank the
      declarations around it.
    - map bounds itself: prunes .git, node_modules, build output and every
      dot-directory; skips files over 1MB, binaries and unreadable files, and
      counts them; stops after 5000 files; and fits its answer into max-chars
      by dropping whole files rather than truncating one. Parse results are
      cached per file on modification time and size, so mapping, editing one
      file and mapping again re-reads only that file.
    - New dependency: TreeSitter::Native.
    - Tool results now include structured `sha256-v1` revisions while preserving
      their existing text. Files hash exact bytes; directories hash a canonical
      recursive tree; absent paths have a stable token.
    - SECURITY: write, edit, mkdir, move and delete accept the unadvertised
      `_expected-revisions` condition. Compare and mutation share one provider
      lock, stale conditions perform no mutation, and concurrent conditional
      writers cannot both win.

0.1.1  2026-08-09T20:26:49+01:00
    - SECURITY: symlink containment now holds on Windows. The defence relied
      on IO::Path.resolve, which does not follow symlinks there, so a link
      planted in the sandbox could be read and written straight through
      (caught by the Windows CI lane). Containment now chases links itself
      (physical-path, realpath-style over readlink, hop budget against
      cycles, fail-closed on unreadable or drive-relative targets) — one
      code path on every platform. path-contains unifies separator spelling
      on Windows; the root itself is chased at construction. New containment
      tests: chains, mid-path directory links, relative targets (in-root
      '..' targets still read through), cycles.

0.1.0  2026-08-09T20:00:53+01:00
    - Initial release
    - MCP::Server::Tool::FileSystem: a root-confined filesystem toolkit that
      plugs into any MCP::Server via $server.plug($kit) or
      MCP::Server.new(:tools['FileSystem' => { root => '...' }]).
    - Tools (default prefix "fs"): read, write, edit, list, glob, grep, stat,
      mkdir, move, delete. A read-only kit registers only the five reading
      tools — read, list, glob, grep and stat — and never advertises the rest.
    - edit(path, old-string, new-string, replace-all): exact-match replacement
      with no fuzzy matching. Zero matches, an ambiguous match without
      replace-all, an empty old-string and an old-string equal to new-string
      are all refused; the confirmation counts occurrences and bytes.
    - read gained optional offset (1-based) and limit. Omitting both returns
      the file byte for byte as before; a range comes back numbered "N<tab>text"
      with a "[lines A-B of N total]" footer, and reading past the end is an
      answer rather than an error.
    - grep(pattern, path, regex, glob, context, max-results): GNU-grep-shaped
      output ("path:line:text", "-" for context lines, "--" between groups).
      Literal by default; regex mode is a native Raku regex, and a pattern
      carrying a code block is refused by Rakudo's interpolation guard rather
      than executed. Binary and non-UTF-8 files are skipped and counted, and
      the search stops at max-results with a notice.
    - move(from, to) and delete(path, recursive). Both ends of a move go
      through the resolver; an existing destination, a missing destination
      parent, and moving or deleting the root itself are all refused. A
      recursive delete unlinks symlinks instead of following them.
    - Files are read as bytes and decoded once rather than slurped as text:
      Raku's text-mode slurp folds CRLF into LF and spurt never puts it back,
      which would have made every edit of a CRLF file rewrite the lines it did
      not touch. read is now verbatim in the sense edit needs it to be.
    - Parameter naming is a documented contract: location parameters are
      exactly "path", "from" and "to", and multi-word parameters are
      kebab-case, so a permission layer in front of the server can find the
      paths a call would touch by name.
    - Containment: every path parameter must be relative, may not contain ".."
      and may not name a Windows device; the nearest existing ancestor is
      resolved and checked against the root by parent-chain walk, so symlinks
      planted inside the sandbox cannot be used as a door out of it.
    - glob is a portable, regex-free matcher: "*" matches within one path
      segment, "?" matches one character, nothing else is special.
