Skip to content

Models, Modes, And Slash Commands

pydantic-acp exposes a small ACP control plane on top of normal prompts.

These controls exist to keep session state explicit and inspectable from the client UI.

Slash Commands

The adapter exposes a small fixed command set, dynamic mode commands, and optional host-defined commands.

Fixed commands

Command Purpose
/model Show the current model
/model <provider:model> Set the current model
/thinking Show the current thinking effort
/thinking <effort> Set the current thinking effort
/tools List visible tools on the active agent
/hooks List registered visible hook callbacks
/mcp-servers List MCP servers derived from toolsets and session metadata

Dynamic mode commands

Mode commands are registered from the current session’s available modes. If your session exposes:

  • review
  • execute

then ACP publishes:

  • /review
  • /execute

The adapter no longer hardcodes ask, plan, and agent as global commands. They are only published when those modes actually exist.

Mode ids must remain compatible with slash-command addressing:

  • they cannot be empty
  • they cannot contain whitespace
  • they cannot collide with reserved commands such as model, thinking, tools, hooks, or mcp-servers
  • they should stay specific enough that the command still reads clearly in the UI

Custom Commands

Configure AdapterConfig(slash_command_provider=...) to add host-owned commands:

from acp.schema import AvailableCommand
from pydantic_acp import SlashCommandResult, StaticSlashCommand, StaticSlashCommandProvider

provider = StaticSlashCommandProvider(
    commands=[
        StaticSlashCommand(
            command=AvailableCommand(name="diagnose", description="Run host diagnostics."),
            handler=lambda request: SlashCommandResult(text="Diagnostics queued."),
        )
    ]
)

Custom handlers receive SlashCommandRequest with the parsed command name, optional raw argument string, session, and active agent. Returning None or SlashCommandResult(handled=False) falls through to normal model execution.

SlashCommandResult.refresh_session_surface defaults to True so commands that mutate visible state refresh commands, config, mode, plan, and session metadata after they run.

Mode Changes Update ACP State

Mode commands do more than print text.

When a mode changes, the adapter updates:

  • current mode state
  • ACP config options when the mode is mirrored as a config option
  • plan state visibility
  • available commands
  • session metadata

This is why /plan or /agent affects the UI surface as well as the next prompt.

Model Selection

Model selection can be provided by either:

  • built-in available_models
  • a SessionModelsProvider

If model selection is enabled, the adapter also mirrors it into ACP config options unless that behavior is disabled or the provider owns it already.

Example built-in model config:

from pydantic_acp import AdapterConfig, AdapterModel

config = AdapterConfig(
    allow_model_selection=True,
    available_models=[
        AdapterModel(
            model_id="fast",
            name="Fast",
            description="Lower latency.",
            override="openai:gpt-5-mini",
        ),
        AdapterModel(
            model_id="smart",
            name="Smart",
            description="More capable model.",
            override="openai:gpt-5",
        ),
    ],
)

Thinking Effort

ThinkingBridge exposes a session-local ACP config option named thinking.

Supported values:

  • default
  • off
  • minimal
  • low
  • medium
  • high
  • xhigh

Example:

from pydantic_acp import AdapterConfig, ThinkingBridge

config = AdapterConfig(capability_bridges=[ThinkingBridge()])

From the UI:

/thinking high

The bridge uses Pydantic AI’s native Thinking capability to generate model settings rather than inventing provider-specific request payloads itself.

Mode-aware Tool Surfaces

The common pattern is:

  • ask: read-only, inspection-focused
  • plan: inspect and draft ACP plan state
  • agent: full tool surface plus plan progress tools

This is usually implemented with PrepareToolsBridge.

from pydantic_acp import PrepareToolsBridge, PrepareToolsMode
from pydantic_ai.tools import RunContext, ToolDefinition


def ask_tools(
    ctx: RunContext[None],
    tool_defs: list[ToolDefinition],
) -> list[ToolDefinition]:
    del ctx
    return [tool_def for tool_def in tool_defs if not tool_def.name.startswith("write_")]


prepare_bridge = PrepareToolsBridge(
    default_mode_id="ask",
    modes=[
        PrepareToolsMode(
            id="ask",
            name="Ask",
            description="Read-only repo inspection.",
            prepare_func=ask_tools,
        ),
        PrepareToolsMode(
            id="plan",
            name="Plan",
            description="Draft ACP plan state.",
            prepare_func=ask_tools,
            plan_mode=True,
        ),
    ],
)

What /tools Actually Lists

/tools lists currently registered visible tools on the active agent.

Important detail:

  • internal ACP tools such as acp_get_plan are intentionally hidden from /tools
  • the list reflects the agent after mode-aware prepare-tools filtering

That makes /tools a good debugging surface for “why can the model see this tool right now?”

What /mcp-servers Actually Lists

The MCP server listing is assembled from:

  • active agent toolsets
  • session MCP server payloads
  • bridge-contributed MCP metadata

It is primarily intended as a client-visible observability surface, not as the source of truth for server wiring.

Common Failure Modes

  • /thinking does not appear unless a ThinkingBridge() is configured
  • /model only appears when model state is actually available
  • mode commands are not global; if a mode is not present in current session state, its slash command is not published
  • mode ids like model or thinking are rejected because they would collide with reserved slash commands