# iron-core
`iron-core` is the core embedding crate for AgentIron.
It provides:
- the stream-first `IronAgent` / `AgentConnection` / `AgentSession` facade
- durable messages, timeline entries, and tool-call records
- tool registration with JSON Schema argument validation
- approval-gated tool execution
- ACP-native transports for in-process, stdio, and TCP integrations
- context compaction, active-context accounting, and handoff export/import
- layered prompt composition with repository instruction loading and runtime context injection
- optional embedded Python execution via the `embedded-python` feature
- WASM integration plugins via Extism with install lifecycle, manifest extraction, session-scoped enablement, auth-gated tool availability, and a 30-second execution timeout
The facade/runtime API is the supported integration surface.
## Requirements
- Rust 1.95+
- Tokio for async embedding code
## Install
Use the git dependency for now:
```toml
[dependencies]
iron-core = { git = "https://github.com/AgentIron/iron-core", branch = "main" }
iron-providers = "0.2.2"
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
`iron-core` is not ready for crates.io publication yet because the optional `embedded-python` feature depends on `monty` from git until a usable crates.io release exists.
If you need the built-in `python_exec` tool, enable the feature explicitly:
```toml
[dependencies]
iron-core = { git = "https://github.com/AgentIron/iron-core", branch = "main", features = ["embedded-python"] }
iron-providers = "0.2.2"
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
## Quick Start
```rust,ignore
use iron_core::{Config, FunctionTool, IronAgent, PromptEvent, ToolDefinition};
use iron_providers::{ApiFamily, ProviderConnection, ProviderProfile, RuntimeConfig};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::new().with_model("gpt-4o");
let provider = ProviderConnection::from_profile(
ProviderProfile::new("openai", ApiFamily::Responses, "https://api.openai.com/v1"),
RuntimeConfig::new(std::env::var("OPENAI_API_KEY")?),
)?;
let agent = IronAgent::new(config, provider);
agent.register_tool(FunctionTool::new(
ToolDefinition::new(
"echo",
"Return the provided text",
json!({
"type": "object",
"properties": {
"text": { "type": "string" }
},
"required": ["text"]
}),
),
|args| Ok(json!({ "echo": args["text"].clone() })),
));
let connection = agent.connect();
let session = connection.create_session()?;
let (handle, mut events) = session.prompt_stream("Call echo with hello.");
while let Some(event) = events.next().await {
match event {
PromptEvent::Output { text } => print!("{text}"),
PromptEvent::ApprovalRequest { call_id, .. } => {
handle.approve(&call_id).expect("approval should be pending");
}
PromptEvent::Complete { outcome } => {
println!("\nprompt finished: {outcome:?}");
break;
}
_ => {}
}
}
Ok(())
}
```
The canonical interaction model is stream-first:
- call `session.prompt_stream(...)`
- consume `PromptEvent`s as they arrive
- use `PromptHandle` to approve, deny, or cancel an active prompt
## Built-In Tools
`iron-core` can register built-in `read`, `write`, `edit`, `glob`, `grep`, and `webfetch` tools, plus `bash` or `powershell` when a shell is available.
Use `BuiltinToolConfig` to scope filesystem access, disable specific tools, and tune limits such as command timeouts and output caps.
## MCP Servers
`iron-core` supports session-scoped MCP (Model Context Protocol) servers as a tool source alongside built-in and custom tools. The full implementation covers:
- Runtime-local MCP server inventory with per-server configuration
- Session-scoped enablement governed by a single runtime-default policy
- Canonical session-effective tool catalog with precise unavailable-tool diagnostics
- Approval strategy enforcement for MCP tool calls
- Transport clients for stdio, HTTP, and HTTP+SSE
- Connection lifecycle management with single-flight connect guarding
- Handoff exclusion — MCP state is runtime-local and not portable
## Integration Plugins
`iron-core` includes a WASM-based integration-plugin surface powered by [Extism](https://extism.org). Plugins are a third tool source alongside built-in tools and MCP servers.
The plugin system is fully implemented:
- **Install lifecycle** — fetch (local or HTTPS+checksum), cache, manifest extraction from WASM binaries, identity validation, and Extism-backed WASM host loading
- **WASM execution** — tools are invoked via `tool_{name}` exports with a JSON request/response envelope and a 30-second timeout
- **Session-scoped enablement** — runtime-level defaults materialised at session creation, with per-session overrides
- **Canonical tool availability** — single-source-of-truth `compute_tool_availability()` gates tools on health, auth requirements, and scope satisfaction
- **Auth model** — runtime-governed auth state, credential bindings, and per-tool scope checks
- **Tool diagnostics** — structured `UnavailableReason` variants for actionable error messages
- **Network policy** — allowlist, blocklist, and wildcard policies declared in plugin manifests
## Development
Install the security tooling if you want to run the same audit check CI reports:
```bash
cargo install cargo-audit
```
Configure the repository pre-commit hook after cloning:
```bash
git config core.hooksPath .githooks
```
The pre-commit hook runs `cargo fmt --manifest-path Cargo.toml -- --check` when staged Rust files are present. Before opening or updating a pull request, run the same checks CI validates:
```bash
cargo build --manifest-path Cargo.toml --all-targets
cargo fmt --manifest-path Cargo.toml -- --check
cargo clippy --manifest-path Cargo.toml --all-targets --all-features -- -D warnings
cargo test --manifest-path Cargo.toml
cargo audit --file Cargo.lock
```
The local `invoke` tasks in `tasks.py` are a convenience layer over these checks:
```bash
inv build
inv test
inv security
```
The `Pull Request` workflow runs build, format, lint, and test checks on every PR to `main`. It also runs `cargo audit` as a non-blocking audit and posts the output as a PR comment.
Merges to `main` trigger an automatic patch release that bumps `Cargo.toml`, creates a `vX.Y.Z` tag, and creates a GitHub release. Publishing to crates.io is skipped until `monty` is available from crates.io because crates.io rejects manifests containing git dependencies.
## Documentation
- [Architecture Overview](docs/architecture-overview.md)
- [Getting Started](docs/getting-started-iron-core.md)
- [Integration Plugins](docs/integration-plugins.md)
- [Prompt Composition](docs/prompt-composition.md)
- [Architecture Cleanup Checklist](docs/architecture-cleanup-checklist.md)
Build the API docs locally with:
```bash
cargo doc -p iron-core --no-deps
```
## License
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).