Skip to content

Tool Reasons and LLM Approval

A tool justification is model-provided context for the approval handler. It is not authorization, a new tool effect, or a separate reasoning-model call. The feature is disabled by default and applies only to tools that may require approval.

LLM-backed Approval Helper

The built-in helpers module provides an LLM-backed dynamic handler that uses the active model. Enable tool reasons with Pydantic AI or LangChain:

> settings:
    tool_reason: enabled

> import: helpers
> approval: `llm_approval`

Experimental and limited-context

llm_approval raises an error when tool_reason is disabled. With the option enabled, it evaluates the tool name, description, declared risk, arguments, and optional model-provided reason. The reason is untrusted context, not proof of user authorization. This helper is not a complete authorization boundary.

Optional Tool Reasons

Both PydanticAdapter and LangChainAdapter accept tool_reason=False by default. In Kedi, use > settings: with tool_reason: enabled or tool_reason: disabled. The setting follows lexical/profile scope and overrides the constructor value without changing the shared adapter. If it is omitted, the constructor default applies. It is not forwarded to provider model settings. Unsupported adapters reject activation rather than silently ignoring it.

A backtick Python expression must return a boolean:

```
import os
```

> settings:
    tool_reason: `os.getenv("TOOL_REASON_FOO_BAR") is not None`

Plain true / false are not accepted for this setting. Python constructors and settings={"tool_reason": True} still use real Python booleans.

Enabling it adds an optional reason: string argument only to tools that may require approval. Statically read-only tools without a risk resolver keep their original schemas. Argument-aware tools expose the optional field, but their resolved risk still determines whether the handler runs.

Instructions for writing a brief action justification are supplied once in the shared model instructions, not repeated in every tool schema. Producing the reason needs no additional model call. Omitting it is valid.

Handlers read request.reason; request.arguments contains only the real tool arguments. Approval edits and risk rechecks retain the original justification; it does not automatically justify an edited action.

This also works with MCP and CodeMode. For MCP, Kedi projects the extra field locally and removes it before calling the server; the server's own schema does not change. External MCP tools retain Kedi's existing conservative approval policy. Provider-hosted MCP that bypasses Kedi's local approval boundary is not supported by this option. An approval-applicable tool that already declares a business argument named reason fails explicitly rather than losing or overwriting that argument. Unmodified read-only schemas keep their own fields.

Choose the Boundary

Use a deterministic approval handler for hard rules: allowed paths, tools, operations, tenants, or spending limits. Add LLM approval only where a fallible semantic judgment is acceptable. A persuasive justification must not override a deterministic deny or an inherited safety ceiling.

Failure Cases

Unsupported adapters, an existing business parameter named reason, invalid configuration values, and LLM approval without enabled tool reasons fail explicitly. Missing optional justification alone is not an invalid tool call. Handlers should decide how to treat that absence for their application.