Mason — an AI agent that builds domain-specific tools.
Mason is the agent core: a model/tool loop, an LLM client, a tool framework, context management, subagents, skills, sessions and an HTTP API. It is domain-independent — domain-specific tools, profiles and prompts live in the application that embeds it.
Export an OpenAI-compatible vendor, then run:
export AI_VENDORS=openai
export AI_OPENAI_URL=https://api.openai.com/v1
export AI_OPENAI_MODEL=your-model-id
export AI_OPENAI_API_KEY=your-api-key
cargo run -- run "explain this repo" # one prompt, answer streamed to stdout
cargo run -- serve --listen 127.0.0.1:3120 # session HTTP API; requires MASON_API_TOKENVendor options, flags and environment fallbacks: docs/getting-started.md.
let agent = Agent::builder()
.vendors(Vendors::from_env()?)
.cwd(repo_root)
.instructions("You are an agent working on the Foo service.")
.tools(builtin_tools())
.build()?;
let answer = agent.run_once("explain this repo").await?;Tools, profiles, prompts, stores and models are all extension points: docs/embedding.md. An app whose tools all come over MCP needs no Rust — a YAML manifest is enough (docs/configuration.md).
| Doc | Contents |
|---|---|
| getting-started.md | Vendor setup, run/serve, common flags |
| configuration.md | Flag/env reference, budgets, declarative app config |
| embedding.md | Builder API and extension points |
| mcp.md | MCP server map, lifecycle, sandbox interaction |
| api.md | HTTP endpoints, events/SSE, session lifecycle |
| security.md | Execution modes, sandbox, access and persistence |
| architecture.md | Runtime limits, context management, source layout |
Profile shell, write_paths and hooks (configuration.md)
hold only when they are operated this way:
- Write
shellpatterns as narrowly as the commands. A trailing*admits any flags:git diff*admitsgit diff --output=FILE, which writes a file. - Give a profile with
write_pathsashelllist too, and never allowln.write_pathsis checked against where a write lands, symlinks followed, but a symlink created or swapped after the check is outside it; an unrestricted shell can write anywhere anyway. - Hook tools must report failure as an error.
bashreports a failing command's exit code in its output, so it is refused as a hook; wrap a shell check in an MCP tool that fails with an error. - Hooks are called like ordinary tool calls (host policy and profile rules
apply) and only on
writeandedit; a file changed another way is not checked. - Delegation retains the parent's restrictions. A child gets only tools both profiles allow, and both profiles' shell and path rules apply. Parent edit hooks also cover child edits, including edits left by a failed child.
- Restart
mason serveafter changingagent.yaml. Profiles, rules and hooks are read at startup. MCP tool lists refresh over existing session connections; unrelated servers are skipped and failed listings retain their last successful tools. Restart local MCP processes after updating their executable: listing tools does not reload server code. See the connection lifecycle.
./etc/check.sh # fmt + clippy (both feature configs) + tests
./etc/check.sh --coverage # the same with a cargo-llvm-cov report (optional)Minimum supported Rust version: 1.94 (the sqlx floor), recorded in
Cargo.toml.