@brycepelletier/agent-env-mcp
v1.0.0
Published
Hardened MCP gateway for reusable containerized software-engineering environments.
Readme
@brycepelletier/agent-env-mcp
Reusable host-side MCP controller for a hardened Linux software-engineering runtime.
0.4.0 trust boundary
agent-env-mcp is software-engineering only. Its single agent service can
inspect, edit, build, test, lint, and debug the active workspace. The real
.git directory is physically masked by a root-owned tmpfs mount, and the
container receives no GitHub credentials or Docker socket.
Software Engineer
|
agent-env-mcp
|
Linux engineering runtime
|-- source tree visible
|-- .git masked
|-- no GitHub credentials
`-- no Docker socket
GitHub Operator
|
github-app-mcp
`-- all Git and GitHub operationsVersion 0.4.0 removes the former git service and public git_command. That is
an intentional breaking change: all local and remote Git operations belong to
@brycepelletier/github-app-mcp and the GitHub Operator trust domain.
Tools
describe_agent_system
Reads the installed ~/.agents/*.agent.md frontmatter and returns the current orchestrator identity, direct capability categories, permitted specialists, their ownership categories, exact runSubagent calls, and source filename/hash. It accepts no path or other arguments, does not start the container environment, and never returns specialist tool inventories or schemas. Invalid, duplicate, or missing definitions fail closed.
ensure_environmentlist_directoryread_filesearch_workspaceworkspace_editrun_command
Recursive workspace listing and search omit .git, .ssh, and .gnupg
directory nodes and their descendants at every depth. Direct read or edit
requests containing those path segments are rejected before filesystem access.
The .git mount remains physically masked from programs launched through
run_command; programs that deliberately access it may receive a permission
error without gaining repository metadata.
The MCP discovers the active VS Code workspace lazily through MCP Roots,
requires exactly one local file: root, and does not accept a model-supplied
host workspace path.
read_file returns only the path, truncation state, and verbatim source text.
Source lines and response fields are never decorated with generated line
numbers. This makes returned content safe to reuse as workspace_edit.old_text
without accidentally searching for presentation-only prefixes or normalized
line endings. search_workspace likewise returns file paths and matching text
without generated line or column numbers. Other MCP modules must not decorate
editable source content with synthetic numbering.
For full-file changes, use workspace_edit with operation: "overwrite", the
complete new_text, and the expected_sha256 returned by read_file. This
avoids sending a second escaped copy of the old file while still preventing a
stale agent from overwriting concurrent work. Use operation: "replace" only
for small, unique, exact snippets.
{
"operation": "overwrite",
"path": "src/example.cpp",
"expected_sha256": "<sha256 returned by read_file>",
"new_text": "<complete desired file>"
}Command path semantics
The workspace reported by ensure_environment is already the authorized
project root. run_command resolves cwd and any executable path containing a
slash relative to that root. Use cwd: "." for repository-root commands or a
relative subdirectory such as scripts; do not pass an absolute path or repeat
the project directory name. In the Linux runtime, prefer python3 unless the
repository defines another interpreter.
Examples:
{"program":"python3","args":["verify_pr_validation.py"],"cwd":"."}
{"program":"python3","args":["scripts/verify_pr_validation.py"],"cwd":"."}
{"program":"python3","args":["verify_pr_validation.py"],"cwd":"scripts"}Invalid paths, missing executables, and permission failures return concrete
sanitized errors. A program that starts and exits unsuccessfully returns its
exit_code, signal, bounded stdout, and bounded stderr, including script,
dependency/import, and network diagnostics emitted by that program. These
details improve recovery without expanding the authorized filesystem boundary.
Host prerequisites
- Node.js 20.10 or newer
- Docker with Linux-container support
- Exactly one local VS Code workspace root
Docker behavior and lifecycle
The trusted host facade starts one deterministic Docker Compose project for the
active workspace. The only runtime service is agent; it runs as the unprivileged
vscode user with all Linux capabilities dropped and no-new-privileges set.
The workspace is bind-mounted, while its .git directory is over-mounted with
an inaccessible tmpfs. No Docker socket or credential path is mounted.
MCP stdin EOF/close, SIGINT, SIGTERM, SIGHUP, or a fatal process error starts an
idempotent shutdown. Shutdown waits for in-flight preparation, runs
docker compose down --remove-orphans, closes the MCP server, and exits. The
15-minute idle timeout is a secondary cleanup path. New calls fail once shutdown
begins.
Local development and validation
From Git Bash:
npm run link
npm test
npm run validate
npm run unlinknpm run validate performs syntax checks, policy and lifecycle tests, and an
npm package dry run. Docker Compose rendering and image builds are separate host
integration checks because they require a running Docker daemon.
VS Code configuration
Published-package configuration:
{
"servers": {
"agent-env": {
"type": "stdio",
"command": "npx",
"args": ["--yes", "@brycepelletier/[email protected]"]
}
}
}While locally linked, replace npx and its arguments with:
"command": "agent-env-mcp"Acceptance checks
Before relying on the boundary:
ensure_environmentreports the expected project workspace.run_commandcan build and test the project.- Direct
gitis rejected by policy. - Invoking the real Git binary indirectly through Python or Node reports that
the workspace is not a Git repository because
.gitis physically hidden. .git,.ssh, and.gnupgpaths are inaccessible through workspace tools.- No GitHub credential variables, PEM, or Docker socket are visible.
- The MCP tool inventory contains no
git_commandor GitHub API tools. - Closing the MCP connection removes its Compose containers.
The .git masking assumes a standard checkout where .git is a directory.
Git worktrees and submodules that use a .git file must be rejected or handled
by a future mount strategy before treating those repository forms as hardened.
License
MIT. See LICENSE.
