---
title: Environment helpers reference
description: >-
  An environment spec sets what the agent starts with, built from the git, file,
  directory, skill, mcp, plugin, and secret helpers.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reeval.ai/llms.txt
> Use this file to discover all available pages before exploring further.

## `EnvironmentSpec`

`defineEnvironment()` and `extend()` take a spec or a callback with `git`, `file`, `directory`, `skill`, `mcp`, `plugin`, and `secret`. Relative host paths passed to those helpers resolve against the calling file. The return is synchronous, and a `Promise` stops the run with exit code 2.

A child merges `env`, `files`, and `mcp` by name, `skills` by skill name, and `plugins` by plugin name, and its value wins. `setup` appends. `sandbox`, `workspace`, `egress`, and `rules` replace when set. An omitted `sandbox` keeps the parent's, and a set `sandbox` keeps none of the parent's install steps.

For more information about defining an environment, see [Environments](/configuration/environments).

```ts title="evals/environments.ts"
import { defineEnvironment } from '{{ pkg }}'

export const chatbot = defineEnvironment('chatbot', ({
  git, file, secret, skill, mcp,
}) => ({
  workspace: git('github:vercel/ai-chatbot', { ref: 'v3.1.0' }),
  files: {
    'src/agent.ts': file('./fixtures/agent.ts'),
  },
  env: {
    SDK_API_KEY: secret.fromEnv('SDK_API_KEY'),
  },
  skills: [skill.local('./skills/review')],
  mcp: {
    docs: mcp.stdio('npx', [
      '-y',
      '@modelcontextprotocol/server-filesystem',
    ]),
  },
  setup: ['pnpm install --frozen-lockfile'],
}))
```

<Property name="sandbox" type="SandboxDescriptor" optional>
  Where the attempt runs. Omitted on a root environment, the cascaded sandbox or the default sandbox is used, and putting `image`, `from`, `dockerfile`, or `install` beside `sandbox` in the environment spec stops the run with exit code 2.
</Property>

<Property name="workspace" type="GitWorkspace" optional>
  The tree the attempt starts from, before `files`, `rules`, and `setup`.
</Property>

<Property name="env" type="Record<string, string | Secret>" optional>
  A sandbox string or secret. The name is letters, digits, and `_`, starting with a letter or `_`, and a literal whose name contains `KEY`, `TOKEN`, `SECRET`, `PASSWORD`, or `CREDENTIAL` stops the run with exit code 2.
</Property>

<Property name="egress" type="'proxy' | 'allow-all' | 'deny-all' | { allow: string[] }" optional>
  Where the sandbox may connect. The default is the inherited policy: `'proxy'` permits any host over HTTP and HTTPS through the egress gateway, `'allow-all'` also permits raw TCP and UDP, `'deny-all'` the automatic hosts, and `{ allow }` extra hosts, with `*.example.com` for subdomains.
</Property>

<Property name="files" type="Record<string, string | HostFile>" optional>
  Workspace paths, or `~/` paths in the home directory. A string is the contents, `file()` is read at attempt start, and `..` or a backslash stops the run with exit code 2.
</Property>

<Property name="rules" type="string | HostFile" optional>
  Instructions for the harness rules file, written after `files`. A string is copied, `file()` is read at attempt start, and a missing file ends the attempt as an infrastructure error. For more information about the file each harness reads, see [Rules and skills](/reference/options#rules-and-skills).
</Property>

<Property name="setup" type="Array<string | { run: string; timeout?: Duration; cwd?: string }>" optional>
  A string or an object after `files`, before the agent. `timeout` defaults to `5m`, `cwd` defaults to the workspace, and a non-zero exit or a timeout is an infrastructure error.
</Property>

<Property name="skills" type="SkillSource[]" optional>
  Skills for the agent, from `skill`.
</Property>

<Property name="mcp" type="Record<string, McpServer>" optional>
  MCP servers by name, from `mcp`.
</Property>

<Property name="plugins" type="PluginSource[]" optional>
  Claude Code layout plugins from `plugin`. A child replaces a parent plugin of the same name.
</Property>

## `git()`

`git()` supplies `workspace`, fetched on the host with credentials kept there. A fetch that cannot complete fails the attempts.

<Property name="repo" type="string | URL" required>
  A URL, `github:owner/repo`, a GitHub `/tree/<ref>/<path>` URL, or a local path. A `/tree/` URL sets `ref` and `path`, and a password or token in the URL stops the run with exit code 2.
</Property>

<Property name="ref" type="string" optional>
  A branch, tag, or commit SHA, defaulting to the remote `HEAD`. A ref other than a commit SHA or a version tag such as `v1.2.0` warns that results are not comparable over time.
</Property>

<Property name="path" type="string" optional>
  The directory that becomes the workspace. It is relative, with forward slashes and no `.` or `..`, and any other path stops the run with exit code 2.
</Property>

<Property name="init" type="boolean" optional>
  `true` writes one commit and no remote. The default omits the `.git` directory.
</Property>

<Property name="token" type="string" optional>
  The host variable for an `https://` fetch. The token text, or `token` on SSH or a local path, stops the run with exit code 2.
</Property>

## `file()`

`file()` is one host file for `files` or `rules`.

<Property name="path" type="string | URL" required>
  A path relative to the calling file, or absolute, read at attempt start. A missing file ends the attempt as an infrastructure error, and an owner execute bit stays executable.
</Property>

## `directory()`

`directory()` walks a host directory at the call, reads those files at attempt start, and skips empty directories. A missing path, a non-directory, no match, or a broken or cyclic symlink stops the run with exit code 2.

<Property name="path" type="string | URL" required>
  The host directory, relative to the calling file or absolute.
</Property>

<Property name="into" type="string" optional>
  A directory relative to the workspace, or a home-directory path starting with `~/`. Forward slashes only, and no `..`.
</Property>

<Property name="glob" type="string[]" optional>
  Ordered gitignore-style patterns. The last match wins; `!` excludes; a slash other than a trailing `/` anchors the pattern; a pattern with no other slash matches a name at any depth; a trailing `/` matches a directory; with no including pattern, every file starts included; and the default ignores win.
</Property>

<Property name="defaultIgnores" type="boolean" optional>
  Defaults to `true` and skips paths such as the `node_modules/` directory and the top-level `/build/` and `/target/` directories. `false` walks those paths; the `vendor/` directory is copied either way; and the `.git` directory stays out. The list is `DEFAULT_DIRECTORY_IGNORES` from `{{ pkg }}`.
</Property>

## `skill.local()`

A host directory with a `SKILL.md` file, or one whose immediate child directories have one. `name` defaults to the directory name and must be kebab-case; a missing `description` or a non-kebab-case name ends the attempt as an infrastructure error.

<Property name="path" type="string | URL" required>
  The host directory, relative to the calling file or absolute. A missing path ends the attempt as an infrastructure error.
</Property>

<Property name="only" type="string[]" optional>
  Names to keep. The default is every skill, and a missing name ends the attempt as an infrastructure error whose message lists the skills found.
</Property>

<Property name="exclude" type="string[]" optional>
  Paths left out of each skill. Setting `exclude` replaces the default path patterns: `/evals/`, `.history/`, and `*.eval.ts`; `*` matches within one name; a leading or inner `/` anchors to the skill root; a trailing `/` matches directories; any other pattern matches a name at any depth; and the `.git` directory is always left out. Import `DEFAULT_SKILL_EXCLUDE` from `{{ pkg }}` and spread it to add patterns.
</Property>

<Property name="placement" type="'harness' | 'workspace'" optional>
  `harness`, the default, places the skill through the harness. `workspace` places it in the repository's skill directory.
</Property>

## `skill.git()`

A Git repository of skills. `only`, `exclude`, `placement`, and the rules for the `SKILL.md` file match `skill.local()`.

<Property name="repo" type="string" required>
  An `https://` URL, an SSH URL, `github:owner/repo`, or a GitHub `/tree/<ref>/<path>` URL. A `/tree/` URL sets `ref` and `path` when they are omitted.
</Property>

<Property name="ref" type="string" optional>
  A branch, tag, or commit SHA. The default and the warning match `ref` on `git()`.
</Property>

<Property name="path" type="string" optional>
  One skill, or a directory of them. With no `path`, the `skills/` directory is read when it exists and the root has no `SKILL.md` file, and the root is read otherwise.
</Property>

## `skill.npm()`

An npm package of skills. `only`, `exclude`, and `placement` match `skill.local()`.

<Property name="spec" type="string" required>
  A package spec pinned to a version. A relative path or a `file:` URL resolves against the calling file, and any other spec is passed to `npm pack`.
</Property>

<Property name="path" type="string" optional>
  The directory to read. The default is the `skills` directory.
</Property>

## `skill.registry()`

One skills.sh skill. reeval clones the repository at `https://github.com/<owner>/<repo>.git` and reads the `skills/` directory when present, otherwise the repository root. `placement` matches `skill.local()`.

<Property name="id" type="string" required>
  `owner/repo@skill`, the name after `@`. Any other id stops the run with exit code 2.
</Property>

<Property name="version" type="string" optional>
  A Git ref. Omitting it warns that results are not comparable over time.
</Property>

## `skill.inline()`

A skill written in the spec. A missing `description`, or a name other than kebab-case, ends the attempt as an infrastructure error, and `placement` matches `skill.local()`.

<Property name="name" type="string" required>
  The skill name.
</Property>

<Property name="description" type="string" required>
  The description.
</Property>

<Property name="content" type="string" required>
  The body of the `SKILL.md` file.
</Property>

<Property name="files" type="Record<string, string>" optional>
  Extra files by relative path. The default is none.
</Property>

## `mcp.stdio()`

A sandbox command on stdin and stdout. The `mcp` key is the server name.

<Property name="command" type="string" required>
  The command. An empty command stops the run with exit code 2.
</Property>

<Property name="args" type="string[]" optional>
  Arguments before the options. The default is none.
</Property>

<Property name="env" type="Record<string, string | Secret>" optional>
  Process variables. A secret with `hosts` is brokered, and one without `hosts` is the process value.
</Property>

## `mcp.http()`

An MCP server over streamable HTTP. The `mcp` key is the server name.

<Property name="url" type="string" required>
  The server URL. Any other value stops the run with exit code 2.
</Property>

<Property name="headers" type="Record<string, string | Secret | SecretTemplate>" optional>
  Request headers. A secret with `hosts` is brokered to them, one without is brokered to an `https` host and sent raw on `http`, and `secret.template()` fills a header from host variables.
</Property>

## `mcp.sse()`

An MCP server over SSE. `url`, `headers`, and the `mcp` key match `mcp.http()`.

## `plugin.local()`

A host directory with `.claude-plugin/plugin.json`.

<Property name="path" type="string | URL" required>
  The plugin directory, relative to the calling file or absolute.
</Property>

<Property name="only" type="PluginPart[]" optional>
  Parts to place: `skills`, `agents`, `commands`, `hooks`, `mcp`, `rules`. Default: every part the plugin has.
</Property>

<Property name="build" type="string | string[] | { run: string | string[]; cwd?: string }" optional>
  Commands baked into a Docker image layer from the plugin directory (or `cwd` relative to the source root).
</Property>

## `plugin.git()`

Same `only` and `build` as `plugin.local()`.

<Property name="repo" type="string" required>
  An `https://` URL, SSH URL, `github:owner/repo`, or GitHub `/tree/<ref>/<path>` URL.
</Property>

<Property name="ref" type="string" optional>
  A branch, tag, or commit SHA, defaulting to the remote `HEAD`.
</Property>

<Property name="path" type="string" optional>
  Plugin directory in the repository. Default: repository root.
</Property>

## `plugin.npm()`

Same `only` and `build` as `plugin.local()`.

<Property name="spec" type="string" required>
  A pinned package spec. Relative and `file:` paths resolve against the calling file.
</Property>

<Property name="path" type="string" optional>
  Plugin directory in the package. Default: package root.
</Property>

## `plugin.marketplace()`

`'name@source'` in a Claude Code marketplace. Same `only` and `build` as `plugin.local()`.

<Property name="entry" type="string" required>
  `name@` followed by a git URL, `github:owner/repo`, or `npm:<spec>`. Any other entry stops the run with exit code 2.
</Property>

<Property name="ref" type="string" optional>
  Commit of a git marketplace. Pin an npm marketplace in its spec.
</Property>

## `secret()`

`secret()` reads the host variable named by its `env` key, so `env: { API_KEY: secret() }` reads `API_KEY`. Options match `secret.fromEnv()`.

## `secret.fromEnv()`

A named host variable for `env` or an MCP header. The record keeps the name and redacts the value.

<Property name="name" type="string" required>
  Letters, digits, and `_`, starting with a letter or `_`. Any other name stops the run with exit code 2.
</Property>

<Property name="optional" type="boolean" optional>
  `true` skips the entry when the host variable is unset, and the record says so. The default ends the attempt as an infrastructure error.
</Property>

<Property name="to" type="'agent' | 'checks'" optional>
  `agent`, the default, sets the sandbox variable. `checks` limits it to `setup` and `c.exec()`.
</Property>

<Property name="hosts" type="readonly string[]" optional>
  Hosts the value is brokered to, with `*.example.com` for subdomains and `secret.hosts` for presets; a brokering sandbox gets a placeholder that its egress gateway fills on HTTPS. Without `hosts`, the value passes through except in an HTTPS MCP header, which brokers it to the server's host.
</Property>

<Property name="in" type="Array<'header' | 'query' | 'body'>" optional>
  Where the value is substituted, defaulting to `['header']`. `in` requires `hosts`, and whitespace or a line break in a header value ends the attempt as an infrastructure error.
</Property>

## `secret.template()`

One MCP header built from host variables.

<Property name="template" type="string" required>
  A string of `${NAME}` host variables. An unset name ends the attempt as an infrastructure error, an `https` URL brokers the result to that host, and any other URL uses the host value.
</Property>

## `secret.hosts`

Host lists for `hosts` on `secret()` and `secret.fromEnv()`.

<Property name="gemini" type="readonly string[]" required>
  `generativelanguage.googleapis.com`.
</Property>

<Property name="openai" type="readonly string[]" required>
  `api.openai.com`.
</Property>

<Property name="anthropic" type="readonly string[]" required>
  `api.anthropic.com`.
</Property>

<Property name="github" type="readonly string[]" required>
  `api.github.com` and `uploads.github.com`.
</Property>

## `extend()`

`extend()` is `extendEnvironment()` on a `defineEnvironment()` parent. `compare` runs the baseline and each variant as its own environment, so shared fields are an `extend()` chain. Merging is described under Environment fields.

<Property name="name" type="string" required>
  The cell-key name. It starts with a letter or a digit, then letters, digits, `.`, `_`, `@`, `+`, or `-`, and any other name stops the run with exit code 2.
</Property>

<Property name="spec" type="EnvironmentSpec | ((helpers) => EnvironmentSpec)" required>
  The child fields, or a synchronous callback. A returned `Promise` stops the run with exit code 2.
</Property>
