> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grantex.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Enforce

> Check whether an agent's grant token permits a specific tool call. Load manifests, call enforce(), and wrap LangChain tools.

## Overview

The enforce API verifies that an agent's grant token includes sufficient scope to call a specific tool on a specific connector. It combines JWT verification with manifest-based permission resolution in a single call.

```python theme={null}
from grantex import Grantex
from grantex.manifests.salesforce import manifest as sf

grantex = Grantex(api_key="gx_...")
grantex.load_manifest(sf)

result = grantex.enforce(
    grant_token=token,
    connector="salesforce",
    tool="delete_contact",
)
if not result.allowed:
    print(result.reason)  # "write scope does not cover delete operations on salesforce"
```

***

## enforce()

Check whether a grant token permits a tool call.

```python theme={null}
def enforce(
    self,
    grant_token: str,
    connector: str,
    tool: str,
    amount: float | None = None,
) -> EnforceResult
```

### Parameters

| Parameter     | Type            | Required | Description                                                                                                                                                            |
| ------------- | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_token` | `str`           | Yes      | The JWT grant token issued by Grantex. Decoded and verified inline.                                                                                                    |
| `connector`   | `str`           | Yes      | The connector name to check against (e.g., `"salesforce"`, `"s3"`). Must match a loaded manifest.                                                                      |
| `tool`        | `str`           | Yes      | The tool name to check (e.g., `"delete_contact"`, `"create_lead"`). Must be declared in the connector's manifest.                                                      |
| `amount`      | `float \| None` | No       | Optional amount for capped scopes. When the token includes a capped scope like `tool:stripe:write:*:capped:500`, pass the transaction amount to check against the cap. |

### Response: `EnforceResult`

| Field        | Type        | Description                                                                                                    |
| ------------ | ----------- | -------------------------------------------------------------------------------------------------------------- |
| `allowed`    | `bool`      | `True` if the tool call is permitted by the token's scopes.                                                    |
| `reason`     | `str`       | Human-readable reason when `allowed` is `False`. Empty string when allowed.                                    |
| `grant_id`   | `str`       | The grant ID extracted from the JWT `grnt` (or `jti`) claim.                                                   |
| `agent_did`  | `str`       | The agent DID extracted from the JWT `agt` claim.                                                              |
| `scopes`     | `list[str]` | All scopes from the JWT `scp` claim.                                                                           |
| `permission` | `str`       | The resolved permission level for this tool from the manifest (`"read"`, `"write"`, `"delete"`, or `"admin"`). |
| `connector`  | `str`       | The connector name that was checked.                                                                           |
| `tool`       | `str`       | The tool name that was checked.                                                                                |

### Example

```python theme={null}
result = grantex.enforce(
    grant_token="eyJhbGciOiJSUzI1NiIs...",
    connector="salesforce",
    tool="create_lead",
)

if result.allowed:
    print(f"Allowed: {result.tool} on {result.connector}")
    print(f"Grant: {result.grant_id}, Agent: {result.agent_did}")
else:
    print(f"Denied: {result.reason}")
```

### Capped Scopes

When a token includes a capped scope, pass the `amount` to enforce against the cap:

```python theme={null}
result = grantex.enforce(
    grant_token=token,
    connector="stripe",
    tool="create_payment_intent",
    amount=750,
)
# If token scope is tool:stripe:write:*:capped:500
# result.allowed = False
# result.reason = "amount 750 exceeds cap of 500 on tool:stripe:write:*:capped:500"
```

***

## load\_manifest()

Load a single tool manifest into the client. Must be called before `enforce()` for the corresponding connector.

```python theme={null}
def load_manifest(self, manifest: ToolManifest) -> None
```

### Example

```python theme={null}
from grantex.manifests.salesforce import manifest as sf

grantex.load_manifest(sf)
```

***

## load\_manifests()

Load multiple tool manifests at once.

```python theme={null}
def load_manifests(self, manifests: list[ToolManifest]) -> None
```

### Example

```python theme={null}
from grantex.manifests.salesforce import manifest as sf
from grantex.manifests.hubspot import manifest as hs
from grantex.manifests.jira import manifest as jira

grantex.load_manifests([sf, hs, jira])
```

***

## ToolManifest

A manifest declares the permission level required for each tool on a connector.

```python theme={null}
class ToolManifest:
    connector: str
    description: str
    version: str

    def __init__(
        self,
        connector: str,
        tools: dict[str, Permission],
        description: str = "",
        version: str = "1.0.0",
    ) -> None: ...

    @property
    def tool_count(self) -> int: ...

    def get_permission(self, tool_name: str) -> Permission | None: ...
    def add_tool(self, tool_name: str, permission: Permission) -> None: ...

    @classmethod
    def from_file(cls, path: str) -> "ToolManifest": ...

    @classmethod
    def from_dict(cls, data: dict) -> "ToolManifest": ...
```

### Constructor

```python theme={null}
from grantex import ToolManifest, Permission

manifest = ToolManifest(
    connector="inventory-service",
    description="Internal warehouse inventory API",
    version="1.0.0",
    tools={
        "get_stock_level":   Permission.READ,
        "reserve_inventory": Permission.WRITE,
        "force_stock_reset": Permission.ADMIN,
    },
)
```

### get\_permission()

Look up the required permission for a tool. Returns `None` if the tool is not in the manifest.

```python theme={null}
perm = manifest.get_permission("reserve_inventory")
# Permission.WRITE

unknown = manifest.get_permission("nonexistent_tool")
# None
```

### add\_tool()

Add a tool to an existing manifest. Useful for extending pre-built manifests with custom tools.

```python theme={null}
from grantex.manifests.salesforce import manifest as sf_manifest

sf_manifest.add_tool("bulk_delete_all", Permission.ADMIN)
sf_manifest.add_tool("export_all_contacts", Permission.READ)

print(sf_manifest.tool_count)  # 8 (6 built-in + 2 custom)
```

### from\_file()

Create a manifest from a JSON file on disk:

```python theme={null}
manifest = ToolManifest.from_file("./manifests/inventory-service.json")
grantex.load_manifest(manifest)
```

The JSON file format:

```json theme={null}
{
  "connector": "inventory-service",
  "version": "1.0.0",
  "description": "Internal warehouse inventory API",
  "tools": {
    "get_stock_level": "read",
    "reserve_inventory": "write",
    "force_stock_reset": "admin"
  }
}
```

### from\_dict()

Create a manifest from a Python dictionary:

```python theme={null}
manifest = ToolManifest.from_dict({
    "connector": "pricing-engine",
    "tools": {
        "get_price": "read",
        "set_price": "write",
        "reset_all_prices": "admin",
    },
})
```

***

## Permission

A class representing the four permission levels in the hierarchy.

```python theme={null}
class Permission:
    READ   = "read"    # Level 0
    WRITE  = "write"   # Level 1
    DELETE = "delete"  # Level 2
    ADMIN  = "admin"   # Level 3
```

Higher levels subsume all lower levels: `admin > delete > write > read`.

### covers()

Check whether this permission level covers a required permission level:

```python theme={null}
Permission.WRITE.covers(Permission.READ)    # True  -- write covers read
Permission.WRITE.covers(Permission.DELETE)   # False -- write does not cover delete
Permission.ADMIN.covers(Permission.DELETE)   # True  -- admin covers everything
Permission.READ.covers(Permission.READ)      # True  -- exact match
```

### is\_valid()

Check whether a string is a valid permission level:

```python theme={null}
Permission.is_valid("write")    # True
Permission.is_valid("execute")  # False
```

***

## wrap\_tool()

Wrap a LangChain `StructuredTool` so that enforcement runs automatically before every invocation.

```python theme={null}
def wrap_tool(
    self,
    tool: StructuredTool,
    connector: str,
    tool_name: str,
    grant_token: str | Callable[[], str],
) -> StructuredTool
```

### Example

```python theme={null}
protected_tool = grantex.wrap_tool(
    my_langchain_tool,
    connector="salesforce",
    tool="create_lead",
    grant_token=lambda: current_state["grant_token"],
)

# Use protected_tool in your LangChain agent chain.
# If the token lacks sufficient scope, the tool raises
# PermissionError instead of executing the underlying function.
```

***

## FastAPI Integration

Use `GrantexAuth` from `grantex_fastapi` as a FastAPI dependency for automatic enforcement on tool execution routes:

```python theme={null}
from grantex_fastapi import GrantexAuth

enforcer = GrantexAuth(grantex)

@app.post("/api/tools/{connector}/{tool}")
async def execute_tool(
    connector: str,
    tool: str,
    auth: EnforceResult = Depends(enforcer),
):
    # auth.allowed is guaranteed True here.
    # If the token lacked sufficient scope, a 403 was raised automatically.
    print(f"Executing {auth.tool} on {auth.connector} for grant {auth.grant_id}")
    ...
```

***

## Related

* [Scope Enforcement guide](/guides/scope-enforcement) — end-to-end walkthrough with framework integrations
* [TypeScript SDK enforce()](/sdks/typescript/enforce) — TypeScript API reference
* [Tool Manifests concept](/concepts/tool-manifests) — permission hierarchy and scope format
* [CLI enforce test](/cli/enforce) — dry-run enforcement from the command line
