Authorizing Tool Calls without MCP
When an AI agent's tools are plain functions — a LangChain @tool, a LangGraph node, or your own custom agent loop — there's no MCP server to put a proxy in front of. You authorize each call by invoking the Vengtoo SDK inside the tool, before the tool does its work.
:::tip Using MCP servers? If your agent's tools are exposed over MCP, use the MCP Gateway (drop-in proxy, no code changes) or the MCP Adapter (embed in a server you own) instead. This guide is only for native function tools that don't go through MCP.
The deciding factor is whether the tools are exposed over MCP — not the language your agent is written in. :::
The pattern
Every tool call becomes an authorization check:
- the agent is the
subject, - the tool is the
resource, - the action is
invoke, - and the tool's arguments go into
resource.propertiesso policies can inspect them.
Run the tool only if the decision is allow.
Unlike the Gateway and Adapter — which capture tool arguments automatically — here you pass the arguments into
resource.propertiesyourself. That's the one bit of wiring the SDK path requires.
Install
pip install vengtoo
A reusable guard
Wrap the check once, then call it from any tool:
from vengtoo import Vengtoo, Subject, Resource, Action
# Cloud, or point at a local agent with base_url="http://localhost:8181"
client = Vengtoo(api_key="azx_...")
def authorize_tool(agent_id: str, tool_name: str, args: dict) -> None:
"""Raise if the agent may not invoke this tool with these arguments."""
allowed = client.check(
subject=Subject(id=agent_id, type="ai_agent"),
action=Action(name="invoke"),
resource=Resource(id=tool_name, type="tool", properties=args),
)
if not allowed:
raise PermissionError(
f"{agent_id} is not allowed to call {tool_name} with {args}"
)
Using it in a LangChain tool
from langchain_core.tools import tool
@tool
def issue_refund(customer_id: str, amount: float) -> str:
"""Refund a customer."""
authorize_tool(
"agent:support-bot",
"issue_refund",
{"customer_id": customer_id, "amount": amount}, # → resource.properties
)
return payments.refund(customer_id, amount) # only runs if authorized
The same shape works for a LangGraph node or a hand-rolled agent loop — call authorize_tool(...) at the top of the function and let the exception short-circuit a denied call.
Argument-level policies
Because the arguments land in resource.properties, your policy can decide on the values, not just the tool. For example, an ABAC rule:
support_rep can invoke
issue_refundonly ifamount < 100
evaluates resource.properties.amount on every call. A refund of 250 is denied before payments.refund ever runs; a refund of 50 goes through. See ABAC conditions for the policy side.
Other languages
The pattern is identical in any SDK — it follows your agent's language, not the framework:
// Node / TypeScript — npm install @vengtoo/sdk
const { decision } = await vengtoo.authorize({
subject: { id: "agent:support-bot", type: "ai_agent" },
resource: { id: "issue_refund", type: "tool", properties: { customer_id, amount } },
action: { name: "invoke" },
});
if (!decision) throw new Error("not authorized");
// Go — go get github.com/vengtoo/vengtoo-go
allowed, _ := client.Check(ctx,
vengtoo.Subject{ID: "agent:support-bot", Type: "ai_agent"},
vengtoo.Action{Name: "invoke"},
vengtoo.Resource{ID: "issue_refund", Type: "tool", Properties: map[string]any{
"customer_id": customerID, "amount": amount,
}},
)
Next steps
- MCP Gateway — If your tools are MCP servers, gate them with no code changes.
- ABAC Conditions — Write rules over tool arguments.
- SDKs & CLI — Full SDK reference for Python, Node, and Go.