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§
Sourcefn detect(&self) -> Option<DevToolInfo>
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
Nonewhen 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.
Sourcefn 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 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::SettingsGenerationFailedwhen the policy contains constructs the tool’s native config cannot express. - Pure: must not touch the filesystem.
Sourcefn 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 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::SettingsApplyFailedon filesystem error. - Idempotent: applying the same
settingstwice is a no-op.
Sourcefn build_launch_command(
&self,
tool_args: &[String],
agent_id: &str,
team_id: Option<&str>,
proxy_addr: Option<&str>,
) -> Result<Command, AdapterError>
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_argsplus the agent and (optional) team identity that the gateway issued for this run. proxy_addr, when set, is thehost:portof the local MitM proxy; the adapter must inject the appropriateHTTPS_PROXY/OPENAI_BASE_URL/ similar env var so the tool routes traffic through it.- Returns
AdapterError::LaunchFailedwhen the tool’s binary cannot be located or its argument format cannot accommodate the wiring. - Sync (no I/O performed) — the returned
Commandis built, not spawned. Spawning is the launcher’s job.
Sourcefn 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 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::McpConfigFailedwhen the config exists but is malformed. - Tools whose
DevToolInfo::supports_mcp == falseshould instead return an emptyVec.
Sourcefn 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 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
allowedlists MCP server names that are permitted; any server not inallowedand present indenied(or in the currently-configured set) must be removed from the tool’s active config.deniedis an explicit blocklist applied even if a server appears inallowed—deniedwins on conflict (matches policy-engine evaluation order).- Returns
AdapterError::McpConfigFailedon filesystem write failure. - Tools without MCP support should return
Ok(())without performing any work.
Sourcefn governance_level(&self) -> GovernanceLevel
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 returnsL1Observe). - Must agree with
detect()’sDevToolInfo::governance_levelfor 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".