Skip to content

Changelog

All notable changes to doc-processing-std-tools are recorded here. Versions follow vMAJOR.MINOR.PATCH, equal to pyproject.toml's version, and every release is tagged.

Unreleased

Nothing yet.

0.6.1 — 2026-10-09

A security and documentation release: the desktop server's key input can no longer run commands or slip past the confirmation gate, the private-address check covers every non-public address, and the docs site is ready to be linked publicly. Upgrade by pinning @v0.6.1.

Migration notes

  • The fetch tools and the browser refuse every address that is not public. Carrier-grade NAT addresses (100.64.0.0/10, which Tailscale uses), 0.0.0.0, the IETF protocol block 192.0.0.0/24, the benchmarking, documentation and reserved ranges, multicast, and IPv4 addresses inside IPv6 forms were reachable before and are now refused. If fetch_webpage, fetch_image or the browser reach an internal host at such an address, add its hostname to DPTTOOLS_WEB_FETCH_ALLOW_HOSTS and DPTTOOLS_BROWSER_ALLOW_HOSTS.
  • The desktop server checks key names. Each key in computer_key, computer_hold_key and the click tools' modifiers must be a single character or a key name (letters, digits and _, such as Page_Down or XF86AudioMute). A name that starts with a digit must be a hex keysym (0x1008ff13) or one of the 3270_* names: xdotool reads any other, such as 119, as a raw key code. Anything else is refused with an Error: naming it. Every real X key name still works; a client that sent other text as a key gets an error instead of a silent no-op. Upgrade: this closes a security issue (below).
  • The desktop confirmation gate covers more. With DPTTOOLS_COMPUTER_CONFIRM=deny or elicit, a chord that contains a gated chord is gated too (ctrl+shift+w, alt+shift+F4, ctrl+Delete, Alt_L+F4, alt+0xffc1, ctrl+U0077), and so are the click tools' modifiers (modifiers="alt+F4"). A pipeline on deny that pressed such a chord now gets an Error:; with off, the default, nothing changes. Separately, whatever the gate's setting, computer_type refuses control characters other than tab, newline and carriage return; press keys such as Delete, Escape or BackSpace with computer_key.
  • ocr_pdf output can differ. Pages are labelled in document order, and the note on a result cut on a page boundary names exactly the pages left out (Re-call ocr_pdf with pages='3,7' to read them.). Code that parses the labels or the note should be checked against the new form.
  • Install hints in errors changed wording. A missing optional library now gives "Install the [docs] extra." rather than a pip command; code that matches the old text must match the new one.

Added

  • The docs site's eight recipes are written: OCR a scanned PDF, Office files to page images, search then summarise, JavaScript-rendered pages, portal download to text, desktop actions with hooks, skills setup, and the launcher with other MCP clients. Each has its goal, steps, what to validate and the tests behind it, and each example was run against real services or software (LLMWhisperer, Tavily, LibreOffice, Chromium, a virtual desktop and a toolkit agent). The home page, the server pages and the README link them.
  • The docs site has a link-preview card. Every page carries Open Graph and Twitter tags (title, description, the card image), from a theme override in overrides/, so a shared link shows a preview. The site also publishes a robots.txt that names the sitemap, and the Architecture page opens with a diagram of the launcher, the servers and the safety controls.

Changed

  • The README links the documentation site and gains a Documentation section, as the toolkit's README has: how to serve the site locally, an index of its key pages, the API reference, and the llms.txt / llms-full.txt files for coding agents. Its Overview now mentions the two optional servers.
  • Missing-dependency errors and tool descriptions name the extra to install ("Install the [docs] extra.") instead of giving a pip command for the package. std-tools is not on PyPI, so that command could install someone else's package of the same name. 47 messages and docstrings change; code that matches the old error text must match the new wording.
  • The changelog and the module docstrings shown on the API pages no longer carry internal planning IDs.
  • The security documentation matches the code. browser_run_script is described as what it is: model-written Python running as the server's user, which can open connections that bypass the egress proxy and read the process environment, so enable it only where execute_command would be acceptable. Its loopback DevTools port, the egress proxy over the browser's own traffic and the fresh browser per session are described for the modes that have them: in cdp mode the script attaches to the configured endpoint and no proxy applies. "No eval/exec of model input" now says "in the server process". The output-truncation row names what is not cut (directory listings, memory views and memory search). The browser page says downloads have no size or count limit of their own, so the container's disk quota is the control. The README's old "Security Model" section is replaced by a short summary that links the Security guide. The computer page says what the desktop confirmation gate does not know: an application's own shortcuts, keys such as XF86Close, typed text and clicks.
  • The Tool Reference page formats each description for reading (RST ``x`` becomes code, the Args section a list), and keeps the exact text the model receives in a collapsed block under each tool. Code in table cells stays on one line, and wide tables scroll inside their box on a phone. The examples use one model ID, claude-sonnet-5, and the home page has a byline.
  • The home page and the quickstart say up front, in a "Source code" note, that the code is in a private repository, that the pages document every tool, setting and API, and how to ask to review the code. The footer of every page reads the same sentence.
  • Browser tool counts read the same everywhere: 18 browser_* tools (core) or 27 (full), plus fetch_webpage_rendered. The llms.txt introduction names the browser and desktop servers.

Fixed

  • Security: computer_key could run commands on the virtual desktop. Its text was split on spaces and every word went to xdotool as a separate argument, and xdotool reads a word that names one of its commands as the start of a chained command. So computer_key("a exec touch /tmp/x") ran touch, with the server's environment, API keys included: command execution for a deployment that exposes only the desktop tools, and a way past hooks that watch execute_command. Click modifiers and computer_hold_key also passed model text to the same parser. Now every key is checked (a single character or a key name, never an xdotool command word), each chord goes to its own xdotool call after --, and keydown/keyup get -- too. Host mode (pynput) was not affected. Reproduced in the Linux test image before the fix, and refused after it, with real keys still pressed.
  • Desktop key names other than the built-in ones reach xdotool as written. Every name the server did not know (it knows ctrl, Return, Tab, F1–F12 and a few more) was lowercased, and X key names are case-sensitive: XF86AudioMute, ISO_Left_Tab, U20AC or F13 were silently ignored while the tool reported them pressed, and 3270_Enter pressed a different key. The descriptions of computer_key, computer_hold_key and the click tools' modifiers now state which keys are accepted.
  • Security: the desktop confirmation gate could be bypassed. The click tools never showed their modifiers to the gate, and the modifiers are pressed before the click, so computer_left_click(x, y, modifiers="alt+F4") closed the focused window under DPTTOOLS_COMPUTER_CONFIRM=deny. The gate also matched chords exactly, so an extra key (alt+shift+F4, ctrl+shift+w, ctrl+Delete) or, on the virtual desktop, a side-specific X name (Alt_L+F4, Control_L+w, KP_Delete) got past it. Numeric spellings got past it too: a hex keysym (0xffff is Delete, alt+0xffc1 is alt+F4), a Unicode form (ctrl+U0077 is ctrl+w), or a raw key code, which xdotool reads from any token that starts with a digit and is not a key name (119 is Delete, 64+70 alt+F4, on a standard keymap); each was checked live with xev. Now the click tools check their modifiers before anything else and show them to the gate as one chord; a chord is gated when it contains a gated chord, with side-specific names and hex and Unicode spellings read as the key they name; and a digit-led token that is not a hex keysym or a 3270_* name is refused. A third route went through computer_type: xdotool types a character with the key that produces it, and the Delete key produces U+007F, so on the virtual desktop computer_type("\x7f") pressed Delete. computer_type now refuses control characters other than tab, newline and carriage return (host mode on Windows sends U+007F as a character, not the Delete key). Present since 0.6.0; only deployments that set DPTTOOLS_COMPUTER_CONFIRM relied on the gate.
  • The desktop's plus key. + separates the keys of a chord, so the plus key itself could not be pressed: computer_key("+") was refused and "ctrl++" pressed ctrl alone. A + where a key is expected is now the plus key ("+", "ctrl++"), and a chord with a dangling or doubled separator ("ctrl+", "ctrl++a") is refused instead of being read as a shorter chord.
  • The private-address check missed ranges. It refused seven networks (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1, fc00::/7), so 0.0.0.0 (which reaches localhost on Linux), ::ffff:127.0.0.1, IPv6 link-local, carrier-grade NAT, NAT64 and 6to4 forms of private addresses got through, contrary to the documented policy. It now refuses every block the IANA special-purpose address registries list as not globally reachable, multicast, reserved addresses and any IPv6 address outside 2000::/3, and judges an IPv6 address that carries an IPv4 one (IPv4-mapped, 6to4, Teredo, NAT64) by that IPv4 address. The blocks are written out in the code rather than read from ipaddress.is_global, whose tables depend on the Python release (on 3.10.11 they call 192.0.0.8 and 3fff::1 global), so the result is the same on every supported Python. fetch_webpage, fetch_image, the browser's egress proxy and its cdp-mode checks all use it.
  • ocr_pdf page labels. OCR returns the requested pages in document order, and the --- Page N --- labels now follow it. An open-ended range (pages="21-") was numbered from 1 instead of from its start, and a list out of order ("3,1") was labelled in the order given, so page 1's text came back as --- Page 3 ---.
  • ocr_pdf's note on a result cut on a page boundary now names exactly the pages left out (Re-call ocr_pdf with pages='3,7'). It gave one range from the first page left out to the last, which could take in pages never requested ('2-7' for pages="1-3,7", so following it paid for pages 4 to 6) or be invalid ('5-2').

0.6.0 — 2026-10-03

Migration notes

  • fetch_webpage no longer uses DPTTOOLS_SANDBOX_TIMEOUT. Its timeout is now DPTTOOLS_WEB_FETCH_TIMEOUT, default 30 s. If you raised the sandbox timeout to allow slow pages, set the new variable when you upgrade.
  • web_search output has a new usage key. Strict-schema parsers must allow it. search_type="news" no longer forces Tavily's 2-credit advanced depth; pass search_depth="advanced" to keep it.
  • DPTTOOLS_OCR_TIMEOUT is validated at start-up. A non-integer or non-positive value now raises a ConfigError naming the variable, instead of silently falling back to 180 s. The value is read once per process, so restart after changing it. Values outside 30–600 are still clamped.
  • fetch_webpage returns a JSON object by default: url, final_url, status, content_type, title, text, truncated, total_chars, offset, next_offset, has_more, byte_cap_reached, likely_js_rendered. Code that used the return value as page text should read text, or pass structured=False for the old plain string. Non-text responses are now refused, and plain text and JSON are no longer run through the HTML-to-text step.

Added

  • DPTTOOLS_WEB_FETCH_TIMEOUT (default 30 s) for fetch_webpage, with connects capped at 10 s. A slow or hostile URL can no longer hold a fetch for the code-execution budget (often several minutes).
  • web_search reports per-call usage, and takes an optional search_depth parameter (basic | advanced | fast | ultra-fast, Tavily only). Tavily's own usage is passed through (include_usage), with a fallback estimate clearly marked estimated; Serper and Brave report provider_reported: false rather than an invented cost.
  • Images are scaled to model-safe limits. read_image, render_document_page, render_document_thumbnail, read_document_pages_as_images and take_screenshot scale any image above DPTTOOLS_IMAGE_MAX_EDGE_PX (default 1568 px long edge) or DPTTOOLS_IMAGE_MAX_MB (default 3 MB). Images within the limits are returned byte-identical. The shared helper, _images.fit_image, also reports the scale it applied.
  • tests/integration/test_toolkit_compat.py checks std-tools against an installed doc-processing-toolkit without a model call: the skills catalog reaches the system prompt, and an in-process read_image reaches the toolkit as an image. It is skipped when the toolkit is not installed.
  • README section "Compatibility with doc-processing-toolkit": tested versions, the in-process image requirement (toolkit 0.5.1+), the skills naming rule, passing env to stdio servers, and a working pre-built-session example for a toolkit issue (0.5.2 and earlier) that leaves the skills catalog empty in-process and with mcp_command configs.
  • .env.example listing every environment variable with its default, and a Configuration section rebuilt as one table per area (default, accepted values, when it is read), plus how configuration is loaded. Both are generated from one table in _config.py by scripts/gen_reference.py; unit tests fail if a variable or a tool goes undocumented.
  • doc_processing_std_tools.__version__, read from the installed package metadata, so a running deployment can report its version.
  • convert_document(path, to, outdir=None, page="all", overwrite=False): converts Office and OpenDocument files with LibreOffice to pdf, docx, xlsx, pptx, odt, ods, odp, csv, html, txt, or png/jpg page images (through PDF and pymupdf), writes next to the source or into an existing outdir, never replaces a file unless overwrite=True, and returns the output paths as JSON. New settings DPTTOOLS_SOFFICE_BIN and DPTTOOLS_CONVERT_TIMEOUT_S (default 120, the previous fixed value). The composed server now has 55 tools.
  • fetch_webpage paging (offset, next_offset, has_more), a likely_js_rendered hint, and the settings DPTTOOLS_WEB_FETCH_MAX_BYTES, DPTTOOLS_WEB_FETCH_MAX_REDIRECTS and DPTTOOLS_WEB_FETCH_ALLOW_HOSTS (an operator allow-list of internal hostnames, still resolved once and pinned).
  • fetch_webpage(format="text" | "markdown" | "html") and the optional [extract] extra (trafilatura): with it, text and Markdown hold the page's main content without navigation and footers; without it, a built-in extractor (now also producing Markdown) is used. The result reports format and extractor. [all] includes [extract].
  • fetch_image(url): downloads a PNG, JPEG, GIF or WebP image under fetch_webpage's SSRF policy, refuses images over DPTTOOLS_FETCH_IMAGE_MAX_BYTES (default 10,000,000) before downloading, checks the real format and pixel count, and returns a caption plus the image scaled to the model limits. fetch_webpage(include_assets=True) adds bounded images (30) and links (100) reference lists. The [web] extra now includes Pillow. The composed server now has 56 tools.
  • docker/integration.Dockerfile and tests/integration/run_in_docker.py: a Linux test image (Python 3.12, LibreOffice, Xvfb, xdotool, CJK fonts) that runs the integration suite, so LibreOffice and virtual-display tests run on any machine with Docker. A .dockerignore keeps .env and local environments out of images.
  • Optional browser server (browser_server, [browser] extra): drives Chromium through element refs, modelled on Anthropic's browser use toolset, with browser_<member> tools (core profile: 18, full: 27). It has three modes (launch, persistent, cdp), one browser context per MCP session, an idle reaper, and a Tab Context footer in every result. Playwright runs on its own thread, so the browser survives callers that create a new event loop per call. It is off by default: the launcher composes it only with DPTTOOLS_ENABLE_BROWSER=true. New DPTTOOLS_BROWSER_* settings are validated only when it is enabled.
  • Browser network policy and confirmation gate. Every connection a launched Chromium makes goes through an egress proxy inside std-tools that applies the domain lists, refuses private and reserved addresses, and connects to the address it validated, closing DNS rebinding. That covers sub-resources, fetch and WebSockets. Navigation refuses non-web schemes and re-checks redirects, and service workers are blocked. DPTTOOLS_BROWSER_CONFIRM = off (default) | deny | elicit gates consequential clicks, password entry, uploads and scripts. With off, approval belongs to the client app. Includes the BROWSER_SAFETY_PROMPT template.
  • Browser downloads are saved per session and reported once, with a path the document tools open. Opt-in tools (DPTTOOLS_BROWSER_ENABLE): browser_javascript_exec, browser_read_console, browser_read_network (both drained per read, per tab) and browser_file_upload (only files inside DPTTOOLS_BROWSER_UPLOAD_DIR).
  • browser_run_script (opt-in): runs model-written Python with Playwright against the session's tab. It runs in a per-session child process, started without the environment variables whose names look like secrets, attached over a loopback DevTools port that is opened only while the tool is enabled (in cdp mode, to the configured endpoint). Variables persist between calls; in launch and persistent modes the browser's own traffic still goes through the egress proxy; a timeout (DPTTOOLS_BROWSER_SCRIPT_TIMEOUT_S, 60) kills the process tree and resets the namespace. The script itself is not sandboxed: it can open its own connections, bypassing the proxy, and read the server's environment (stated in the docs from 0.6.1).
  • fetch_webpage_rendered (browser server): fetch_webpage's JSON envelope (plus "rendered": true) from a page rendered by Chromium, for single-page apps and other pages fetch_webpage sees as an empty shell. Each call uses a fresh, credential-free context behind the egress proxy, closed afterwards; it exists only where the browser server is enabled.
  • Optional computer (desktop) server (computer_server, [computer] extra): 17 computer_<member> tools modelled on Anthropic's computer use toolset. virtual backend (default): an Xvfb display per MCP session driven through xdotool, with an optional VNC view; Linux containers only. host backend: the real screen through pynput and mss, one session at a time, on a chosen monitor, with per-monitor DPI awareness on Windows, a kill switch (top-left corner or DPTTOOLS_COMPUTER_KILL_HOTKEY) and a pause on human activity. Large screens are scaled for the model and coordinates mapped back. DPTTOOLS_COMPUTER_CONFIRM = off (default) | deny | elicit gates host sessions and key chords that close, quit or delete. Off by default: the launcher composes it only with DPTTOOLS_ENABLE_COMPUTER=true. On Linux the extra leaves out pynput, whose evdev dependency compiles from source.

Changed

  • DPTTOOLS_OCR_TIMEOUT is read through the shared ToolsConfig, like every other variable. It and LLMWHISPERER_API_KEY are now documented in the README.
  • The mcp floor is now >=1.25, the range the toolkit already requires and the one std-tools is tested on.
  • Contributor guides for coding agents (CLAUDE.md, AGENTS.md).
  • take_screenshot and the computer server's host backend share their capture code (_display.py); the browser and computer servers share their MCP-session keys (_sessions.py). The Screenshot Server README entry now says plainly that it captures the real screen of the machine the server runs on.
  • The README installs from the repository at the release tag (pip install "doc-processing-std-tools[...] @ git+https://github.com/rzoeb/doc-processing-std-tools.git@v0.6.0"), as the docs site does: std-tools is not on PyPI, and a bare pip install doc-processing-std-tools would fetch whatever package holds that name there. Unit tests now also check the docs site: its generated tool reference and llms files are current, its configuration page lists every variable, no page names an unknown variable, and no page carries an email address, a link to the repository or a Vercel preview URL.

Fixed

  • apply_patch holds the read-modify-write lock like edit_file. In the composed server two overlapping patches, or a patch and an edit_file call, could both report success while the last write silently discarded the other change.
  • Images are turned upright before they are resized. A phone photo stored sideways with an EXIF orientation tag came back sideways once fit_image had to shrink it, because re-encoding dropped the tag. Images that need no resizing are still returned byte-identical.
  • Colour-key transparency survives resizing. An RGB or greyscale PNG made transparent with a tRNS chunk (no alpha channel) was treated as opaque and could be re-encoded as JPEG.
  • browser_run_script responses are bounded in the child process. Output is cut to the browser's 50,000-character limit, a call returns at most five screenshots, each fitted to the image limits within an overall budget, so a response can no longer exceed the 64 MiB pipe limit; if a response is still too large or unreadable, the worker is stopped and the namespace reset. The child keeps the protocol on private file descriptors, so a script that writes to the real stdout, reads input or starts a subprocess cannot corrupt it. Browser truncation notices no longer end with a stray number (the limit was passed as the notice's hint).
  • browser_run_script no longer lets one client's script reach another client's pages. A script attaches to the whole browser, so while run_script is enabled only one browser session is open at a time (a second client gets an error until the first is idle), in launch mode each session starts with a fresh browser, and cdp mode requires DPTTOOLS_BROWSER_CDP_SHARE_TABS=true (a ConfigError otherwise). To serve several clients with scripts enabled, run one server per client.
  • Exclusive creates under concurrency. create_file and memory_create create the file exclusively, and memory_create and memory_rename hold the memory lock, so overlapping calls for the same path can no longer both succeed with one silently overwriting the other.
  • Every file change in the File System and Memory servers holds the lock, not only read-modify-write edits. write_file, create_file, delete_file, move_file, copy_file, write_memory, delete_memory and memory_delete now take it too. Before, two overlapping overwrites could interleave (the shorter text followed by the longer one's tail, invalid JSON for a memory entry), and an edit overlapping a delete or move wrote the old file back. A unit test fails if a tool changes a file outside the lock.
  • A browser_run_script screenshot sets the click scaling. Coordinates are mapped back through the scale of the latest screenshot, but a script's await screenshot() did not update it, so with a viewport larger than DPTTOOLS_IMAGE_MAX_EDGE_PX a click read off a script's screenshot missed (by about 20% at 1920x1080). The worker now reports each screenshot's scale; a viewport screenshot updates the mapping, a full-page one does not.
  • tests/integration/run_in_docker.py makes the integration folder pytest's default (testpaths) instead of a fixed argument, so -- tests/integration/<file>.py runs that file only.
  • Host mode picks the real primary monitor on Linux. With DPTTOOLS_COMPUTER_MONITOR=primary (the default) the computer server used the monitor at the desktop's origin, which on X11 need not be the primary output. It now uses the monitor mss marks primary (the XRandR primary output on X11, the primary display on Windows) and falls back to the origin rule. The [computer] extra now requires mss>=10.2, the first release that reports it.
  • The Dockerfile examples install git (absent from python:3.12-slim, so the git+https install failed) and show how to pass a GitHub token as a BuildKit build secret without storing it in the image.
  • The browser integration tests restore the environment when Chromium cannot start. Before, a skip during start-up left loopback allow-lists set for every later test.
  • Child processes no longer inherit the server's stdin. execute_command, execute_skill_script, the LibreOffice conversions and the computer server's helpers start their children with stdin closed. Under the stdio launcher the server's stdin is the MCP message stream, so a command that read input could consume protocol messages or hang the session.
  • Tool descriptions the model reads: read_excel says it returns saved cell values (not formulas) from the sheet active when the file was saved; load_skill states its listing limits instead of a private constant name; take_window_screenshot no longer cites a version number. The DPTTOOLS_COMPUTER_CONFIRM description lists all seven chords the gate checks, and the README explains how to reach the VNC view safely.
  • Tavily search authenticates with a Bearer header. The key used to travel in the JSON body, which Tavily no longer documents and which development-tier keys reject with a 401. Errors returned by web_search no longer include request URLs or the API key.
  • format_search_results accepts web_search's output unchanged. It used to accept only a bare JSON array, so the two tools could not be chained. Plain arrays still work as before.
  • The live OCR integration tests skip when the [ocr] extra is missing. Before, a key in .env without the client installed made them fail instead of skip.
  • The composed server no longer freezes on a slow tool. FastMCP calls synchronous tools on its event loop, so one OCR job, LibreOffice conversion or long command stalled every other request and the transport. The launcher now registers every synchronous tool as an async wrapper that runs it on a worker thread. Names, descriptions and schemas are unchanged, and a test checks this. edit_file, memory_str_replace and memory_insert lock their read-modify-write, so overlapping calls cannot lose updates.
  • Concurrent LibreOffice conversions in render_document_page, render_document_thumbnail and read_document_pages_as_images no longer collide: each run gets its own profile (-env:UserInstallation), so a second conversion no longer exits without output. Failed conversions no longer leave temporary directories behind.
  • fetch_webpage no longer decodes images, PDFs or other binary responses into garbage: they are refused, with their size, before the body is downloaded. The page charset now comes from a byte-order mark, the header or <meta charset>, so pages that declare their encoding only in HTML decode correctly.
  • take_screenshot on Linux with mss 10.2: captures now use mss.MSS with the xgetimage backend. mss's default backend, called from a worker thread (where the composed server runs every capture), left the X server unable to map new windows.
  • The browser and computer servers tolerate the placeholders that strict function calling (OpenAI) sends for unused optional arguments: tab_id 0 means the active tab and browser_navigate opens a tab when none is open whatever tab_id says; ref="" means no ref; x=0, y=0 next to a ref is ignored; browser_scroll and computer_scroll at (0, 0) scroll at the centre. Found in end-to-end tests with a model that uses strict function calling.

0.5.0 — baseline (tag v0.5.0, commit 565c3e4)

The first tagged release. It contains 8 FastMCP tool servers with 54 tools: file system, web search, memory, document processing, code execution, screenshot, image tools and skills, plus the composed launcher python -m doc_processing_std_tools. Changes before this release are recorded in the git history only.

Before the first tag

The 0.5.0 baseline above is the first tagged release; until then, changes were recorded in the git history only. In summary:

0.5.0 — 2026-06-09

  • New read_document_pages_as_images: renders several PDF or Office pages as images, the vision fallback for scanned and visual documents.
  • Richer Office extraction, all additive:
    • read_word reads headers, footers and text boxes, and reports embedded images.
    • read_excel reports merged ranges and whether the sheet has images or charts.
    • read_powerpoint reads tables and grouped shapes, and counts images and charts per slide.

0.4.0 — 2026-05-18

  • Breaking: extract_pdf_via_ocr is replaced by ocr_pdf(path, pages) and ocr_image(path), which call LLMWhisperer directly. OCR gains a deadline (DPTTOOLS_OCR_TIMEOUT), page-aware truncation and sanitised errors.
  • sanitize_path no longer claims to contain paths: it rejects only NUL bytes, and the container is the boundary. take_screenshot no longer creates folders.
  • Image tools keep orientation, colour profiles and quality, add a frame parameter for multi-frame images, and refuse decompression bombs.

0.3.1 — 2026-05-09

  • read_image reports the image format detected from the file's bytes, not its extension.

0.3.0 — 2026-05-07

  • read_image, the page renderers and take_screenshot return images the model can see, instead of base64 text.
  • New get_image_metadata.

0.2.2 — 2026-05-06

  • Tool descriptions rewritten so each states its accepted formats and points to the right alternative.

0.2.1 — 2026-05-01

  • fetch_webpage re-validates every redirect against the SSRF policy.
  • apply_patch refuses a patch whose context no longer matches the file.
  • search_in_files skips files over the size limit.

0.2.0 — 2026-05-01

  • Skills server rebuilt: PyYAML frontmatter parsing, path and diagnostics in list_skills, the <skill_content> wrapper in load_skill, and make_skills_server().
  • New composed launcher, python -m doc_processing_std_tools.

0.1.0

  • First version: the file system, web search, memory, document processing, code execution, screenshot, image tools and skills servers.