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
namecontainsskill; - its server was started with
DPTTOOLS_SKILLS_DIRinenv; DPTTOOLS_SKILLS_DIRis 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_callfor the browser tools that act —browser_left_click,browser_form_input,browser_type,browser_key,browser_navigateto a new domain — and the opt-inbrowser_javascript_exec,browser_file_uploadandbrowser_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=elicitasks a human through MCP elicitation. The toolkit does not support elicitation today, so with the toolkitelicitrefuses every flagged action.- Add
BROWSER_SAFETY_PROMPTto 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_callfor 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_keyandcomputer_hold_key.computer_screenshot,computer_zoom,computer_cursor_positionandcomputer_waitonly look or wait. - A hook sees
computer_left_click(x=412, y=230). To show the user what is about to happen, keep the latestcomputer_screenshotand display it with the coordinates, or ask the model to state its purpose in each call's optionalintentargument (std-tools only logs it). DPTTOOLS_COMPUTER_CONFIRM=denyrefuses 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'smodifiers. It does not cover clicks on a "Delete" button.DPTTOOLS_COMPUTER_CONFIRM=elicitwould ask before the first action of each host session and before those chords. The toolkit does not support elicitation, so with the toolkitelicitrefuses them.- In
hostmode, 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).