# Everything as code

Keep a complete AI setup in a repository and import it into a clean environment.

Every example in this section was built from pieces - credentials, groups, gateways, connections, services, skills. The setup below puts all of them into files a repository can hold and a clean environment can import, so recreating production in staging takes a checkout and an import.

## The pieces {#the-pieces}

Every piece is kept in a file, and each kind of piece has its own form and deployment method:

| Piece | Form | How it deploys |
| --- | --- | --- |
| Security, groups, gateways, LLM connections | One [enmasse](https://zato.io/docs/ai/enmasse.html) YAML | Imported - on demand or on startup |
| Services | Python files | [Hot-deployed](https://zato.io/docs/dev/hot-deploy.html) |
| [Skills](https://zato.io/docs/ai/skills/index.html) | A `SKILL.md` per directory under `config/repo/skills` | Files - copied or checked out in place |
| Secrets | Environment variables | Never in the repository |

The YAML refers by name to services and skills that are deployed as files - enmasse provides the configuration, the files provide the content, and the names link the two.

## The YAML {#the-yaml}

The complete configuration of a support setup - an agent credential, its group, a gateway with two tools and a prompt, and the LLM connection the tools use:

```yaml
security:
  - name: agent.support
    type: apikey
    username: support-agent
    password: Zato_Enmasse_Env.Support_Agent_Key

groups:
  - name: support-agents
    members:
      - agent.support

llm:
  - name: Support LLM
    model: gpt-4o-mini
    address: https://api.openai.com/v1
    secret: Zato_Enmasse_Env.Support_LLM_Key
    timeout: 15
    max_tokens: 2048

mcp_gateway:
  - name: support
    url_path: /mcp/support
    services:
      - example.support.get-ticket
      - example.support.summarize-ticket
    security_groups:
      - support-agents
    skills:
      - ticket-triage
    validate_input: true
    is_audit_log_active: true
    max_response_size: 2000
```

Two properties matter:

- No secrets - the `Zato_Enmasse_Env.` prefix reads each secret from an environment variable where the import runs, so this file is safe in git and each environment brings its own keys.
- Importing the gateway also creates the REST channel that makes it reachable - the YAML above is complete.

## The repository {#the-repository}

```text
support-ai/
├── enmasse.yaml
├── services/
│   └── support.py          # the two services the gateway names
└── skills/
    └── ticket-triage/
        └── SKILL.md        # the prompt the gateway serves
```

## Import into a clean environment {#import-into-a-clean-environment}

With a fresh environment and the two environment variables set, the files go in first and the configuration follows:

```bash
# Services hot-deploy by being placed in the pickup directory
cp services/support.py ~/env/qs-1/server1/pickup/incoming/services/

# Skills are files under the server's config
cp -r skills/ticket-triage ~/env/qs-1/server1/config/repo/skills/
```

Then the YAML imports as one unit - in the Dashboard through **System** > **Config** > **Import enmasse**, or, in containers, as a mount point: `enmasse.yaml` under `/opt/hot-deploy/enmasse/` imports on startup, as the [DevOps guide](https://zato.io/tutorials/devops/deployment.html) describes. Either way the environment now serves `/mcp/support`, the agent's key works, the prompt is listed and the audit log is on - nothing was configured by hand.

Running the import again is safe - it creates what does not exist and updates what does, matching by name. That makes the YAML the reviewable unit of change: raising `max_response_size` is a one-line diff, a pull request and a re-import.

## Failure behavior {#failure-behavior}

Because the YAML refers to services, skills and secrets by name, a name can point to something that is not there yet:

- A service in the gateway's list that is not deployed yet is not an error - the gateway imports, and the tool appears once the service is hot-deployed. The export marks such services with a `Not deployed` note, as [sharing with clients](https://zato.io/docs/ai/mcp/sharing-with-clients.html) shows.
- A skill named in `skills` that has no directory yet is not served until the files arrive - `prompts/list` shows what is there.
- A missing environment variable does not stop the import - the value becomes a placeholder that names the missing variable, so the first call that needs the secret fails and the placeholder in the error points straight at which variable was not set.

## See also {#see-also}

- [GitOps](https://zato.io/docs/ai/enmasse.html) - The llm and mcp\_gateway enmasse sections in full
- [Multi-agent isolation](https://zato.io/docs/ai/examples/multi-agent-isolation.html) - The same layout grown to several agents and gateways
- [Self-hosted models](https://zato.io/docs/ai/examples/self-hosted-models.html) - Per-environment YAML that swaps the model backends
