Codex CLI for Bazel Monorepo Workflows: MCP Server Integration, Remote Builds, and AGENTS.md Conventions

Codex CLI for Bazel Monorepo Workflows: MCP Server Integration, Remote Builds, and AGENTS.md Conventions
Bazel monorepos present a unique challenge for AI coding agents. The build graph is explicit and hermetic, the dependency model is declarative, and a single misplaced BUILD.bazel entry can cascade into hundreds of broken targets. These properties make Bazel repositories simultaneously the hardest environment for agents to operate in and the one where they can deliver the most value — because the build system itself acts as a deterministic verification layer that catches agent mistakes before they reach production.
This article covers how to configure Codex CLI for effective Bazel monorepo work, integrating the Bazel MCP Server for build-graph-aware tool calls, offloading validation to BuildBuddy Remote Bazel for fast feedback loops, and encoding Bazel conventions into AGENTS.md so the agent respects the build graph from the first turn.
Why Bazel Demands Special Agent Configuration
Standard Codex CLI sandbox defaults assume a workspace where running npm test or cargo build is sufficient to validate changes. Bazel breaks these assumptions in several ways:
- External cache directories — Bazel writes to
~/.cache/bazel(or a custom--output_base), which sits outside the workspace sandbox boundary 1 - Network dependencies — Bzlmod fetches modules from the Bazel Central Registry and other registries during
bazel fetch2 - Runfiles resolution — binaries and test fixtures resolve via runfiles trees, not filesystem paths relative to source 3
- WORKSPACE removal in Bazel 9 — the legacy WORKSPACE system was completely removed in Bazel 9.0 (January 2026), meaning agents must understand Bzlmod exclusively 4
Without addressing these, Codex will either fail to build or request danger-full-access, defeating the sandbox’s purpose.
Sandbox Configuration for Bazel
The minimum viable config.toml for Bazel monorepos extends the sandbox with writable roots for Bazel’s output directories and selective network access for dependency fetching:
[sandbox_workspace_write]
writable_roots = [
"~/.cache/bazel",
"~/.cache/bazelisk",
"/tmp/bazel-*",
]
network_access = true
For tighter control, use a named permission profile that restricts network access to known registries:
[permissions.bazel]
[permissions.bazel.filesystem]
"~/.cache/bazel" = "write"
"~/.cache/bazelisk" = "write"
[permissions.bazel.network]
enabled = true
mode = "limited"
[permissions.bazel.network.domains]
"bcr.bazel.build" = "allow"
"mirror.bazel.build" = "allow"
"github.com" = "allow"
"objects.githubusercontent.com" = "allow"
Activate it with codex --profile bazel or set default_permissions = "bazel" in your project-scoped .codex/config.toml 5.
Bazel MCP Server: Build-Graph-Aware Tool Calls
The Bazel MCP Server exposes six tools that give Codex direct access to the build graph without shelling out to bazel and parsing stdout 6:
| Tool | Purpose |
|---|---|
bazel_build_target |
Compile specified targets |
bazel_query_target |
Query the dependency graph |
bazel_test_target |
Run tests for specified targets |
bazel_list_targets |
Enumerate all targets in a package |
bazel_fetch_dependencies |
Fetch external dependencies |
bazel_set_workspace_path |
Switch workspace at runtime |
Configuration
Add the MCP server to your Codex configuration:
[mcp_servers.bazel]
command = "npx"
args = ["-y", "github:nacgarg/bazel-mcp-server"]
[mcp_servers.bazel.env]
MCP_WORKSPACE_PATH = "."
Each tool accepts optional additionalArgs for passing flags like --verbose_failures or --test_output=all 6.
Why MCP Beats Shell Commands
When Codex shells out to bazel query, it must parse the text output and infer structure. The MCP server returns structured responses that the model can reason about directly. More importantly, the MCP server handles workspace path resolution, .bazelrc loading, and error propagation consistently — eliminating an entire class of “it works on my machine” failures.
Remote Builds with BuildBuddy
For large monorepos, local Bazel builds become the bottleneck. BuildBuddy’s Remote Bazel service solves five critical problems for agent workflows 7:
- Network latency — runners are co-located with cache and RBE servers, achieving sub-millisecond round-trips
- Resource contention — remote runners provide up to 100 GB of disk and substantial CPU, far exceeding typical agent sandboxes
- Workspace locking — Bazel’s output-base lock prevents parallel builds; cloned remote runners eliminate this constraint
- Analysis cache thrash — snapshotted runners maintain warm caches across builds, avoiding the 30–60 second cold-start penalty
- Cross-platform testing — agents can test on Linux and macOS without local hardware
Integration via SKILL.md
Create a reusable skill at .codex/skills/remote-bazel/SKILL.md 8:
# Remote Bazel Build & Test
## When to use
Use `bb remote` instead of `bazel` for all build and test commands
in this repository. This sends builds to a remote runner with
a warm analysis cache and co-located remote cache.
## Commands
- Build: `bb remote build //path/to:target`
- Test: `bb remote test //path/to:target`
- Test all: `bb remote test //...`
- Cross-platform: `bb remote --os=linux --arch=amd64 test //...`
## Environment
Requires BB_API_KEY in the environment. The CLI mirrors local
git state including uncommitted changes automatically.
## Important
- Always use `bb remote` rather than raw `bazel` commands
- Check build results before moving to the next task
- Use `--test_output=errors` for concise failure output
The bb remote CLI automatically mirrors uncommitted local changes to the remote runner, meaning Codex can edit a file and immediately validate without committing 7.
AGENTS.md Conventions for Bazel Repositories
The OpenAI Codex repository itself uses Bazel and documents critical conventions in its AGENTS.md 3. Adopt similar patterns for your own monorepo:
# Bazel Conventions
## Build file maintenance
- Every new source file MUST have a corresponding entry in its
package's BUILD.bazel
- Never create source files without updating BUILD.bazel in the
same change
- Use `bazel_query_target` via MCP to verify dependency edges
before adding new deps
## Runfiles resolution
- Never use hardcoded filesystem paths for test fixtures
- Use the language-appropriate runfiles library
(e.g., `@rules_python//python/runfiles` for Python,
`codex_utils_cargo_bin::find_resource!` for Rust)
- Paths that work under `cargo test` may break under `bazel test`
## Bzlmod (Bazel 9+)
- All external dependencies use MODULE.bazel, not WORKSPACE
- WORKSPACE files do not exist in this repository
- Use `bazel mod show_repo --all_repos` to inspect available
external repos
- When adding dependencies, update MODULE.bazel and run
`bazel mod tidy`
## Compile-time file access
- If adding include_str!, include_bytes!, sqlx::migrate!, or
similar build-time reads, update the crate's BUILD.bazel
(compile_data, build_script_data, or data attributes)
- Bazel does NOT make source-tree files available to compile-time
access automatically
## Prohibited patterns
- Do not use glob() with allow_empty = True to paper over
missing sources
- Do not add deps without verifying visibility with
`bazel query 'visible(...)'`
- Do not bypass the build graph by running compilers directly
Workflow: Agent-Driven Bazel Development
The following diagram shows the edit-build-test loop when Codex operates in a Bazel monorepo with MCP and Remote Bazel configured:
sequenceDiagram
participant Dev as Developer
participant Codex as Codex CLI
participant MCP as Bazel MCP Server
participant BB as BuildBuddy Remote
Dev->>Codex: "Add rate limiting to the auth service"
Codex->>MCP: bazel_list_targets //src/auth/...
MCP-->>Codex: [auth_lib, auth_test, auth_bin]
Codex->>Codex: Edit source + BUILD.bazel
Codex->>MCP: bazel_query_target "deps(//src/auth:auth_lib)"
MCP-->>Codex: Dependency graph
Codex->>BB: bb remote test //src/auth:auth_test
BB-->>Codex: PASSED (warm cache, 8s)
Codex->>BB: bb remote test //src/auth/...
BB-->>Codex: 3 targets PASSED
Codex->>Dev: Changes complete, all tests pass
The key insight is that the agent validates incrementally — first querying the build graph to understand the dependency surface, then running targeted tests, then broadening to the full package. This mirrors how experienced Bazel developers work and avoids the naive bazel test //... that can take minutes even on remote runners.
Structured Audits with codex exec
Use codex exec with --output-schema to audit BUILD.bazel files across the monorepo 9:
codex exec \
--profile bazel \
--sandbox read-only \
--output-schema '{
"type": "object",
"properties": {
"packages_audited": { "type": "integer" },
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"package": { "type": "string" },
"severity": { "type": "string", "enum": ["error", "warning"] },
"description": { "type": "string" },
"fix": { "type": "string" }
}
}
}
}
}' \
"Audit all BUILD.bazel files under src/ for: missing test targets, \
overly broad globs, visibility violations, and deps that should be \
replaced with more specific targets. Use bazel query to verify."
This produces machine-parseable JSON suitable for CI gates or dashboard ingestion.
Model Selection for Bazel Workflows
| Task | Recommended Model | Rationale |
|---|---|---|
| BUILD.bazel authoring | gpt-5.5 |
Complex dependency reasoning requires frontier capability 10 |
| Bzlmod MODULE.bazel edits | gpt-5.5 |
Registry resolution and version constraint logic |
| Source code within targets | codex-spark |
Fast iteration on implementation; build graph handled separately 11 |
| Build graph audits | gpt-5.4 |
Good balance of reasoning and cost for structured analysis 10 |
| Starlark macro authoring | gpt-5.5 |
Starlark’s subtle scoping rules require strong reasoning |
Anti-Patterns
Running bazel test //... on every change. This rebuilds the entire dependency closure. Use bazel query "rdeps(//..., set(changed_files))" to compute the affected targets, then test only those.
Granting danger-full-access because Bazel needs cache writes. Use writable_roots to grant access to ~/.cache/bazel specifically, preserving sandbox protection for the rest of the filesystem.
Trusting agent-generated visibility attributes. Codex may default to visibility = ["//visibility:public"] to avoid errors. Encode your visibility policy in AGENTS.md and use a PostToolUse hook to reject overly permissive visibility:
[[hooks]]
type = "PostToolUse"
command = "grep -rn '//visibility:public' --include='BUILD.bazel' . && echo 'BLOCK: public visibility detected' && exit 1 || exit 0"
Ignoring compile_data and data attributes. When Codex adds source files that use build-time file inclusion (e.g., include_str! in Rust, resource loading in Java), BUILD.bazel must be updated simultaneously. The AGENTS.md convention above makes this explicit 3.
Skipping bazel mod tidy after MODULE.bazel changes. ⚠️ Agents frequently add dependencies to MODULE.bazel without running the tidy command, leaving the lockfile inconsistent. Enforce this with a hook or skill instruction.
Known Limitations
- Output-schema and resume are mutually exclusive —
codex exec --output-schemasessions cannot be resumed if interrupted 12 - Context window pressure — large monorepos with hundreds of BUILD.bazel files may exceed context limits; scope queries to specific subtrees
- Bazel MCP Server is community-maintained — it tracks Bazel releases but may lag behind new Bazel 9 features
- Remote Bazel requires BuildBuddy account — the
bb remotecommand needs a validBB_API_KEY; self-hosted alternatives exist but require more setup - Non-deterministic generation — agent-generated BUILD.bazel content may vary between runs; always validate with
bazel buildbefore committing