> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runaether.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Repository MCP Config

> MCP servers committed in .mcp.json and .codex/config.toml work in Aether tasks as they do locally

Commit your team's MCP servers in the repository the way Claude Code and Codex read them, and Aether tasks get them too: Claude Code's `.mcp.json` and the `[mcp_servers]` tables of Codex's `.codex/config.toml`, on either harness. Aether reads both files through GitHub at the commit the task's base branch names when the task is created, and keeps that commit for the whole task. Values come from the project's [environment variables](/guides/environment-variables). The servers are the task's own: nothing is added to the organization's catalog.

A file Aether cannot honor refuses the task at creation, naming the file and the server. The composer shows the same refusal before you send.

## Remote servers

A server with a `url` (`"type": "http"` in `.mcp.json`) is reached over Streamable HTTP through Aether's MCP gateway, so its URL and headers never enter the workspace. `${VAR}` and `${VAR:-default}` in `.mcp.json`, and `bearer_token_env_var` and `env_http_headers` in `config.toml`, resolve from the project's environment variables. A server that sets no `Authorization` header and has the URL of a catalog server the project enables uses that server's connection.

## Local (stdio) servers

A server with a `command` runs inside the task's workspace as a catalog local server does: Aether's workspace service starts the process and both harnesses reach it as one more MCP server. The harness never starts it.

### Supported commands

The command is a package runner with a package pinned to one release:

| Runs | `.mcp.json` | `.codex/config.toml` |
| - | - | - |
| An npm package | `"command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything@2026.8.31"]` | `command = "npx"`<br />`args = ["-y", "@modelcontextprotocol/server-everything@2026.8.31"]` |
| A PyPI package | `"command": "uvx", "args": ["mcp-server-fetch@2025.4.7"]` | `command = "uvx"`<br />`args = ["mcp-server-fetch@2025.4.7"]` |
| `python -m` from a PyPI package | `"command": "uvx", "args": ["--from", "py-docs-mcp==0.3.1", "python", "-m", "py_docs_mcp"]` | `command = "uvx"`<br />`args = ["--from", "py-docs-mcp==0.3.1", "python", "-m", "py_docs_mcp"]` |

`-y` (or `--yes`) is optional for `npx`; `uvx --from <package>==<version> <package>` is the same as `uvx <package>@<version>`. Arguments after the package go to the server unchanged.

* **The version is pinned.** An npm package takes a full semver version (`1.2.3`), a PyPI package an exact PEP 440 version. A package with no version, `latest`, a tag, a range or a wildcard is refused: the task would run whichever release is newest when it starts.
* **Other commands are refused.** `node server.js`, `python -m …` without `uvx`, `docker`, `uv run`, a binary or script in the repository, a path to `npx`, and package-runner options other than the ones above.

### Environment

The process gets `PATH`, `HOME`, the npm and uv package caches, and the variables its file declares — nothing of the agent's environment, and none of the project's other environment variables.

* **`.mcp.json`:** each `env` value expands `${VAR}` and `${VAR:-default}` from the project's environment variables.
* **`.codex/config.toml`:** `env` values are used as written (Codex expands nothing in them). Each name in `env_vars` is set from the project's environment variable of that name, as Codex forwards it from the environment it runs in. A name may be in `env` or `env_vars`, not both.

A variable the project does not set, with no default in the file, refuses the task at creation. A value that references a variable holds the project's environment variables, so it is a secret: it is masked wherever the server's output is logged. Literal values are not.

`command` and `args` never reference a variable — a project's environment variables reach a local server only through its `env`. `cwd` in `config.toml` is refused: a local server runs in the task's repository checkout.

<Warning>
  The process runs as the agent's user in the workspace. It cannot inherit the agent's environment, but it can read whatever the agent can read.
</Warning>

### Required and disabled servers

A `.mcp.json` server is required; one with `"disabled": true` is skipped. A `config.toml` server is required unless it sets `required = false`; one with `enabled = false` is skipped. A required server that does not start within 90 seconds fails the turn, naming it; an optional one is left out and the turn runs without it.

## When both files, or the organization, declare a server

* **The same name in both files** is one server when both declare the same URL and headers, or the same command, arguments and environment (`env_vars = ["TOKEN"]` in `config.toml` is the same as `"TOKEN": "${TOKEN}"` in `.mcp.json`). Two different declarations under one name are refused. Names become server keys as the harnesses write them into tool names, lowercased.
* **A name the organization's catalog already uses** is the organization's server: the task uses it instead, and its server list says why the repository's copy was not given.

## Errors

Task creation refuses with a message naming the file and the server; the composer's readiness check refuses before you send, with the code `mcp_repo_config_invalid` and the same message:

| Message contains | Fix |
| - | - |
| `its package "…" pins no version` / `must be a full semver version` / `must be an exact PEP 440 version` | Pin one release, as in `<package>@1.2.3` or `<package>==1.2.3` |
| `its command "…" is not one Aether runs` | Run the server through `npx` or `uvx` with its package |
| `${VAR} is not set in the project's environment variables` | Set the variable in the project, or give a default in `.mcp.json` |
| `references a variable` | Move the value into `env` |

A variable removed from the project after a task was created makes its local server unavailable at the task's next turn: a required one fails the turn with `mcp_repo_config_invalid` and the same message.
