Claude-Agent

 view release on metacpan or  search on metacpan

examples/04-permissions.pl  view on Meta::CPAN

#!/usr/bin/env perl
#
# Permissions Example
#
# This example demonstrates how to use the can_use_tool callback
# to implement custom permission logic for tool usage.
#

use 5.020;
use strict;
use warnings;

use lib 'lib';
use Claude::Agent qw(query);
use Claude::Agent::Options;
use Claude::Agent::Permission;

# Track which tools have been approved this session
my %approved_tools;

# Custom permission handler
my $permission_handler = sub {
    my ($tool_name, $input, $context) = @_;

    # Always allow read-only tools
    if ($tool_name =~ /^(Read|Glob|Grep)$/) {
        return Claude::Agent::Permission->allow(
            updated_input => $input,
        );
    }

    # Check if tool was previously approved
    if ($approved_tools{$tool_name}) {
        return Claude::Agent::Permission->allow(
            updated_input => $input,
        );
    }

    # For Bash commands, check the command
    if ($tool_name eq 'Bash') {
        my $command = $input->{command} // '';

        # Auto-allow safe read-only commands
        if ($command =~ /^(ls|pwd|echo|cat|head|tail|wc|date|whoami)(\s|$)/) {
            return Claude::Agent::Permission->allow(
                updated_input => $input,
            );
        }

        # Deny dangerous commands
        if ($command =~ /rm|sudo|chmod|chown|mv|cp.*-f/) {
            return Claude::Agent::Permission->deny(
                message => "Command '$command' is not allowed for safety reasons.",
            );
        }

        # Ask for other commands (in a real app, prompt the user)
        say "\n[PERMISSION] Bash command requested: $command";
        say "[PERMISSION] Auto-approving for demo purposes...";

        $approved_tools{$tool_name} = 1;
        return Claude::Agent::Permission->allow(
            updated_input => $input,
        );
    }

    # For Write/Edit, be more careful
    if ($tool_name =~ /^(Write|Edit)$/) {
        my $file_path = $input->{file_path} // '';

        say "\n[PERMISSION] $tool_name requested for: $file_path";

        # Only allow writes to specific directories
        if ($file_path =~ m{^(/tmp/|\./)}) {
            say "[PERMISSION] Allowing write to temp/local path";
            return Claude::Agent::Permission->allow(
                updated_input => $input,
            );
        }

        say "[PERMISSION] Denying write to protected path";
        return Claude::Agent::Permission->deny(
            message => "Writes are only allowed to /tmp/ or current directory.",
        );
    }

    # Default: deny unknown tools
    return Claude::Agent::Permission->deny(
        message => "Tool '$tool_name' requires explicit approval.",
    );
};

# Configure options with permission handler
my $options = Claude::Agent::Options->new(
    allowed_tools => ['Read', 'Glob', 'Grep', 'Bash', 'Write'],
    can_use_tool  => $permission_handler,
    max_turns     => 5,
);

# Send a query
my $iter = query(
    prompt  => 'Run "date" command to show the current date and time.',
    options => $options,
);

say "Sending query with custom permissions...";
say "-" x 50;

while (my $msg = $iter->next) {
    if ($msg->isa('Claude::Agent::Message::Assistant')) {
        for my $block (@{$msg->content_blocks}) {



( run in 0.418 second using v1.01-cache-2.11-cpan-71847e10f99 )