Metadata-Version: 2.5
Name: crewai-agentvalet
Version: 0.1.0
Summary: Governed CrewAI tools. Your crew's tool surface is your AgentValet grant matrix, so an agent cannot form a call it was never granted.
Project-URL: Homepage, https://agentvalet.ai
Project-URL: Documentation, https://docs.agentvalet.ai/frameworks/crewai
Project-URL: Source, https://github.com/MCSEdwin/agentvalet
Author: AgentValet
License: MIT
Keywords: agents,agentvalet,ai,authorization,crewai,governance,tools
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: agentvalet>=0.1.0
Requires-Dist: crewai>=1.0
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# crewai-agentvalet

Governed CrewAI tools. Your crew's tool surface is your AgentValet grant matrix,
so an agent cannot form a call it was never granted.

```bash
pip install crewai-agentvalet
```

```python
from crewai import Agent
from crewai_agentvalet import governed_tools

agent = Agent(
    role="Operations Engineer",
    goal="Keep the team informed",
    backstory="You handle routine ops chores end to end.",
    tools=governed_tools(),
)
```

That is the integration. `governed_tools()` asks AgentValet what this agent is
actually granted and builds one tool per platform. Nothing else appears.

## What makes this different from a tool allow-list

CrewAI can filter tools client-side. That is a convenience: the filter lives in
your code, and an agent that is not offered a tool can still be talked into
calling something adjacent.

Here the list comes from the server, already filtered by your grants **and** your
policy. A platform you have not granted produces no tool at all. A scope your
policy denies is not in the tool's scope enum, so pydantic rejects it before a
request is ever signed. And if the model hand-writes a call anyway, the broker
refuses it: the schema is a convenience, the broker is the boundary.

The crew holds no platform credential at any point. It holds one identity key.
AgentValet decrypts the real credential in memory at call time.

## Refusals come back as text, not exceptions

CrewAI treats a raised exception as a task failure. A denial is not a failure,
it is information, so every refusal is returned as a string the agent can reason
about:

| What happened | What the agent gets back |
| --- | --- |
| Policy denied it | `Denied by policy: ... do not retry the same call` |
| Owner must approve, nobody answered in 50s | `Waiting on owner approval (approval id ...). Do NOT issue this call again.` |
| Owner declined | `The owner declined this action. Do not retry it.` |
| Approved, but the SaaS 4xx'd | `Slack returned an error: ...` |

The approval-timeout wording is deliberate. The action stays queued server-side
and still runs if the owner approves later, so a retry would queue a **second**
copy. For an outbound message that means sending it twice.

## Narrowing the surface

```python
tools = governed_tools(platforms=["slack", "linear"])
```

`platforms` can only narrow. Naming something you have not been granted skips
it; it cannot conjure access.

To see what was left out and why:

```python
tools = governed_tools(on_skip=lambda pid, why: print(f"skipped {pid}: {why}"))
```

Worth wiring up the first time a crew mysteriously lacks a tool. The two reasons
are "not granted to this agent" and "granted but every scope is denied by
policy", and they need different fixes.

## Using your own client

```python
from agentvalet import AgentValet
from crewai_agentvalet import governed_tools

av = AgentValet.from_env(on_approval_pending=lambda p: print("waiting:", p["approval_id"]))
tools = governed_tools(av)
```

## Getting an agent identity

```bash
pip install agentvalet
agentvalet register --code <invite-or-enrollment-code>
```

The keypair is generated on your machine. Only the public half is sent; the
private key lands in `~/.agentvalet/agent.key` (mode 0600).

## The MCP alternative

If you would rather not add a dependency, AgentValet also ships an MCP server
and CrewAI speaks MCP:

```python
Agent(role="Ops", mcps=[MCPServerStdio(command="npx", args=["-y", "@agentvalet/mcp-server"], env={...})])
```

Both paths hit the same broker with the same guarantees. Use this package when
you want typed tools and Python-only dependencies; use MCP when you want zero
integration code and have Node available. The
[docs](https://docs.agentvalet.ai/frameworks/crewai) compare them, including why
the stdio server is the right MCP choice and the remote HTTP endpoint usually
is not.

## Notes

- Importing `crewai_agentvalet` does not import CrewAI. The CrewAI-facing names
  resolve lazily, so the permissions parser is usable on its own.
- Verified against CrewAI 1.15.15. The floor is `crewai>=1.0`.

MIT.
