Configuration (ToolNode)¶
What it is / when to use it¶
This page documents how to configure external tools for ReactPlanner using ToolNode.
You need it when you are:
- connecting MCP servers or UTCP endpoints,
- tuning timeouts/retries/concurrency for production,
- enforcing safe tool visibility (read-only subsets, allowlists),
- wiring OAuth/HITL flows.
Non-goals / boundaries¶
- This page does not describe every MCP/UTCP server; it documents PenguiFlow’s configuration contract.
- ToolNode does not replace your authorization/policy layer; it helps you safely connect to tool sources.
- “Presets” are for development convenience; production should treat tool servers as managed services.
Contract surface¶
Core types¶
The main config model is:
penguiflow.tools.config.ExternalToolConfig
Used by:
penguiflow.tools.node.ToolNode
Key enums:
TransportType:MCP | HTTP | UTCP | CLI(UTCP/HTTP/CLI are supported via theutcpclient)AuthType:NONE | API_KEY | BEARER | COOKIE | OAUTH2_USER
Required fields¶
name: namespace prefix for tools (example:github)transport: which protocol to useconnection: MCP command or URL; UTCP manual/base URL
Tool names are namespaced: {name}.{tool} (example: github.create_issue).
Environment variable substitution (${VAR})¶
ToolNode substitutes ${VAR} inside certain config fields at runtime.
Behavior:
- if the env var is present: it is substituted
- if it is missing: ToolNode raises
ToolAuthError(fail-fast)
This is intentional: missing credentials should fail at startup/connect time, not later during agent execution.
Note
Implementation detail: substitution uses a regex replace (re.sub) over ${...} patterns, so multiple variables per string are supported.
Auth modes (what they do)¶
AuthType.NONE: no auth headersAuthType.BEARER: sendsAuthorization: Bearer <token>usingauth_config["token"]AuthType.API_KEY: injects an API key header (auth_config["api_key"], default headerX-API-Key)AuthType.COOKIE: injects a cookie header (cookie_name,cookie_value)AuthType.OAUTH2_USER: user-scoped OAuth via HITL pause/resume (see OAuth & HITL)
ToolNode resolves auth:
- during
connect(...)(connection-time headers), and - per tool call (
ToolNode.call(...)) so tokens can refresh/rotate.
Resilience knobs¶
ExternalToolConfig includes:
timeout_s: per-call timeout (default 30s)retry_policy: tenacity-based backoff (max_attempts, min/max wait, retryable HTTP status codes)max_concurrency: concurrency limit per ToolNode instance (default 10)
Tool filtering (tool_filter)¶
tool_filter is an allowlist of regex patterns.
Semantics:
- ToolNode uses
re.match(pattern, tool_name)(match from the start of the string). - If
tool_filterisNone, all discovered tools are exposed.
Prefer allowlists for production.
Artifact extraction and resources¶
ToolNode can extract large/binary content into artifacts and handle MCP resources:
ExternalToolConfig.artifact_extractioncontrols the extraction pipeline- MCP resources can generate tools like
{namespace}.resources_read(see MCP resources)
Note: The extraction pipeline is an internal (plumbing) mechanism — it uses
ctx._artifacts(the rawArtifactStore) directly. Tool developers storing artifacts manually should usectx.artifacts(theScopedArtifactsfacade) withupload()/download()/list().
Operational defaults (recommended)¶
- Start safe:
- use a narrow
tool_filter(read-only patterns) before broadening - gate write/external tools behind HITL/policy
- Bound concurrency:
- external SaaS:
max_concurrency=3..5 - internal services: start at
10and tune - Make hangs impossible:
- set
timeout_sexplicitly for every ToolNode - keep retries bounded (default
max_attempts=3) - Prefer service-based MCP:
- for production, run MCP servers as services and connect via URL
- avoid
npx -y ...in long-lived workers unless you own the environment
Failure modes & recovery¶
Missing ${VAR} at startup¶
Symptoms
- ToolNode raises
ToolAuthErrorwhile connecting or calling a tool
Fix
- ensure the environment is present in the worker process (not only your shell)
- prefer explicit env var names (avoid
${TOKEN})
OAuth pauses but never completes¶
Likely causes
- no OAuth callback endpoint wired to store tokens
- missing
user_idintool_context - distributed deployment without durable pause state
Fix
- implement OAuth callback + token persistence (see OAuth & HITL)
- configure a
StateStorethat supports planner pause persistence
Rate-limits / 429 storms¶
Fix
- lower
max_concurrency - tune retry backoff and retryable status codes
- reduce planner parallelism (
planning_hints.max_parallel)
Observability¶
At minimum, record:
- connect/discovery duration and tool count per ToolNode
- planner tool call telemetry (
PlannerEvent(event_type="tool_call_*")) - error rates and retry counts by tool source
- artifact storage (
PlannerEvent(event_type="artifact_stored")) when artifact extraction is enabled
Security / multi-tenancy notes¶
- Treat ToolNode config as sensitive (it may reference secrets via
${VAR}). - Never surface secrets into
llm_context; keep secrets intool_contextor provider-specific stores. - Enforce per-tenant tool visibility (and avoid mixing tenant-scoped ToolNodes without a visibility policy).
Runnable example: safe, read-only GitHub ToolNode¶
from __future__ import annotations
import asyncio
from penguiflow import ModelRegistry
from penguiflow.tools import AuthType, ExternalToolConfig, ToolNode, TransportType
async def main() -> None:
registry = ModelRegistry()
github = ToolNode(
config=ExternalToolConfig(
name="github",
transport=TransportType.MCP,
connection="npx -y @modelcontextprotocol/server-github",
auth_type=AuthType.BEARER,
auth_config={"token": "${GITHUB_TOKEN}"},
tool_filter=["get_.*", "list_.*", "search_.*"],
max_concurrency=3,
timeout_s=30.0,
),
registry=registry,
)
await github.connect()
for spec in github.get_tools():
print(spec.name)
if __name__ == "__main__":
asyncio.run(main())
Troubleshooting checklist¶
- Discovery fails: verify dependencies (FastMCP/UTCP), env vars, and network reachability.
- Too many tools: add
tool_filterallowlists and enforce planner tool visibility. - Calls are slow: tune
timeout_s, retries, and concurrency; check provider rate limits.