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

# integrations.yml

> Model groups, input channels, optional MCP servers, and Langfuse tracing.

`integrations.yml` is required at the project root. It declares the LLM that
drives the agent, the channels customers reach it through, optional MCP tool
servers, and any named model groups.

The file is copied into the model archive at `rasa train`, and each section is
read from the place that suits what it configures:

| Section | Read from | Effect |
| - | - | - |
| `llm` | The live project directory | Select the model group that drives the agent and optionally set its prompt budget. Restart after changing it. |
| `channels` | The live project directory | Change channel settings and restart. |
| `mcp_servers` | The live project directory | Change MCP URLs or auth and restart so connections reopen. |
| `model_groups` used by `llm` | The live project directory | Restart after changing the agent's LLM group. |
| `model_groups` used by embeddings | The packaged snapshot | Retrain after changing a reference embedder. |
| `tracing` | The live project directory | Optional Langfuse observability; restart to pick up changes. |

The agent's LLM and reference embedder use the same model-group syntax, but have
different lifecycles. The LLM group is resolved from the live project at
startup. Reference embeddings are resolved from the packaged snapshot because
query vectors must match the vectors built at training time.

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: openai
        model: gpt-5.1
        api_base: https://api.openai.com/v1
        api_key_env: OPENAI_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }

# Optional — Langfuse tracing (often left commented until keys are set)
# tracing:
#   type: langfuse
#   public_key: ${LANGFUSE_PUBLIC_KEY}
#   private_key: ${LANGFUSE_SECRET_KEY}
#   host: https://cloud.langfuse.com
```

That is a complete, working file. Everything else is optional.

## `llm`

**Required.** Select the model group that drives the agent.

| Key | Required | Description |
| - | - | - |
| `model_group` | **Yes** | ID of a group declared under `model_groups`. |
| `max_prompt_tokens` | No | Positive token budget for the assembled prompt. Defaults to `8000`. |

Provider, model, credentials, and provider-specific options belong on the
selected group. Inline `provider`, `model`, or `deployment` keys under `llm`
fail validation.

### Provider examples

`provider: openai`, `azure`, and `self-hosted` have dedicated clients. Any other
value is passed through to LiteLLM, so a provider LiteLLM supports works by
naming it and its model.

**OpenAI**

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: openai
        model: gpt-5.1
        api_key_env: OPENAI_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

**Anthropic**

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: anthropic
        model: claude-sonnet-4-5-20250929
        api_key_env: ANTHROPIC_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

**Google Gemini**

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: gemini
        model: gemini-2.5-pro
        api_key_env: GEMINI_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

**Azure OpenAI.** Name the `deployment` rather than the model, and give the
endpoint and API version:

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: azure
        deployment: my-gpt-deployment
        api_base: https://my-resource.openai.azure.com/
        api_version: "2024-10-21"
        api_key_env: AZURE_OPENAI_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

**Self-hosted, OpenAI-compatible.** For vLLM, Ollama, TGI, or anything else
serving the OpenAI API shape. `provider`, `model`, and `api_base` are all
required:

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: self-hosted
        model: mistralai/Mistral-7B-Instruct-v0.3
        api_base: http://localhost:8000/v1
        api_key_env: LOCAL_LLM_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

`model` is the name your server advertises, and `api_base` points at the
OpenAI-compatible route, usually ending in `/v1`. Drop `api_key_env` if the
server takes no key.

### Secrets

`api_key_env` names an environment variable and works on every model in a
group. Mantle resolves it into the API key before the config reaches the client.

`${VAR}` expansion happens in the `channels:` block, which is read with
environment variables expanded. `llm:` and `model_groups:` are read without
expansion, so `api_key_env` is the mechanism there. Never commit a literal key.

## `channels`

Declares the channels customers reach the agent through.

```yaml theme={null}
channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

Each key is a channel name from the built-in channel registry. Values may be:

| Form | Meaning |
| - | - |
| `{ enabled: true }` | Enable with defaults |
| `{ enabled: true, <key>: <value>, … }` | Enable with those keyword arguments |
| `{ enabled: false }` | Disabled: kept in the file but not registered |
| *(empty / null)* | Enable with defaults |

The `enabled` flag is stripped before the remaining keys are handed to the
channel. A non-boolean `enabled`, or an entry that is neither a mapping nor
empty, raises at load.

> [!NOTE]
>
> `rest` and `inspector` are the practical minimum: `rest` is what the evaluation
> runner talks to, and `inspector` is required for `rasa inspect` and for Studio's
> Agent Preview. A project with no `channels:` block registers no channels at all.

Studio's model service also accepts a top-level `inspector:` key in a CALM v1
`credentials.yml` when serving a legacy assistant, so a project ported from
CALM v1 keeps working during migration. In a Mantle project, put `inspector`
under `channels:` here. CALM v1 `credentials.yml` is not read for a Mantle
assistant.

`${VAR}` references inside `channels:` are expanded when the file is read, so
channel credentials can come from the environment.

### Voice channels

A voice channel takes `asr:` and `tts:` sub-mappings, and that is where speech
recognition and synthesis are configured. Each takes a `name:` selecting the
engine, plus that engine's own settings:

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: openai
        model: gpt-5.1
        api_key_env: OPENAI_API_KEY

channels:
  browser_audio:
    enabled: true
    server_url: localhost:5005
    asr:
      name: deepgram
      language_map:
        en-US:
          language: en
          model: flux-general-en
    tts:
      name: deepgram
      language_map:
        en-US:
          model: aura-2-asteria-en
```

| Engine | Built-in names |
| - | - |
| `asr.name` | `deepgram`, `azure` |
| `tts.name` | `deepgram`, `azure`, `cartesia`, `rime` |

Every key besides `name` goes to that engine, so the available settings are the
engine's own.

`language_map` keys the model and language off the conversation language, which
is how both engines take their settings. `name` also accepts a Python module
path, which loads a custom engine implementing `from_config_dict`.

#### Deepgram Flux for speech recognition

Flux is the model family to reach for on a live voice agent. It does turn
detection inside the ASR rather than inferring it from silence, so the agent
takes its turn on the signal the recogniser already has.

Naming a `flux-` model selects it. The engine reads the model name out of
`language_map` and switches to the Flux API on its own, so there is nothing else
to declare:

```yaml integrations.yml theme={null}
llm:
  model_group: orchestrator

model_groups:
  - id: orchestrator
    models:
      - provider: openai
        model: gpt-5.1
        api_key_env: OPENAI_API_KEY

channels:
  browser_audio:
    enabled: true
    server_url: localhost:5005
    asr:
      name: deepgram
      language_map:
        en-US:
          language: en
          model: flux-general-en
      eot_threshold: 0.8
      eot_timeout_ms: 5000
    tts:
      name: deepgram
      language_map:
        en-US:
          model: aura-2-asteria-en
```

The two turn-detection knobs are optional:

| Key | Range | Default | Effect |
| - | - | - | - |
| `eot_threshold` | 0.5 to 0.9 | 0.7 | Confidence needed to call end of turn. Lower responds faster and interrupts more. |
| `eot_timeout_ms` | 500 to 10000 | 5000 | Ceiling on silence before the turn is ended regardless. |

A Flux config takes these two keys and no others. Anything unrecognised fails
the load rather than being ignored, so a typo surfaces at startup.

The same `asr:` / `tts:` shape applies to every voice channel, including
`jambonz`, `audiocodes`, `twilio_media_streams`, and `genesys`. What differs
between them is the telephony connection: `server_url`, and any credentials that
provider needs.

## `model_groups`

**Required.** Named model configurations used by the agent's LLM and, when
configured, the references embedder.

```yaml theme={null}
model_groups:
  - id: orchestrator
    models:
      - provider: openai
        model: gpt-5.1
        api_key_env: OPENAI_API_KEY
  - id: reference_embeddings
    models:
      - provider: openai
        model: text-embedding-3-large
```

The required `llm.model_group` selects the orchestrator group. Point
`agent.yml` at an embeddings group by id:

```yaml agent.yml theme={null}
agent:
  persona: You are a helpful assistant.

references:
  embeddings: reference_embeddings
```

Every model-group reference must name a group declared here. Naming an
undeclared group fails validation. Changes to the orchestrator group require a
restart; changes to a reference embeddings group require retraining.

A group with one model is the usual orchestrator setup. Without a `router`
block, only `models[0]` is used — extra models are ignored, there is no
fallback, and `rasa data validate` warns. Set `router` when the group should
select among multiple models.

## `mcp_servers`

Optional. A list of named MCP servers a skill can import tools from with
`mcp/<server-name>:<tool-name>` in
[`import_tools`](https://rasa.com/docs/mantle/reference/skill-md#import_tools).

Each entry needs a unique `name` and a `url`. Auth, if any, is one of `token`,
`api_key`, `oauth`, or `module` at the top level of that entry. Secrets must
use `${ENV_VAR}` form — literal secret strings are rejected.

```yaml integrations.yml theme={null}
mcp_servers:
  - name: banking
    url: http://localhost:8000/mcp/
    token: ${MCP_BANKING_TOKEN}
```

| Key | Required | Description |
| - | - | - |
| `name` | **Yes** | Server id used in `mcp/<name>:<tool>`. Letters, digits, `_`, `.`, and `-`. Must be unique. Tool names are stricter — see [`import_tools`](https://rasa.com/docs/mantle/reference/skill-md#import_tools). |
| `url` | **Yes** | MCP server endpoint. |
| `type` | No | `http` or `https`. When omitted, inferred only from an `http://` or `https://` URL. Other schemes (or a missing scheme) are rejected. |
| `token` | No | Bearer token (`${ENV_VAR}`). |
| `api_key` | No | API key (`${ENV_VAR}`). Optional `header_name` / `header_format` override how it is sent. |
| `oauth` | No | Client-credentials OAuth mapping (`token_url`, `client_id`, `client_secret`, optional `scope` / `audience` / `timeout`). |
| `module` | No | Dotted path to a custom auth strategy class. |
| `tool_timeout` | No | Per-call timeout in seconds for tools on this server. When omitted, falls back to [`tool_timeout` in `agent.yml`](https://rasa.com/docs/mantle/reference/agent-yml#tool_timeout), then to `10`. |
| `meta_map` | No | Optional `_meta` payload built for each tool call. See [`meta_map`](#meta_map). |

Use at most one of `token`, `api_key`, `oauth`, or `module` per server. Omit all
four when the server needs no auth. Auth examples are in
[Auth examples](#auth-examples).

### Auth examples

> **Bearer token**
>
> ```yaml integrations.yml theme={null}
> mcp_servers:
>   - name: banking
>     url: http://localhost:8000/mcp/
>     token: ${MCP_BANKING_TOKEN}
> ```

> **API key**
>
> ```yaml integrations.yml theme={null}
> mcp_servers:
>   - name: catalog
>     url: https://catalog.example.com/mcp/
>     api_key: ${MCP_CATALOG_API_KEY}
>     # header_name: X-API-Key
>     # header_format: "{key}"
> ```

> **OAuth2 client credentials**
>
> ```yaml integrations.yml theme={null}
> mcp_servers:
>   - name: payments
>     url: https://payments.example.com/mcp/
>     oauth:
>       token_url: https://auth.example.com/oauth/token
>       client_id: mantle-agent
>       client_secret: ${MCP_PAYMENTS_CLIENT_SECRET}
>       # scope: tools.read
>       # audience: https://payments.example.com
> ```

> **Custom module**
>
> ```yaml integrations.yml theme={null}
> mcp_servers:
>   - name: internal
>     url: https://internal.example.com/mcp/
>     module: my_package.auth.CustomMcpAuth
> ```

### `meta_map`

Optional. Builds an MCP `_meta` object on each tool call without exposing those
values to the LLM:

```yaml integrations.yml theme={null}
mcp_servers:
  - name: banking
    url: http://localhost:8000/mcp/
    token: ${MCP_BANKING_TOKEN}
    tool_timeout: 30
    meta_map:
      static:
        api_version: "v2"
        source: "rasa_agent"
```

`static` is a mapping of fixed string key/value pairs always sent in `_meta`.

### How skills use a server

Allowlist only the tools you need in the skill's `import_tools`:

```yaml skills/check_balance/skill.md theme={null}
import_tools:
  - mcp/banking:get_balance
```

See [Import MCP tools](https://rasa.com/docs/mantle/concepts/tools.md#mcp-tools) for a short walkthrough and
[`skill.md` → `import_tools`](https://rasa.com/docs/mantle/reference/skill-md#import_tools) for collision
and load-failure rules.

## Validation

`rasa train` and `rasa data validate` check this file:

| Code | Cause |
| - | - |
| `mantle.validation.config.missing_integrations` | No `integrations.yml` at the project root |
| `mantle.validation.config.missing_llm` | No `llm:` section, or it is not a mapping |
| `mantle.validation.config.invalid_llm` | `llm:` does not contain a valid `model_group`, contains inline provider settings, or has an invalid prompt budget |
| `mantle.validation.config.unknown_model_group` | An LLM or embeddings reference names a group that is not declared, or `model_groups:` is missing or empty |
| `mantle.validation.config.unused_model_group_models` | A group lists more than one model and has no `router` (warning) |
| `mantle.validation.config.invalid_tracing` | Invalid or unsupported `tracing:` section |
| `mantle.validation.config.invalid_mcp_server` | A `mcp_servers:` entry is invalid (shape, auth, URL, secrets) |
| `mantle.validation.config.duplicate_mcp_server_name` | Two `mcp_servers:` entries share the same `name` |

## Tracing (Langfuse)

Mantle projects configure Langfuse under `tracing:` in `integrations.yml`.

```yaml theme={null}
tracing:
  type: langfuse
  public_key: ${LANGFUSE_PUBLIC_KEY}
  private_key: ${LANGFUSE_SECRET_KEY}
  host: https://cloud.langfuse.com
```

| Key | Required | Notes |
| - | - | - |
| `type` | yes | Must be `langfuse`. |
| `public_key` | yes | Must use a `${ENV_VAR}` reference — literal secrets are rejected. |
| `private_key` | yes | Must use a `${ENV_VAR}` reference — literal secrets are rejected. |
| `host` | yes | Langfuse Cloud or your self-hosted URL. |
| `environment` | no | Optional Langfuse environment name (lowercase, digits, `-`/`_`). |
| `timeout` | no | HTTP timeout in seconds (maps to `LANGFUSE_TIMEOUT`). |
| `debug` | no | Enable Langfuse debug logging (`LANGFUSE_DEBUG`). |
| `release` | no | Release/version tag attached to traces (`LANGFUSE_RELEASE`). |
| `sample_rate` | no | Trace sampling rate between 0 and 1 (`LANGFUSE_SAMPLE_RATE`). |
| `media_upload_thread_count` | no | Media upload thread pool size (`LANGFUSE_MEDIA_UPLOAD_THREAD_COUNT`). |

`public_key` and `private_key` must use `${ENV_VAR}` references — literal secrets
are rejected at validation and apply time. Those references are kept as literal
strings when `integrations.yml` is parsed (they are not expanded like channel
credentials). They are resolved when Langfuse configures at CLI startup; if a
variable is unset, resolution is best-effort and may leave the literal `${VAR}`
in the process environment, so tracing is skipped rather than aborting startup.
Use `rasa data validate` to catch invalid shape or key syntax before deploy.

Install the monitoring extra (`pip install rasa-pro[monitoring]`), set
`LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY`, then enable the block. Example
agents ship it commented out so local defaults stay key-free.

## See also

* [`agent.yml`](https://rasa.com/docs/mantle/reference/agent-yml): identity, persona, prompt tuning


## Related topics

- [agent.yml](https://rasa.com/docs/mantle/reference/agent-yml.md)
- [Changelog](https://rasa.com/docs/mantle/changelog.md)
- [Authoring files](https://rasa.com/docs/mantle/reference/schemas/index.md)
- [Connect a phone line](https://rasa.com/docs/mantle/voice/telephony.md)
- [Test and monitor voice](https://rasa.com/docs/mantle/voice/testing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.