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
Metadata
Release files for acp-pydantic-ai 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| acp_pydantic_ai-0.2.0.tar.gz | 5.0 kB | Without provenance | Details for acp_pydantic_ai-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| acp_pydantic_ai-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for acp_pydantic_ai-0.2.0-py3-none-any.whl |
Total release size: 11.2 kB
Release files / acp_pydantic_ai-0.2.0.tar.gz
| Download URL | acp_pydantic_ai-0.2.0.tar.gz |
|---|---|
| Size | 5.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
be7fee9b184eac9f37f2fdfd7f2f106e2a4945e86b861f5dd12d34fe0f5ef0fe
|
|
BLAKE2b-256 checksum How to use checksums |
0502ee2df577c051af906e71347f28bd71c9099553ef75f831602a3ae83c2cbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / acp_pydantic_ai-0.2.0-py3-none-any.whl
| Download URL | acp_pydantic_ai-0.2.0-py3-none-any.whl |
|---|---|
| Size | 6.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19913e42d5db6ef862a8687901982ad2d95a87a3db3335f7f8a6238a5fb2a5cd
|
|
BLAKE2b-256 checksum How to use checksums |
daffb477abc650460ffd2ba06611deda30ed3b5270e7bc3e0670252de577ed8b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Security for acp-pydantic-ai 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.