Skip to content

Using with the Toolkit

std-tools is the standard tool library for the Agent class in doc-processing-toolkit. This page covers the ways to attach the servers to an agent, what each way implies, and how your application can approve tool calls. Version requirements are on the Compatibility page.

The toolkit's own documentation covers the agent side: Tools, MCP, Hooks and steering, Skills auto-injection, Client integration and Speech and images from an agent in your app.

Ways to attach

In-process: pass the server objects

from doc_processing_toolkit import Agent
from doc_processing_std_tools import document_processing_server, file_system_server

agent = Agent(
    model="claude-sonnet-5",
    tools=[
        file_system_server,
        {"type": "fastmcp", "server": document_processing_server,
         "allowed_tools": ["read_pdf", "read_image"]},
    ],
)

The dict form with allowed_tools (toolkit 0.5.1 or later) shows the model only the tools you list. The tools run in your process and read their configuration from your process's environment.

Out-of-process: a session you open yourself

Start the composed launcher (or one server) over stdio, with the environment it needs, and hand the toolkit the open session:

import os
import sys

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import get_default_environment, stdio_client
from doc_processing_toolkit import Agent

env = get_default_environment()
env.update({k: v for k, v in os.environ.items() if k.startswith("DPTTOOLS_")})
env["LLMWHISPERER_API_KEY"] = os.environ.get("LLMWHISPERER_API_KEY", "")  # if you use OCR

params = StdioServerParameters(
    command=sys.executable, args=["-m", "doc_processing_std_tools"], env=env
)

async with stdio_client(params) as (read, write), ClientSession(read, write) as session:
    await session.initialize()
    agent = Agent(model="claude-sonnet-5", tools=[{"session": session, "name": "std-tools-skills"}])
    result = await agent.run("Summarise ./reports/latest.pdf")

A pre-built session belongs to the event loop that opened it: run the agent with await agent.run() inside the async with block, not with run_sync. The name contains skill so that the toolkit also recognises the session as a skills server (see Skills below).

Out-of-process: let the toolkit start the server

The toolkit's mcp_command and mcp_script configs start a stdio server for you:

agent = Agent(
    model="claude-sonnet-5",
    tools=[{"type": "mcp_command", "command": sys.executable,
            "args": ["-m", "doc_processing_std_tools"], "name": "std-tools"}],
)

In toolkit 0.5.2 and earlier these configs pass the server no environment beyond the MCP SDK's allow-list, so the server runs on its defaults: no search key, no OCR key, default folders and limits. Use them only when the defaults are what you want; otherwise open the session yourself, as above.

Which to choose

In-process Session you open Toolkit-started (mcp_command)
Configuration Your process's environment The env you pass Defaults only (toolkit ≤ 0.5.2)
Image results Need toolkit 0.5.1+ Any toolkit version Any toolkit version
Skills auto-injection Empty catalog (toolkit ≤ 0.5.2) Works Empty catalog (toolkit ≤ 0.5.2)
A slow tool (OCR, LibreOffice, a long command) Runs on the agent's event loop and holds up streaming and hooks until it returns (toolkit ≤ 0.5.2) Runs on a worker thread in the composed server; the agent keeps going Same as a session you open
Arguments Passed to the function as the model sent them Validated against the tool's schema by the server Validated by the server
run_sync Yes No — await agent.run() Yes

Passing configuration to a stdio server

The MCP Python SDK starts a stdio server with only a short allow-list of variables: on Windows APPDATA, HOMEDRIVE, HOMEPATH, LOCALAPPDATA, PATH, PATHEXT, PROCESSOR_ARCHITECTURE, SYSTEMDRIVE, SYSTEMROOT, TEMP, USERNAME and USERPROFILE; elsewhere HOME, LOGNAME, PATH, SHELL, TERM and USER. Everything else — every DPTTOOLS_* variable and every API key — must be passed in the session's env, as in the example above. Build env from get_default_environment() so the allow-list is kept, then add what std-tools needs. The Configuration page lists every variable.

Other MCP clients, such as desktop assistants and IDEs, take an env block in their server configuration for the same reason.

Images

Six tools return images: read_image, render_document_page, render_document_thumbnail, read_document_pages_as_images, take_screenshot and fetch_image — plus, when they are enabled, the browser server's browser_screenshot and browser_zoom and the computer server's computer_screenshot and computer_zoom. Each scales its image to model-safe limits first (Images).

  • Out of process, image results reach the model with any toolkit version.
  • In-process, they need toolkit 0.5.1 or later. Older versions turned them into the text <...Image object at 0x...>, and a model could then describe a picture it never saw.
  • On a model that cannot take images in tool results, the toolkit replaces the image with a short notice.

To let a model look at a generated image, attach only read_image: {"type": "fastmcp", "server": document_processing_server, "allowed_tools": ["read_image"]}.

Skills

The toolkit reads the skills server automatically: it calls list_skills, puts each skill's name, description and path into the system prompt, and hides load_skill, load_skill_resource and execute_skill_script when there are no skills. The Skills server page describes the payload.

Toolkit 0.5.2 and earlier: use a pre-built session for skills

Auto-injection works only through a session you open yourself, as in the example above:

  • the session's name contains skill;
  • its server was started with DPTTOOLS_SKILLS_DIR in env;
  • DPTTOOLS_SKILLS_DIR is also set in your application's own environment and names a folder that exists there.

In-process (Agent(tools=[skills_server])) and mcp_command configs return an empty catalog, and the agent is never told the skills exist. This is a toolkit issue; see Compatibility.

Approving tool calls with hooks

std-tools does not ask for confirmation: a tool call runs when the model makes it. To put a person or a policy in the loop, use a toolkit hook. A hook sees every before_tool_call event and can decline the call with skip_tool, so the tool never runs and the model is told why:

from doc_processing_toolkit.agent import Agent, HookDirective

NEEDS_APPROVAL = {"execute_command", "delete_file", "move_file", "memory_delete", "write_file"}

async def approve(event):
    if event.event_type == "before_tool_call" and event.metadata["tool_name"] in NEEDS_APPROVAL:
        allowed = await ask_the_user(event.metadata["tool_name"], event.metadata["arguments"])
        if not allowed:
            return HookDirective(action="skip_tool", message="The user declined this action.")
    return None

agent = Agent(model="claude-sonnet-5", tools=[...], hooks=approve)

ask_the_user stands for your application's own approval step. Hooks fire for tools the toolkit calls itself — in-process servers and MCP sessions alike. See the toolkit's Hooks and steering page for the full event list and directives.

Browser actions

The browser server acts on live websites, where a click can submit an order or send a message. Its server-side confirmation gate, DPTTOOLS_BROWSER_CONFIRM, is off by default, so approving consequential actions is your application's job:

  • Hook before_tool_call for the browser tools that act — browser_left_click, browser_form_input, browser_type, browser_key, browser_navigate to a new domain — and the opt-in browser_javascript_exec, browser_file_upload and browser_run_script.
  • A hook sees browser_left_click(ref="ref_12"), not "button 'Place order'", so approval is per tool, per mode or per domain. The tool's result names the element it acted on, for your audit log.
  • For unattended pipelines, set DPTTOOLS_BROWSER_CONFIRM=deny: std-tools then refuses clicks on elements named like purchases, payments, deletions, sending or publishing, account closure or accepting terms, as well as password entry, uploads and scripts.
  • DPTTOOLS_BROWSER_CONFIRM=elicit asks a human through MCP elicitation. The toolkit does not support elicitation today, so with the toolkit elicit refuses every flagged action.
  • Add BROWSER_SAFETY_PROMPT to the agent's instructions (Browser).

The Security guide sets out exactly what std-tools enforces and what your application must do.

Desktop actions

The computer server operates a desktop: its own virtual display by default, or the user's real screen in host mode. Its confirmation gate, DPTTOOLS_COMPUTER_CONFIRM, is also off by default, and it can do less than the browser's: it sees pixels, not element names, so it cannot tell what a click will do. Approval in your application matters more here.

  • Hook before_tool_call for the computer tools that act — the clicks (computer_left_click, computer_right_click, computer_middle_click, computer_double_click, computer_triple_click), computer_left_click_drag, computer_left_mouse_down, computer_type, computer_key and computer_hold_key. computer_screenshot, computer_zoom, computer_cursor_position and computer_wait only look or wait.
  • A hook sees computer_left_click(x=412, y=230). To show the user what is about to happen, keep the latest computer_screenshot and display it with the coordinates, or ask the model to state its purpose in each call's optional intent argument (std-tools only logs it).
  • DPTTOOLS_COMPUTER_CONFIRM=deny refuses the key chords that close, quit or delete (alt+f4, ctrl+w, ctrl+q, super+w, super+q, delete, shift+delete), also with extra keys and as a click's modifiers. It does not cover clicks on a "Delete" button.
  • DPTTOOLS_COMPUTER_CONFIRM=elicit would ask before the first action of each host session and before those chords. The toolkit does not support elicitation, so with the toolkit elicit refuses them.
  • In host mode, get the user's consent before the agent starts, and tell them their screen goes to the model provider. The kill switch and the pause on human activity are always on, whatever your hooks do (Security).