The short answer
Wrap standalone tool functions with govern_tool(tool, dg, connector="orders") before passing them to LangGraph’s prebuilt ToolNode. When an agent generates a tool call, govern_tool sends the arguments to Deedglass for evaluation against your declared policies. If the call is allowed, execution proceeds normally. If denied or waiting for an approval, govern_tool intercepts execution and returns a descriptive error message formatted for the language model to read, enabling the agent to re-plan or report the decision without crashing the graph runtime.
Deedglass
A tool call meets your rules before it runs
Asks to run
DELETE FROM visits;
Deletes every row in visits
Deedglass checks your rules
- Reading dataAllowed
- Deleting a whole table (DROP, TRUNCATE)Never
- Changing or deleting rows without saying which (WHERE)This one
- Other changesWait for a person
Why ToolNode needs pre-execution governance
In LangGraph architectures, the language model outputs tool calls containing structured arguments, and ToolNode executes those calls sequentially or in parallel. By default, ToolNode assumes all returned tool calls are safe to execute as long as their JSON schema matches the function signature.
This creates several operational risks:
- Unbounded parameters: An agent might supply parameters outside safe boundaries, such as issuing a bulk refund, querying an unrestricted tenant ID, or requesting destructive file edits.
- Missing human oversight: Sensitive operations (such as transferring credits or modifying database records) run immediately without waiting for human approval.
- Graph interruption on exceptions: If a tool raises a raw exception when an invalid operation is attempted, the entire graph iteration terminates abruptly unless custom exception catchers are wired into every node.
govern_tool addresses these failure modes by inserting a policy checkpoint directly ahead of function invocation. It inspects tool arguments against your rule definitions, logs an audit record, and converts denials into structured strings the agent can reason over.
Wrapping tools with govern_tool
The govern_tool wrapper integrates directly into Python applications using LangChain or LangGraph. It takes an existing @tool definition, a Deedglass client instance, and a connector name.
Code Program code, for developers. The text around it says what it does.
from langchain_core.tools import tool
from deedglass import Deedglass
from deedglass.langchain import govern_tool
dg = Deedglass()
@tool
def cancel_order(order_id: str, reason: str) -> str:
"""Cancel an active customer order."""
# Production database or API operation
return f"Order {order_id} successfully cancelled."
# Wrap the tool with Deedglass policy governance
governed_cancel = govern_tool(cancel_order, dg, connector="orders")
When governed_cancel is invoked, it automatically queries Deedglass before calling the underlying function. The evaluation workflow follows three deterministic paths:
- Allow: The original function runs, and the result is returned to the graph state.
- Deny: The function does not run. The wrapper returns an informative message such as
Tool execution denied by policy: Cancellation forbidden for delivered orders. - Require Approval: Execution pauses. A pending approval record is created in Deedglass with a unique action ID, and the agent receives a notification that approval is pending.
Handling pending approvals in graph state
When an operation requires approval from a human reviewer, govern_tool creates an action record in the Deedglass vault and returns the approval status in the tool response. This allows the graph execution loop to either pause gracefully or inform the user that their request has been routed to a team lead:
Code Program code, for developers. The text around it says what it does.
@tool
def issue_refund(customer_id: str, amount_cents: int) -> str:
"""Issue a monetary refund to a customer account."""
return f"Refunded {amount_cents} cents to customer {customer_id}."
governed_refund = govern_tool(issue_refund, dg, connector="billing")
If your Deedglass policy declares that refunds over 5,000 cents require human sign-off, invoking issue_refund with 6000 returns:
Code What the screen or file shows, copied as is.
Action requires approval. Approval ID: act_01h7x9k2. An administrator must confirm this operation.
The model receives this feedback directly in its message context. Rather than throwing an unhandled exception, the agent can reply: “I have prepared your refund request for review by our billing manager under reference act_01h7x9k2.”
How govern_tool differs from LangGraph interrupts
LangGraph provides built-in interrupt functions for human-in-the-loop workflows. While both tools help control agent behavior, they serve complementary purposes in production systems:
- Enforcement layer: LangGraph interrupts pause at graph node state machine boundaries; Deedglass
govern_toolenforces policies at each individual tool invocation boundary before function entry. - Policy source: Interrupts rely on custom conditional branching in Python code; Deedglass evaluates centralized, declarative policy files (
policy/v1). - Audit records: Interrupts save ephemeral checkpoint snapshots in checkpointer storage; Deedglass produces cryptographically sealed, immutable records verified offline.
- Multi-agent sharing: Interrupt definitions are coupled to a specific graph topology; Deedglass policies are shared across LangGraph, Claude Code, Cursor, and CLI tools.
- Engine mechanics: Interrupts suspend the thread until user intervention;
govern_toolreturns structured, model-readable guidance so the agent can self-correct or notify the user.
Teams often combine both approaches: use LangGraph interrupts for conversational pauses (such as asking a user to confirm their shipping address), and use govern_tool for authoritative database and API policy enforcement.
A complete runnable pipeline
Here is an end-to-end implementation demonstrating a governed LangGraph workflow:
Code Program code, for developers. The text around it says what it does.
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from deedglass import Deedglass
from deedglass.langchain import govern_tool
dg = Deedglass()
@tool
def lookup_balance(account_id: str) -> str:
"""Retrieve current account balance."""
return f"Account {account_id} available balance: 1250 units"
@tool
def transfer_credits(from_id: str, to_id: str, amount: int) -> str:
"""Transfer credits between accounts."""
return f"Successfully transferred {amount} units from {from_id} to {to_id}"
tools = [
govern_tool(lookup_balance, dg, connector="ledger"),
govern_tool(transfer_credits, dg, connector="ledger"),
]
model = ChatAnthropic(model="claude-sonnet-5").bind_tools(tools)
def agent_node(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": [response]}
workflow = StateGraph(MessagesState)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", ToolNode(tools))
workflow.add_edge(START, "agent")
workflow.add_conditional_edges(
"agent",
lambda state: "tools" if state["messages"][-1].tool_calls else END,
)
workflow.add_edge("tools", "agent")
app = workflow.compile()
When an agent invokes lookup_balance, the read operation proceeds immediately. When it attempts transfer_credits, the transaction is validated against your active Deedglass policies before any ledger update occurs.
Testing policies before deployment
Before deploying an agent graph to production, verify policy behavior using unit tests. Deedglass includes local linting and policy simulation utilities:
Code Commands typed in a terminal. The text around it says what happens.
deedglass policy test --dir ./policies
This ensures that every tool rule compiles cleanly, matches expected tool names, and specifies valid decisions (allow, deny, or require_approval). In continuous integration environments, you can run automated assertions to confirm that forbidden actions are rejected consistently.
Verifying sealed records
Every action evaluated through govern_tool seals an audit record in Deedglass. You can inspect the record via the local web app (npx deedglass) or check the ledger using the CLI:
Code Commands typed in a terminal. The text around it says what happens.
deedglass actions list --connector ledger
Each record contains the agent identity, the exact arguments submitted, the policy rule that decided the call, and cryptographic seals that can be verified offline across Python, Go, and TypeScript.3
To see how Deedglass evaluates tool calls and database updates in real time, explore the playground. To calculate the blast radius of credentials accessible to agents on your system, run npx deedglass scan (what the grade means).
Written with AI assistance and checked against our claims register.