Configuring Content Cortex AI Services to support external MCP clients

Configure the Content Cortex AI Services Core MCP Server to support external Model Context Protocol (MCP) clients by registering an OAuth client in IBM Identity Management and updating the CCXAIServices operator configuration.

Before you begin

Before you begin, make sure that you have the following prerequisites:

  • Admin access to Cloud Pak for Data
  • OpenShift CLI (oc) installed and logged in to your cluster
  • Access to the namespace where the MCP server is deployed

Set the following environment variables for your environment:


# Your Cloud Pak for Data URL
export CPD_URL="https://<your-cp4d-hostname>"

# Your MCP server public URL
export MCP_URL="https://<your-mcp-server-hostname>"

# Your OpenShift namespace where MCP server is deployed
export NAMESPACE="<your-namespace>"

Example:


export CPD_URL="https://cpd-adp.apps.rosier.cp.fyre.ibm.com"
export MCP_URL="https://core-mcp-adp.apps.rosier.cp.fyre.ibm.com"
export NAMESPACE="adp"

About this task

To configure Content Cortex AI Services to support external MCP clients, complete the following steps.

Note: The redirect_uris parameter must match the redirect URIs of your MCP client. For watsonx Orchestrate, see MCP servers.

Procedure

  1. Retrieve the OAuth admin password from the platform-oidc-credentials secret.
    OAUTH_ADMIN_SECRET=$(oc get secret platform-oidc-credentials -n $NAMESPACE \
      -o jsonpath='{.data.OAUTH2_CLIENT_REGISTRATION_SECRET}' | base64 -d)
  2. Generate unique OAuth client credentials for the MCP server.
    CLIENT_ID="mcp-server-$(openssl rand -hex 8)"
    CLIENT_SECRET="$(openssl rand -base64 32)"
    
    echo "Generated Client ID: $CLIENT_ID"
    echo "Generated Client Secret: $CLIENT_SECRET"
  3. Register an OAuth client in IBM Identity Management with appropriate redirect URIs for your MCP client (such as watsonx Orchestrate).
    curl -sk -X POST -u "oauthadmin:$OAUTH_ADMIN_SECRET" \
      -H "Content-Type: application/json" \
      -d "{
        \"client_id\": \"$CLIENT_ID\",
        \"client_secret\": \"$CLIENT_SECRET\",
        \"client_name\": \"CCX Core MCP Server OAuth Client\",
        \"token_endpoint_auth_method\": \"client_secret_basic\",
        \"scope\": \"openid profile email\",
        \"grant_types\": [\"authorization_code\", \"refresh_token\"],
        \"response_types\": [\"code\"],
        \"application_type\": \"web\",
        \"subject_type\": \"public\",
        \"redirect_uris\": [
          \"http://localhost:8002/oauth/callback\",
          \"http://localhost:8002/oauth/redirect\",
          \"http://localhost:8002/auth/callback\",
          \"http://127.0.0.1:8002/oauth/callback\",
          \"$MCP_URL/oauth/callback\",
          \"$MCP_URL/oauth/redirect\",
          \"$MCP_URL/auth/callback\"
        ],
        \"post_logout_redirect_uris\": [
          \"http://localhost:8002/logout\",
          \"$MCP_URL/logout\"
        ],
        \"preauthorized_scope\": \"openid profile email\",
        \"introspect_tokens\": true
      }" \
      $CPD_URL/oidc/endpoint/OP/registration | jq '.'
    Note: If you want to support non-Dynamic Client Registration (DCR) MCP clients (such as Watson Orchestrate), register a separate OAuth client for each non-DCR client. This provides better isolation and makes it easier to manage credentials for different client types.

    To register an additional OAuth client for non-DCR MCP clients:

    # Generate new credentials for the non-DCR client
    CLIENT_ID_NONDCR="mcp-server-nondcr-$(openssl rand -hex 8)"
    CLIENT_SECRET_NONDCR="$(openssl rand -base64 32)"
    
    echo "Generated Non-DCR Client ID: $CLIENT_ID_NONDCR"
    echo "Generated Non-DCR Client Secret: $CLIENT_SECRET_NONDCR"
    
    # Register the OAuth client with non-DCR client redirect URIs
    # Replace <your-non-dcr-client-redirect-uris> with actual redirect URIs from your MCP client's documentation
    curl -sk -X POST -u "oauthadmin:$OAUTH_ADMIN_SECRET" \
      -H "Content-Type: application/json" \
      -d "{
        \"client_id\": \"$CLIENT_ID_NONDCR\",
        \"client_secret\": \"$CLIENT_SECRET_NONDCR\",
        \"client_name\": \"CCX Core MCP Server OAuth Client (Non-DCR)\",
        \"token_endpoint_auth_method\": \"client_secret_basic\",
        \"scope\": \"openid profile email\",
        \"grant_types\": [\"authorization_code\", \"refresh_token\"],
        \"response_types\": [\"code\"],
        \"application_type\": \"web\",
        \"subject_type\": \"public\",
        \"redirect_uris\": [
          \"<your-non-dcr-client-redirect-uris>\"
        ],
        \"post_logout_redirect_uris\": [
          \"<your-non-dcr-client-logout-uris>\"
        ],
        \"preauthorized_scope\": \"openid profile email\",
        \"introspect_tokens\": true
      }" \
      $CPD_URL/oidc/endpoint/OP/registration | jq '.'
  4. Create a Kubernetes secret named mcp-oauth-client-secret to store the OAuth credentials.
    oc create secret generic mcp-oauth-client-secret -n $NAMESPACE \
      --from-literal=client_id="$CLIENT_ID" \
      --from-literal=client_secret="$CLIENT_SECRET"
  5. Optional: If you are using Redis for OAuth proxy storage, create a storage encryption key secret named ibm-core-mcp-secret.
    # Generate a Fernet encryption key
    ENCRYPTION_KEY=$(python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')
    
    # Create the secret
    oc create secret generic ibm-core-mcp-secret -n $NAMESPACE \
      --from-literal=storage_encryption_key="$ENCRYPTION_KEY"
  6. Update the ibm-ai-services-integration-config ConfigMap to reference the OAuth secret.
    oc patch configmap ibm-ai-services-integration-config -n $NAMESPACE --type=merge -p '{
      "data": {
        "oidc_client_secret": "mcp-oauth-client-secret"
      }
    }'
  7. Create a ConfigMap named core-mcp-override-custom-configmap with OAuth settings.
    Note: Configuration differs for ROKS and non-ROKS environments.

    For non-ROKs environments:

    oc apply -f - << EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: core-mcp-override-custom-configmap
      namespace: $NAMESPACE
    data:
      AUTH_MODE: "dual"
      OIDC_CONFIG_URL: "$CPD_URL/ccxai/mcpserver/.well-known/openid-configuration"
      OIDC_AUTHORIZATION_SERVER: "$CPD_URL/idprovider/v1/auth"
      OIDC_JWKS_URL: "$CPD_URL/auth/jwks"
      OIDC_ISSUER: "KNOXSSO,https://127.0.0.1:443/idauth/oidc/endpoint/OP"
      OIDC_REQUIRED_SCOPES: "openid,profile,email"
      MCP_SERVER_BASE_URL: "$MCP_URL"
    EOF

    For ROKS environments:

    oc apply -f - << EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: core-mcp-override-custom-configmap
      namespace: $NAMESPACE
    data:
      AUTH_MODE: "dual"
      OIDC_CONFIG_URL: "$CPD_URL/ccxai/mcpserver/.well-known/openid-configuration"
      OIDC_AUTHORIZATION_SERVER: "$CPD_URL/idprovider/v1/auth"
      OIDC_JWKS_URL: "$CPD_URL/auth/jwks"
      OIDC_ISSUER: "KNOXSSO,https://127.0.0.1:443/idauth/oidc/endpoint/OP"
      OIDC_REQUIRED_SCOPES: "openid,profile,email"
      MCP_SERVER_BASE_URL: "$MCP_URL"
      IDP_CERT_PATH: ""
    EOF
  8. Update the CCXAIServices custom resource to use the override ConfigMap.
    oc patch ccxaiservices ai-services -n $NAMESPACE --type=merge -p '{
      "spec": {
        "core_mcp_server_configuration": {
          "custom_configmap": [
            {
              "name": "core-mcp-override-custom-configmap",
              "is_env": true
            }
          ]
        }
      }
    }'
  9. Restart the MCP server pod to apply the new configuration.
    # Delete the pod (it will be recreated by the deployment)
    oc delete pod -n $NAMESPACE -l app.kubernetes.io/name=ibm-core-mcp-server-deploy
    
    # Wait for new pod to be ready
    oc wait --for=condition=ready pod -n $NAMESPACE -l app.kubernetes.io/name=ibm-core-mcp-server-deploy --timeout=120s

Results

After you complete the steps, the Content Cortex AI Services Core MCP Server is configured to support external MCP clients with OAuth authentication.

What to do next

Verify that all resources were created correctly:

  • Secrets exist: mcp-oauth-client-secret and ibm-core-mcp-secret (Redis only)
  • ConfigMaps exist: ibm-ai-services-integration-config and core-mcp-override-custom-configmap
  • CCXAIServices custom resource ai-services is updated
  • MCP server pod is running with correct environment variables (AUTH_MODE=dual and MCP_SERVER_BASE_URL set correctly)