AI agents become useful when they can do more than generate text.

They can:

Code
Read files
Query databases
Create tickets
Send emails
Call APIs
Modify repositories
Deploy applications
Execute workflows

That capability is also the security problem.

An agent that can access every available tool effectively becomes an identity with broad privileges.

The traditional approach is often:

Code
User
 ↓
AI Agent
 ↓
All Available Tools

A safer architecture is:

Code
User
 ↓
AI Agent
 ↓
Capability Policy
 ↓
Allowed Tools
 ↓
Allowed Operations
 ↓
Allowed Resources

This article explores how to implement that model for AI tool plugins using capability-based security.

The central principle is simple:

An AI agent should receive only the capabilities required for the current task.

This is closely aligned with current security guidance from OWASP and Microsoft, which recommends least-privilege tool access, scoped permissions, server-side authorization, and explicit controls for sensitive operations.

What Is Capability-Based Security?

Capability-based security represents authority as an explicit capability.

Instead of saying:

Code
Agent = Administrator

we define:

Code
Agent
    ├── read_orders
    ├── create_ticket
    └── read_customer_profile

The agent does not automatically receive everything associated with an administrator role.

Its authority is the collection of capabilities explicitly granted to it.

A useful model is:

Code
Capability
=
Action
+
Resource
+
Constraints

For example:

Code
read_customer
+
customer/123
+
read-only

is significantly narrower than:

Code
customer_database
+
full_access

Why AI Agents Need Fine-Grained Capabilities

Traditional applications usually have deterministic control flow.

For example:

C#
if (user.CanDeleteOrder)
{
    DeleteOrder(orderId);
}

An AI agent introduces another decision-maker:

Code
User request
     ↓
LLM
     ↓
Tool selection
     ↓
Tool arguments
     ↓
Application

The model may choose:

Code
Which tool?
Which parameters?
Which sequence?
How many times?

That makes tool authorization especially important.

OWASP recommends granting agents the minimum tools required for their specific task and implementing per-tool permission scoping, including distinctions such as read-only versus write operations.

Capability Security vs Role-Based Access Control

RBAC answers:

Code
"What role does this identity have?"

Capability security answers:

Code
"What exact authority has been granted?"

Both approaches can be useful.

ModelExampleGranularity
RBACAgent = SupportAgentCoarse
ABACDepartment = Support + Region = EUMedium
CapabilityRead ticket 123Fine
Resource capabilityRead /reports/2026/q1Very fine

In an AI system, capabilities can be derived from RBAC or ABAC policies.

The important point is that the final tool invocation should still be authorized server-side.

The Basic Architecture

A secure tool architecture can look like this:

Code
                 User
                   |
                   v
             AI Application
                   |
                   v
              Agent/LLM
                   |
                   v
           Capability Policy
                   |
          +--------+--------+
          |                 |
       Allowed           Denied
          |                 |
          v                 X
      Tool Gateway
          |
          v
      Tool Server
          |
          v
       Resource

The model should not be the final authority.

The authorization layer should be.

Never Use the System Prompt as Authorization

A weak implementation might say:

Code
You are a support agent.
Never delete customer records.
Only read customer information.

That is useful as behavioral guidance.

It is not authorization.

A prompt can be influenced by:

Code
User input
Retrieved documents
Tool output
Web pages
Other agents

OWASP specifically recommends server-side enforcement because prompt instructions should not be relied upon to restrict tool access.

The secure architecture is:

Code
LLM says:
"Call delete_customer"

        ↓

Authorization layer

        ↓

Does this agent have
delete_customer capability?

        ↓

NO

        ↓

Reject

Define Capabilities Explicitly

A capability can be represented as a strongly typed object.

For example, in C#:

C#
public sealed record ToolCapability(
    string Tool,
    string Operation,
    string Resource,
    string Effect);

Then define:

C#
var capabilities = new[]
{
    new ToolCapability(
        "customer",
        "read",
        "customer:*",
        "allow")
};

This is only a starting point.

Production systems should normally use a structured authorization model rather than ad-hoc strings.

Add Constraints

A capability should be more specific than:

Code
customer.read

Consider:

Code
customer.read
region = EU
department = support

or:

Code
order.update
orderId = 12345
fields = status

This creates a capability such as:

Code
Update order status
for order 12345

rather than:

Code
Update any order

Capability Object

A more detailed C# model could be:

C#
public sealed record Capability(
    string Tool,
    string Action,
    string ResourceType,
    IReadOnlySet<string> AllowedResources,
    DateTimeOffset ExpiresAt);

Example:

C#
var capability = new Capability(
    Tool: "orders",
    Action: "read",
    ResourceType: "order",
    AllowedResources: new HashSet<string>
    {
        "order:12345",
        "order:12346"
    },
    ExpiresAt: DateTimeOffset.UtcNow.AddMinutes(10));

Now the authorization decision can consider:

Code
Tool
Action
Resource
Expiration

Use Deny by Default

The safest starting policy is:

Code
No capability
    ↓
No operation

Not:

Code
Everything allowed
    ↓
Try to block dangerous operations

OWASP's MCP security guidance explicitly recommends least privilege and deny-by-default authorization for agent/tool systems.

For example:

C#
public bool IsAllowed(
    Capability capability,
    string tool,
    string action,
    string resource)
{
    if (capability.ExpiresAt <= DateTimeOffset.UtcNow)
        return false;

    if (!string.Equals(
        capability.Tool,
        tool,
        StringComparison.Ordinal))
        return false;

    if (!string.Equals(
        capability.Action,
        action,
        StringComparison.Ordinal))
        return false;

    return capability.AllowedResources.Contains(resource);
}

The important behavior is:

Code
Unknown
→ Deny

Expired
→ Deny

Wrong tool
→ Deny

Wrong action
→ Deny

Wrong resource
→ Deny

Separate Read and Write Capabilities

Do not combine:

Code
customer.read
customer.write
customer.delete

into:

Code
customer.full_access

Instead:

Code
customer.read
customer.update
customer.delete

should be independent capabilities.

This allows:

Code
Support Agent
    ↓
customer.read

while:

Code
Customer Administrator
    ↓
customer.read
customer.update

and:

Code
Privileged Workflow
    ↓
customer.delete

has a separate authorization path.

Separate Sensitive Tools

High-risk tools should not sit in the same unrestricted tool collection as ordinary read operations.

For example:

Code
General Agent
 ├── search_documents
 ├── get_customer
 └── get_order

Privileged Agent
 ├── refund_order
 ├── delete_customer
 └── deploy_application

OWASP recommends separate tool sets for different trust levels and explicit authorization for sensitive operations.

Add Human Approval for High-Risk Capabilities

Some capabilities should require an additional control.

For example:

Code
read_customer
    ↓
Automatic

update_customer
    ↓
Policy check

delete_customer
    ↓
Policy check
+
Human approval

Sensitive actions can include:

Code
Delete
Refund
Transfer money
Send external email
Change permissions
Deploy production
Rotate credentials

The confirmation should happen outside the model's reasoning.

The user should see:

Code
Action:
Delete customer

Resource:
Customer 12345

Reason:
Requested by current workflow

Approve?
[Yes] [No]

OWASP recommends explicit user confirmation for destructive, financial, and data-sharing operations.

Do Not Ask the LLM to Approve Its Own Action

This is unsafe:

Code
Agent:
"Should I delete the customer?"

LLM:
"Yes."

Agent:
"Delete customer."

The same decision-maker is both requesting and approving the operation.

Instead:

Code
Agent
 ↓
Authorization Service
 ↓
Human Approval
 ↓
Tool

Short-Lived Capabilities

Capabilities should normally have limited lifetimes.

For example:

C#
ExpiresAt =
    DateTimeOffset.UtcNow.AddMinutes(10);

This limits the impact of leaked or stale authority.

Microsoft's current guidance for least-privilege AI agents recommends scoped access and short-lived credentials, while OWASP's MCP guidance similarly recommends narrow scopes and ephemeral credentials where possible.

Avoid Permanent Agent Permissions

Avoid:

Code
Agent A
    ↓
Permanent database.write

Prefer:

Code
Task starts
    ↓
Capability issued
    ↓
Task executes
    ↓
Capability expires

This makes authority proportional to the task.

Bind Capabilities to an Identity

A capability should not be treated as an anonymous permission.

It should be associated with:

Code
User
Agent
Application
Session/task
Tenant

For example:

C#
public sealed record CapabilityContext(
    string UserId,
    string AgentId,
    string TenantId,
    string TaskId);

Then the authorization layer can evaluate:

Code
Who?
Which agent?
Which tenant?
Which task?
Which capability?
Which resource?

Multi-Tenant AI Systems Need Tenant Boundaries

Consider:

Code
Tenant A
    ↓
Agent A
    ↓
Customer data

Tenant B
    ↓
Agent B
    ↓
Customer data

A capability such as:

Code
customer.read

is not sufficient.

It should be constrained by tenant:

Code
customer.read
tenant = tenant-a

The authorization layer must never trust:

Code
tenantId

because it came from the model.

The server should derive tenant identity from authenticated context.

Prevent Cross-Tenant Resource Access

An unsafe tool might accept:

JSON
{
  "tenantId": "tenant-b",
  "customerId": "123"
}

and trust both fields.

A safer implementation derives the tenant from the authenticated principal:

C#
var tenantId =
    userContext.TenantId;

var customer =
    await repository.GetCustomerAsync(
        tenantId,
        customerId,
        cancellationToken);

The agent supplies:

Code
customerId

but not the authoritative tenant identity.

Capability-Based Tool Contracts

Tool schemas should also reflect authorization boundaries.

Instead of:

JSON
{
  "name": "execute_sql",
  "description": "Execute SQL against the database"
}

prefer domain-specific operations:

JSON
{
  "name": "get_customer",
  "description": "Read a customer record by ID"
}

The second tool has a much smaller capability surface.

OWASP recommends avoiding unrestricted tools such as arbitrary shell access and instead exposing narrowly scoped tools with explicit allowed operations and resources.

Avoid Generic execute_command

This is dangerous:

Code
execute_command(command)

because the capability becomes:

Code
Run arbitrary operating-system commands

A safer design might expose:

Code
get_build_status
restart_test_service
read_deployment_logs

Each operation can have its own authorization policy.

Avoid Generic Database Tools

This:

Code
execute_sql(sql)

creates a large capability.

Prefer:

Code
get_customer
list_orders
get_order_status

If a specialized reporting workflow truly requires SQL, isolate it behind:

Code
Read-only database
+
Restricted schema
+
Query validation
+
Timeout
+
Resource limits

Tool Authorization Middleware

Authorization should happen immediately before execution.

A simplified C# middleware might look like:

C#
public async Task<ToolResult> ExecuteAsync(
    ToolRequest request,
    ToolContext context,
    CancellationToken cancellationToken)
{
    var decision =
        await authorization.AuthorizeAsync(
            context,
            request,
            cancellationToken);

    if (!decision.Allowed)
    {
        return ToolResult.Denied(
            decision.Reason);
    }

    return await toolExecutor.ExecuteAsync(
        request,
        cancellationToken);
}

The critical property is:

Code
Authorization
    ↓
Tool execution

not:

Code
LLM
    ↓
Tool execution
    ↓
Authorization later

Once the tool has executed, authorization is too late.

Authorization Must Be Server-Side

The client can send:

JSON
{
  "tool": "refund_order",
  "approved": true
}

That does not mean the request is authorized.

The server should determine:

Code
Authenticated identity
+
Current policy
+
Capability
+
Resource
+
Action

and make its own decision.

OWASP identifies reliance on client-provided identity or authorization context as a major MCP authorization weakness.

Policy Decision Point

For larger systems, centralize authorization.

Code
Agent
 ↓
Tool Gateway
 ↓
Policy Decision Point
 ↓
Allow / Deny
 ↓
Tool

The policy engine can evaluate:

Code
Subject
Action
Resource
Environment

For example:

Code
Subject:
agent-support-01

Action:
read

Resource:
customer:12345

Context:
tenant=tenant-a

Result:

Code
ALLOW

Capability Gateway Example

A tool gateway can expose:

C#
public interface ICapabilityAuthorizer
{
    Task<AuthorizationDecision> AuthorizeAsync(
        CapabilityRequest request,
        CancellationToken cancellationToken);
}

Then:

C#
public sealed record CapabilityRequest(
    string AgentId,
    string UserId,
    string TenantId,
    string Tool,
    string Action,
    string Resource);

This makes authorization explicit rather than embedding it inside every tool implementation.

Use Resource-Level Authorization

Do not stop at:

Code
agent can call orders.read

Check:

Code
agent can call orders.read
AND
agent can read this particular order

For example:

Code
orders.read
resource = order:12345

This prevents an agent with legitimate read access from automatically gaining access to every record.

Attribute-Based Conditions

Capabilities can include conditions.

For example:

Code
Action:
read_customer

Conditions:
tenant = tenant-a
region = EU
classification <= Internal

Another example:

Code
Action:
refund_order

Conditions:
amount <= 100
currency = USD
humanApproval = true

This is where capability-based authorization becomes more powerful than simple role checks.

Tool Result Security

Authorization does not end when the tool returns.

Tool output can contain:

Code
Customer data
Secrets
Internal URLs
Prompt injection
Untrusted instructions

OWASP recommends treating tool responses as untrusted data because returned content can influence subsequent agent behavior.

Therefore:

Code
Tool
 ↓
Output validation
 ↓
Redaction
 ↓
Agent context

is preferable to:

Code
Tool
 ↓
Raw output
 ↓
LLM

Redact Sensitive Fields

Suppose the database returns:

JSON
{
  "name": "Alice",
  "email": "[email protected]",
  "passwordHash": "...",
  "internalNotes": "..."
}

The tool should return only what the agent needs:

JSON
{
  "name": "Alice",
  "email": "[email protected]"
}

Do not rely on the LLM to ignore sensitive fields.

Data minimization belongs at the tool boundary.

Prevent Capability Leakage

Never expose internal authorization details unnecessarily.

Avoid returning:

Code
Agent has:
database.admin
secrets.read
production.deploy

to the model unless the information is genuinely required.

A denied result can simply say:

JSON
{
  "status": "denied",
  "reason": "Operation is not permitted."
}

Detailed authorization logs should go to the security telemetry system.

Capability Revocation

Capabilities should be revocable.

For example:

Code
User account disabled
        ↓
Capabilities revoked
        ↓
Tool calls rejected

Similarly:

Code
Agent compromised
        ↓
Revoke agent capabilities
        ↓
No further privileged calls

This is especially important for long-running agents.

Token Revocation vs Capability Revocation

These are related but different.

Code
Token
=
Authentication credential

while:

Code
Capability
=
Authorization authority

Revoking a token may prevent authentication.

Revoking a capability can remove one specific authority while leaving other operations intact.

For example:

Code
Agent
 ├── customer.read
 ├── order.read
 └── order.refund

If refund behavior is compromised:

Code
Revoke:
order.refund

Keep:
customer.read
order.read

This is a more targeted response.

Prevent Scope Creep

A common failure mode is gradual permission expansion.

It starts as:

Code
read_customer

Then someone adds:

Code
update_customer

Then:

Code
delete_customer

Then:

Code
database.admin

The agent has accumulated privileges that are no longer related to its original purpose.

OWASP identifies privilege escalation through scope creep as a specific MCP security risk.

Add Capability Review

Treat capability changes like code changes.

For example:

Code
Pull Request
    ↓
Capability diff
    ↓
Security review
    ↓
Approval
    ↓
Deployment

A useful review output could be:

Code
Added:
orders.update

Removed:
orders.read

Changed:
orders.refund
  maxAmount: 100 → 1000

This makes authorization changes auditable.

Version Tool Definitions

Tool definitions themselves are security-sensitive.

OWASP recommends inspecting and pinning tool descriptions and schemas because changes can introduce unexpected behavior or create tool-poisoning risks.

For a controlled tool ecosystem:

Code
Tool Definition
    ↓
Version
    ↓
Hash
    ↓
Approved

Then detect changes:

Code
Approved hash
      ≠
Current hash
      ↓
Review required

Capability Versioning

Capabilities can also be versioned.

For example:

Code
orders.refund:v1

might allow:

Code
amount <= 100

while:

Code
orders.refund:v2

allows:

Code
amount <= 500
+
human approval

This creates a clear authorization migration path.

Audit Every Tool Call

A security audit record should answer:

Code
Who?
Which agent?
Which tenant?
Which tool?
Which action?
Which resource?
Which capability?
Allowed or denied?
Why?
When?
What was the outcome?

For example:

JSON
{
  "timestamp": "2026-08-11T10:15:00Z",
  "userId": "user-123",
  "agentId": "support-agent",
  "tenantId": "tenant-a",
  "tool": "orders",
  "action": "read",
  "resource": "order:12345",
  "decision": "allow"
}

Avoid logging secrets or unnecessary sensitive payloads.

OWASP recommends centralized logging and auditing of authentication and authorization decisions.

Detect Suspicious Capability Usage

Authorization logs can also become security signals.

For example:

Code
Normal:
orders.read

Suddenly:

Code
orders.refund

or:

Code
database.export

The system can trigger:

Code
Alert
+
Human review
+
Capability revocation

This is more effective than logging without analyzing the events.

Example Capability Policy

A simplified policy could be represented as:

JSON
{
  "agent": "support-agent",
  "capabilities": [
    {
      "tool": "customer",
      "actions": ["read"],
      "resource": "tenant/customer/*"
    },
    {
      "tool": "orders",
      "actions": ["read"],
      "resource": "tenant/order/*"
    }
  ]
}

A privileged action remains outside the normal capability set:

Code
orders.refund

and therefore requires a separate workflow.

Capability Evaluation Flow

A complete request might look like:

Code
User
 ↓
Agent
 ↓
Tool request
 ↓
Authenticate caller
 ↓
Resolve tenant
 ↓
Resolve capability
 ↓
Check expiration
 ↓
Check tool
 ↓
Check action
 ↓
Check resource
 ↓
Check policy conditions
 ↓
Human approval if required
 ↓
Execute tool
 ↓
Validate output
 ↓
Audit
 ↓
Return result

This is the security boundary.

Example Secure Tool Flow in C#

C#
public async Task<ToolResult> HandleAsync(
    ToolRequest request,
    RequestContext context,
    CancellationToken cancellationToken)
{
    var capabilityRequest =
        new CapabilityRequest(
            context.AgentId,
            context.UserId,
            context.TenantId,
            request.Tool,
            request.Action,
            request.Resource);

    var decision =
        await authorizer.AuthorizeAsync(
            capabilityRequest,
            cancellationToken);

    if (!decision.Allowed)
    {
        await audit.LogDeniedAsync(
            capabilityRequest,
            decision.Reason,
            cancellationToken);

        return ToolResult.Denied(
            "Operation is not permitted.");
    }

    if (decision.RequiresApproval)
    {
        return ToolResult.PendingApproval(
            request.Tool,
            request.Action,
            request.Resource);
    }

    var result =
        await executor.ExecuteAsync(
            request,
            context,
            cancellationToken);

    var safeResult =
        outputSanitizer.Sanitize(result);

    await audit.LogAllowedAsync(
        capabilityRequest,
        cancellationToken);

    return safeResult;
}

The example demonstrates the sequence:

Code
Authorize
 ↓
Approve if necessary
 ↓
Execute
 ↓
Sanitize
 ↓
Audit

Common Mistakes

Trusting the LLM

The model is not an authorization engine.

Trusting Client-Provided Roles

The server should derive identity and authorization context from trusted authentication infrastructure.

Giving Agents Full Database Access

Prefer domain-specific tools.

Using One Token for Every Tool

Use scoped identities and credentials.

Giving Every Agent the Same Tools

Tool access should reflect task requirements.

Ignoring Resource-Level Authorization

Being allowed to call orders.read does not necessarily mean the agent can read every order.

Allowing Arbitrary URLs

URL-fetching tools can create SSRF risks if the destination is not restricted. OWASP explicitly recommends strict allowlists for such tools.

Allowing Arbitrary Shell Commands

Avoid generic:

Code
execute_command(command)

for normal agent workflows.

Skipping Human Approval

High-impact actions need stronger controls.

Logging Secrets

Audit tool activity without storing sensitive credentials or unnecessary payloads.

Never Reviewing Capability Changes

Authorization policies can drift just like application code.

Troubleshooting

Agent Says It Has Permission but Tool Rejects It

This is often expected.

The model's context may contain:

Code
"I can perform this action."

but the server evaluates the actual authorization.

Check:

Code
Identity
Tenant
Tool
Action
Resource
Capability
Expiration
Policy

Tool Works for One User but Not Another

Compare the capability sets.

For example:

Code
User A:
orders.read

User B:
orders.read
orders.update

The difference should be visible in authorization telemetry.

Agent Can Read Data From Another Tenant

Treat this as a critical authorization defect.

Check whether tenant identity comes from:

Code
Authenticated context

or:

Code
LLM-supplied parameter

The latter should not be trusted.

A Tool Suddenly Has More Permissions

Compare the capability policy version and tool definition hash.

Possible causes include:

Code
Policy change
Configuration drift
Tool update
New scope
Identity mapping change

Sensitive Tool Executes Without Approval

Verify that approval is enforced server-side.

Do not rely solely on the agent's instructions to request confirmation.

Best Practices

  1. Start with deny-by-default authorization.

  2. Grant the minimum required capabilities.

  3. Separate read, write, and destructive operations.

  4. Enforce authorization server-side.

  5. Never treat system prompts as access controls.

  6. Bind capabilities to authenticated identity.

  7. Bind permissions to tenant context.

  8. Perform resource-level authorization.

  9. Use short-lived capabilities where practical.

  10. Support revocation.

  11. Separate privileged workflows.

  12. Require human approval for high-impact operations.

  13. Avoid generic shell and unrestricted database tools.

  14. Validate tool parameters.

  15. Validate and minimize tool output.

  16. Protect against SSRF and path traversal.

  17. Audit every authorization decision.

  18. Monitor capability changes and unusual tool usage.

  19. Review capability changes like code changes.

  20. Treat tool definitions and schemas as security-sensitive assets.

Capability Security Checklist

Before exposing an AI tool, ask:

QuestionExpected Answer
Who can call it?Explicit identity
What action is allowed?Explicit capability
Which resources?Explicit scope
Which tenant?Authenticated context
How long?Limited lifetime
Can it be revoked?Yes
Does it modify data?Clearly classified
Does it require approval?Defined by risk
Are parameters validated?Yes
Is output sanitized?Yes
Is the call audited?Yes
Can the tool execute arbitrary code?Preferably no
Can permissions drift?Detect and review
Can tool definitions change silently?Detect and review

Frequently Asked Questions

What is capability-based security?

Capability-based security represents authority as an explicit permission to perform an action against a particular resource.

For AI agents, this can mean:

Code
Tool
+
Action
+
Resource
+
Constraints

rather than simply assigning a broad role.

Is capability-based security the same as RBAC?

No.

RBAC assigns permissions through roles.

Capability-based security focuses on explicit authority.

The two can be combined. A role can determine which capabilities an agent receives.

Should the LLM decide whether it has permission?

No.

The LLM can select a tool, but the final authorization decision should be made by trusted server-side policy enforcement. OWASP specifically recommends server-side enforcement rather than relying on prompts or client-side controls.

Should every tool require human approval?

No.

Human approval should be proportional to risk.

A read-only search may not require approval.

A financial transfer or destructive operation may.

Why are short-lived capabilities useful?

They reduce the lifetime of authority.

If a capability is leaked or misused, its usefulness decreases after expiration.

Should each MCP server have a separate credential?

Where appropriate, yes.

OWASP recommends scoped, per-server credentials rather than sharing broad tokens across servers.

Can capability security prevent prompt injection?

Not by itself.

Prompt injection can influence model behavior.

Capability enforcement limits what the resulting tool call can actually do.

That is why server-side authorization remains important.

Can I use capability-based security without MCP?

Yes.

The model applies to any AI system that invokes tools:

Code
AI Agent
+
Plugins
+
Function Calling
+
MCP
+
Internal APIs

MCP is one implementation context, not a prerequisite.

Should tools expose generic database or shell access?

Generally, avoid this for ordinary agent workflows.

Narrow domain-specific tools produce smaller and more controllable capability surfaces.

How should I secure multi-tenant agents?

Bind authorization to the authenticated tenant context and perform resource-level authorization on every request.

Never trust a tenant identifier supplied solely by the model.

Conclusion

AI agents should not receive authority simply because a tool exists.

A secure design starts with:

Code
Agent
 ↓
Capabilities
 ↓
Policy
 ↓
Authorized Tool
 ↓
Authorized Resource

rather than:

Code
Agent
 ↓
All Tools

The capability model gives developers a practical way to implement least privilege:

Code
customer.read

is better than:

Code
customer.full_access

and:

Code
order.refund
amount <= 100
human approval required

is better than:

Code
order.admin

The most important security boundary is the tool execution layer.

The model can request:

Code
delete_customer

but that request should pass through:

Code
Authentication
 ↓
Capability check
 ↓
Resource authorization
 ↓
Policy evaluation
 ↓
Human approval
 ↓
Tool execution

before anything destructive happens.

Current OWASP guidance highlights excessive permissions, scope creep, insufficient authorization, tool poisoning, and cross-server interactions as important risks in MCP and agentic systems.

The practical objective is therefore not to make an AI agent incapable of doing dangerous things.

It is to make dangerous capabilities:

Code
Explicit
+
Scoped
+
Time-limited
+
Auditable
+
Revocable
+
Policy-controlled

That is the foundation of capability-based security for AI tool plugins.

Give the agent enough authority to complete the task, but never more authority than the task requires.