Skip to main content

DevToolAdapter

Trait DevToolAdapter 

Source
pub trait DevToolAdapter: Send + Sync {
    // Required methods
    fn detect(&self) -> Option<DevToolInfo>;
    fn generate_managed_settings<'life0, 'life1, 'async_trait>(
        &'life0 self,
        policy: &'life1 PolicyDocument,
    ) -> Pin<Box<dyn Future<Output = Result<String, AdapterError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn apply_settings<'life0, 'life1, 'async_trait>(
        &'life0 self,
        settings: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = Result<(), AdapterError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn build_launch_command(
        &self,
        tool_args: &[String],
        agent_id: &str,
        team_id: Option<&str>,
        proxy_addr: Option<&str>,
    ) -> Result<Command, AdapterError>;
    fn list_mcp_servers<'life0, 'async_trait>(
        &'life0 self,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<McpServerInfo>, AdapterError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
    fn apply_mcp_governance<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        allowed: &'life1 [String],
        denied: &'life2 [String],
    ) -> Pin<Box<dyn Future<Output = Result<(), AdapterError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn governance_level(&self) -> GovernanceLevel;
}
Expand description

Per-tool integration contract for the dev tool governance framework.

Every per-tool adapter (Claude Code, Codex, GitHub Copilot, Windsurf Cascade, third-party SaaS coding agents) implements this trait. The gateway and the aa run launcher consume adapters via dyn DevToolAdapter, so the trait must be object-safe — that property is locked in by an explicit compile-time check added in AAASM-925.

Implementations live in their own crates / Stories and are out of scope for this Subtask (see AAASM-201 through AAASM-205, AAASM-918).

§Async dispatch

The async fn methods are macro-desugared by async_trait::async_trait into boxed-future return types so that dyn DevToolAdapter is dyn-safe on stable Rust. The boxing cost is negligible compared to the I/O these methods perform (filesystem reads, subprocess writes, MCP discovery).

Required Methods§

Source

fn detect(&self) -> Option<DevToolInfo>

Detect whether the tool this adapter targets is installed on the current host.

§Contract
  • Returns Some(DevToolInfo) when the tool’s binary or well-known installation marker is present and readable.
  • Returns None when the tool is not installed, is unreadable, or cannot be confirmed (e.g. the user lacks filesystem permission).
  • Must not perform network I/O — detection runs at every CLI invocation and is on the hot path.
Source

fn generate_managed_settings<'life0, 'life1, 'async_trait>( &'life0 self, policy: &'life1 PolicyDocument, ) -> Pin<Box<dyn Future<Output = Result<String, AdapterError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Translate an Agent Assembly PolicyDocument into the tool’s native managed-settings format (e.g. JSON for Claude Code, .codex/config.toml for Codex).

§Contract
  • On success, returns the rendered settings document as a UTF-8 string ready to be written by apply_settings.
  • Returns AdapterError::SettingsGenerationFailed when the policy contains constructs the tool’s native config cannot express.
  • Pure: must not touch the filesystem.
Source

fn apply_settings<'life0, 'life1, 'async_trait>( &'life0 self, settings: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<(), AdapterError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Write the rendered managed settings into the tool’s configuration surface, replacing any prior managed block.

§Contract
  • On success, the tool will pick up the new policy on its next launch (some tools require a restart; the adapter is expected to document that in its own crate-level docs).
  • Returns AdapterError::SettingsApplyFailed on filesystem error.
  • Idempotent: applying the same settings twice is a no-op.
Source

fn build_launch_command( &self, tool_args: &[String], agent_id: &str, team_id: Option<&str>, proxy_addr: Option<&str>, ) -> Result<Command, AdapterError>

Build the std::process::Command used by the aa run launcher to start the tool with governance wiring (proxy, env vars, agent identity, optional team identity).

§Contract
  • Caller passes raw tool_args plus the agent and (optional) team identity that the gateway issued for this run.
  • proxy_addr, when set, is the host:port of the local MitM proxy; the adapter must inject the appropriate HTTPS_PROXY / OPENAI_BASE_URL / similar env var so the tool routes traffic through it.
  • Returns AdapterError::LaunchFailed when the tool’s binary cannot be located or its argument format cannot accommodate the wiring.
  • Sync (no I/O performed) — the returned Command is built, not spawned. Spawning is the launcher’s job.
Source

fn list_mcp_servers<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<McpServerInfo>, AdapterError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Enumerate the MCP servers the tool is currently configured to connect to.

§Contract
  • Returns the parsed list from the tool’s native MCP config surface (e.g. ~/.claude/mcp_servers.json).
  • Returns an empty Vec (not an error) when the tool supports MCP but has no servers configured.
  • Returns AdapterError::McpConfigFailed when the config exists but is malformed.
  • Tools whose DevToolInfo::supports_mcp == false should instead return an empty Vec.
Source

fn apply_mcp_governance<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, allowed: &'life1 [String], denied: &'life2 [String], ) -> Pin<Box<dyn Future<Output = Result<(), AdapterError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Apply an MCP allow / deny list to the tool’s configuration.

§Contract
  • allowed lists MCP server names that are permitted; any server not in allowed and present in denied (or in the currently-configured set) must be removed from the tool’s active config.
  • denied is an explicit blocklist applied even if a server appears in alloweddenied wins on conflict (matches policy-engine evaluation order).
  • Returns AdapterError::McpConfigFailed on filesystem write failure.
  • Tools without MCP support should return Ok(()) without performing any work.
Source

fn governance_level(&self) -> GovernanceLevel

Highest governance level this adapter can achieve for the tool it targets.

§Contract
  • Returns the static, build-time-known cap for this adapter (e.g. an SDK-integrated adapter returns L3Native; a SaaS coding agent’s observability-only adapter returns L1Observe).
  • Must agree with detect()’s DevToolInfo::governance_level for any successful detection — gateway uses this to short-circuit policy decisions when an action would require a level the adapter cannot enforce.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§