Chat any backend: Kafka, k8s/OCP, Postgres, MSSQL, Oracle, Redis, Mongo, AWS/Azure, Cloudera
2.8K
A service-agnostic chatbot (web and CLI) that connects to any
Model Context Protocol server(s) you list in
a config file and drives any OpenAI-compatible LLM (cloud or on-prem) via
litellm. No code is tied to a backend — you describe
your servers in config.yaml, and the host connects, aggregates their tools,
namespaces them (server__tool), and runs an agentic tool-calling loop.
config.yaml ──▶ mcp-chat-all ──▶ connects to N MCP servers (stdio + remote SSE/HTTP)
aggregates + namespaces their tools
├─ web UI (Streamlit, port 8501)
└─ CLI (one-shot or REPL)
-e). A backend
commented out (#) in the config can still be toggled on at runtime — a
session-only load that does not edit the config file.candy (default, glossy Candy-Crush), matrix (green
terminal with live digital-rain), atlassian (clean, professional
enterprise skin), or claude (dark VS Code editor with the Claude coral
accent). Pick with -e MCP_CHAT_THEME=claude or config theme:.MCP_ENABLE_BACKENDS (only these) or
MCP_DISABLE_BACKENDS (all but these); the sidebar toggles show that selection
on first paint and still flip at runtime.tools.*.yaml while it runs and the change is
live with no restart: the server re-reads on mtime change, the UI re-probes, and
every add/update/remove is logged per tool (LOADED tool: … / UNLOADED tool: …).deep.py (planning + sub-agents + virtual
filesystem) baked in for long multi-step investigations over the same tools; run
it with --entrypoint python … deep.py "<task>" (see DEEP_AGENT.md).KAFKA_TIMEOUT/KAFKA_CMD_TIMEOUT) and a paged list_topics (prefix/limit)
for clusters with 9000+ topics.MCP_TLS_VERIFY=true to re-enable.secretKeyRef); see the helm/mcp-chat chart in the repo.cp creds.env.example creds.env # fill in the backends you use
docker compose up # → http://localhost:8501
docker-compose.yml runs the image with every backend wired (config.all.yaml),
pulling creds from creds.env. Set only what you use; load the rest from the
sidebar anytime.
Self-contained and airgap-friendly: every CLI + dependency is baked in, all TLS verification is off by default (internal CAs, self-signed, hostname mismatch), and nothing calls out at runtime. Point the LLM at a self-hosted endpoint:
-e LLM_MODEL=openai/mistral-7b-instruct \
-e LLM_API_BASE=http://vllm.internal:8000/v1 \
-e LLM_API_KEY=dummy # some servers still want a non-empty key
Move the image across the airgap and deploy:
# connected host — pin a tag (not :latest)
docker save n8500x/mcp-chat-all:20260803-183945 | gzip > mcp-chat-all.tgz
# airgapped host
gunzip -c mcp-chat-all.tgz | docker load # or push to your internal registry
# Kubernetes / OpenShift — secrets come from k8s Secrets (secretKeyRef)
helm upgrade --install mcp-chat helm/mcp-chat -n mcp-chat --create-namespace \
-f helm/mcp-chat/values-example.yaml # edit: model, backends, secretRefs
MCP_TLS_VERIFY=true re-enables TLS verification everywhere if you'd rather trust
a specific CA. Use host.docker.internal (not localhost) to reach the model
from inside a container.
Create a config.yaml and run. This example talks to a filesystem MCP server:
# config.yaml
title: "MCP Chat"
llm:
model: openai/gpt-4o
api_key: ${LLM_API_KEY}
servers:
filesystem:
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
docker run --rm -p 8501:8501 \
-e LLM_API_KEY=sk-... \
-v "$PWD/config.yaml:/app/config.yaml:ro" \
-v "$PWD/data:/data:ro" \
n8500x/mcp-chat-all:latest
Open http://localhost:8501 and start chatting. That's it.
The image bundles Node, so
npx-based MCP servers (filesystem, github, …) work out of the box — nothing else to install.
You provide a config; the container does the rest.
| You set in config | The host does |
|---|---|
servers: (any number) | connects to each, over stdio or HTTP/SSE |
| tool names | namespaces them server__tool so they never collide |
llm: | routes every model call through litellm |
system_prompt: | steers the assistant |
servers:
# stdio — the host spawns it as a subprocess
files:
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
# remote — the host connects to a hosted endpoint
remote_api:
url: https://mcp.example.com/sse
transport: sse # or: http (streamable-http)
headers:
Authorization: "Bearer ${REMOTE_MCP_TOKEN}"
${VAR} and ${VAR:-default} expand from the environment at load time, so
secrets never have to live in the file. Add disabled: true to skip an entry.
llm:
model: openai/gpt-4o # cloud (OpenAI, Anthropic, Azure, Bedrock…)
# model: openai/mistral-7b-instruct # on-prem: vLLM / LocalAI / LM Studio
# api_base: http://vllm-host:8000/v1
# model: ollama/llama3 # Ollama
api_key: ${LLM_API_KEY:-}
Don't want to write a Python MCP server at all? The image ships a generic,
config-driven adapter (generic_server.py). Declare each tool in a
tools.yaml — a name, its inputs, and an action (an HTTP call or a shell
command) — and wire it in like any other server. {param} placeholders are
filled from the call arguments.
# tools.yaml
defaults:
http:
base_url: ${API_BASE}
headers: { Authorization: "Bearer ${API_TOKEN}" }
tools:
- name: get_order
description: "Look up an order by id."
parameters:
type: object
properties: { id: { type: string } }
required: [id]
http: { method: GET, path: "/orders/{id}" }
- name: disk_free
description: "Show disk usage."
parameters: { type: object, properties: {} }
shell: { command: ["df", "-h"] }
# in your chat config.yaml
servers:
orders:
command: python
args: ["generic_server.py"]
env:
TOOLS_CONFIG: /app/tools.yaml
API_BASE: https://api.internal
API_TOKEN: ${API_TOKEN}
Shell commands are passed as an argv list (never through a shell), so tool arguments can't inject extra commands.
Every backend is just a declared tools file (tools.<backend>.yaml) that the
generic server loads. Pick which one at runtime with
-e TOOLS_CONFIG=/app/tools.<backend>.yaml and pass that backend's connection
env. One image covers everything — n8500x/mcp-chat-all bundles every CLI
(Kafka, kubectl/oc, psql, sqlcmd, sqlplus, redis-cli, mongosh,
aws, az); HTTP backends like Cloudera Manager need nothing extra.
| Backend | TOOLS_CONFIG | Connection env / mounts |
|---|---|---|
| Cloudera Manager | tools.cloudera.yaml | CM_URL, CM_USER, CM_PASSWORD (HTTP, no CLI) |
| YARN ResourceManager | tools.yarn.yaml | YARN_RM_URL (+ optional YARN_USER/YARN_PASSWORD) — Spark3 jobs are YARN apps |
| Spark3 History Server | tools.sparkhistory.yaml | SPARK_HISTORY_URL (+ optional SPARK_HISTORY_USER/SPARK_HISTORY_PASSWORD) |
| Apache Flink | tools.flink.yaml | FLINK_URL (JobManager REST — HTTP, no CLI) |
| Prometheus | tools.prometheus.yaml | PROMETHEUS_URL (+ optional PROMETHEUS_USER/PROMETHEUS_PASSWORD) — HTTP |
| Elasticsearch (ELK) | tools.elasticsearch.yaml | ELASTIC_URL (+ optional ELASTIC_USER/ELASTIC_PASSWORD) — HTTP |
| Grafana | tools.grafana.yaml | GRAFANA_URL, GRAFANA_TOKEN |
| Confluence | tools.confluence.yaml | CONFLUENCE_URL, CONFLUENCE_USER, CONFLUENCE_API_TOKEN |
| Kafka Connect | tools.kafkaconnect.yaml | endpoint per call + KCONNECT_USER/KCONNECT_PASSWORD (HTTP) |
| Kafbat UI (kafka-ui) | tools.kafbat.yaml | KAFBAT_URL, KAFBAT_CLUSTER (+ optional KAFBAT_USER/KAFBAT_PASSWORD) — UI API over a disparate Kafka cluster |
| Confluent Schema Registry | tools.schemaregistry.yaml | SCHEMA_REGISTRY_URL (+ optional SCHEMA_REGISTRY_USER/SCHEMA_REGISTRY_PASSWORD) |
| Kafka | tools.kafka.yaml | KAFKA_BOOTSTRAP_SERVERS, KAFKA_SASL_USERNAME/PASSWORD, CA cert |
| Kubernetes | tools.k8s.yaml | mount kubeconfig |
| OpenShift | tools.ocp.yaml | mount kubeconfig |
| PostgreSQL | tools.postgres.yaml | PGHOST, PGUSER, PGPASSWORD, PGDATABASE |
| SQL Server | tools.mssql.yaml | MSSQL_SERVER (host only), MSSQL_PORT (default 1433), MSSQL_USER, MSSQL_PASSWORD, MSSQL_DB |
| Oracle | tools.oracle.yaml | ORACLE_USER, ORACLE_PASSWORD, ORACLE_DSN (Easy Connect or TNS alias); TNS via ORACLE_TNSNAMES/_B64 rendered at runtime |
| Redis | tools.redis.yaml | REDIS_HOST (single) or REDIS_CLUSTERS (multi) + REDIS_PASSWORD; OCP TLS route :443 |
| MongoDB | tools.mongodb.yaml | MONGODB_URI |
| AWS / Azure | tools.cloud.yaml | mount ~/.aws / ~/.azure |
Every command below also needs -p 8501:8501 and an LLM (-e LLM_API_KEY=sk-...).
Open http://localhost:8501 when it's up.
Cloudera Manager (REST API — no CLI):
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/app/tools.cloudera.yaml \
-e CM_URL=https://cm-host:7183/api/v45 -e CM_USER=admin -e CM_PASSWORD=... \
n8500x/mcp-chat-all:latest
Kafka (SASL_SSL / SCRAM — the entrypoint writes client.properties from env):
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/app/tools.kafka.yaml \
-e KAFKA_BOOTSTRAP_SERVERS=broker1:9093,broker2:9093 \
-e KAFKA_SASL_USERNAME=my-user -e KAFKA_SASL_PASSWORD=my-pass \
-v "$PWD/ca.pem:/certs/ca.pem:ro" \
n8500x/mcp-chat-all:latest
PostgreSQL (use a read-only role):
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/app/tools.postgres.yaml \
-e PGHOST=db -e PGUSER=ro -e PGPASSWORD=... -e PGDATABASE=app \
n8500x/mcp-chat-all:latest
SQL Server · Oracle · Redis · MongoDB (same shape, swap the env):
# SQL Server
-e TOOLS_CONFIG=/app/tools.mssql.yaml \
-e MSSQL_SERVER=host -e MSSQL_USER=ro -e MSSQL_PASSWORD=... -e MSSQL_DB=app
# Oracle
-e TOOLS_CONFIG=/app/tools.oracle.yaml \
-e ORACLE_USER=ro -e ORACLE_PASSWORD=... -e ORACLE_DSN=host:1521/service
# Redis (OCP TLS route :443; route host passed per query)
-e TOOLS_CONFIG=/app/tools.redis.yaml \
-e REDIS_HOST=redis-a.apps.ocp.example.com -e REDIS_PASSWORD=...
# MongoDB
-e TOOLS_CONFIG=/app/tools.mongodb.yaml \
-e MONGODB_URI="mongodb://ro:pass@host:27017/db"
Kubernetes / OpenShift (mount your kubeconfig):
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/app/tools.k8s.yaml \
-v "$HOME/.kube/config:/root/.kube/config:ro" \
n8500x/mcp-chat-all:latest
# OpenShift: -e TOOLS_CONFIG=/app/tools.ocp.yaml (same mount)
AWS / Azure (mount your credentials):
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/app/tools.cloud.yaml \
-v "$HOME/.aws:/root/.aws:ro" -v "$HOME/.azure:/root/.azure:ro" \
n8500x/mcp-chat-all:latest
Then ask, e.g., "which consumer groups have lag over 1000?", "describe the orders table", "which pods are crash-looping in the payments namespace?" — the model calls the backend's tools and summarises. Everything is read-only when you connect with a read-only credential.
Load several backends together — use the bundled config.multi.yaml, which
wires each backend as its own server so tools are namespaced (flink__…,
kafka__…) and never collide:
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e MCP_CHAT_CONFIG=/app/config.multi.yaml \
-e FLINK_CLUSTERS="prod-a=https://flink-a:8081" \
-e KAFKA_BOOTSTRAP_SERVERS=broker:9093 -e KAFKA_SASL_USERNAME=u -e KAFKA_SASL_PASSWORD=p \
n8500x/mcp-chat-all:latest
(Or point one server's TOOLS_CONFIG at several files: "/app/tools.flink.yaml,/app/tools.cloudera.yaml" or a glob "/app/mytools/*.yaml".)
Add or change tools at runtime — mount a tools file/dir and edit it; the generic server re-reads on every request, so new or updated tools take effect without a restart. Hit ↻ Refresh tools in the UI to re-list them:
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e TOOLS_CONFIG=/tools/mytools.yaml \
-v "$PWD/mytools.yaml:/tools/mytools.yaml" \
n8500x/mcp-chat-all:latest
# edit mytools.yaml on the host → click "↻ Refresh tools" → new tools appear
Multi-cluster (e.g. Flink) — the target endpoint is a per-call argument,
so one chat can query many clusters. Set FLINK_CLUSTERS="name=url, …" and the
assistant discovers them via a list_clusters tool, then passes the chosen URL.
Override the entrypoint to use the REPL / one-shot client:
# one-shot
docker run --rm -v "$PWD/config.yaml:/app/config.yaml:ro" \
-e LLM_API_KEY=sk-... --entrypoint python n8500x/mcp-chat-all:latest \
chat.py "list the files under /data"
# interactive REPL
docker run --rm -it -v "$PWD/config.yaml:/app/config.yaml:ro" \
-e LLM_API_KEY=sk-... --entrypoint python n8500x/mcp-chat-all:latest chat.py
Tool calls are printed to stderr so you can see what the model invokes.
| Variable | Purpose |
|---|---|
MCP_CHAT_CONFIG | Path to the config file inside the container (default /app/config.yaml) |
MCP_CHAT_THEME | UI theme: candy (default), matrix, or atlassian |
LLM_MODEL / LLM_API_BASE / LLM_API_KEY | model string, on-prem base URL, key |
KAFKA_TIMEOUT | Kafka librdkafka request timeout, seconds (default 90) — raise for huge clusters |
KAFKA_CMD_TIMEOUT | Kafka per-tool subprocess deadline, seconds (default 300; keep > KAFKA_TIMEOUT) |
MCP_TLS_VERIFY | set true to re-enable TLS verification (off by default) |
any ${VAR} used in your config | expanded at load time — pass secrets this way |
The image reads no fixed backend env of its own — every setting comes from your config (which may reference the environment).
Every example ships inside the image under /app, so you can run one without
writing anything — just point MCP_CHAT_CONFIG at it:
File (in /app) | What it is |
|---|---|
config.generic.yaml | one config, any backend — pick with TOOLS_CONFIG (default in mcp-chat-all) |
config.example.yaml | generic starter (filesystem server) |
tools.<backend>.yaml | declared tools for kafka, k8s, ocp, postgres, mssql, oracle, redis, mongodb, cloud, cloudera |
tools.example.yaml | no-code tool declarations for generic_server.py |
kafka.env.example | environment template for the Kafka example |
README.md | full documentation |
# try it with zero config files of your own:
docker run --rm -p 8501:8501 -e LLM_API_KEY=sk-... \
-e MCP_CHAT_CONFIG=/app/config.example.yaml n8500x/mcp-chat-all:latest
| Port | Service |
|---|---|
8501 | Streamlit web UI |
| Key | Default | Meaning |
|---|---|---|
title / subtitle | "MCP Chat" | UI header text |
system_prompt | generic | instruction handed to the LLM |
max_rounds | 10 | max tool-call rounds per turn |
suggestions | [] | up to 4 clickable example prompts on the empty screen |
llm.model | — | any litellm model string (required) |
llm.api_base | — | OpenAI-compatible base URL (on-prem) |
llm.api_key | — | key for the LLM endpoint |
servers.<name>.command / args / env | — | stdio server |
servers.<name>.url / transport / headers | — | remote (sse|http) server |
servers.<name>.disabled | false | skip this server |
| Tag | Description |
|---|---|
latest | most recent build |
YYYYMMDD-HHMMSS | immutable, timestamped builds |
FROM n8500x/mcp-chat-all:latest
# bake in a config and any extra server dependencies
COPY config.yaml /app/config.yaml
RUN pip install --no-cache-dir confluent-kafka
License / source: internal MLOps tooling. Read-only by design when paired with read-only MCP servers.
Content type
Image
Digest
sha256:6e907b24b…
Size
841.9 MB
Last updated
2 months ago
docker pull n8500x/mcp-chat-all