ACP Kit is a Python SDK and CLI for turning an existing agent surface into a truthful ACP server.
acpkitis the root CLI and target resolverpydantic-acpadaptspydantic_ai.Agentinstances to ACPcodex-auth-helperturns a local Codex login into apydantic-aiResponses model
ACP Kit is not a new agent framework. The core use case is:
- keep your current agent surface
- expose it through ACP without rewriting the agent
- only publish models, modes, plans, approvals, MCP metadata, and host tools that the runtime can actually honor
The core workflow is simple:
- build a normal
pydantic_ai.Agent - expose it through ACP with
run_acp(...)orcreate_acp_agent(...) - optionally add session stores, approvals, providers, projection maps, and bridges
# production
uv pip install "acpkit[pydantic]"
# production with acpkit launch support
uv pip install "acpkit[pydantic,launch]"
# development
uv pip install -e ".[dev,docs,pydantic]"This repo also ships an acpkit-sdk skill package for Codex.
Use it when you want Codex to help integrate ACP into an existing agent surface, especially for:
- exposing an existing
pydantic_ai.Agentthrough ACP - choosing between
run_acp(...),create_acp_agent(...), providers, bridges, andAgentSource - wiring plans, approvals, session stores, thinking, MCP metadata, and host-backed tools
- keeping docs and examples aligned with the real SDK surface
From a checkout of this repo, install the skill with Unix commands:
mkdir -p "$HOME/.codex/skills/acpkit-sdk" \
&& cp -R .agents/skills/acpkit-sdk/. "$HOME/.codex/skills/acpkit-sdk/"The canonical skill package lives here:
.agents/skills/acpkit-sdk/: packaged ACP Kit SDK skill for Codex
Example prompts:
Use $acpkit-sdk to expose my existing pydantic_ai.Agent through ACP.Use $acpkit-sdk to add ACP plans, approvals, and slash-command mode switching to this agent.
Run a supported agent target through ACP:
acpkit run strong_agent
acpkit run strong_agent:agent
acpkit run strong_agent:agent -p ./examplesacpkit resolves module or module:attribute targets, auto-detects pydantic_ai.Agent
instances, and dispatches them to the installed adapter package. If only the module is given, it
selects the last defined pydantic_ai.Agent instance in that module.
If the matching adapter extra is not installed, acpkit fails with an install hint such as
uv pip install "acpkit[pydantic]".
Launch a target through Toad ACP:
acpkit launch strong_agent
acpkit launch strong_agent:agent -p ./examplesacpkit launch TARGET mirrors the resolved target to:
toad acp "acpkit run TARGET [-p PATH]..."The command is dispatched through uvx --python 3.14 --from batrachian-toad, so Toad runs in a
separate Python 3.14 tool environment and does not replace your project Python.
If the script already starts its own ACP server and should be launched directly, use --command:
acpkit launch -c "python3.11 strong_agent.py"launch TARGET and launch --command ... are mutually exclusive. -p/--path only applies to
TARGET mode.
Use run_acp(...) when you want to start an ACP server directly from a Pydantic AI agent:
from pydantic_ai import Agent
from pydantic_acp import run_acp
agent = Agent("openai:gpt-5", name="weather-agent")
@agent.tool_plain
def get_weather(city: str) -> str:
return f"Weather in {city}: sunny"
run_acp(agent=agent)Use create_acp_agent(...) when you want the ACP agent object without starting the server
immediately:
import asyncio
from acp import run_agent
from pydantic_ai import Agent
from pydantic_acp import AdapterConfig, MemorySessionStore, create_acp_agent
agent = Agent("openai:gpt-5", name="composable-agent")
acp_agent = create_acp_agent(
agent=agent,
config=AdapterConfig(
agent_name="my-service",
agent_title="My Service Agent",
session_store=MemorySessionStore(),
),
)
asyncio.run(run_agent(acp_agent))AdapterConfig is the main runtime configuration surface. Common fields include:
- agent metadata:
agent_name,agent_title,agent_version - persistence:
session_store - model selection:
allow_model_selection,available_models,models_provider - mode and config state:
modes_provider,config_options_provider - plans:
plan_provider, or native plan state viaPrepareToolsMode(plan_mode=True) - approvals:
approval_bridge,approval_state_provider - bridges:
capability_bridges - projection and classification:
projection_maps,tool_classifier,enable_generic_tool_projection
Configured adapter example:
from pathlib import Path
from pydantic_ai import Agent
from pydantic_acp import (
AdapterConfig,
AdapterModel,
FileSessionStore,
NativeApprovalBridge,
run_acp,
)
agent = Agent("openai:gpt-5", name="configured-agent")
config = AdapterConfig(
agent_name="configured-agent",
agent_title="Configured Agent",
allow_model_selection=True,
available_models=[
AdapterModel(
model_id="fast",
name="Fast",
description="Lower-latency responses",
override="openai:gpt-5-mini",
),
AdapterModel(
model_id="smart",
name="Smart",
description="Higher-quality responses",
override="openai:gpt-5",
),
],
session_store=FileSessionStore(root=Path(".acp-sessions")),
approval_bridge=NativeApprovalBridge(enable_persistent_choices=True),
)
run_acp(agent=agent, config=config)When plan_provider is not configured, the adapter can manage ACP plan state natively. Enable it
by marking one PrepareToolsMode with plan_mode=True inside a PrepareToolsBridge:
from pydantic_ai import Agent
from pydantic_ai.tools import RunContext, ToolDefinition
from pydantic_acp import AdapterConfig, PrepareToolsBridge, PrepareToolsMode, run_acp
def plan_tools(
ctx: RunContext[None], tool_defs: list[ToolDefinition]
) -> list[ToolDefinition]:
del ctx
return list(tool_defs)
def agent_tools(
ctx: RunContext[None], tool_defs: list[ToolDefinition]
) -> list[ToolDefinition]:
del ctx
return list(tool_defs)
agent = Agent("openai:gpt-5", name="plan-agent")
run_acp(
agent=agent,
config=AdapterConfig(
capability_bridges=[
PrepareToolsBridge(
default_mode_id="agent",
modes=[
PrepareToolsMode(
id="plan",
name="Plan",
description="Inspect and write plans.",
prepare_func=plan_tools,
plan_mode=True,
),
PrepareToolsMode(
id="agent",
name="Agent",
description="Full tool surface.",
prepare_func=agent_tools,
),
],
),
],
),
)When the session is in plan mode, the adapter:
- injects
acp_get_planandacp_set_planas hidden tools on the agent - extends
output_typewithNativePlanGenerationso the agent can emit a structured plan in a single response
NativePlanGeneration fields:
plan_entries: list[PlanEntry]— structured plan entriesplan_md: str— optional markdown representation
Native plan state and plan_provider are mutually exclusive. See
Providers for full
details.
Use a factory or custom AgentSource when agent construction depends on the current session:
from pydantic_ai import Agent
from pydantic_acp import AcpSessionContext, create_acp_agent
def build_agent(session: AcpSessionContext) -> Agent[None, str]:
return Agent(
"openai:gpt-5",
name=f"agent-{session.cwd.name}",
system_prompt=f"Work inside {session.cwd.name}.",
)
acp_agent = create_acp_agent(agent_factory=build_agent)StaticAgentSource accepts an optional deps field to pass typed runtime dependencies alongside
a shared agent instance without a factory:
from pydantic_acp import AdapterConfig, create_acp_agent
from pydantic_acp.agent_source import StaticAgentSource
from pydantic_ai import Agent
from myapp.deps import AppDependencies
deps = AppDependencies(db=my_db, cache=my_cache)
agent = Agent("openai:gpt-5", name="deps-agent", deps_type=AppDependencies)
acp_agent = create_acp_agent(
agent_source=StaticAgentSource(agent=agent, deps=deps),
)MemorySessionStore is the default. Use FileSessionStore when sessions should survive process
restarts:
from pathlib import Path
from pydantic_ai import Agent
from pydantic_acp import AdapterConfig, FileSessionStore, run_acp
agent = Agent("openai:gpt-5", name="persistent-agent")
run_acp(
agent=agent,
config=AdapterConfig(
session_store=FileSessionStore(root=Path(".acp-sessions")),
),
)Session lifecycle support includes create, load, list, fork, resume, close, transcript replay, and message-history replay.
The adapter exposes a small ACP control plane alongside normal prompts:
/modelprint the current session model id/model provider:modelswitch the current session model/toolslist registered tools/hookslist registeredHookscallbacks visible on the current agent/mcp-serverslist MCP servers extracted from the current agent toolsets and session metadata
Codex-backed model changes must be explicit:
/model codex:gpt-5.4
Pydantic AI approval-gated tools are bridged to ACP permission requests:
from pydantic_ai import Agent
from pydantic_ai.exceptions import ApprovalRequired
from pydantic_ai.tools import RunContext
from pydantic_acp import AdapterConfig, NativeApprovalBridge, run_acp
agent = Agent("openai:gpt-5", name="approval-agent")
@agent.tool
def delete_file(ctx: RunContext[None], path: str) -> str:
if not ctx.tool_call_approved:
raise ApprovalRequired()
return f"Deleted {path}"
run_acp(
agent=agent,
config=AdapterConfig(
approval_bridge=NativeApprovalBridge(enable_persistent_choices=True),
),
)projection_maps lets known tool families render as richer ACP content instead of raw text.
FileSystemProjectionMap can project:
- read tools into ACP diff previews
- write tools into ACP file diffs
- bash tools into command previews and terminal references
from pydantic_acp import FileSystemProjectionMap, run_acp
run_acp(
agent=agent,
projection_maps=(
FileSystemProjectionMap(
default_read_tool="read_file",
default_write_tool="write_file",
default_bash_tool="execute",
),
),
)HookProjectionMap controls how existing pydantic_ai.capabilities.Hooks callbacks render into
ACP updates:
from pydantic_ai import Agent
from pydantic_ai.capabilities import Hooks
from pydantic_acp import HookProjectionMap, run_acp
hooks = Hooks[None]()
@hooks.on.before_model_request
async def log_request(ctx, request_context):
del ctx
return request_context
agent = Agent("openai:gpt-5", capabilities=[hooks])
run_acp(
agent=agent,
projection_maps=(
HookProjectionMap(
hidden_event_ids=frozenset({"after_model_request"}),
event_labels={"before_model_request": "Preparing Request"},
),
),
)Capability bridges extend ACP exposure without coupling the adapter core to one product runtime. Built-in bridges cover:
HookBridgePrepareToolsBridgeHistoryProcessorBridgeMcpBridgeAgentBridgeBuilder
See Bridges for the full bridge model.
Providers let the host own session state while the adapter exposes it through ACP:
SessionModelsProviderSessionModesProviderConfigOptionsProviderPlanProviderApprovalStateProvider
See Providers for full details.
ClientHostContext provides session-scoped access to ACP client-backed filesystem and terminal
operations:
from acp.interfaces import Client as AcpClient
from pydantic_ai import Agent
from pydantic_acp import AcpSessionContext, ClientHostContext
def build_agent(client: AcpClient, session: AcpSessionContext) -> Agent[None, str]:
host = ClientHostContext.from_session(client=client, session=session)
agent = Agent("openai:gpt-5")
@agent.tool
async def read_user_file(ctx, path: str) -> str:
del ctx
result = await host.filesystem.read_text_file(path)
return result.content
return agentSee Host Backends And Projections for the filesystem and terminal API surface.
codex-auth-helper reads ~/.codex/auth.json, refreshes tokens when needed, builds a Codex-aware
AsyncOpenAI client, and returns a ready-to-use OpenAIResponsesModel.
from pydantic_ai import Agent
from codex_auth_helper import create_codex_responses_model
agent = Agent(create_codex_responses_model("gpt-5.4"))See Helpers for helper package details.
Runnable and focused examples live under examples/pydantic:
- static_agent.py
smallest direct
run_acp(agent=...)setup - factory_agent.py session-aware factory plus session-local model selection
- providers.py models, modes, config options, plan updates, and approval metadata
- bridges.py bridge builder, prepare-tools, history processors, and MCP metadata
- approvals.py native deferred approval flow
- host_context.py
ClientHostContextusage inside a factory-built agent - hook_projection.py
existing
Hookscapability introspection rendered throughHookProjectionMap - strong_agent.py
full-featured workspace agent example combining factories, providers, approvals, bridges, projection maps,
ask/plan/agentmodes, and host helpers
Canonical local checks:
make tests
make check
make save-coveragePreview the docs locally:
make serve- ACP Kit: landing page and package overview
- Installation: install paths and validation workflow
- Quickstart: first ACP server in a few steps
- CLI: root
acpkitCLI behavior - Pydantic ACP Overview: adapter architecture and entry points
- AdapterConfig: full
AdapterConfigguide - Models, Modes, and Slash Commands: models, modes, slash commands, and thinking
- Plans, Thinking, and Approvals: ACP planning, reasoning effort, and approval flow
- Providers: provider seams and host-owned state
- Bridges: capability bridges and bridge builder usage
- Host Backends And Projections: client filesystem, terminal helpers, and projections
- Helpers: helper packages including
codex-auth-helper - Workspace Agent: full coding-agent walkthrough
- Testing: behavioral test surface and validation commands
- examples/pydantic/README.md: runnable demos and focused SDK examples
Apache 2.0