Skip to content

Python SDK

The Scopebound Python SDK provides runtime tool-call enforcement for AI agents. Wrap your agent's tools with Scopebound and every tool call is evaluated against the role's authorization scope before it executes.

Install

pip install scopebound

For LangChain framework integration:

pip install 'scopebound[langchain]'

What this SDK does

Unlike the TypeScript SDK (which evaluates whole workflows before execution), the Python SDK enforces at the individual tool call level. Every time your agent calls a tool, the SDK:

  1. Verifies the tool is in the role's allowed_tools list
  2. Checks parameter constraints (rate limits, value ranges, environment scope)
  3. Resolves and validates credential bindings
  4. Enforces human-in-the-loop approval gates if required
  5. Returns the call result or raises a typed exception explaining why it was blocked

This makes it well-suited for runtime enforcement in long-running agent processes, while the TypeScript SDK and n8n node are better suited for pre-execution validation of full workflow definitions.

Quickstart

import os
from scopebound import ScopeboundSDK

sdk = ScopeboundSDK(
    base_url=os.environ["SCOPEBOUND_BASE_URL"],
    api_key=os.environ["SCOPEBOUND_API_KEY"],
)

# Enforce a single tool call
result = sdk.enforce(
    role_id="vision-invoice-processor",
    tool_name="post_to_erp",
    call_args={"invoice_id": "INV-12345", "amount": 1500.00, "env": "production"},
)

# If allowed: result contains the evaluation details and proceeds
# If denied: ScopeboundDenyError is raised with the violation details

The enforce() call returns when the tool is allowed; it raises a typed exception when denied. Your code calls the actual tool only after enforce() returns successfully.

LangChain integration

The @enforce decorator wraps a LangChain BaseTool class so every call is automatically gated by Scopebound:

from scopebound import ScopeboundSDK, enforce
from langchain_core.tools import BaseTool

sdk = ScopeboundSDK(
    base_url=os.environ["SCOPEBOUND_BASE_URL"],
    api_key=os.environ["SCOPEBOUND_API_KEY"],
)

@enforce(sdk, role="invoice-processor")
class PostToErpTool(BaseTool):
    name = "post_to_erp"
    description = "Post an invoice to the ERP system"

    def _run(self, invoice_id: str, amount: float) -> str:
        # This method only executes if Scopebound's enforcement permits the call.
        return submit_to_erp(invoice_id, amount)

Calls to the tool from your agent are evaluated against the invoice-processor role's scope before the underlying _run method is invoked.

Provisioning and session lifecycle

For multi-tool agent sessions, provision a session up front and reuse it across enforcement calls:

session = sdk.provision(
    role_id="vision-invoice-processor",
    requested_ttl=3600,        # 1 hour
    task_type="invoice_audit",
    framework="langchain",
)

# Use the session token for subsequent calls
# Session is automatically revoked when its TTL expires, or manually:
sdk.revoke(reason="task_completed")

Managing roles

The SDK includes the full management API for creating and updating agent roles:

# Create a role
role = sdk.create_role(
    name="invoice-processor",
    allowed_tools=["read_invoices", "post_to_erp", "request_approval"],
    description="Reads invoices and posts to ERP after approval",
    default_ttl_seconds=3600,
    max_delegation_depth=0,
    rate_limit_per_minute=60,
    allowed_envs=["staging", "production"],
    approval_required=["post_to_erp"],
)

# Update a role
sdk.update_role(
    role_id=role["id"],
    allowed_tools=role["allowed_tools"] + ["emit_audit_event"],
)

# List or fetch
sdk.list_roles()
sdk.get_role(role_id=role["id"])

Approval workflow (human-in-the-loop)

When a role requires approval for a tool, calls block on a pending approval:

try:
    sdk.enforce(
        role_id="invoice-processor",
        tool_name="post_to_erp",
        call_args={"invoice_id": "INV-12345", "amount": 50000.00},
    )
except ScopeboundPendingError as e:
    # An approval has been created; surface to the human reviewer
    approval = sdk.get_approval(e.approval_id)
    # ... display in UI, send to Slack, etc.

# Reviewer approves it via the UI, an API call, or:
sdk.approve(approval_id=e.approval_id, approved_by="[email protected]")

# Now the original call can proceed (re-call enforce)

MCP enforcement

For Model Context Protocol servers, use mcp_enforce to validate tool calls against MCP-specific schemas:

sdk.mcp_enforce(
    role_id="vision-invoice-processor",
    tool_name="read_file",
    arguments={"path": "/invoices/2026-Q1/"},
    mcp_server="filesystem",
    mcp_tool_schema={...},
    mcp_session_id="session_abc",
)

See the MCP framework guide for details on MCP-specific enforcement.

Error handling

The SDK raises typed exceptions for each failure mode:

from scopebound import (
    ScopeboundSDK,
    ScopeboundDenyError,         # Tool call denied by policy
    ScopeboundPendingError,      # Awaiting human approval
    ScopeboundTimeoutError,      # Evaluation exceeded timeout
    ScopeboundUnavailableError,  # Enforcement plane unreachable
)

try:
    sdk.enforce(role_id="invoice-processor", tool_name="post_to_erp", call_args={...})
except ScopeboundDenyError as e:
    # Tool is not allowed for this role, or parameter constraints failed
    logger.warning(f"Blocked tool call: {e.code}{e.message}")
except ScopeboundPendingError as e:
    # Approval workflow triggered — surface to human reviewer
    notify_approver(e.approval_id)
except ScopeboundTimeoutError:
    # Enforcement plane took too long
    # By default this blocks the call (fail-closed). Configure fail_open=True
    # in ScopeboundSDK() to allow calls when enforcement is slow.
    pass
except ScopeboundUnavailableError:
    # Enforcement plane is down or unreachable
    # Same fail-open/fail-closed configuration applies.
    pass

Fail-open vs fail-closed

By default, the SDK is fail-closed: if the enforcement plane is unreachable or evaluation times out, the tool call is blocked. This is the safe default for production agents.

For non-production environments where enforcement-plane uptime would block development, set fail_open=True:

sdk = ScopeboundSDK(
    base_url="...",
    api_key="...",
    fail_open=True,  # Allow tool calls when enforcement plane is unreachable
)

Audit log export

Export evaluation records for compliance retention:

audit_bytes = sdk.export_audit_log(
    format="json",        # or "csv"
    window_hours=168,     # last 7 days
)

with open("audit-2026-Q2.json", "wb") as f:
    f.write(audit_bytes)

Framework integrations

The Python SDK ships with first-class framework integrations:

Next steps