Skip to main content

ACP Kit provides a common adapter for Agent Frameworks.

Project description

ACP Kit

ACP Kit is a monorepo for ACP-facing agent runtime packages.

  • acpkit is the root CLI and target resolver
  • pydantic-acp adapts pydantic_ai.Agent instances to ACP
  • codex-auth-helper turns a local Codex login into a pydantic-ai Responses model

The core workflow is simple:

  1. build a normal pydantic_ai.Agent
  2. expose it through ACP with run_acp(...) or create_acp_agent(...)
  3. optionally add session stores, approvals, providers, projection maps, and bridges

Installation

Production:

uv pip install "acpkit[pydantic]"
pip install "acpkit[pydantic]"

To use acpkit launch ..., install the optional launch extra:

uv pip install "acpkit[launch]"
pip install "acpkit[launch]"

Development:

uv sync --extra dev --extra docs --extra pydantic
pip install -e ".[dev,docs,pydantic]"

CLI

Run a supported agent target through ACP:

acpkit run strong_agent
acpkit run strong_agent:agent
acpkit run strong_agent:agent -p ./examples

acpkit 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 ./examples

acpkit 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.

run_acp

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)

create_acp_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

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 via PrepareToolsMode(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(base_dir=Path(".acp-sessions")),
    approval_bridge=NativeApprovalBridge(enable_persistent_choices=True),
)

run_acp(agent=agent, config=config)

Native Plan State

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_plan and acp_set_plan as hidden tools on the agent
  • extends output_type with NativePlanGeneration so the agent can emit a structured plan in a single response

NativePlanGeneration fields:

  • plan_entries: list[PlanEntry] — structured plan entries
  • plan_md: str — optional markdown representation

Native plan state and plan_provider are mutually exclusive. See docs/providers.md for full details.

Agent Factories

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),
)

Session Stores

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(base_dir=Path(".acp-sessions")),
    ),
)

Session lifecycle support includes create, load, list, fork, resume, close, transcript replay, and message-history replay.

Runtime Controls

The adapter exposes a small ACP control plane alongside normal prompts:

  • /model print the current session model id
  • /model provider:model switch the current session model
  • /tools list registered tools
  • /hooks list registered Hooks callbacks visible on the current agent
  • /mcp-servers list MCP servers extracted from the current agent toolsets and session metadata

Codex-backed model changes must be explicit:

/model codex:gpt-5.4

Approval Flow

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

projection_maps lets known tool families render as richer ACP content instead of raw text.

FileSystemProjectionMap

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

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

Capability bridges extend ACP exposure without coupling the adapter core to one product runtime. Built-in bridges cover:

  • HookBridge
  • PrepareToolsBridge
  • HistoryProcessorBridge
  • McpBridge
  • AgentBridgeBuilder

See docs/bridges.md for the full bridge model.

Providers

Providers let the host own session state while the adapter exposes it through ACP:

  • SessionModelsProvider
  • SessionModesProvider
  • ConfigOptionsProvider
  • PlanProvider
  • ApprovalStateProvider

See docs/providers.md for full details.

Host Backends

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 agent

See docs/host-backends.md for the filesystem and terminal API surface.

Codex Auth Helper

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 docs/helpers.md for helper package details.

Examples

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 ClientHostContext usage inside a factory-built agent
  • hook_projection.py existing Hooks capability introspection rendered through HookProjectionMap
  • strong_agent.py full-featured workspace agent example combining factories, providers, approvals, bridges, projection maps, ask/plan/agent modes, and host helpers

Development

Canonical local checks:

uv run ruff check
uv run ty check
uv run basedpyright
make tests
make check

Preview the docs locally:

make serve

Documentation Map

License

Apache 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

acpkit-0.4.2.tar.gz (109.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

acpkit-0.4.2-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file acpkit-0.4.2.tar.gz.

File metadata

  • Download URL: acpkit-0.4.2.tar.gz
  • Upload date:
  • Size: 109.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for acpkit-0.4.2.tar.gz
Algorithm Hash digest
SHA256 f7ebff786ef35154e4299b6aa1b9a185a7db1121aeda4195dde6d84ceab39067
MD5 6a5b66dc8ec083d334f7b9f6c91177cf
BLAKE2b-256 af9a8df06d3611713c9ceabcd2c34a1a13b46ff087b1a94a727a663ddc40f1a3

See more details on using hashes here.

File details

Details for the file acpkit-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: acpkit-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for acpkit-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7cfccd798cf73e9df9f2bb1978ee8a1661444df9802f4a7995aa1e072b67d468
MD5 0abe3d3a83365a274e6d4567fcba4d98
BLAKE2b-256 5da6335c8ab7e4eeb2efba241faa19f8e2bbdef99b7af482773a345fd35d4880

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page