# Infrastructure

> Step-by-step connection and configuration for Kubernetes, Docker, Terraform and Confluent / Kafka.

Source: https://triagic.com/docs/integrations/infrastructure

What is running, what changed, and what got stuck in a queue on the way.

## Kubernetes [#kubernetes]

`kubernetes` · runs `mcp-server-kubernetes` via **npx** · restricted to the server's
read-only tool allowlist, which this catalog pins on: get, describe, logs, explain,
list API resources, context and ping. Apply, create, scale, patch, rollout, Helm
install and upgrade, exec-in-pod and port-forwarding are not offered.

Overview, prompts and the tool list: [/integrations/kubernetes](/integrations/kubernetes).

> **Note:** Paste the kubeconfig to make this a shared data source
>
> **Paste a kubeconfig** is the method to pick when you are configuring the cluster once
> for the whole organization: the file travels with the configuration, is encrypted at
> rest like any other secret, and lands on every member's desktop on the next sync. No
> member has to have a kubeconfig on their own machine.
>
> **Kubeconfig file on this machine** is a *file path*, and a path only means something
> on the machine that has that file. Use it for your own laptop, or configure it
> per-member with **Pre-add data sources for members** on the Organization page. See
> [Integrations](/docs/admin/integrations#pre-adding-data-sources-for-members).

1. **Create a read-only service account**, rather than pointing at a cluster-admin
   context:

   ```bash
   kubectl create serviceaccount triagic -n default
   kubectl create clusterrolebinding triagic-view \
     --clusterrole=view --serviceaccount=default:triagic
   ```

   The `view` ClusterRole is Kubernetes' own read-only aggregate.

2. **Choose how to reach the cluster.** **Authentication method** decides which other
   fields appear.

   *Paste a kubeconfig* is the answer for a shared data source. Produce a self-contained
   file and paste the whole thing:

   ```bash
   kubectl config view --raw --minify --flatten
   ```

   `--raw` keeps the credentials (a plain `config view` redacts them), `--minify` drops
   every context except the current one, and `--flatten` inlines certificates that would
   otherwise be paths to files no other machine has. Switch context first if the one you
   want is not current.

   *Kubeconfig file on this machine* is the usual answer for your own laptop: build a
   kubeconfig from the service account above and give its path.

   *API server and service-account token* suits a machine with no kubeconfig on it. Mint
   a token for the account and read the cluster's CA out of any existing kubeconfig:

   ```bash
   kubectl create token triagic --duration=8760h
   kubectl config view --raw --minify \
     -o jsonpath='{.clusters[0].cluster.certificate-authority-data}'
   ```

   The second command prints the value for **Cluster CA certificate**. It's already
   base64-encoded, and it goes in the field as-is. There is no way to give a CA file path
   here, only that string.

   *In-cluster service account* is for a Triagic running as a pod in the cluster it reads.
   It takes no fields: the server finds the pod's own mounted service-account token.

3. **Fill the form.**

   | Field                      | Required                      | What to put                                                                                                                                            |
   | -------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
   | **Authentication method**  | no, defaults to the file path | See above.                                                                                                                                             |
   | **Kubeconfig**             | yes, with *paste*             | The whole file, pasted into the box. Encrypted at rest and never shown again after saving.                                                             |
   | **Kubeconfig path**        | yes, with *Kubeconfig file*   | An **absolute** path on the machine running Triagic, e.g. `/Users/you/.kube/config`. Not a path on the portal's host, and not on anyone else's laptop. |
   | **API server URL**         | yes, with *token*             | `https://k8s.example.com:6443`                                                                                                                         |
   | **Service-account token**  | yes, with *token*             | The bearer token from `kubectl create token`.                                                                                                          |
   | **Cluster CA certificate** | no, with *token*              | The base64-encoded PEM string, not a path. Filling it also forces certificate verification back on.                                                    |
   | **Verify TLS certificate** | no, defaults on, with *token* | Turn off only for a self-signed API server with no CA to hand. Ignored when the CA above is filled in.                                                 |
   | **Context**                | no                            | Defaults to the kubeconfig's current context. Set it to pin a cluster, so a member switching contexts locally doesn't repoint the integration.         |
   | **Default namespace**      | no, defaults `default`        | Where a read looks when the agent names no namespace. Worth setting: `default` is rarely where the workload is.                                        |

4. **Verify.** Health check lists API resources.

> **Warning:** A Triagic running inside the cluster ignores your kubeconfig path
>
> The server checks for an in-cluster service account **before** it reads the kubeconfig
> *path*. So if Triagic itself runs as a pod, a configured **Kubeconfig path** is
> silently skipped and the pod's own service account is used instead, with whatever
> permissions that account has, not the ones you granted `triagic`. Choose *In-cluster
> service account* there, so the behaviour is the one you asked for. A **pasted**
> kubeconfig is the exception: it is read first and wins even inside a pod.

> **Warning:** A kubeconfig that logs in through a cloud CLI does not travel
>
> EKS, GKE and AKS kubeconfigs usually authenticate through an `exec` credential plugin:
> `aws eks get-token`, `gke-gcloud-auth-plugin`, `kubelogin`. `--flatten` inlines
> certificates but cannot inline a program: every machine running that configuration
> would need the CLI installed **and logged in as someone with cluster access**, which
> also means the integration would run with whichever human's credentials that machine
> has. Use *API server and service-account token* for those clusters. It needs nothing
> installed and pins the identity to the service account you created.

| If it reports                                                                             | It usually means                                                                                                                                                                            |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Failed to parse KUBECONFIG_YAML`                                                         | The pasted text is not valid YAML, usually a partial copy, or indentation lost on the way through a chat window. Re-run `kubectl config view --raw --minify --flatten` and paste all of it. |
| `executable file not found`, `exec plugin`                                                | The kubeconfig authenticates through a cloud CLI that is not installed on that machine. See the callout above.                                                                              |
| `x509: certificate signed by unknown authority`, `unable to verify the first certificate` | With *token*: the API server's certificate is not trusted. Fill in **Cluster CA certificate**, or turn off **Verify TLS certificate** if it is self-signed and you have no CA.              |
| `Unauthorized`                                                                            | The token expired or was minted for a different cluster. `kubectl create token` defaults to one hour unless `--duration` is given.                                                          |
| `forbidden: User "system:serviceaccount:…"`                                               | The service account has no read access. Bind it to the `view` ClusterRole.                                                                                                                  |
| Reads land in the wrong place                                                             | **Default namespace** is unset, so namespaced reads go to `default`.                                                                                                                        |

## Docker [#docker]

`docker` · runs `mcp-server-docker` via **uvx** · restricted to the five read-only
tools upstream marks as such: list containers, fetch container logs, list images, list
networks, list volumes. Create, run, start, stop, remove, pull, push and build exist
in that server and are **not** exposed. The allow-list is the read-only guarantee
here, because this server has no read-only switch of its own.

Overview, prompts and the tool list: [/integrations/docker](/integrations/docker).

For "the deploy went out and the container is restarting": what is running, when it
last restarted, and what it printed on the way down.

> **Note:** This one needs uv, not the Docker CLI
>
> The server talks to the engine's API socket directly, so the machine running Triagic
> needs [uv](https://docs.astral.sh/uv/) and a reachable Docker engine; the `docker`
> command itself isn't a prerequisite. (Unlike GitHub, Grafana and Terraform, which run
> *inside* a container and therefore do need Docker installed.)

1. **Decide which engine to read.** Blank means the Docker engine on the machine running
   Triagic, which is right for a developer laptop or a Triagic that already lives on the
   host you care about.

   For a remote server, `ssh://you@host.example.com` is the method to reach for. It uses
   the OpenSSH configuration of the machine running Triagic, so it works exactly when
   `ssh you@host.example.com` works there, and it needs nothing opened on the server.
   Give that SSH user read access to the socket (on Linux: membership of the `docker`
   group).

2. **Only for a `tcp://` engine: collect the TLS material.** A `tcp://` daemon with no
   TLS is an unauthenticated root shell on that host: treat one as a finding, not as a
   connection option. With TLS, copy `ca.pem`, `cert.pem` and `key.pem` into one
   directory on the machine running Triagic; those three names are fixed, because the
   client appends them to the directory itself.

3. **Fill the form.**

   | Field                         | Required | What to put                                                                                                                                                                                                                                                                   |
   | ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | **Docker host**               | no       | Blank for the local engine. `ssh://you@host.example.com` for a remote one. `tcp://host.example.com:2376` with the TLS directory below.                                                                                                                                        |
   | **TLS certificate directory** | no       | Only with a `tcp://` host: a directory holding `ca.pem`, `cert.pem` and `key.pem`. Setting it turns TLS on and checks the engine's certificate against `ca.pem`. Saving it against any other kind of host is refused, because it would silently break the connection instead. |

4. **Verify.** Health check lists containers, a real call to the daemon, so a socket
   that isn't there fails the save rather than leaving a row that starts and answers
   nothing.

> **Warning:** Docker Desktop may not create /var/run/docker.sock
>
> Docker Desktop only creates the default socket when **Settings → Advanced → Allow the
> default Docker socket to be used** is on. With it off, leave nothing blank: set
> **Docker host** to `unix:///Users/<you>/.docker/run/docker.sock` (macOS) or
> `unix:///home/<you>/.docker/desktop/docker.sock` (Linux).

| If it reports                                       | It usually means                                                                                                                                                                              |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Error while fetching server API version`           | Not a version problem: nothing answered at all. The engine is not running, or **Docker host** points at a socket that does not exist.                                                         |
| `permission denied`                                 | The user Triagic runs as cannot read the socket. On Linux, add it to the `docker` group and log back in.                                                                                      |
| `SSHException`, `Authentication failed`, `Host key` | `ssh://` failed. `ssh <the same user@host>` has to work from that machine first, host key already accepted; nothing here can answer a prompt.                                                 |
| `certificate verify failed`, `SSLError`             | The `tcp://` engine's certificate was not accepted. Check the directory really holds `ca.pem`, `cert.pem` and `key.pem` under those names, and that `ca.pem` signed the daemon's certificate. |

## Terraform [#terraform]

`terraform` · runs HashiCorp's official `terraform-mcp-server` image via **Docker** ·
scoped to the workspace/run/state toolset, with run and apply operations disabled.

Overview, prompts and the tool list: [/integrations/terraform](/integrations/terraform).

This is for answering *what changed* (workspaces, runs, plans, plan logs, apply logs
and state versions) rather than for reading public registry documentation.

1. **Create an API token.** HCP Terraform → **Account settings → Tokens**, or a **team
   token** scoped to read access, which is the least-privilege option. User, team and
   organization tokens all work.

2. **Get the CA bundle, for Terraform Enterprise behind a private CA.** HCP Terraform
   uses a public CA and needs nothing here. A self-hosted instance usually presents a
   certificate from your own internal CA, which the container has no reason to trust.
   Ask whoever runs it for that CA's PEM bundle and save it on the machine running
   Triagic. You give the host path and it is mounted into the container for you.

3. **Fill the form.**

   | Field                      | Required                                | What to put                                                                                                                                                     |
   | -------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | **Terraform address**      | no, defaults `https://app.terraform.io` | Set your own URL for Terraform Enterprise. Remember the server runs in a container, so a self-hosted instance on the same machine needs `host.docker.internal`. |
   | **API token**              | yes                                     |                                                                                                                                                                 |
   | **CA certificate path**    | no                                      | `/etc/ssl/certs/tfe-ca.pem`, the internal CA bundle, for Terraform Enterprise.                                                                                  |
   | **Verify TLS certificate** | no, defaults on                         | Turn off only for a self-signed certificate you cannot get a CA bundle for.                                                                                     |

4. **Verify.** Health check is `get_token_permissions`, not `whoami`: no such tool
   exists in the pinned image. It still fails precisely when the token is wrong.
   Without it the server would still start (the registry side needs no token at all)
   and every workspace tool would fail later instead.

| If it reports          | It usually means                                                                                                                                                                                                 |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`                  | Wrong token, or a token belonging to a different organization.                                                                                                                                                   |
| `404`                  | Authenticated but cannot see that organization or workspace. Team tokens are scoped, so check the team has read access.                                                                                          |
| `x509` / `certificate` | A Terraform Enterprise certificate the container doesn't trust. Set **CA certificate path** to that CA's PEM bundle. Turning off **Verify TLS certificate** also connects, but then nothing proves who answered. |

## Confluent / Kafka [#confluent--kafka]

`confluent` · runs `@confluentinc/mcp-confluent` via **npx** · read-only is enforced
with an explicit allow-list of read tools, so `produce-message`, `delete-topics`,
connector mutations and the Flink/Tableflow write families are simply not offered.

Overview, prompts and the tool list: [/integrations/confluent](/integrations/confluent).

Kafka, Schema Registry, connectors and metrics are covered. Flink is not: this
integration sets no Flink credentials, so the server would hide those tools anyway.

Only the first three fields are required. Each remaining block unlocks a group of
tools, and a block that is incomplete makes its tools **absent** rather than broken,
which is the single most confusing failure mode here.

1. **Create a Kafka API key.** Confluent Cloud → your cluster → **API keys → Add key**.
   Keys are per-cluster; a key from a sibling cluster fails with an authentication error.

   Create it for a **service account**, not for your own user account: a key made under
   "My account" carries everything you can do, and dies with your membership. Give the
   service account read-only access: the **DeveloperRead** role (or ACLs granting
   `READ`/`DESCRIBE`) on the topics and consumer groups you want visible. The read-tool
   allowlist on this side keeps the agent read-only; the service account's grants make
   that hold at the cluster too, and decide *which* topics are readable at all.

2. **Collect the cluster's REST endpoint and ID** from the cluster overview, if you want
   the topic and consumer-group tools (you do, consumer lag is the point).

3. **Optionally add the control-plane and Schema Registry keys.** The Confluent Cloud API
   key is a separate, account-level key that lists environments and clusters. The Schema
   Registry endpoint has its own key pair again.

4. **Only for a self-hosted cluster: write a Kafka client config file.**

   Confluent Cloud needs nothing here. The server talks SASL/PLAIN over TLS and has no
   setting for anything else (not SCRAM, not mTLS, not a private CA), so a self-hosted
   cluster wanting one of those needs a librdkafka properties file, which is the one
   override that can supply them.

   Write it on the machine running Triagic and give the absolute path. Be aware of what
   you are trading: that file overrides every field above, and it holds credentials on
   disk in plain text rather than encrypted alongside the rest of this form. Restrict it
   to the user Triagic runs as.

   ```properties
   security.protocol=SASL_SSL
   sasl.mechanisms=SCRAM-SHA-512
   sasl.username=triagic
   sasl.password=…
   ssl.ca.location=/etc/ssl/certs/kafka-ca.pem
   ```

5. **Fill the form.**

   | Field                                               | Required | What it unlocks                                                                              |
   | --------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------- |
   | **Bootstrap servers**                               | yes      | `pkc-abc12.us-east-1.aws.confluent.cloud:9092`, comma-separated, **ports included**.         |
   | **Kafka API key** / **secret**                      | yes      | Consuming messages, consumer groups.                                                         |
   | **Kafka REST endpoint**                             | no       | Topic and consumer-group tools. `https://pkc-abc12….confluent.cloud:443`                     |
   | **Kafka cluster ID**                                | no       | Required alongside the REST endpoint. `lkc-abc123`                                           |
   | **Confluent Cloud API key** / **secret**            | no       | Listing environments, clusters, organizations.                                               |
   | **Schema Registry endpoint** + **key** / **secret** | no       | Reading schemas.                                                                             |
   | **Kafka client config file**                        | no       | SASL\_SSL, SCRAM, mTLS or a private CA on a self-hosted cluster. Overrides everything above. |

6. **Verify.** Health check lists topics, a real Kafka read, deliberately not one of the
   server's credential-free diagnostics, which would answer happily for a broken config.

| If it reports                         | It usually means                                                                                                                        |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `unknown tool` / `tool not available` | A configuration block is incomplete, not a broken server. Topic and consumer-group tools need the REST endpoint **and** the cluster ID. |
| `401` / `403` / `SASL`                | The Kafka key and secret must be a matching pair issued for *this* cluster.                                                             |
| `ETIMEDOUT`, `ECONNREFUSED`           | The brokers didn't answer from that machine. Check the port is in **Bootstrap servers** and the network is reachable.                   |
