acp-pydantic-ai
Agentic Control Plane governance for Pydantic AI agents.
Register ACPHooks() once on your agent. Before any tool runs, ACP decides allow / deny / redact based on your workspace's policy, the end user's scopes, rate limits, and PII detection — for every tool the agent has, with zero per-function decorators.
Same governance model as Claude Code. If you have workspace policies set up for Claude Code, they apply to Pydantic AI tools automatically.
Install
pip install acp-pydantic-ai
Requires pydantic-ai >= 2 (the package builds on Pydantic AI's first-class Hooks capability).
Usage
from fastapi import FastAPI, Header
from pydantic_ai import Agent
from acp_pydantic_ai import ACPHooks, configure, set_context
configure(base_url="https://api.agenticcontrolplane.com")
app = FastAPI()
# One registration. Every tool on this agent is governed — the ones
# below, and any you add later.
agent = Agent(
"anthropic:claude-sonnet-4-6",
instructions="You are an ACP-governed agent. Use the tools available.",
capabilities=[ACPHooks()],
)
@agent.tool_plain
def lookup_record(id: str) -> str:
"""Look up a record by ID."""
return db.lookup(id) # your code, your credentials
@agent.tool_plain
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email on behalf of the user."""
return sendmail(to, subject, body)
@app.post("/run")
def run(prompt: str, authorization: str = Header(...)):
# Bind the end user's JWT to this request's context. Every tool call
# in the run below carries the user's identity to ACP.
set_context(user_token=authorization.removeprefix("Bearer ").strip())
return {"result": agent.run_sync(prompt).output}
What happens per tool call
- Pre-check — POSTs to ACP
/govern/tool-usewith{ tool_name, tool_input, session_id }+ the user's Bearer JWT. - Decide — ACP evaluates workspace policy, the user's scopes, rate limits, and PII.
- Deny → the tool function is never called. The model receives
"tool_error: <reason>"as the tool result and adapts. - Allow → your tool runs.
- Post-audit — POSTs to
/govern/tool-outputwith the result. PII scan runs.redact→ the redacted version replaces the output;block→ the model sees"[ACP] Blocked: <reason>".
How it hooks in
ACPHooks() returns a Pydantic AI Hooks capability carrying a single tool_execute wrap hook (wrap_tool_execute). Pydantic AI invokes it around every tool execution; the hook runs the pre-check, calls await handler(args) only on allow, then runs the post-audit. It composes with your other capabilities and hooks — first-registered wraps outermost.
Scope it if you need to:
ACPHooks(tools=["send_email", "delete_record"]) # govern only these
ACPHooks(exclude=["get_time"]) # govern all but these
Decorator pattern (v1-era, still works)
Before Pydantic AI 2, this package's story was stacking @governed under the tool decorator:
from acp_pydantic_ai import governed
@agent.tool_plain
@governed("lookup_record")
def lookup_record(id: str) -> str: ...
That still works on v2 and is still re-exported here. Prefer ACPHooks() — one registration, nothing to forget on the next tool. Don't combine both on the same tool, or the call is checked (and audited) twice.
View activity
Every tool call shows up in the ACP Activity view, rooted in the end user's identity. Sessions group related calls — one request from one user = one session.
Fail-open
Network errors, timeouts (5s default), gateway errors → the tool proceeds with reason "fail-open". Matches Claude Code behavior. Governance is never a single point of failure for the agent.
API
acp-pydantic-ai re-exports the full acp-governance API for convenience:
ACPHooks(tools=None, exclude=None) # → pydantic_ai.capabilities.Hooks
governed(name_or_fn=None) # v1-era decorator, still supported
set_context(user_token, *, session_id=None, agent_tier=None, agent_name=None)
get_context()
clear_context()
configure(base_url=..., timeout_s=..., client_header=...)
Related
acp-governance— core SDK (this package wraps it)acp-crewai— same story for CrewAIacp-langchain— same story for LangChain / LangGraph- Pydantic AI integration guide
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file acp_pydantic_ai-0.2.0.tar.gz.
File metadata
- Download URL: acp_pydantic_ai-0.2.0.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be7fee9b184eac9f37f2fdfd7f2f106e2a4945e86b861f5dd12d34fe0f5ef0fe
|
|
| MD5 |
eac5347abe2187f9f5027716d63d3b5c
|
|
| BLAKE2b-256 |
0502ee2df577c051af906e71347f28bd71c9099553ef75f831602a3ae83c2cbc
|
File details
Details for the file acp_pydantic_ai-0.2.0-py3-none-any.whl.
File metadata
- Download URL: acp_pydantic_ai-0.2.0-py3-none-any.whl
- Upload date:
- Size: 6.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19913e42d5db6ef862a8687901982ad2d95a87a3db3335f7f8a6238a5fb2a5cd
|
|
| MD5 |
31a0c520040914de9ca3e41362f2c6fe
|
|
| BLAKE2b-256 |
daffb477abc650460ffd2ba06611deda30ed3b5270e7bc3e0670252de577ed8b
|