Skip to main content
Glama
Arize-ai

@arizeai/phoenix-mcp

Official
by Arize-ai
configuration.md20.3 kB
# Configuration Reference **⚠️ CRITICAL: These environment variable names are ONE-WAY door decisions. Once released, changing them breaks user configurations.** ## Environment Variable Contract Summary | Variable | Required? | Default | Type | Notes | |----------|-----------|---------|------|-------| | `PHOENIX_LDAP_HOST` | ✅ **Required** | - | string | Comma-separated for failover | | `PHOENIX_LDAP_PORT` | Optional | `389` (starttls) or `636` (ldaps) | int | Defaults based on `tls_mode` | | `PHOENIX_LDAP_TLS_MODE` | Optional | `starttls` | `none\|starttls\|ldaps` | TLS connection mode | | `PHOENIX_LDAP_TLS_VERIFY` | Optional | `true` | boolean | **Always true in production** | | `PHOENIX_LDAP_TLS_CA_CERT_FILE` | Optional | - | path | Custom CA certificate (PEM) for private CAs | | `PHOENIX_LDAP_TLS_CLIENT_CERT_FILE` | Optional | - | path | Client certificate (PEM) for mutual TLS | | `PHOENIX_LDAP_TLS_CLIENT_KEY_FILE` | Optional | - | path | Client private key (PEM) for mutual TLS | | `PHOENIX_LDAP_BIND_DN` | Optional | - | string | Service account (required for search-and-bind) | | `PHOENIX_LDAP_BIND_PASSWORD` | Optional | - | string | Service account password | | `PHOENIX_LDAP_USER_SEARCH_BASE_DNS` | ✅ **Required** | - | JSON array | `'["ou=users,dc=example,dc=com"]'` | | `PHOENIX_LDAP_USER_SEARCH_FILTER` | Optional | `(&(objectClass=user)(sAMAccountName=%s))` | string | `%s` = username placeholder | | `PHOENIX_LDAP_ATTR_EMAIL` | Optional | `mail` | string | Email attribute name | | `PHOENIX_LDAP_ATTR_DISPLAY_NAME` | Optional | `displayName` | string | Display name attribute | | `PHOENIX_LDAP_ATTR_MEMBER_OF` | Optional | `memberOf` | string | Group membership attribute (used when `GROUP_SEARCH_FILTER` not set) | | `PHOENIX_LDAP_GROUP_SEARCH_BASE_DNS` | Conditional | - | JSON array | Required when `GROUP_SEARCH_FILTER` is set | | `PHOENIX_LDAP_GROUP_SEARCH_FILTER` | Optional | - | string | When set, enables POSIX mode (ignores `ATTR_MEMBER_OF`) | | `PHOENIX_LDAP_GROUP_SEARCH_FILTER_USER_ATTR` | Optional | - | string | Attribute to substitute in GROUP_SEARCH_FILTER (default: login username) | | `PHOENIX_LDAP_GROUP_ROLE_MAPPINGS` | ✅ **Required** | `[]` | JSON array | **Grafana-compatible format** | | `PHOENIX_LDAP_ALLOW_SIGN_UP` | Optional | `true` | boolean | Auto-create users on first login | | `PHOENIX_LDAP_ATTR_UNIQUE_ID` | Optional | - | string | Immutable ID (only if expecting email changes) | ### TLS Mode Default - Phoenix defaults `PHOENIX_LDAP_TLS_MODE` to `starttls` so deployments can connect over the commonly open port 389 while still upgrading to TLS before any bind. - This matches what most Active Directory/OpenLDAP environments expect out of the box and avoids asking operators to expose port 636 unless they explicitly choose LDAPS. - The implementation always calls `start_tls()` before `bind()` (see [Protocol Compliance](./protocol-compliance.md#7-starttls-implementation--security)), and the docker MITM profile continuously verifies that StartTLS traffic stays encrypted, so the default remains a secure channel rather than plaintext. - Admins who prefer TLS-from-byte-zero can set `PHOENIX_LDAP_TLS_MODE=ldaps`, which automatically defaults the port to 636 in `LDAPConfig.from_env`. **Key Design Decisions** (verified against Grafana source code): - ✅ **Prefix**: `PHOENIX_LDAP_*` matches Phoenix conventions, no conflict with Grafana - ✅ **Boolean values**: String `"true"`/`"false"` (not `1`/`0`) for clarity - ✅ **JSON format**: `GROUP_ROLE_MAPPINGS` uses Grafana's `GroupToOrgRole` structure - ✅ **Defaults**: Match Grafana defaults where applicable (timeout: 10s, port: 389, etc.) - ⚠️ **Limitation**: Replica-only multi-server (all servers must share config) ## Phoenix vs. Grafana Configuration Comparison This table maps Phoenix's environment variables to Grafana's TOML configuration file structure, helping users migrate between systems. | **Configuration** | **Phoenix (Environment Variables)** | **Grafana (ldap.toml)** | **Notes** | |-------------------|-------------------------------------|-------------------------|-----------| | **Enable LDAP** | `PHOENIX_LDAP_HOST` (presence) | `[auth.ldap]`<br>`enabled = true`<br>`config_file = "/etc/grafana/ldap.toml"` | Phoenix: Auto-enabled when HOST is set<br>Grafana: Requires explicit enable + file path | | **Server Host** | `PHOENIX_LDAP_HOST="dc1.com,dc2.com"` | `[[servers]]`<br>`host = "dc1.com"`<br>`[[servers]]`<br>`host = "dc2.com"` | Phoenix: Comma-separated string<br>Grafana: Multiple `[[servers]]` blocks | | **Server Port** | `PHOENIX_LDAP_PORT=389` | `port = 389` | Both default to 389 | | **TLS Mode** | `PHOENIX_LDAP_TLS_MODE="starttls"` | `use_ssl = false`<br>`start_tls = false` (default) | Phoenix: Single TLS_MODE with StartTLS default<br>Grafana: Separate SSL/StartTLS flags; StartTLS disabled until explicitly enabled | | **TLS Verification** | `PHOENIX_LDAP_TLS_VERIFY="true"` | `ssl_skip_verify = false` | Both verify certificates by default | | **Custom CA Cert** | `PHOENIX_LDAP_TLS_CA_CERT_FILE="/path/to/ca.pem"` | `root_ca_cert = "/path/to/ca.pem"` | Identical semantics for private CAs | | **Client Cert (mTLS)** | `PHOENIX_LDAP_TLS_CLIENT_CERT_FILE="/path/to/client.crt"`<br>`PHOENIX_LDAP_TLS_CLIENT_KEY_FILE="/path/to/client.key"` | `client_cert = "/path/to/client.crt"`<br>`client_key = "/path/to/client.key"` | Identical semantics for mutual TLS | | **Bind DN** | `PHOENIX_LDAP_BIND_DN="cn=admin,..."` | `bind_dn = "cn=admin,..."` | Identical semantics | | **Bind Password** | `PHOENIX_LDAP_BIND_PASSWORD="secret"` | `bind_password = "secret"`<br>or `bind_password = '${ENV_VAR}'` | Phoenix: Direct env var<br>Grafana: TOML value or interpolation | | **User Search Base** | `PHOENIX_LDAP_USER_SEARCH_BASE_DNS='["ou=users,..."]'` | `search_base_dns = ["ou=users,dc=..."]` | Both: JSON/TOML array of DNs | | **User Search Filter** | `PHOENIX_LDAP_USER_SEARCH_FILTER="(uid=%s)"` | `search_filter = "(uid=%s)"` | Identical: `%s` = username | | **Email Attribute** | `PHOENIX_LDAP_ATTR_EMAIL="mail"` | `[servers.attributes]`<br>`email = "mail"` | Phoenix: Direct env var<br>Grafana: Nested TOML section | | **Name Attribute** | `PHOENIX_LDAP_ATTR_DISPLAY_NAME="displayName"` | `name = "displayName"` | Both map display name | | **Unique ID** | `PHOENIX_LDAP_ATTR_UNIQUE_ID="objectGUID"` | Not supported | Phoenix: Optional immutable identifier for email change resilience | | **Group Membership** | `PHOENIX_LDAP_ATTR_MEMBER_OF="memberOf"` | `member_of = "memberOf"` | Active Directory attribute | | **Group Search Base** | `PHOENIX_LDAP_GROUP_SEARCH_BASE_DNS='["ou=groups,..."]'` | `group_search_base_dns = ["ou=groups,dc=..."]` | Both: JSON/TOML array of DNs | | **Group Search Filter** | `PHOENIX_LDAP_GROUP_SEARCH_FILTER="(member=%s)"` | `group_search_filter = "(member=%s)"` | Identical: `%s` = substituted value | | **Group Filter User Attr** | `PHOENIX_LDAP_GROUP_SEARCH_FILTER_USER_ATTR="uid"` | `group_search_filter_user_attribute = "uid"` | Attribute value to substitute for `%s` in GROUP_SEARCH_FILTER. When not set, uses login username. | | **Group→Role Mapping** | `PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='[`<br>` {"group_dn": "cn=admins,...", "role": "ADMIN"}`<br>`]'` | `[[servers.group_mappings]]`<br>`group_dn = "cn=admins,..."`<br>`org_role = "Admin"` | Phoenix: JSON with `role` (no org)<br>Grafana: TOML with `org_role` | | **Allow Sign-Up** | `PHOENIX_LDAP_ALLOW_SIGN_UP="true"` | `[auth.ldap]`<br>`allow_sign_up = true` | Both default to `true` | | **Timeout** | Not exposed (uses ldap3 defaults) | `timeout = 10` | Grafana: Configurable in seconds | | **Connection Pooling** | Not exposed (uses ldap3 defaults) | Not configurable | Both use underlying library defaults | **Key Differences**: 1. **Configuration Method** - Phoenix: Pure environment variables (12-factor app pattern) - Grafana: TOML configuration file with optional env var interpolation 2. **Multi-Server Configuration** - Phoenix: Comma-separated replicas (identical config only) - Grafana: Multiple `[[servers]]` blocks (can have different configs per server) 3. **Role Mapping Format** - Phoenix: JSON with `role` field, uppercase values (`ADMIN`, `MEMBER`, `VIEWER`) - Grafana: TOML with `org_role` field, capitalized values (`Admin`, `Editor`, `Viewer`) 4. **Migration Path** - Grafana → Phoenix: Must convert TOML to env vars manually - Cannot reuse `ldap.toml` files directly - Role mapping requires case conversion (`Admin` → `ADMIN`, etc.) **Compatibility Notes**: - **Grafana users**: Cannot directly reuse `ldap.toml` files (must convert to env vars) - **Field names**: Grafana uses `org_role`, Phoenix uses `role` (no org concept) - **Role values**: Grafana uses capitalized (`Admin`, `Editor`, `Viewer`), Phoenix uses uppercase (`ADMIN`, `MEMBER`, `VIEWER`) **Common Configuration Mistakes to Avoid**: | ❌ **WRONG** | ✅ **CORRECT** | Reason | |-------------|---------------|---------| | `PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='{"admin":["..."]}'` | `PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='[{"group_dn":"...", "role":"ADMIN"}]'` | Must use JSON array structure with required fields | | `"role": "admin"` (lowercase) | `"role": "ADMIN"` (uppercase) | Phoenix uses uppercase role names | | `"role": "ADMIN"` (Grafana field) | `"role": "ADMIN"` (Phoenix field) | Phoenix uses `role`, not `org_role` (no org concept) | | `PHOENIX_LDAP_TLS_MODE=starttls` (unquoted) | `PHOENIX_LDAP_TLS_MODE="starttls"` (quoted) | Shell may require quotes for strings | | `PHOENIX_LDAP_PORT="389"` (string) | `PHOENIX_LDAP_PORT=389` (int) | Port should be unquoted integer | | Multiple `PHOENIX_LDAP_HOST` lines | `PHOENIX_LDAP_HOST="dc1.com,dc2.com"` | Use comma-separated, not multiple vars | | `%s` in `GROUP_SEARCH_FILTER` for username | `%s` in `USER_SEARCH_FILTER` | User filter uses username; group filter uses login username by default (or attribute from GROUP_SEARCH_FILTER_USER_ATTR) | **Validation Checklist** (implemented in code): 1. ✅ `PHOENIX_LDAP_HOST` is not empty (config.py:1423) 2. ✅ `PHOENIX_LDAP_GROUP_ROLE_MAPPINGS` is valid JSON array (config.py:1433-1442) 3. ✅ Each mapping has `group_dn` and `role` fields (config.py:1457-1476) 4. ✅ `role` values are `"ADMIN"`, `"MEMBER"`, or `"VIEWER"` (case-insensitive) (config.py:1449) 5. ✅ If `GROUP_SEARCH_FILTER` is set, `GROUP_SEARCH_BASE_DNS` must also be set 6. ✅ `TLS_MODE` is either `"starttls"` or `"ldaps"` (config.py:1484-1488) 7. ⚠️ **Security**: Warn if `TLS_MODE=none` or `TLS_VERIFY=false` in production (config.py) --- ## Complete Environment Variables ```bash # ============================================================================== # LDAP Server Connection # ============================================================================== # LDAP server hosts (comma-separated for multiple servers) # Examples: # - "ldap.example.com" # - "dc1.corp.com,dc2.corp.com,dc3.corp.com" PHOENIX_LDAP_HOST="ldap.corp.example.com" # LDAP server port (default: 389 for StartTLS, 636 for LDAPS) PHOENIX_LDAP_PORT=389 # Use TLS (recommended: always true for production) # Options: "none", "starttls", "ldaps" PHOENIX_LDAP_TLS_MODE="starttls" # TLS mode: "starttls" or "ldaps" # - starttls: Start with plaintext, upgrade to TLS (port 389) # - ldaps: TLS from the start (port 636) PHOENIX_LDAP_TLS_MODE="starttls" # Verify TLS certificates (recommended: always true for production) # Options: "true" or "false" PHOENIX_LDAP_TLS_VERIFY="true" # ============================================================================== # Bind Configuration (Service Account) # ============================================================================== # Service account DN for binding (optional for direct bind) # Examples: # - Active Directory: "CN=svc-phoenix,OU=Service Accounts,DC=corp,DC=com" # - OpenLDAP: "cn=readonly,dc=example,dc=com" PHOENIX_LDAP_BIND_DN="CN=svc-phoenix,OU=Service Accounts,DC=corp,DC=com" # Service account password PHOENIX_LDAP_BIND_PASSWORD="service-account-password-here" # ============================================================================== # User Search Configuration # ============================================================================== # JSON array of base DNs for user searches (searched in order until user found) # Examples: # - '["OU=Users,DC=corp,DC=com"]' # - '["OU=Employees,DC=corp,DC=com", "OU=Contractors,DC=corp,DC=com"]' PHOENIX_LDAP_USER_SEARCH_BASE_DNS='["OU=Users,DC=corp,DC=com"]' # User search filter (use %s as placeholder for username) # Examples: # - Active Directory: "(&(objectClass=user)(sAMAccountName=%s))" # - OpenLDAP: "(&(objectClass=inetOrgPerson)(uid=%s))" PHOENIX_LDAP_USER_SEARCH_FILTER="(&(objectClass=user)(sAMAccountName=%s))" # ============================================================================== # Attribute Mapping # ============================================================================== # LDAP attribute containing user's email # Examples: "mail", "userPrincipalName", "email" PHOENIX_LDAP_ATTR_EMAIL="mail" # LDAP attribute containing user's display name # Examples: "displayName", "cn", "name" PHOENIX_LDAP_ATTR_DISPLAY_NAME="displayName" # LDAP attribute containing group memberships (for Active Directory) # Examples: "memberOf" (Active Directory), "" (use group search for POSIX) PHOENIX_LDAP_ATTR_MEMBER_OF="memberOf" # ============================================================================== # User Identification (Optional) # ============================================================================== # Immutable unique ID attribute (only configure if you expect email changes) # Use cases: company rebranding, M&A, frequent name changes, compliance requirements # - Active Directory: "objectGUID" # - OpenLDAP: "entryUUID" # - 389 Directory Server: "nsUniqueId" # When not set (default), email is used as the identifier. # PHOENIX_LDAP_ATTR_UNIQUE_ID="objectGUID" # ============================================================================== # Group Search (for POSIX/OpenLDAP without memberOf) # ============================================================================== # JSON array of base DNs for group searches (groups collected from all bases) # Examples: # - '["OU=Groups,DC=corp,DC=com"]' # - '["OU=Groups,DC=corp,DC=com", "OU=Teams,DC=corp,DC=com"]' PHOENIX_LDAP_GROUP_SEARCH_BASE_DNS='["OU=Groups,DC=corp,DC=com"]' # Group search filter (use %s as placeholder for substituted value) # Example: "(&(objectClass=group)(member=%s))" PHOENIX_LDAP_GROUP_SEARCH_FILTER="(&(objectClass=group)(member=%s))" # Attribute from user entry to substitute for %s in GROUP_SEARCH_FILTER # When set: Reads the specified attribute from the user's LDAP entry # When not set (default): Uses the login username directly # # Common values: # - "uid": For POSIX groups using memberUid (contains uid attribute value) # - "dn" or "distinguishedName": For groups using member with full DNs (AD) # - Not set: Uses login username (works for most POSIX setups) # # Example POSIX (memberUid contains usernames): # PHOENIX_LDAP_GROUP_SEARCH_FILTER="(&(objectClass=posixGroup)(memberUid=%s))" # # GROUP_SEARCH_FILTER_USER_ATTR not set - uses login username # # Example AD (member contains full DNs): # PHOENIX_LDAP_GROUP_SEARCH_FILTER="(member:1.2.840.113556.1.4.1941:=%s)" # PHOENIX_LDAP_GROUP_SEARCH_FILTER_USER_ATTR="dn" # PHOENIX_LDAP_GROUP_SEARCH_FILTER_USER_ATTR="uid" # ============================================================================== # Group to Role Mappings (Grafana-Compatible Format) # ============================================================================== # Grafana-compatible format (verified against Grafana source code) # Based on: https://github.com/grafana/grafana/blob/main/pkg/services/ldap/settings.go # # Grafana's GroupToOrgRole struct (for reference): # type GroupToOrgRole struct { # GroupDN string `json:"group_dn"` // REQUIRED # OrgRole RoleType `json:"org_role"` // REQUIRED # OrgId int64 `json:"org_id"` // Optional (defaults to 1) # IsGrafanaAdmin *bool `json:"grafana_admin"` // Optional # } # # Phoenix JSON Schema (TypeScript notation): # Array<{ # group_dn: string; // REQUIRED: LDAP group DN or "*" for wildcard # role: "ADMIN" | "MEMBER" | "VIEWER"; // REQUIRED: Capitalized! # }> # # Phoenix Role Mapping (Direct - No Intermediary): # - "ADMIN" → ADMIN role (Phoenix) # - "MEMBER" → MEMBER role (Phoenix) # - "VIEWER" → VIEWER role (Phoenix) # # Note: Phoenix uses its internal role names directly in the configuration. # This differs from the original Grafana-compatible approach which used # Grafana role names ("Admin", "Editor", "Viewer") as an intermediary. # # Matching Logic (verified against Grafana helpers.go): # - Wildcard "*" matches ALL users (checked first) # - DN matching is case-insensitive (strings.EqualFold) # - First match wins (priority order matters!) # PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='[ {"group_dn": "CN=Phoenix Admins,OU=Groups,DC=corp,DC=com", "role": "ADMIN"}, {"group_dn": "CN=Phoenix Users,OU=Groups,DC=corp,DC=com", "role": "MEMBER"}, {"group_dn": "CN=Engineering,OU=Groups,DC=corp,DC=com", "role": "MEMBER"}, {"group_dn": "CN=Phoenix Viewers,OU=Groups,DC=corp,DC=com", "role": "VIEWER"}, {"group_dn": "*", "role": "VIEWER"} ]' # ============================================================================== # Sign-Up Control (Grafana-Compatible) # ============================================================================== # Allow automatic user creation on first LDAP login # Options: "true" (default, matches Grafana) or "false" # - "true": Any valid LDAP user can create an account on first login # - "false": Users must be pre-provisioned before first login: # * Via PHOENIX_ADMINS env var at startup # * Via the application's user management UI # Pre-provisioned users are matched by email on first LDAP login. # Security: Set to "false" in environments requiring pre-approved access PHOENIX_LDAP_ALLOW_SIGN_UP="true" ``` ## Configuration Examples **Example 1: Active Directory (Single Server)** ```bash PHOENIX_LDAP_HOST="ad.corp.com" PHOENIX_LDAP_PORT=389 PHOENIX_LDAP_TLS_MODE="starttls" PHOENIX_LDAP_BIND_DN="CN=svc-phoenix,OU=Service Accounts,DC=corp,DC=com" PHOENIX_LDAP_BIND_PASSWORD="password" PHOENIX_LDAP_USER_SEARCH_BASE_DNS='["OU=Users,DC=corp,DC=com"]' PHOENIX_LDAP_USER_SEARCH_FILTER="(&(objectClass=user)(sAMAccountName=%s))" PHOENIX_LDAP_ATTR_EMAIL="mail" PHOENIX_LDAP_ATTR_DISPLAY_NAME="displayName" PHOENIX_LDAP_ATTR_MEMBER_OF="memberOf" # Optional: Only set if you expect user emails to change (rebranding, M&A) # PHOENIX_LDAP_ATTR_UNIQUE_ID="objectGUID" PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='[ {"group_dn": "CN=Phoenix Admins,OU=Groups,DC=corp,DC=com", "role": "ADMIN"}, {"group_dn": "CN=Phoenix Users,OU=Groups,DC=corp,DC=com", "role": "MEMBER"} ]' ``` **Example 2: OpenLDAP (POSIX Groups)** ```bash PHOENIX_LDAP_HOST="ldap.example.com" PHOENIX_LDAP_PORT=389 PHOENIX_LDAP_TLS_MODE="starttls" PHOENIX_LDAP_BIND_DN="cn=readonly,dc=example,dc=com" PHOENIX_LDAP_BIND_PASSWORD="password" PHOENIX_LDAP_USER_SEARCH_BASE_DNS='["ou=people,dc=example,dc=com"]' PHOENIX_LDAP_USER_SEARCH_FILTER="(&(objectClass=inetOrgPerson)(uid=%s))" PHOENIX_LDAP_ATTR_EMAIL="mail" PHOENIX_LDAP_ATTR_DISPLAY_NAME="cn" # Note: When GROUP_SEARCH_FILTER is set, POSIX mode is enabled automatically # Optional: Only set if you expect user emails to change # PHOENIX_LDAP_ATTR_UNIQUE_ID="entryUUID" PHOENIX_LDAP_GROUP_SEARCH_BASE_DNS='["ou=groups,dc=example,dc=com"]' PHOENIX_LDAP_GROUP_SEARCH_FILTER="(&(objectClass=posixGroup)(memberUid=%s))" PHOENIX_LDAP_GROUP_ROLE_MAPPINGS='[ {"group_dn": "cn=phoenix-admins,ou=groups,dc=example,dc=com", "role": "ADMIN"}, {"group_dn": "*", "role": "VIEWER"} ]' ``` **Example 3: Multiple LDAP Servers (High Availability)** ```bash PHOENIX_LDAP_HOST="dc1.corp.com,dc2.corp.com,dc3.corp.com" PHOENIX_LDAP_PORT=389 PHOENIX_LDAP_TLS_MODE="starttls" # ... rest of configuration same as Example 1 ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Arize-ai/phoenix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server