Tool governance and auditing framework for LangChain
Project description
LangChain Tool Gate
A lightweight tool gate framework for LangChain applications - manage tool lifecycles with dynamic approval workflows and runtime guardrails.
Overview
LangChain Tool Governance provides a complete solution for managing AI agent tools in LangChain applications. It enables organizations to implement tool approval workflows and enforce runtime permission controls without requiring application restarts.
Core Principles:
- Risk-Based Governance: Permission levels determine approval requirements (auto-activate, single-approval, dual-approval + quiet period)
- Dynamic Runtime: Active tools are fetched with TTL caching - changes take effect immediately without restarting the Agent
- Status-Only Interception: Tool calls are blocked based solely on
status(active/inactive), not permission levels
Features
- Tool Registration: Automatically scan and register tools with comprehensive metadata
- Approval Workflow: Manage tool lifecycle (pending → active → rejected → deprecated)
- Risk-Based Approval: Permission levels determine initial status and approval requirements
- Dynamic Runtime: TTL-cached tool list for real-time updates without Agent restarts
- Runtime Guardrails: Block inactive tool calls at execution time
- CLI Management: Command-line interface for tool governance operations
- External Approval Integration: Support for DingTalk and Feishu approval workflows
Installation
pip install langchain-tool-gate
Quick Start
Step 1: Define Governed Tools
from pydantic import BaseModel, Field
from tool_governance import governed_tool
class CancelOrderInput(BaseModel):
order_id: str = Field(description="Order ID")
@governed_tool(
name="cancel_order",
description="Cancel user's order",
permission_level="confidential",
args_schema=CancelOrderInput,
creator="dev@company.com",
)
def cancel_order(order_id: str) -> str:
return f"Order {order_id} cancelled"
@governed_tool(
name="get_weather",
description="Get weather information",
permission_level="public",
creator="dev@company.com",
)
def get_weather(city: str) -> str:
return f"Weather for {city}"
Step 2: Register Tools
from tool_governance.core.registry import ToolRegistryService
registry = ToolRegistryService(db_url="sqlite:///./tools.db", cache_ttl=60)
result = registry.scan_and_register()
print(result)
Step 3: Approve Tools via CLI
# List pending tools
tool-gov list --status pending
# Approve a tool
tool-gov approve cancel_order --approver team_leader
Step 4: Apply Guardrails to Agent
from tool_governance import apply_guardrails
from langchain.agents import AgentExecutor
executor = AgentExecutor(agent=agent, tools=[cancel_order, get_weather])
executor = apply_guardrails(executor, db_url="sqlite:///./tools.db")
# Only active tools can be called
result = executor.invoke({"input": "Please cancel order ORDER-123"})
Risk-Based Approval Matrix
The framework uses permission levels to determine the initial approval workflow:
| Permission Level | Initial Status | Approval Required | Quiet Period | Developer Experience |
|---|---|---|---|---|
public |
auto-activated | 0 approvals | None | Push code → 5 min → LLM uses tool |
internal |
pending | 1 approval (Leader) | None | DingTalk/Feishu notification → one click approve |
confidential |
pending | 2 approvals | 24 hours | Two approvals + 24h waiting period |
restricted |
pending | 2 approvals | 24 hours | Same as confidential |
Permission Level Guidelines
public: Read-only, non-sensitive operations (e.g., get_weather, query_public_data)internal: User-level write operations (e.g., update_profile, submit_form)confidential: High-risk operations (e.g., cancel_order, delete_data, batch_operations)restricted: System-level operations requiring strict controls
CLI Usage
# List tools with filters
tool-gov list --status pending
tool-gov list --status active
tool-gov list --status all
# Approve/reject tools
tool-gov approve <tool_name> --approver <name>
tool-gov reject <tool_name>
# Submit approval to external system (DingTalk/Feishu)
tool-gov submit-approval <tool_name> --platform dingtalk
tool-gov submit-approval <tool_name> --platform feishu
# Sync approval status from external system
tool-gov sync-approval --platform dingtalk
tool-gov sync-approval --platform feishu
# Specify database URL
tool-gov --db-url sqlite:///./my_db.db list
# Start Prometheus metrics server
tool-gov metrics --port 8000 --host 0.0.0.0
API Reference
governed_tool Decorator
Register a tool with governance metadata.
@governed_tool(
name: Optional[str] = None,
description: Optional[str] = None,
permission_level: str = "default",
args_schema: Optional[BaseModel] = None,
creator: str = "system",
)
def my_tool():
pass
apply_guardrails Function
Apply runtime guardrails to an AgentExecutor - blocks inactive tool calls.
executor = apply_guardrails(
executor: AgentExecutor,
db_url: str = "sqlite:///./tools.db",
)
ToolRegistryService Class
Manage tool registration and lifecycle with TTL caching.
registry = ToolRegistryService(
db_url: str = "sqlite:///./tools.db",
cache_ttl: int = 60, # Cache expiration in seconds
)
registry.scan_and_register() # Scan and register tools
registry.get_active_tools() # Get active tools (with TTL cache)
registry.approve_tool(name, approver) # Approve a tool
registry.reject_tool(name) # Reject a tool
registry.invalidate_cache() # Manually invalidate cache
External Approval Integration
Submit and sync approvals with DingTalk or Feishu:
from tool_governance.core.approval import create_approval_client
client = create_approval_client(
platform="dingtalk",
app_key="your_app_key",
app_secret="your_app_secret",
process_code="your_process_code",
)
# Submit approval
result = registry.submit_approval("cancel_order", client)
# Sync approval status
results = registry.sync_approval_status(client)
Approval Status Sync Flow
1. submit_approval → Submit to DingTalk/Feishu, save approval_id
2. Approver operates in DingTalk/Feishu (approve/reject)
3. sync_approval_status → Query external status, update local status
- "approved": Activate based on approval count and quiet period
- "rejected": Mark as REJECTED
4. Auto invalidate_cache() on status change, Agent picks up changes immediately
Dynamic Runtime Architecture
The framework enables online maintenance without restarting the Agent:
┌─────────────────────────────────────────────────────────────────┐
│ Agent Runtime │
│ │
│ AgentExecutor.invoke() │
│ │ │
│ ▼ │
│ apply_guardrails() │
│ │ │
│ ▼ │
│ get_active_tools() ──┬── Cache Hit (TTL < 60s) ──► Return │
│ │ │
│ ▼ │
│ Database Query ──► Update Cache ──► Return │
│ │
└─────────────────────────────────────────────────────────────────┘
▲
│ Cache Invalidation
▼
┌─────────────────────────────────────────────────────────────────┐
│ Approval Actions │
│ │
│ approve_tool() → invalidate_cache() │
│ reject_tool() → invalidate_cache() │
│ sync_approval_status() → invalidate_cache() │
│ │
└─────────────────────────────────────────────────────────────────┘
Key Benefits:
- No Restart Required: Cache invalidation ensures changes take effect immediately
- Performance: TTL caching reduces database queries during high-frequency tool calls
- Flexibility: Adjust
cache_ttlbased on your update frequency requirements
Database Configuration
SQLite (Default)
registry = ToolRegistryService(db_url="sqlite:///./tools.db")
PostgreSQL
registry = ToolRegistryService(db_url="postgresql://user:password@host:5432/dbname")
Database Schema
Table: tool_registry
| Column | Type | Description |
|---|---|---|
id |
INTEGER | Primary key |
name |
VARCHAR(100) | Tool name (unique) |
description |
TEXT | Tool description |
schema_json |
TEXT | Tool schema as JSON |
permission_level |
VARCHAR(50) | Permission level (public/internal/confidential/restricted/default) |
status |
VARCHAR(20) | Tool status (active/pending/rejected/deprecated) |
created_by |
VARCHAR(100) | Creator email |
created_at |
DATETIME | Creation timestamp |
approved_at |
DATETIME | Approval timestamp (nullable) |
approval_count |
INTEGER | Current approval count |
approval_required |
INTEGER | Required approvals |
approval_id |
VARCHAR(200) | External approval ID (nullable) |
approval_platform |
VARCHAR(50) | Approval platform (dingtalk/feishu) |
approvers |
TEXT | JSON list of approvers |
quiet_period_until |
DATETIME | Quiet period end time (nullable) |
Monitoring & Metrics
Prometheus Integration
The framework exposes Prometheus metrics for monitoring tool governance activity.
Start Metrics Server
# Using CLI options
tool-gov metrics --port 9090 --host 127.0.0.1
# Using environment variables
export TOOL_GOVERNANCE_METRICS_PORT=9090
export TOOL_GOVERNANCE_METRICS_HOST=127.0.0.1
tool-gov metrics
# Default values (port: 8000, host: 0.0.0.0)
tool-gov metrics
Configuration
The metrics server can be configured via:
-
CLI arguments (highest priority)
--port: Metrics server port--host: Metrics server host
-
Environment variables
TOOL_GOVERNANCE_METRICS_PORT: Port (default: 8000)TOOL_GOVERNANCE_METRICS_HOST: Host (default: 0.0.0.0)
-
Default values (lowest priority)
- Port:
8000 - Host:
0.0.0.0
- Port:
Metrics Endpoint
http://localhost:8000/metrics
Available Metrics
| Metric | Type | Description | Labels |
|---|---|---|---|
toolgovernance_tool_registered_total |
Counter | Total tools registered | permission_level |
toolgovernance_tool_activated_total |
Counter | Total tools activated | permission_level |
toolgovernance_tool_rejected_total |
Counter | Total tools rejected | permission_level |
toolgovernance_tool_approvals_total |
Counter | Total approvals | tool_name |
toolgovernance_tool_active_count |
Gauge | Active tools count | permission_level |
toolgovernance_tool_status_count |
Gauge | Tools count by status | status |
toolgovernance_approval_pending_seconds |
Histogram | Pending duration before approval | permission_level |
toolgovernance_cache_hits_total |
Counter | Cache hits | None |
toolgovernance_cache_misses_total |
Counter | Cache misses | None |
toolgovernance_cache_invalidations_total |
Counter | Cache invalidations | None |
Grafana Dashboard Suggestions
Create panels for:
- Tool Status Distribution: Pie chart of
toolgovernance_tool_status_count - Active Tools by Permission Level: Bar chart of
toolgovernance_tool_active_count - Approval Rate: Rate of
toolgovernance_tool_approvals_total - Approval Latency: Histogram of
toolgovernance_approval_pending_seconds - Cache Hit Ratio:
toolgovernance_cache_hits_total / (hits + misses)
Prometheus Scrape Config
scrape_configs:
- job_name: 'tool-governance'
static_configs:
- targets: ['localhost:8000']
metrics_path: /metrics
Configuration
Environment Variables
# DingTalk Approval
export DINGTALK_APP_KEY=your_app_key
export DINGTALK_APP_SECRET=your_app_secret
export DINGTALK_PROCESS_CODE=your_process_code
# Feishu Approval
export FEISHU_APP_ID=your_app_id
export FEISHU_APP_SECRET=your_app_secret
export FEISHU_APPROVAL_CODE=your_approval_code
# Prometheus Metrics
export TOOL_GOVERNANCE_METRICS_PORT=8000
export TOOL_GOVERNANCE_METRICS_HOST=0.0.0.0
Testing
pip install pytest
pytest tests/ -v
License
MIT License
Contributing
Contributions are welcome! Please submit a pull request or create an issue.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langchain_tool_gate-1.0.0.tar.gz.
File metadata
- Download URL: langchain_tool_gate-1.0.0.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3631dd287d3d59e6376721e63bbc3f3e1460a693748fcf0424576692228b8722
|
|
| MD5 |
1a1a81f8cca153af481cdf49817b4b51
|
|
| BLAKE2b-256 |
3d976fb60501f3a0c7d2fdff29f84f2071da2861cf87afe45762fdf4b0508d2b
|
Provenance
The following attestation bundles were made for langchain_tool_gate-1.0.0.tar.gz:
Publisher:
release.yml on yusishuma/langchain-tool-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
langchain_tool_gate-1.0.0.tar.gz -
Subject digest:
3631dd287d3d59e6376721e63bbc3f3e1460a693748fcf0424576692228b8722 - Sigstore transparency entry: 2047849485
- Sigstore integration time:
-
Permalink:
yusishuma/langchain-tool-gate@c1179f782a9a3539d6be6a2abbc42974fd31f6f1 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/yusishuma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1179f782a9a3539d6be6a2abbc42974fd31f6f1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file langchain_tool_gate-1.0.0-py3-none-any.whl.
File metadata
- Download URL: langchain_tool_gate-1.0.0-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6fe2a43f42677c54930713c06c62f845830365d179769c4ae51b4d95a9bba8f
|
|
| MD5 |
8d353cf398f29dea4088fd29b5b1fabf
|
|
| BLAKE2b-256 |
1664cad67b8ed0f59cbadc1962c8fda3b5be8ddbd37f4b3c7f78a67dbc03bb6f
|
Provenance
The following attestation bundles were made for langchain_tool_gate-1.0.0-py3-none-any.whl:
Publisher:
release.yml on yusishuma/langchain-tool-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
langchain_tool_gate-1.0.0-py3-none-any.whl -
Subject digest:
d6fe2a43f42677c54930713c06c62f845830365d179769c4ae51b4d95a9bba8f - Sigstore transparency entry: 2047849511
- Sigstore integration time:
-
Permalink:
yusishuma/langchain-tool-gate@c1179f782a9a3539d6be6a2abbc42974fd31f6f1 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/yusishuma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1179f782a9a3539d6be6a2abbc42974fd31f6f1 -
Trigger Event:
push
-
Statement type: