---
title: goose()
description: >-
  The Goose harness, its options and id, its Gemini API key, the tokens it can't
  report, and how its MCP servers and skills are set up.
---
> ## 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.

`goose()` runs [Goose](https://goose-docs.ai) in the Docker sandbox over its ACP server (`goose acp`), on its `google` provider with a Gemini API key. `model` is required.

```ts title="reeval.config.ts"
import { defineConfig } from '{{ pkg }}'
import { goose } from '{{ pkg }}/harness'

export default defineConfig({
  harness: goose({ model: 'gemini-3.8-flash' }),
})
```

## Options

<Property name="model" type="string">
  A Gemini model id, such as `gemini-3.8-flash`, without a provider. Required: Goose won't start a session without one.
</Property>

<Property name="effort" type="string" optional>
  Reasoning effort as `GOOSE_THINKING_EFFORT`: `off`, `low`, `medium`, `high` or `max`. The CLI's name is thinking; the factory field is `effort`. It isn't the measured [effort](/concepts/lift#effort-next-to-lift).
</Property>

<Property name="id" type="string" optional>
  The harness's id in cell keys, filters and the record. Default `goose:<model>`, or `goose:<model>[effort=<value>]` when `effort` is set.
</Property>

<Property name="gateway" type="'direct' | Gateway" optional>
  The gateway this harness's model calls go through, by name or helper, such as `direct()`. It skips the config's `gateway` fallback: the plan refuses a gateway that isn't listed or can't serve this harness. See [Gateways](/reference/gateway#pinning-a-harness).
</Property>

<Property name="maxConcurrent" type="number" optional>
  The most Goose attempts at once across the run, for example for your key's rate limits.
</Property>

<Property name="startTimeout" type="Duration" optional>
  How long its session may take to become ready (bootstrap and session start) before the attempt is an infra error, such as `'3m'`. Default `'90s'`. A start that times out is aborted and started once more; a second timeout fails the attempt with `goose: session did not become ready within 90s (tried twice)`, and the record's `startRetries` notes a retried start.
</Property>

## At a glance

| | |
|---|---|
| Import | `'{{ pkg }}/harness'` |
| Credential | `GOOGLE_GENERATIVE_AI_API_KEY` (a Gemini API key; Goose sees it as `GOOGLE_API_KEY`) |
| Adapter | `@reeval/harness-goose` 0.1.6 (over `@ai-sdk/harness-acp`), bundled with reeval |
| CLI | Goose 1.56.0, from its GitHub release, checked against its sha256 |
| Sandbox | `docker()` only |
| Image | `reeval-sandbox:goose-0.1.6` |
| Rules file | `AGENTS.md` |
| Skills (default placement) | `.config/goose/skills` in Goose's own home (below) |
| Skills (`placement: 'workspace'`) | `.goose/skills` in the workspace |
| Egress hosts added | `generativelanguage.googleapis.com` |
| Tokens | Not reported (below) |
| Plugins | `skills` and `mcp`, beside the eval's own (below) |

Build its image once with `reeval image build` in a project whose config uses `goose()`.

## The key

reeval passes `GOOGLE_GENERATIVE_AI_API_KEY` to the adapter explicitly, which gives it to Goose as `GOOGLE_API_KEY`; the real key never enters the container. The gateway swaps it in on one kind of request, the only one Goose 1.56.0 sends it on:

| Host | Endpoint |
|---|---|
| `generativelanguage.googleapis.com` | `POST /v1beta/models/<model>:streamGenerateContent` |
| `generativelanguage.googleapis.com` | `GET /v1beta/models` (the model list, at the start of every turn) |

The adapter refuses to run in a sandbox that can't broker the key.

On a [`gateway` list](/reference/gateway#vercel-ai-gateway) that resolves the harness to `vercel()`, the key is `AI_GATEWAY_API_KEY` (else `VERCEL_OIDC_TOKEN`), and Goose runs its own `vercel_ai_gateway` provider with the model as `google/<model>`. The gateway brokers it as a Bearer token on `ai-gateway.vercel.sh` only, on `POST /v1/chat/completions`, `POST /v1/responses` and `GET /v1/models`, and reads the served model from each response. `effort` isn't taken there yet, so a cell with it falls through to the next gateway.

On [`openrouter()`](/reference/gateway#openrouter), the key is `OPENROUTER_API_KEY`, and Goose runs its own `openrouter` provider with the model as `google/<model>`. The gateway brokers it as a Bearer token on `openrouter.ai` only, on `POST /api/v1/chat/completions` and `GET /api/v1/models`, and reads the served model, the upstream provider and the cost from each response. `effort` isn't taken there yet.

## Tokens

| A 4-call turn | Input tokens |
|---|---|
| Used | 16,253 |
| Goose's ACP server reported | 4,179, the last model call's |
| reeval records | Nothing: `usage` is `null` |

Goose's ACP server reports its last model call's tokens as the turn's, so reeval records nothing rather than an undercount. The served model is still recorded, from the gateway's requests.

## Settings

| Variable | Why |
|---|---|
| `GOOSE_PROVIDER=google`, `GOOSE_MODEL=<model>` | The Gemini API key's provider, and the model the session starts on. |
| `GOOSE_MODE=auto` | Tool calls run without asking. |
| `GOOSE_TELEMETRY_OFF=1` | No telemetry. |
| `GOOSE_DISABLE_KEYRING=1` | No system keyring in a container. |
| `GOOSE_DISABLE_SESSION_NAMING=1` | No extra model call to name the session. |

Besides the Gemini API, each session fetches Goose's model catalogue from `models.dev`, which no setting turns off. Nothing needs it: a restricted egress policy refuses it and records it under the record's `egress.expectedBlocked`.

Goose runs with a private home, `~/.ai-sdk-harness/.harness-bootstrap/goose/implementation/home`, so its config and home skills live there, not in the sandbox user's `~/.config/goose`. The attempt's `context` lists them under that path. In the workspace it reads `.goose/skills`, and also `.agents/skills` and `.claude/skills`; nothing turns those off.

MCP servers go in Goose's `config.yaml` as enabled extensions (`stdio`, or `streamable_http` for `http` servers; Goose has no SSE). Servers passed over ACP start disabled in 1.54.0 until the model enables them itself. Their tools are named `mcp__<server>__<tool>` in the transcript.

## Plugins

```ts title="evals/review.eval.ts"
const review = defineEnvironment('review', ({ plugin }) => ({
  plugins: [plugin.local('../plugins/review')],
}))
```

Goose takes a plugin's skills and MCP servers as if the eval declared them. reeval copies the plugin to `~/.reeval/plugins/<name>/`, places its skills in Goose's home skills directory beside the eval's own, and adds its servers to `config.yaml` as `<plugin>:<server>`, through reeval's MCP timer. Goose renames such an extension (`review:docs` becomes `review_docs`); the transcript still names a call by reeval's name, `mcp__review:docs__<tool>`, so `toHaveCalledMcp('review:docs', …)` matches it.

The names share one space, so the plan refuses a plugin skill or server that another already has, including two servers Goose would key the same. It also refuses hooks and subagents, which Goose doesn't load:

```text
reeval: evals/p.eval.ts: evaluate("reviews") on goose:gemini-3.8-flash: Goose can't load the review plugin's hooks (hooks/hooks.json): use only: ['skills'], or leave the plugin out of this cell
```

reeval doesn't use `goose plugin install`: it needs the plugin in a git repository, and it takes only the skills.

## Errors

| Situation | Message |
|---|---|
| No model, or one with a provider | `goose() needs a Gemini model id, without a provider, since it runs on a Gemini API key, e.g. goose({ model: 'gemini-3.8-flash' }) (got …)` |
| No key when the attempt starts | `GOOGLE_GENERATIVE_AI_API_KEY isn't set, so the Goose harness can't authenticate. Set it in the environment or .env.local.` |
| A non-Docker sandbox | `The Goose harness needs the Docker sandbox: set sandbox: docker() …` |
| Goose doesn't start | `Goose harness failed to start: …` |
| Goose reports a failed model call as its message | The turn fails with `Goose's model call failed: <error>` |
| An `mcp.sse` server (at plan time, exit 2) | `evaluate("<eval>") on goose:<model>: Goose has no SSE MCP transport, so it can't use the <name> server (mcp.sse): use mcp.http or mcp.stdio` |
| Two MCP servers Goose keys the same, such as a plugin's `review:docs` and `mcp.review_docs` (at plan time, exit 2) | `evaluate("<eval>") on goose:<model>: Goose keys the review_docs and review:docs MCP servers both as review_docs, and keeps only one: rename one` |

## Related

- [Harnesses](/guide/harnesses)
- [Secrets and egress](/guide/secrets-and-egress)
- [Transcripts](/reference/transcript)
