Menu

Permissions & Safety

Control what MCP tools agents can access with permission tiers and constraints.

How does paso control what agents can do?

paso provides multiple layers of safety to control what agents can do with your API. Every capability is classified by risk, and you can add constraints, consent gates, and a forbidden list.

read: Safe operations that don’t modify data. Agents can call these without special approval.

write: Operations that modify data. Should use consent_required: true so the user confirms before execution.

admin: High-risk operations like deleting resources or changing access controls. Always require consent.

forbidden: Capabilities explicitly blocked from agents. They are never exposed as MCP tools.

consent_required: A boolean flag that forces the agent to show a confirmation dialog before executing the capability.

constraint: A business rule applied to a capability. Limits calls per hour, items per request, or restricts input values.

Permission Tiers

Every capability has a permission field that classifies its risk level.

read

Safe operations that don’t modify data. Listing items, fetching details, searching.

- name: list_issues
  description: List issues in a project
  method: GET
  path: /projects/{organization_slug}/{project_slug}/issues/
  permission: read

write

Operations that modify data. Creating items, updating fields, posting content.

- name: resolve_issue
  description: Mark an issue as resolved
  method: PUT
  path: /issues/{issue_id}/
  permission: write
  consent_required: true
  inputs:
    issue_id:
      type: string
      required: true
      description: The issue ID
      in: path
    status:
      type: enum
      required: true
      values: [resolved, unresolved, ignored]
      description: New status for the issue

admin

High-risk operations. Deleting resources, changing settings, managing access.

- name: delete_issue
  description: Permanently delete an issue and all its events
  method: DELETE
  path: /issues/{issue_id}/
  permission: admin
  consent_required: true
  inputs:
    issue_id:
      type: string
      required: true
      description: The issue ID
      in: path

For sensitive operations, require the user to explicitly approve each call.

- name: create_payment_intent
  description: Create a payment intent to charge a customer
  method: POST
  path: /payment_intents
  permission: write
  consent_required: true
  inputs:
    amount:
      type: integer
      required: true
      description: Amount in cents (e.g., 2000 = $20.00)
    currency:
      type: string
      required: true
      description: Three-letter ISO currency code (e.g., usd)

When consent_required: true, the agent shows a confirmation dialog before executing.

Constraints

Limit resource usage and prevent abuse. Constraints are an array of objects on each capability.

max_per_hour

Maximum calls per hour.

constraints:
  - max_per_hour: 100
    description: Status changes are rate-limited

max_per_request

Maximum items per request (for bulk operations).

constraints:
  - max_per_request: 50
    description: Batch operations limited to 50 items

max_value

Maximum numeric value for an input.

constraints:
  - max_value: 99999999
    description: Maximum single charge is $999,999.99

allowed_values

Restrict an input to specific values beyond its enum.

constraints:
  - allowed_values: [usd, eur, gbp]
    description: Only these currencies are supported

requires_field

Another input that must be present when this constraint applies.

constraints:
  - requires_field: currency
    description: Currency is required when specifying an amount

Combining Constraints

Use multiple constraint objects together:

constraints:
  - max_per_hour: 500
    description: Payment creation is rate-limited
  - max_value: 99999999
    description: Maximum single charge is $999,999.99
  - requires_field: currency
    description: Currency is required when specifying an amount

Permissions Object

The top-level permissions object groups capabilities by tier. This provides an explicit access control list.

permissions:
  read:
    - list_issues
    - get_issue
    - list_projects
  write:
    - resolve_issue
    - assign_issue
  admin:
    - delete_issue

If permissions is omitted, each capability’s permission field is used directly.

Forbidden

The forbidden list explicitly blocks capabilities from being exposed to agents.

permissions:
  read:
    - list_customers
    - get_customer
  write:
    - create_customer
  forbidden:
    - delete_customer

A capability cannot appear in both a permission tier and forbidden. The forbidden tier may reference capability names not declared in capabilities, to explicitly block endpoints you don’t want exposed.

Safety Best Practices

1. Start Conservative

Expose only what’s needed. It’s easier to add capabilities later than remove them.

2. Use Permission Tiers

Assign the lowest tier appropriate for each operation.

# Read for safe queries
- name: list_customers
  permission: read

# Write for modifications
- name: create_customer
  permission: write

# Admin for deletion
- name: refund_payment
  permission: admin

Any operation that creates, modifies, or deletes data should require consent.

- name: create_customer
  permission: write
  consent_required: true

- name: delete_issue
  permission: admin
  consent_required: true

4. Add Constraints to Write and Admin

Rate limits and guardrails prevent abuse.

- name: resolve_issue
  permission: write
  consent_required: true
  constraints:
    - max_per_hour: 100
      description: Bulk status changes are rate-limited

5. Use Forbidden for Sensitive Operations

Explicitly block capabilities you never want agents to use.

permissions:
  read:
    - list_customers
  write:
    - create_customer
  forbidden:
    - delete_customer

Real-World Example

A Stripe declaration with careful permission boundaries:

version: "1.0"

service:
  name: Stripe
  description: Payment processing and billing infrastructure for internet businesses
  base_url: https://api.stripe.com/v1
  auth:
    type: bearer

capabilities:
  - name: list_customers
    description: List customers, optionally filtered by email
    method: GET
    path: /customers
    permission: read
    inputs:
      email:
        type: string
        description: Filter by exact email match
        in: query
      limit:
        type: integer
        description: Number of results (1-100)
        default: 10
        in: query

  - name: create_customer
    description: Create a new customer
    method: POST
    path: /customers
    permission: write
    consent_required: true
    inputs:
      email:
        type: string
        required: true
        description: Customer email address
      name:
        type: string
        description: Customer full name

  - name: create_payment_intent
    description: Create a payment intent to charge a customer
    method: POST
    path: /payment_intents
    permission: write
    consent_required: true
    inputs:
      amount:
        type: integer
        required: true
        description: Amount in cents (e.g., 2000 = $20.00)
      currency:
        type: string
        required: true
        description: Three-letter ISO currency code
    constraints:
      - max_value: 99999999
        description: Maximum single charge is $999,999.99
      - max_per_hour: 500
        description: Payment creation is rate-limited

  - name: refund_payment
    description: Create a refund for a payment intent
    method: POST
    path: /refunds
    permission: admin
    consent_required: true
    inputs:
      payment_intent:
        type: string
        required: true
        description: The payment intent ID to refund
      amount:
        type: integer
        description: Amount to refund in cents (partial refund). Omit for full refund.

permissions:
  read:
    - list_customers
  write:
    - create_customer
    - create_payment_intent
  admin:
    - refund_payment
  forbidden:
    - delete_customer

Your API is locked down.

Permission tiers, consent gates, constraints, and a forbidden list. Agents can only do what you allow.

Next, you might want to:

From the blog: