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.handlerPermissionHandler❌Your permission logic. If omitted, all calls are allowed
ctx.toolNamestring✅Name of the tool being requested
ctx.userIdstring❌ID of the user making the request
ctx.userRolestring❌Role of the user (e.g. "admin", "viewer")
ctx.metadataobject❌Any 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