Skip to Content
Control LayercheckPermission

checkPermission

Problem

Not every user should be able to trigger every tool. An agent with access to a “delete_record” tool or a “send_email” tool should not run those freely for all users. Without a permission layer, you end up checking user roles in each tool’s execute function — scattered, inconsistent, and easy to miss.


Solution

checkPermission is a centralized gate you put before any tool execution. You pass it a config with your own handler function. It calls that handler with context about the tool call and the user, and blocks execution if the handler returns false.


Feature & Use-Case

Use checkPermission when:

  • You have role-based access control — admins can use all tools, users can only use safe ones
  • You want to block specific tools for certain user IDs
  • You are building a multi-tenant Node.js agent where different clients have different tool access
  • You want permission logic in one place, not scattered inside every tool

Import

import { checkPermission } from "llm-layer-engine";

Function Signature

async function checkPermission( config: PermissionConfig, ctx: PermissionContext ): Promise<boolean>

Types

type PermissionContext = { toolName: string; userId?: string; userRole?: string; metadata?: Record<string, unknown>; }; interface PermissionConfig { handler?: PermissionHandler; } type PermissionHandler = (ctx: PermissionContext) => boolean | Promise<boolean>;

Parameters

ParameterTypeRequiredDescription
config.handlerPermissionHandlerYour permission logic. If omitted, all calls are allowed
ctx.toolNamestringName of the tool being requested
ctx.userIdstringID of the user making the request
ctx.userRolestringRole of the user (e.g. "admin", "viewer")
ctx.metadataobjectAny extra context you want to pass to the handler

Example — Role-Based Tool Access

import { checkPermission } from "llm-layer-engine"; const permissionConfig = { handler: async ({ toolName, userRole }) => { const adminOnlyTools = ["delete_record", "send_email", "update_user"]; if (adminOnlyTools.includes(toolName) && userRole !== "admin") { return false; // Block } return true; // Allow }, }; // Before executing a tool in your loop: const allowed = await checkPermission(permissionConfig, { toolName: "delete_record", userId: "user_456", userRole: "viewer", }); if (!allowed) { console.log("Permission denied for delete_record"); } else { // Proceed with tool execution }

Example — No Handler (Allow All)

const allowed = await checkPermission({}, { toolName: "get_weather", userId: "user_123", }); console.log(allowed); // → true (no handler = allow everything)

When no handler is provided, checkPermission always returns true. Safe default — you only add a handler when you need restrictions.


Example — Integrating Into a Custom Loop

import { checkPermission, executeStep } from "llm-layer-engine"; // Before calling executeStep, gate with permission check const toolName = "send_email"; const allowed = await checkPermission(permissionConfig, { toolName, userId: currentUser.id, userRole: currentUser.role, }); if (!allowed) { throw new Error(`User ${currentUser.id} is not allowed to use ${toolName}`); }

Conclusion

checkPermission centralizes your access control in one handler. Define it once, call it before tool execution across all your agents. No handler means open access — add one only when you need to restrict. Pair it with requestApproval for a full human-in-the-loop + RBAC setup.

Last updated on