Configuring the Confluent MCP Server

July 27, 2026 · View on GitHub

This is the single reference for configuring mcp-confluent. It covers the YAML config file (-c config.yaml), the legacy env-var path (-e config.env) that the server still accepts during the transition, and how the two interact via ${VAR} interpolation.

Contents

Two paths, one configuration

mcp-confluent accepts configuration through either of two entry points, both of which feed the same internal configuration model at startup:

PathInvocationStatus
YAML-c path/to/config.yamlPreferred.
Env vars (legacy)-e path/to/config.env or exported in the parent shellAccepted; deprecated.

The two paths are mutually exclusive: if you pass -c, the YAML file is the source of truth; otherwise the server reads the legacy environment variables listed in Legacy env-var configuration.

Roadmap. The two paths have full parity today. In a near-future release the env-var-only path will emit a startup warning; a release or two later it will be removed.

If you are starting fresh, begin with YAML. If you have an existing .env, migrate when convenient — both -c and -e continue to work for now.

Quick start (YAML)

# 1. Drop a starter config.yaml into the current directory.
npx @confluentinc/mcp-confluent --init-config

# 2. Edit ./config.yaml — fill in the blocks you need.

# 3. Start the server.
npx @confluentinc/mcp-confluent --config ./config.yaml

--init-config copies config.example.yaml into ./config.yaml and idempotently appends the filename to a sibling .gitignore so secrets cannot slip into a commit. It refuses to overwrite an existing config.yaml.

For OAuth-only setups use --init-oauth-config instead; it drops config.oauth.example.yaml at the same destination. The two flags are mutually exclusive.

Anatomy of a YAML config

A config is a connections: map plus an optional top-level server: block for transport/security/logging settings. Each connection block is one of two types — OAuth or direct — taken in turn below; a single configuragion may hold several connections and mix their types (see Multiple connections (and zero connections)). The fully annotated reference is config.example.yaml; per-field comments live there rather than being duplicated here. Compact examples for common local-Docker setups live in sample_configs/.

OAuth connection (type: oauth)

Confluent Cloud login via PKCE: the browser opens on the first tool call that needs Cloud access, the resulting session is reused for the rest of the process, and there are no API keys to provision. The connection itself carries no service blocks — resource IDs (cluster, env) flow in as tool arguments at call time.

server:
  transports: [stdio]
  log_level: info

connections:
  ccloud-oauth:
    type: oauth
    description: "Confluent Cloud Production"

Get a starter file via --init-oauth-config. Not every tool is OAuth-eligible yet — see Authentication modes for the supported list.

Direct connection (type: direct)

Credentials live in the YAML — API key/secret per service block, typically ${VAR}-interpolated from your shell environment. Each service block under the connection is independently optional: include the ones you need and omit the rest. The tools that depend on missing blocks disable themselves. At least one service block must remain.

server:
  transports: [stdio]
  log_level: info

connections:
  staging:
    type: direct
    description: "Confluent Cloud Staging via API Keys"
    kafka: { ... }
    schema_registry: { ... }
    confluent_cloud: { ... }
    flink: { ... }
    tableflow: { ... }
    telemetry: { ... }

The connection id (staging above) is freeform. Either connection flavor also accepts an optional description — a free-text label echoed back by the list-configured-connections tool so an agent can tell your connections apart. A blank or whitespace-only description is treated as no description.

Either connection flavor also accepts an optional read_only flag (default false). When set to read_only: true, every tool that mutates state is automatically disabled for that connection, leaving only the read-only tools enabled. A tool's mutation posture comes from its readOnlyHint annotation, so there is nothing to configure per-tool: reads stay available while writes — produce, create/alter/delete, and the like — can never fire against that connection. This is the recommended posture for handing an agent a staging or production connection while keeping full read/write on a throwaway dev cluster. The list-configured-connections tool reports each connection's read-onlyness, so an agent can see at a glance which connections are read-only or write-enabled.

Service blocks

BlockPurpose
kafkaBootstrap address + auth for the native Kafka admin/producer/consumer client; REST endpoint + cluster/env IDs for REST-proxy tools.
schema_registryEndpoint + auth for Confluent Schema Registry.
confluent_cloudEndpoint + auth for the Confluent Cloud control plane (environments, clusters, connectors, billing, catalog).
flinkEndpoint + auth + IDs for Flink SQL and the Flink catalog. All five of endpoint, auth, environment_id, organization_id, compute_pool_id are required if the block is present.
tableflowAuth for Tableflow topic and catalog-integration tools. Pairs with confluent_cloud for environment/cluster lookups.
telemetryEndpoint + auth for the Metrics API. Optional override; defaults inherit from confluent_cloud.auth.

Field-level details, defaults, and which CLI/env-var each field replaces are in config.example.yaml.

The top-level server: block

The top-level server: block sits beside connections: and applies to the whole config, regardless of which connection flavors you defined. It is entirely optional — omit it to accept the same defaults today's env-var users get. The fully annotated example is in config.example.yaml; when present, it replaces these env vars:

FieldReplaces env var
server.transports--transport CLI flag
server.log_levelLOG_LEVEL
server.do_not_trackDO_NOT_TRACK (env wins as the floor; disables usage analytics and Sentry error reporting; see Telemetry)
server.http.portHTTP_PORT
server.http.hostHTTP_HOST
server.http.mcp_endpointHTTP_MCP_ENDPOINT_PATH
server.http.sse_endpointSSE_MCP_ENDPOINT_PATH
server.http.sse_message_endpointSSE_MCP_MESSAGE_ENDPOINT_PATH
server.auth.api_keyMCP_API_KEY
server.auth.allowed_hostsMCP_ALLOWED_HOSTS
server.auth.disabledMCP_AUTH_DISABLED / --disable-auth

server.transports and the --transport CLI flag are mutually exclusive — declare transports in YAML or on the command line, not both.

Multiple connections (and zero connections)

A single config.yaml may define several named entries under connections: — for example a Confluent Cloud connection alongside a local Apache Kafka broker — and each tool call routes to the connection you address by id. There is no fixed ceiling on the number of connections. Ready-to-use two-connection starters pairing a read/write local-Docker connection with a read-only Cloud or Confluent Platform connection — across OAuth, API-key, and CP auth — live in sample_configs/.

One rule constrains the mix: you may pair as many type: direct connections as you like, but at most one may be type: oauth. The shared Confluent Cloud sign-in owns a single browser session and identity, so a second OAuth connection is rejected at startup with Multiple OAuth connections defined in configuration; only one is supported.

A config may also define no connections at all. The five connection-independent tools — search-product-docs, get-product-doc-page, explain-disabled-tools, list-configured-connections, and describe-configured-connection — stay enabled regardless, so an empty config still gives you documentation search plus the server-diagnostic tools.

This capability is YAML-only — the legacy env-var path can express only a single connection.

${VAR} interpolation

YAML values support ${VAR} and ${VAR:-default} substitution, so secrets can live in your shell environment (or a -e dotenv) while structure lives in the file:

connections:
  production:
    type: direct
    kafka:
      bootstrap_servers: "broker.confluent.cloud:9092"
      auth:
        type: api_key
        key: "${KAFKA_API_KEY}"
        secret: "${KAFKA_API_SECRET}"

A missing variable with no :-default is a parse-time error. Plain literals also work; interpolation is a convenience, not a requirement.

How env vars and .env files fit into the YAML world

Even after the legacy env-var-only configuration path retires, env vars keep doing real work for mcp-confluent. They have three jobs with different lifecycles — keep them straight when you read the rest of this document:

  1. Interpolation source for ${VAR} in YAML. Long-term supported. This is the recommended way to keep secrets out of version control: structure in config.yaml, secrets in your environment (or a dotenv loaded via -e).
  2. Linked-library passthrough. Long-term supported. Several libraries mcp-confluent depends on read environment variables directly — OpenSSL (SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, ...), cyrus-sasl (SASL_PATH), krb5 (KRB5_CONFIG, KRB5CCNAME, ...), undici (HTTPS_PROXY, NO_PROXY, ...). There is no YAML knob for these; they have to reach the server as env vars.
  3. Sole configuration source on the legacy path. Deprecated; will be removed in a near-future release. This is what happens when you start the server without -c — every KAFKA_API_KEY, FLINK_REST_ENDPOINT, etc. listed in Legacy env-var configuration is serving this job today and only this job.

The -e <file> CLI flag loads a dotenv file into the server's environment once, at startup, and the resulting values then feed all three jobs above. Same flag, three lifecycles:

# Job 1 only: YAML structure, secrets and linked-library vars in the dotenv.
npx @confluentinc/mcp-confluent -c config.yaml -e secrets.env

# Jobs 1 + 2: same as above; linked-library vars in the same dotenv.
npx @confluentinc/mcp-confluent -c config.yaml -e env-with-tls-and-secrets.env

# Job 3 (legacy): no -c, the server reads every known var from the dotenv.
npx @confluentinc/mcp-confluent -e legacy.env

Caveat — dynamic-linker vars. LD_LIBRARY_PATH, LD_PRELOAD, macOS DYLD_*, and anything else consumed before Node starts cannot be set via -e. Set those in the shell that launches mcp-confluent.

Authentication modes

Two type: values on a connection, with different ergonomics:

type: direct — API keys in YAML

Each service block carries its own auth: { type: api_key, key, secret }. The keys can live in YAML literals (fine for local dev), or — much more commonly — ${VAR} interpolated from the environment:

connections:
  prod:
    type: direct
    confluent_cloud:
      auth:
        type: api_key
        key: "${CONFLUENT_CLOUD_API_KEY}"
        secret: "${CONFLUENT_CLOUD_API_SECRET}"

type: oauth — Confluent Cloud login (PKCE)

The big new feature of this release. The server opens the Confluent Cloud sign-in page in your browser on the first tool call that needs Cloud access; the resulting session is reused for the rest of the process. No API keys to provision.

connections:
  ccloud-oauth:
    type: oauth

OAuth connections carry no service blocks. Resource IDs (cluster_id, environment_id, ...) that direct-mode connections pin in YAML instead flow in as tool arguments at call time. A server may define at most one OAuth connection, alongside any number of type: direct connections — see Multiple connections (and zero connections). Get a starter file via --init-oauth-config.

OAuth-eligible tools. Not every tool has been migrated to OAuth yet — REST-only categories (Connect, Tableflow, Flink, Metrics, Catalog & Tags) still require type: direct. The currently-supported list:

CategoryToolsNotes
Kafka (native)list-topics, create-topics, delete-topics, produce-message, consume-messagesPass cluster_id + environment_id as arguments. Schema Registry serialization works on produce/consume for AVRO, JSON, and PROTOBUF.
Kafka RESTget-topic-config, alter-topic-configPass clusterId + environmentId as arguments.
Schema Registrylist-schemas, create-schema, delete-schemaPass environment_id; the SR cluster and endpoint are auto-resolved.
Organizations, Environments & Clusterslist-organizations, list-environments, read-environment, list-clusters
Billinglist-billing-costs

HTTP/SSE transport security

HTTP and SSE transports require API-key authentication by default to prevent unauthorized access and DNS rebinding. Enable them by listing them in server.transports (or via --transport http,sse); auth is on unless you explicitly opt out.

Generating an API key

npx @confluentinc/mcp-confluent --generate-key

Add the result to server.auth.api_key:

server:
  transports: [http]
  auth:
    api_key: "${MCP_API_KEY}"

Clients pass it in the cflt-mcp-api-key header on every request:

curl -H "cflt-mcp-api-key: $MCP_API_KEY" http://localhost:8080/mcp

DNS-rebinding protection

The server validates the Host header against server.auth.allowed_hosts (default: [localhost, 127.0.0.1]):

server:
  auth:
    allowed_hosts: [localhost, 127.0.0.1, myhost.local]

It also binds to 127.0.0.1 by default — set server.http.host to expose it elsewhere.

Disabling auth (development only)

server:
  auth:
    disabled: true

Or use --disable-auth on the command line. Never disable auth in production or when the server is network-accessible.

Tool enablement: which block lights up what

Tools auto-enable based on which service blocks are present in the resolved configuration. Run --list-tools to see the live set for your config, or use the explain-disabled-tools MCP tool to ask the running server why a specific tool is missing.

Block(s) requiredTools enabled
(always on)search-product-docs, get-product-doc-page, explain-disabled-tools, list-configured-connections, config-help, describe-configured-connection
kafka with bootstrap_servers or type: oauthlist-topics, create-topics, delete-topics, produce-message, consume-messages
kafka with rest_endpoint + auth or type: oauthget-topic-config, alter-topic-config
schema_registry or type: oauthlist-schemas, create-schema, delete-schema
flinkcreate-flink-statement, list-flink-statements, get-flink-statement-results, delete-flink-statements, get-flink-statement-exceptions, check-flink-statement-health, detect-flink-statement-issues, list-flink-catalogs, list-flink-databases, list-flink-tables, describe-flink-table, get-flink-table-info
flink + telemetryget-flink-statement-profile
tableflowcreate-tableflow-topic, list-tableflow-topics, read-tableflow-topic, update-tableflow-topic, delete-tableflow-topic, list-tableflow-regions, create-tableflow-catalog-integration, list-tableflow-catalog-integrations, read-tableflow-catalog-integration, update-tableflow-catalog-integration, delete-tableflow-catalog-integration
confluent_cloud or type: oauthlist-organizations, list-environments, read-environment, list-clusters, list-billing-costs
confluent_cloud (direct only)list-connectors, get-connector-config, get-connector-offsets, get-connector-status, get-connector-tasks, get-connector-error-summary, get-connector-error-recommendations, get-connector-logs, delete-connector, pause-connector, resume-connector, restart-connector, update-connector-config
confluent_cloud + kafka.auth (direct only)create-connector
schema_registry with api-key auth (direct only)search-topics-by-tag, search-topics-by-name, create-topic-tags, delete-tag, remove-tag-from-entity, add-tags-to-topic, list-tags
telemetrylist-available-metrics, query-metrics

Legacy env-var configuration (deprecated)

If you are starting fresh, skip this section — use Quick start (YAML) instead.

What "deprecated" means here. Setting credentials and endpoints purely via env vars (job 3 in How env vars and .env files fit into the YAML world) will emit a startup warning in a near-future release and be removed a release or two later. Using env vars to feed ${VAR} interpolation in YAML (job 1) and to pass through linked-library settings (job 2) is long-term supported and unaffected.

Run the server without -c and these variables, read either from the parent shell or from a -e <file> dotenv, populate the same internal configuration the YAML path would build.

Show full variable list
VariableDescriptionDefaultRequired
HTTP_HOSTHost to bind for HTTP transport. Defaults to localhost only for security.127.0.0.1No
HTTP_MCP_ENDPOINT_PATHHTTP endpoint path for MCP transport (e.g. /mcp)./mcpNo
HTTP_PORTPort to use for HTTP transport.8080No
LOG_LEVELLog level for application logging (trace, debug, info, warn, error, fatal).infoNo
MCP_API_KEYAPI key for HTTP/SSE authentication. Generate with --generate-key. Required when auth is enabled.If HTTP/SSE without --disable-auth
MCP_AUTH_DISABLEDDisable authentication for HTTP/SSE transports. Development only.falseNo
MCP_ALLOWED_HOSTSComma-separated Host header allowlist for DNS rebinding protection.localhost,127.0.0.1No
SSE_MCP_ENDPOINT_PATHSSE endpoint path for establishing SSE connections./sseNo
SSE_MCP_MESSAGE_ENDPOINT_PATHSSE endpoint path for receiving messages./messagesNo
BOOTSTRAP_SERVERSComma-separated Kafka broker addresses (host1:port1,host2:port2).For Kafka tools
CONFLUENT_CLOUD_API_KEYConfluent Cloud control-plane API key.For CCloud tools
CONFLUENT_CLOUD_API_SECRETConfluent Cloud control-plane API secret.For CCloud tools
CONFLUENT_CLOUD_REST_ENDPOINTBase URL for Confluent Cloud's REST API.https://api.confluent.cloudNo
FLINK_API_KEYConfluent Cloud Flink API key.For Flink tools
FLINK_API_SECRETConfluent Cloud Flink API secret.For Flink tools
FLINK_CATALOG_NAMEFlink catalog name used as sql.current-catalog. Typically the CCloud environment's display name.No
FLINK_COMPUTE_POOL_IDFlink compute pool ID; must start with lfcp-.For Flink tools
FLINK_DATABASE_NAMEDefault Flink database, used as sql.current-database.No
FLINK_ENV_IDFlink environment ID; must start with env-.For Flink tools
FLINK_ORG_IDConfluent Cloud organization ID.For Flink tools
FLINK_REST_ENDPOINTBase URL for Confluent Cloud's Flink REST API.For Flink tools
KAFKA_API_KEYKafka SASL username.For authenticated Kafka
KAFKA_API_SECRETKafka SASL password.For authenticated Kafka
KAFKA_CLUSTER_IDKafka cluster ID within Confluent Cloud.For Kafka REST tools
KAFKA_ENV_IDEnvironment ID for the Kafka cluster; must start with env-.For Kafka REST tools
KAFKA_REST_ENDPOINTREST endpoint for Kafka cluster management.For Kafka REST tools
SCHEMA_REGISTRY_API_KEYSchema Registry API key.For Schema Registry tools
SCHEMA_REGISTRY_API_SECRETSchema Registry API secret.For Schema Registry tools
SCHEMA_REGISTRY_ENDPOINTSchema Registry endpoint URL.For Schema Registry tools
TABLEFLOW_API_KEYTableflow API key.For Tableflow tools
TABLEFLOW_API_SECRETTableflow API secret.For Tableflow tools
TELEMETRY_ENDPOINTBase URL for the Confluent Cloud Telemetry (Metrics) API.https://api.telemetry.confluent.cloudNo
TELEMETRY_API_KEYTelemetry API key; falls back to CONFLUENT_CLOUD_API_KEY if unset. See the Metrics API auth docs.No
TELEMETRY_API_SECRETTelemetry API secret; falls back to CONFLUENT_CLOUD_API_SECRET if unset.No
DO_NOT_TRACKSet to true to opt out of anonymous telemetry and Sentry error reporting. See Telemetry. Wins over server.do_not_track in YAML.No

To migrate, run --init-config, then translate each variable you currently set into the matching block from config.example.yaml. For a side-by-side, every ${VAR:-...} placeholder in config.example.yaml names the env var that field used to come from. You can keep secrets in your existing .env and reference them via ${VAR} from the YAML — that is job 1 above, and is the recommended migration target.

Troubleshooting

Tools not appearing. The tool's required service block is not present in your resolved config. Run --list-tools to see the live set, or call the explain-disabled-tools MCP tool from your client — it prints exactly which YAML block or field is missing for each disabled tool. When you have a specific tool in mind, call config-help with that tool name to get the exact YAML snippet to add, per connection, to unlock it.

${VAR} parse-time error. A ${VAR} reference resolved to no value and had no ${VAR:-default}. Either export the variable, pass it through -e, or add a default.

Authentication errors on HTTP/SSE. Generate a key with --generate-key, put it in server.auth.api_key, and pass it in the cflt-mcp-api-key header on every request. Or run with server.auth.disabled: true if and only if you are on localhost-only development. See HTTP/SSE transport security.

HTTP_PORT / port conflicts. The default is 8080. Set server.http.port (YAML) or HTTP_PORT (env-var path) to something else.

Tableflow authorization errors. Tableflow tools require IAM roles in your cloud account that allow the Flink runtime to access your storage and catalog. See the Confluent Tableflow quick start.