toolgovern

by rudrendupaul

Not rated yet

About

MCP server wrapping the toolgovern CLI for agent-tool policy validation.

Explore

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name toolgovern
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

A generic, documented adapter for wrapping a multi-agent framework's tool-executor call site. It is not a submitted or merged integration against any specific upstream project -- it's a working starting point to adapt, not a claim that any framework ships this today.

npm install toolgovern-integration-oma toolgovern

Two shapes, matching the two real patterns frameworks actually use. Start with the first one:

// Per-tool, registration-time wrapping -- the pattern most frameworks with a tool registry // actually use (register one governed tool at a time). import { governedTool } from 'toolgovern-integration-oma'; import { loadPolicy } from 'toolgovern'; const policy = loadPolicy('./toolgovern.policy.yml'); registry.register(governedTool(myTool, policy));
// Dispatcher wrapping -- for frameworks whose tool-executor is a single // runTool(name, args) dispatcher instead of per-tool registration. import { governedExecutor } from 'toolgovern-integration-oma'; import { loadPolicy } from 'toolgovern'; const policy = loadPolicy('./toolgovern.policy.yml'); const executor = governedExecutor(baseExecutor, policy); // wherever your framework currently calls baseExecutor.runTool(name, args) directly, // call executor.runTool(name, args) instead

LangGraph.js'sToolNodehas nowrap_tool_callhook -- that only exists in the separately maintained Pythonlanggraphpackage. The working Node-only integration point is one level up, at tool-definition time: wrap each tool withgovernTool(), then re-wrap it with LangChain's owntool()factory before it goes intonew ToolNode(](https://github.com/RudrenduPaul/toolgovern/blob/HEAD/docs/security-model.md)[...]).

npm install toolgovern-integration-langgraph @langchain/core @langchain/langgraph toolgovern
import { ToolNode } from '@langchain/langgraph/prebuilt'; import { governedLangGraphTools } from 'toolgovern-integration-langgraph'; import { loadPolicy } from 'toolgovern'; const policy = loadPolicy('./toolgovern.policy.yml'); const toolNode = new ToolNode( governedLangGraphTools(myLangChainTools, { ...policy, agentId: 'research-sub', sessionId: 'demo-session', }), ); // wire toolNode into your StateGraph exactly as you would with the raw tools array -- // every call now flows through toolgovern's classifier first.

This is new capability for LangGraph.js users going forward -- it does not retroactively resolve any previously reported LangGraph issue, since every LangGraph issue this project has validated was filed against the Pythonlangchain-ai/langgraphrepository, notlanggraphjs.

The separately maintained Pythonlanggraphpackage DOES expose awrap_tool_callhook, a publicToolNodeconstructor parameter (confirmed against the real, installedlanggraph==1.2.9/langgraph-prebuilt==1.1.0source). Every real LangGraph GitHub issue this project has validated (langchain-ai/langgraph#8026, #7687, #7178, #8169) is filed against exactly this package, so this is the integration that targets real, reported behavior -- seeintegrations/langgraph-python/docs/root-cause.mdfor the per-issue PASS/PARTIAL/FAIL verdicts.

This isn't published to PyPI yet -- install it from source:

git clone https://github.com/RudrenduPaul/toolgovern.git cd toolgovern pip install -e python pip install -e integrations/langgraph-python
from langgraph.prebuilt import ToolNode from toolgovern import GovernToolOptions, load_policy from toolgovern_integration_langgraph import governed_tool_node policy = load_policy("./toolgovern.policy.yml") options = GovernToolOptions.from_policy(policy, agent_id="research-sub", session_id="demo-session") tool_node = governed_tool_node(my_tools, options) # wire tool_node into your StateGraph exactly as you would with the raw tools array -- # every call now flows through toolgovern's classifier first.

Seeintegrations/langgraph-python/README.mdfor the tool-definition-boundary alternative (governed_tool/governed_tools) and the verified, version-specifichandle_tool_errorsbehavior a denial surfaces through.

Seeintegrations/agent-framework/README.mdfor the full writeup, including honest PASS/PARTIAL/FAIL verdicts against real upstreammicrosoft/agent-frameworkissues. This one is Python-only; the .NET side of Agent Framework has its own separate adapter -- see the ".NET" section below.

This isn't published to PyPI yet -- install it from source:

git clone https://github.com/RudrenduPaul/toolgovern.git cd toolgovern pip install -e python pip install -e integrations/agent-framework
from toolgovern import GovernToolOptions, ScopeDeclaration from toolgovern_integration_agent_framework import governed_function_tool def read_file(path: str) -> str: with open(path) as f: return f.read() tool = governed_function_tool( read_file, GovernToolOptions(scope=ScopeDeclaration(filesystem=["/workspace"]), agent_id="research-agent"), description="Read a file from the workspace.", ) # tool is a real agent_framework.FunctionTool -- use it exactly like any other tool.

AToolGovernFunctionMiddlewareis also included for surfacing a per-call require-approval verdict through Agent Framework's ownfunction_approval_request/function_approval_responseflow (rather than a separate side channel), plus a connection-time MCP-server trust gate wiring toolgovern'smcp_trustmodule toMCPStreamableHTTPTool. See that package's README for both.

CrewAI's tool-execution surface iscrewai.tools.BaseTool-- a concreterun()that validates arguments and claims a usage-count slot, then calls an abstract_run()a subclass implements (confirmed against the real, installedcrewai1.15.4 wheel, not assumed from an older release). CrewAI does ship a global, process-widebefore_tool_callhook registry, but that's a different shape fromgovern_tool()'s per-tool-instance, per-agent-identity, per-scope gate -- so this package wraps at theBaseToolboundary itself instead, the same approach the LangGraph.js adapter above uses. No monkey-patching: it returns a newBaseToolwith the samename,description, andargs_schema, calling the real tool's ownrun()only after the classifier allows the call. This isn't published to PyPI yet -- install it from source:

git clone https://github.com/RudrenduPaul/toolgovern.git cd toolgovern pip install -e python pip install -e integrations/crewai
from crewai import Agent from crewai.tools import BaseTool from toolgovern import GovernToolOptions, ScopeDeclaration from toolgovern_integration_crewai import governed_crewai_tool class ShellTool(BaseTool): name: str = "shell" description: str = "Runs a shell command." def _run(self, command: str) -> str: import subprocess return subprocess.run(command, shell=True, capture_output=True, text=True).stdout governed_shell = governed_crewai_tool( ShellTool(), GovernToolOptions( scope=ScopeDeclaration(network=False, filesystem=["./workspace"]), agent_id="research-sub", session_id="demo-session", ), ) agent = Agent(role="Researcher", goal="...", backstory="...", tools=[governed_shell])

Seeintegrations/crewai/README.mdfor the full writeup, including why there's no pluralgoverned_crewai_tools()helper (CrewAI tools are commonly assigned per-agent with different scopes, so wrapping a whole list with one shared options object is the wrong default here).

Targets AutoGen's two real dispatch call sites directly:GovernedCodeExecutorwraps anyCodeExecutor(LocalCommandLineCodeExecutor,DockerCommandLineCodeExecutor, ...) so everyCodeBlockis classified by TG01/TG02 before the wrapped executor runs it -- the flagship issue this addresses,microsoft/autogen#7462, is thatLocalCommandLineCodeExecutorwrites LLM-generated code straight to disk with only a construction-timeUserWarningas a safeguard.governed_autogen_tool()wraps anyautogen_core.tools.Toolat itsrun_json()dispatch point instead, the same oneToolAgent/AssistantAgentboth use. This isn't published to PyPI yet -- install it from source:

git clone https://github.com/RudrenduPaul/toolgovern.git cd toolgovern pip install -e python pip install -e integrations/autogen
from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor from toolgovern import GovernToolOptions, ScopeDeclaration, ToolGovernDenialError from toolgovern_integration_autogen import GovernedCodeExecutor real_executor = LocalCommandLineCodeExecutor(work_dir="./coding") governed = GovernedCodeExecutor(real_executor, GovernToolOptions(scope=ScopeDeclaration())) # A dangerous block never reaches LocalCommandLineCodeExecutor.execute_code_blocks() at all. try: await governed.execute_code_blocks( [CodeBlock(code="import os; os.system('rm -rf /')", language="python")], CancellationToken() ) except ToolGovernDenialError as e: print(f"denied before execution: {e}")

Seeintegrations/autogen/README.mdfor the full writeup, including honest verdicts against real upstream issues this does and doesn't address -- it's a pre-execution classifier, not a sandbox: it doesn't enforce process isolation or resource limits, so pair it withDockerCommandLineCodeExecutor(or similar) for genuine isolation.

Routes tool calls through a realPreToolUsehook -- verified against the installedclaude-agent-sdkpackage (claude_agent_sdk/types.py) directly, not a docs summary. The hook fires before any tool executes, receives the tool name and input the model is about to invoke, and returns a structuredpermissionDecisionthe CLI itself enforces, so there's no per-tool wrapper call site to get right or accidentally miss. This isn't published to PyPI yet -- install it from source:

git clone https://github.com/RudrenduPaul/toolgovern.git cd toolgovern pip install -e python pip install -e integrations/claude-agent-sdk pip install claude-agent-sdk
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher from toolgovern import ScopeDeclaration from toolgovern_integration_claude_agent_sdk import GovernedHookOptions, governed_pretooluse_hook hook = governed_pretooluse_hook( GovernedHookOptions( scope=ScopeDeclaration(filesystem=["/workspace"], network=["api.internal.example.com"]), agent_id="research-sub", session_id="demo-session", ) ) options = ClaudeAgentOptions(hooks={"PreToolUse": [HookMatcher(hooks=[hook])]})

Arequire-approvalverdict has no in-hook way to pause for asynchronous human review, so it's wired to the samePendingApprovalRegistrythe core ships (see the Approval table above): the decision is registered durably first, an optionalon_approval_requiredhandler gets a bounded window to answer, and if there's no handler, it raises, or it times out, the hook fails closed (deny) with the pending-approval ID named in the reason so it can be resolved out of band. Seeintegrations/claude-agent-sdk/README.mdfor the full writeup.

A faithful .NET port of the core (the same multi-rule classifier -- shell-risk, filesystem-scope, network-egress, credential-access, cross-agent-inheritance, information-flow -- the intersection-only scope registry, the signed hash-chained trace, and theGovernTool()pre-execution middleware gate) lives underdotnet/ToolGovern, targetingnet10.0.ToolGovern.AgentFrameworkbuilds on it to gate Microsoft Agent Framework (.NET)AIFunctiontool calls, using the exactDelegatingAIFunctionextension point the framework's own maintainer pointed integrators to inagent-framework#2254. Neither package is published to NuGet yet -- build from source:

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "toolgovern": {
            "server": {
                "command": "uvx",
                "args": [
                    "toolgovern-cli"
                ]
            }
        }
    }
}

McpServers

{
    "server": {
        "command": "uvx",
        "args": [
            "toolgovern-cli"
        ]
    }
}

Transport

"stdio"

Package

"toolgovern-cli"

Registry

"pypi"

A generic, documented adapter for wrapping a multi-agent framework's tool-executor call site. It is not a submitted or merged integration against any specific upstream project -- it's a working starting point to adapt, not a claim that any framework ships this today.

npm install toolgovern-integration-oma toolgovern

Two shapes, matching the two real patterns frameworks actually use. Start with the first one:

// Per-tool, registration-time wrapping -- the pattern most frameworks with a tool registry // actually use (register one governed tool at a time). import { governedTool } from 'toolgovern-integration-oma'; import { loadPolicy } from 'toolgovern'; const policy = loadPolicy('./toolgovern.policy.yml'); registry.register(governedTool(myTool, policy));
// Dispatcher wrapping -- for frameworks whose tool-executor is a single // runTool(name, args) dispatcher instead of per-tool registration. import { governedExecutor } from 'toolgovern-integration-oma'; import { loadPolicy } from 'toolgovern'; const policy = loadPolicy('./toolgovern.policy.yml'); const executor = governedExecutor(baseExecutor, policy); // wherever your framework currently calls baseExecutor.runTool(name, args) directly, // call executor.runTool(name, args) instead
No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.