Installation
IBM Verify MCP Server — Installation Guide
Overview
This guide walks you through deploying the IBM Verify MCP Server. Two deployment methods are supported:
| Method | Best for |
|---|---|
| Option A — Kubernetes | Production environments (single-pod; horizontal scaling is not yet supported) |
| Option B — Local Docker | Testing and evaluation |
🚀 Quick start: For a quick evaluation of the server, see Option B: Local Docker Deployment. Switch to Option A when you are ready for production.
Prerequisites
Before proceeding, ensure you have completed the Tenant Setup Prerequisites and have:
- ✅ IBM Verify tenant configured with MCP Server applications
- ✅ Client IDs and secrets for the Subject, STS, and Actor applications — see Tenant Setup
- ✅ Users entitled to the "MCP Server Subject" application
- ✅ Docker or Kubernetes environment ready
- ✅ Nginx setup if deploying on a VM or Fyre environment
Step 1: Pull the Docker Image
The IBM Verify MCP Server is distributed as a Docker image from IBM Container Registry.
Image details:
- Registry:
icr.io/ibm-verify - Image:
verify-mcp-server - Tag:
latest - Full reference:
icr.io/ibm-verify/verify-mcp-server:latest
docker pull icr.io/ibm-verify/verify-mcp-server:latest
# Verify the pull succeeded
docker images | grep verify-mcp-serverExpected output:
icr.io/ibm-verify/verify-mcp-server latest <image-id> <size> <time>Step 2: Create deployment configuration file
config.yaml is the single source of truth for all server configuration. Every string value is processed before the server starts — values that begin with a recognized prefix are resolved from their source at startup; values with no prefix are used as-is.
Supported value prefixes
| Prefix | Syntax | What it does | Requires |
|---|---|---|---|
| (none) | key: my-value | Used as-is | — |
!envvar | !envvar VAR_NAME | Reads from the named environment variable | — |
!base64 | !base64 <encoded> | Base64-decodes the payload | — |
!file | !file /path/to/file | Reads entire contents of a file | File accessible inside the container |
!enc | !enc <base64-ciphertext> | RSA/PKCS#1-v1.5 decryption | ENC_KEY env var |
!obf | !obf <base64-ciphertext> | AES-256-CBC decryption | secrets.obf_key in config.yaml |
!secret | !secret <secret-name>/<field> | Reads a field from a Kubernetes Secret | K8s cluster + RBAC |
!configmap | !configmap <configmap-name>/<field> | Reads a field from a Kubernetes ConfigMap | K8s cluster + RBAC |
!base64 is encoding, not encryption. Do not use it to protect secrets in production — use !enc or !obf instead.
You can mix prefixes freely within a single file.
Using
!encor!obffor secrets: These prefixes encrypt credentials at rest insideconfig.yamland are recommended for production.!encrequires an RSA key pair;!obfrequires a shared passphrase. See Encryption & Obfuscation for step-by-step instructions on generating encrypted values and supplying the keys.
config.yaml template
config.yaml templateThe following template uses !secret and !configmap — the recommended model for Kubernetes. Templates for other deployment methods (Docker, .env file) are provided in the relevant deployment sections.
# IBM Verify OAuth MCP Server — Configuration File
#
# Every string value supports a normalization prefix that controls how it is
# resolved at startup. Mix and match freely within this file.
#
# PREFIX REFERENCE:
# plain value — used as-is (safe for non-sensitive config)
# !envvar VAR_NAME — reads from environment variable VAR_NAME
# !base64 <encoded> — base64-decodes the payload inline
# !file /path/to/file — reads the entire contents of a file
# !enc <base64-ciphertext> — RSA decryption; requires ENC_KEY env var
# ⚠ Only needed when using !enc-prefixed values.
# See: Encryption & Obfuscation guide.
# !obf <base64-ciphertext> — AES-256-CBC decryption; requires secrets.obf_key
# ⚠ Only needed when using !obf-prefixed values.
# See: Encryption & Obfuscation guide.
# !secret <name>/<field> — reads from a Kubernetes Secret (K8s only)
# !configmap <name>/<field> — reads from a Kubernetes ConfigMap (K8s only)
#
# Default location: config.yaml in the working directory.
# ---------------------------------------------------------------------------
# Secrets
# Only required when using !obf-prefixed values. obf_key is the passphrase
# used to decrypt them. Remove this entire section if you are not using !obf.
# ---------------------------------------------------------------------------
secrets:
obf_key: !secret verify-mcp-credentials/obf_key
# ---------------------------------------------------------------------------
# Server — network binding.
# ---------------------------------------------------------------------------
server:
host: 0.0.0.0 # Listen on all interfaces
port: 8000
# ---------------------------------------------------------------------------
# IBM Verify — OAuth credentials and endpoint configuration.
#
# Required fields:
# tenant_url, client_id, client_secret, base_url,
# sts_client_id, sts_client_secret, actor_client_id, actor_client_secret
#
# Optional fields are auto-constructed from tenant_url if omitted.
# ---------------------------------------------------------------------------
ibm_verify:
# IBM Verify tenant base URL
# Example: https://my-tenant.verify.ibm.com
tenant_url: !configmap verify-mcp-config/IBM_VERIFY_TENANT_URL
# tenant_url: https://my-tenant.verify.ibm.com # plain text alternative
# Subject application OAuth credentials
client_id: !secret verify-mcp-credentials/client-id
# client_id: my-subject-client-id # plain text example
client_secret: !secret verify-mcp-credentials/client-secret
# client_secret: my-subject-client-secret # plain text; use !enc or !obf in production
# Public URL of this MCP server — used as the OAuth redirect base.
# Use http://localhost:8000 for local testing.
# Use your public HTTPS hostname for production deployments.
base_url: https://mcp-server.example.com
# STS client for token exchange
sts_client_id: !secret verify-mcp-credentials/sts-client-id
# sts_client_id: my-sts-client-id # plain text example
sts_client_secret: !secret verify-mcp-credentials/sts-client-secret
# sts_client_secret: my-sts-client-secret # plain text; use !enc or !obf in production
# Actor client for enhanced audit logging
actor_client_id: !secret verify-mcp-credentials/actor-client-id
# actor_client_id: my-actor-client-id # plain text example
actor_client_secret: !secret verify-mcp-credentials/actor-client-secret
# actor_client_secret: my-actor-client-secret # plain text; use !enc or !obf in production
# Recommended for production: restrict allowed OAuth redirect URIs.
# If unset, any redirect URI is accepted — open-redirect risk.
# allowed_redirect_uris: https://client.mycompany.com/callback
# Optional — auto-constructed from tenant_url if omitted
# oidc_config_url: https://...
# token_exchange_url: https://...
# introspection_url: https://...
scopes: openid profile email
# ---------------------------------------------------------------------------
# Transport
# The only accepted value is streamable-http.
# ---------------------------------------------------------------------------
transport:
type: streamable-http
# authentication_method: oidc_proxy # default; change to direct_token if needed
# ---------------------------------------------------------------------------
# Cache — token cache (streamable-http only).
# ---------------------------------------------------------------------------
cache:
ttl: 900 # seconds
maxsize: 1000 # max cached tokens
# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
logging:
level: INFO # DEBUG | INFO | WARNING | ERROR | CRITICAL
# Log file inside the container.
file: /app/logs/mcp-server.log
max_bytes: 10485760 # 10 MB per file
backup_count: 5config.yaml Configuration Reference
config.yaml Configuration ReferenceAll the supported configuration parameters are listed as follows. Required parameters must be present or the server doesn't start.
secrets section
secrets section| Parameter | Type | Required | Description |
|---|---|---|---|
secrets.obf_key | string | Only if using !obf values | Passphrase used to decrypt !obf-prefixed values (AES-256-CBC/PBKDF2-SHA512). Never store in plain text — supply through !envvar or !secret. |
secrets:
obf_key: !envvar OBF_KEYserver section
server section| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
server.host | string | ✅ Yes | 0.0.0.0 | Network interface the server listens on. |
server.port | integer | ✅ Yes | 8000 | Port the server listens on. |
ibm_verify section
ibm_verify section| Parameter | Type | Required | Description |
|---|---|---|---|
ibm_verify.tenant_url | string | ✅ Yes | IBM Verify tenant base URL. Example: https://mytenant.verify.ibm.com |
ibm_verify.client_id | string | ✅ Yes | OAuth client ID for the Subject application. |
ibm_verify.client_secret | string | ✅ Yes | OAuth client secret. Use !enc or !obf in production. |
ibm_verify.base_url | string | ✅ Yes | Public URL of this MCP server, used as the OAuth redirect base (streamable-http only). |
ibm_verify.sts_client_id | string | ✅ Yes | Client ID for the STS application (token exchange). |
ibm_verify.sts_client_secret | string | ✅ Yes | Client secret for the STS application. Use !enc or !obf in production. |
ibm_verify.actor_client_id | string | ✅ Yes | Client ID for the Actor application (audit logging). |
ibm_verify.actor_client_secret | string | ✅ Yes | Client secret for the Actor application. Use !enc or !obf in production. |
ibm_verify.oidc_config_url | string | No | OIDC discovery document URL. Auto-constructed from tenant_url if omitted. |
ibm_verify.token_exchange_url | string | No | Token exchange endpoint URL. Auto-constructed from tenant_url if omitted. |
ibm_verify.introspection_url | string | No | Token introspection endpoint URL. Auto-constructed from tenant_url if omitted. |
ibm_verify.scopes | string | No | Space-separated OAuth scopes. Default: openid profile email |
ibm_verify.allowed_redirect_uris | string | No | Comma-separated list of allowed OAuth redirect URIs. Always set this in production to prevent open-redirect attacks. |
transport section
transport section| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
transport.type | string | ✅ Yes | streamable-http | Transport protocol. Only accepted value: streamable-http. |
transport.authentication_method | string | No | oidc_proxy | Authentication method for streamable-http. Accepted values: oidc_proxy, direct_token. |
cache section (streamable-http only)
cache section (streamable-http only)| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
cache.ttl | integer | No | 900 | Token cache time-to-live in seconds. |
cache.maxsize | integer | No | 1000 | Maximum number of tokens held in cache. |
logging section
logging section| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
logging.level | string | ✅ Yes | INFO | Log verbosity: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
logging.file | string | No | (stderr) | Path to log file inside the container. |
logging.max_bytes | integer | No | 10485760 | Maximum log file size before rotation (bytes). Default is 10 MB. |
logging.backup_count | integer | No | 5 | Number of rotated log file backups to retain. |
Step 3: Choose Your Deployment Method
Select the path that matches your environment:
- Option A: Kubernetes Deployment — for production clusters
- Option B: Local Docker Deployment — for development and testing
Option A: Kubernetes Deployment
This is the recommended deployment model for production. Credentials are resolved directly from Kubernetes Secrets at pod startup — they are never written to environment variables or the filesystem.
How it works
- Non-sensitive configuration (tenant URL, server port, etc.) is stored in a ConfigMap, created from
config.yaml. - All secrets (client credentials, obfuscation key) are stored in a Kubernetes Secret and referenced in
config.yamlby using the!secretprefix. - If you use
!enc-encrypted values, the RSA private key is stored in a separate Kubernetes Secret and referenced throughENC_KEY.
A.1 Prerequisites
- Kubernetes (v1.19+) or OpenShift cluster
kubectl/ocCLI configured to access your cluster- Ingress controller (nginx, Traefik, etc.) or an OpenShift Route for external access
- IBM Verify tenant credentials from Tenant Setup
A.2 Create a Namespace
NAMESPACE=verify-mcp-server
kubectl create namespace "${NAMESPACE}"A.3 Create the Credentials Secret
Store all sensitive values in a Kubernetes Secret. The config.yaml template in Step 2 references these by using !secret verify-mcp-credentials/<key>.
kubectl create secret generic verify-mcp-credentials \
--namespace="${NAMESPACE}" \
--from-literal=client-id='<subject-app-client-id>' \
--from-literal=client-secret='<subject-app-client-secret>' \
--from-literal=sts-client-id='<sts-app-client-id>' \
--from-literal=sts-client-secret='<sts-app-client-secret>' \
--from-literal=actor-client-id='<actor-app-client-id>' \
--from-literal=actor-client-secret='<actor-app-client-secret>'
# Add --from-literal=obf_key='<passphrase>' only if using !obf valuesUsing encryption: You can store
!enc <ciphertext>values here instead of plain text. See Encryption & Obfuscation for instructions on generating ciphertexts.
Update config.yaml to set all client IDs and secrets to reference the secrets created above:
ibm_verify:
client_id: !secret verify-mcp-credentials/client-id
client_secret: !secret verify-mcp-credentials/client-secret
sts_client_id: !secret verify-mcp-credentials/sts-client-id
sts_client_secret: !secret verify-mcp-credentials/sts-client-secret
actor_client_id: !secret verify-mcp-credentials/actor-client-id
actor_client_secret: !secret verify-mcp-credentials/actor-client-secret
# only if using !obf values is used
secrets:
obf_key: !secret verify-mcp-credentials/obf_keyA.4 Create the RSA Private Key Secret (only if using !enc)
!enc)Step A.4 is required only when any value in your config.yaml uses the !enc prefix. The RSA private key is used to decrypt the !enc-prefixed values at startup.
kubectl create secret generic verify-mcp-enc-key \
--namespace="${NAMESPACE}" \
--from-file=private.pem=./private.pemThe deployment manifest (Step A.6) sets ENC_KEY to reference this secret:
- name: ENC_KEY
value: "!secret verify-mcp-enc-key/private.pem"Keep
private.pemout of version control. Add it to.gitignoreand use a secrets manager for production deployments. The!secretapproach means the PEM content is fetched directly from the cluster — it never touches an env var or mounted file.
A.5 Create the ConfigMap from config.yaml
config.yamlIf your
config.yamluses!configmap verify-mcp-config/<key>references (as in the template above), create that ConfigMap:kubectl create configmap verify-mcp-config \ --namespace="${NAMESPACE}" \ --from-literal=IBM_VERIFY_TENANT_URL='https://mytenant.verify.ibm.com'
Update config.yaml to set tenant_url to reference the ConfigMap previously created:
ibm_verify:
tenant_url: !configmap verify-mcp-config/IBM_VERIFY_TENANT_URLValidate config.yaml, then load it into Kubernetes as a ConfigMap so it can be mounted into the container:
kubectl create configmap verify-mcp-files \
--namespace="${NAMESPACE}" \
--from-file=config.yaml=./config.yamlA.6 Apply the Deployment Manifest
Save the following as verify-mcp-server.yaml and apply it. Update the namespace, image tag, and base_url in config.yaml before applying.
# ============================================================================
# IBM Verify MCP Server — Kubernetes manifest
# Apply with: kubectl apply -f verify-mcp-server.yaml
# ============================================================================
# ----------------------------------------------------------------------------
# 1. ServiceAccount
# A dedicated ServiceAccount instead of the default namespace SA.
# ----------------------------------------------------------------------------
apiVersion: v1
kind: ServiceAccount
metadata:
name: verify-mcp-server-sa
namespace: verify-mcp-server
---
# ----------------------------------------------------------------------------
# 2. Role
# Grants the pod permission to read Secrets and ConfigMaps.
# Required for !secret and !configmap prefix resolution.
# ----------------------------------------------------------------------------
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: verify-mcp-server-role
namespace: verify-mcp-server
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
# ----------------------------------------------------------------------------
# 3. RoleBinding
# ----------------------------------------------------------------------------
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: verify-mcp-server-rolebinding
namespace: verify-mcp-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: verify-mcp-server-role
subjects:
- kind: ServiceAccount
name: verify-mcp-server-sa
namespace: verify-mcp-server
---
# ----------------------------------------------------------------------------
# 4. Deployment
# ----------------------------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: verify-mcp-server
namespace: verify-mcp-server
labels:
app: verify-mcp-server
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: verify-mcp-server
template:
metadata:
labels:
app: verify-mcp-server
version: v1
annotations:
instana.io/instrumentation: "false"
spec:
# securityContext can be updated as per security requirement
securityContext: {}
serviceAccountName: verify-mcp-server-sa
containers:
- name: verify-mcp-server
image: icr.io/ibm-verify/verify-mcp-server:latest
imagePullPolicy: Always
# DO NOT override command — doing so bypasses all prefix resolution
# and the server will fail to start.
ports:
- name: http
containerPort: 8000
protocol: TCP
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# ENC_KEY — required ONLY when config.yaml contains !enc-prefixed values.
# Points to the RSA private key used to decrypt those values.
# Using !secret here means the PEM content is fetched directly from the cluster secrets store — it never touches a mounted file or env var.
# Remove this entry entirely if you are not using !enc.
- name: ENC_KEY
value: "!secret verify-mcp-enc-key/private.pem"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
# Liveness probe — 30s delay allows config resolution to complete before the probe fires.
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: logs
mountPath: /app/logs
# config.yaml is mounted read-only from the verify-mcp-files ConfigMap.
- name: mcp-files
mountPath: /app/config/config.yaml
subPath: config.yaml
readOnly: true
volumes:
- name: logs
emptyDir: {}
- name: mcp-files
configMap:
name: verify-mcp-files
restartPolicy: Always
---
# ----------------------------------------------------------------------------
# 5. Service
# ----------------------------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: verify-mcp-server
namespace: verify-mcp-server
labels:
app: verify-mcp-server
spec:
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: verify-mcp-serverkubectl apply -f verify-mcp-server.yamlExpected output:
serviceaccount/verify-mcp-server-sa is created
role.rbac.authorization.k8s.io/verify-mcp-server-role is created
rolebinding.rbac.authorization.k8s.io/verify-mcp-server-rolebinding is created
deployment.apps/verify-mcp-server is created
service/verify-mcp-server is createdA.7 Expose through Ingress or Route
Add the appropriate resource to expose the service externally.
For OpenShift (Route):
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: verify-mcp-server
namespace: verify-mcp-server
labels:
app: verify-mcp-server
spec:
host: <external-hostname, e.g. verify-mcp-server.example.com>
to:
kind: Service
name: verify-mcp-server
weight: 100
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
wildcardPolicy: NoneFor standard Kubernetes (Ingress):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: verify-mcp-server
namespace: verify-mcp-server
labels:
app: verify-mcp-server
annotations:
# Adjust for your ingress controller, e.g.:
# nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: <external-hostname, e.g. verify-mcp-server.example.com>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: verify-mcp-server
port:
number: 8000A.8 Verify the Deployment
# Check all resources in the namespace
kubectl get all -n "${NAMESPACE}"
# Check configmaps and secrets
kubectl get configmap,secret -n "${NAMESPACE}"Expected output:
NAME READY STATUS RESTARTS AGE
pod/verify-mcp-server-xxxxxxxxxx-xxxxx 1/1 Running 0 30s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/verify-mcp-server ClusterIP 10.96.xxx.xxx <none> 8000/TCP 30s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/verify-mcp-server 1/1 1 1 30s
NAME DATA AGE
configmap/verify-mcp-config 1 30s
configmap/verify-mcp-files 1 30s
NAME TYPE DATA AGE
secret/verify-mcp-credentials Opaque 6 30s
secret/verify-mcp-enc-key Opaque 1 30sA.9 Test the Deployment
# Port-forward to test locally
kubectl port-forward -n "${NAMESPACE}" svc/verify-mcp-server 8000:8000
# In a separate terminal
curl http://localhost:8000/healthExpected response:
{
"status": "healthy",
"service": "IBM Verify MCP Server"
}If the health check fails, inspect logs:
kubectl logs -n "${NAMESPACE}" -l app=verify-mcp-server --tail=50. Common causes are missing or misconfigured secrets, or an unreachable IBM Verify tenant. See the Troubleshooting Guide.
A.10 Kubernetes Management Commands
View logs:
# Follow logs
kubectl logs -f -n "${NAMESPACE}" -l app=verify-mcp-server
# View last 100 lines
kubectl logs -n "${NAMESPACE}" -l app=verify-mcp-server --tail=100Update configuration:
# Recreate the ConfigMap from an updated config.yaml:
kubectl create configmap verify-mcp-files \
--namespace="${NAMESPACE}" \
--from-file=config.yaml=./config.yaml \
--dry-run=client -o yaml | kubectl apply -f -
# Restart the deployment to pick up changes:
kubectl rollout restart deployment verify-mcp-server -n "${NAMESPACE}"Update image:
# Update the image tag in verify-mcp-server.yaml, then re-apply:
kubectl apply -f verify-mcp-server.yaml
# Or update in-place:
kubectl set image deployment/verify-mcp-server \
verify-mcp-server=icr.io/ibm-verify/verify-mcp-server:latest \
-n "${NAMESPACE}"
kubectl rollout status deployment verify-mcp-server -n "${NAMESPACE}"Scale:
# Only single-pod deployment is currently supported.
# Do not scale above 1 — session/cache state is in-process and not shared
# across pods. HA support is planned for a future release.
kubectl scale deployment verify-mcp-server -n "${NAMESPACE}" --replicas=1Delete deployment:
kubectl delete namespace "${NAMESPACE}"Option B: Local Docker Deployment
Run the MCP Server locally for testing or evaluation.
Prerequisites
- Docker installed and running
- Access to IBM Container Registry (
icr.io) config.yamlcreated in Step 2
B.1 Prepare the Working Directory
mkdir -p ./config ./logs
cp /path/to/config.yaml ./config/config.yaml./
├── config/
│ └── config.yaml ← required
└── logs/ ← mount to persist logsIf you are using
!enc-prefixed values, also create asecrets/folder and place yourprivate.peminside it.
A minimal config.yaml for Docker to reference credentials:
# config.yaml for Docker / local deployment
# Either we can edit this file directly or make Uses !envvar so credentials are loaded from the .env file or -e flags at runtime.
# Credentials can be set as plain text, or resolved at runtime using !envvar, !enc, or !obf prefixes.
secrets:
obf_key: !envvar OBF_KEY # Needed only when using !obf-prefixed values; supply the passphrase through an environment variable
server:
host: 0.0.0.0
port: 8000
ibm_verify:
tenant_url: https://<my-tenant.verify.ibm.com>
client_id: <your-subject-client-id> # For production: !enc <base64-ciphertext>
client_secret: <your-subject-client-secret> # For production: !enc <base64-ciphertext>
base_url: https://<mcp-server.example.com>
sts_client_id: <your-sts-client-id> # For production: !enc <base64-ciphertext>
sts_client_secret: <your-sts-client-secret> # For production: !enc <base64-ciphertext>
actor_client_id: <your-actor-client-id> # For production: !enc <base64-ciphertext>
actor_client_secret: <your-actor-client-secret> # For production: !enc <base64-ciphertext>
scopes: openid profile email
transport:
type: streamable-http
logging:
level: INFO
file: /app/logs/mcp-server.logB.2 Run the Container
Apple Silicon / non-amd64 hosts: Add
--platform linux/amd64immediately afterdocker run.
Run the following command, including the optional flags that match how your config.yaml is configured:
docker run -d \
--name verify-mcp-server \
-p 8000:8000 \
-v "$(pwd)/config:/app/config:ro" \
-v "$(pwd)/logs:/app/logs" \
# [Optional: Only if using !enc (RSA encryption)] Mount private key and set ENC_KEY
# -v "$(pwd)/secrets:/app/secrets:ro" \
# -e ENC_KEY="!file /app/secrets/private.pem" \
# [Optional: Only if using !envvar in config.yaml] Load environment variables
# --env-file .env \
--restart unless-stopped \
icr.io/ibm-verify/verify-mcp-server:latestAdditional parameters by configuration type
-
When using
!enc(RSA encryption):
Mount your private key directory and pointENC_KEYat the file path inside the container:-v "$(pwd)/secrets:/app/secrets:ro" \ -e ENC_KEY="!file /app/secrets/private.pem"(Note: The path in
ENC_KEYmust match the mount path inside the container). -
When using
!obf(Obfuscation):
Provide the passphrase throughsecrets.obf_keyinconfig.yaml. Never store the passphrase as plain text inconfig.yaml— supply it at runtime through an environment variable or a mounted file:secrets: # obf_key: MyMasterPassword # ⚠️ INSECURE — plain text in config file; do not use in production obf_key: !envvar OBF_KEY # recommended: read from environment variable # obf_key: !file /app/secrets/obf # alternative: read from a mounted file -
When using
!envvar:
Pass variables using--env-file .envor individual-e KEY="value"flags.
B.3 Confirm the Container is Running
docker ps | grep verify-mcp-server
docker logs verify-mcp-server --tail=30B.4 Container Management
docker logs -f verify-mcp-server # follow live logs
docker logs verify-mcp-server --tail=50 # view last 50 lines
docker stop verify-mcp-server
docker start verify-mcp-server
docker restart verify-mcp-server
docker rm -f verify-mcp-serverValidate the Installation
Regardless of deployment method, confirm the server is running correctly before proceeding.
Health Check
# Docker
curl http://localhost:8000/health
# Kubernetes (through port-forward)
kubectl port-forward -n "${NAMESPACE}" svc/verify-mcp-server 8000:8000
curl http://localhost:8000/health
# Kubernetes (through ingress hostname)
curl https://mcp.mycompany.com/healthExpected response:
{
"status": "healthy",
"service": "IBM Verify MCP Server"
}Verify Server Logs
# Docker
docker logs verify-mcp-server --tail=50
# Kubernetes
kubectl logs -n "${NAMESPACE}" -l app=verify-mcp-server --tail=50Look for:
- ✅ Server started successfully
- ✅ No error messages
- ✅ Listening on port 8000
Post-Installation: Nginx Reverse Proxy (VM / Fyre)
When deploying on a VM or Fyre environment, the server runs on plain HTTP (port 8000). Because MCP clients require HTTPS, place an nginx reverse proxy in front of it.
- nginx terminates HTTPS on port 443 (or a custom port such as 2443)
- nginx proxies requests to
http://localhost:8000
Self-signed certificates: In Fyre or lab environments you can generate a self-signed TLS certificate. MCP clients must be configured to trust it. See the Configuring MCP Clients guide for self-signed certificate setup instructions.
For nginx setup instructions, refer to the nginx reverse proxy guide or the DigitalOcean nginx reverse proxy tutorial.
Resource and Network Requirements
Compute (per pod / container):
| CPU | Memory | |
|---|---|---|
| Minimum | 500m (0.5 cores) | 512 Mi |
| Recommended | 1000m (1 core) | 1 Gi |
Network:
| Direction | Port | Protocol | Purpose |
|---|---|---|---|
| Inbound | 8000 | HTTP | MCP Server API |
| Outbound | 443 | HTTPS | IBM Verify tenant communication |
Troubleshooting
For a full list of issues and solutions, see the Troubleshooting Guide.
Quick debug commands:
# Docker — check logs
docker logs verify-mcp-serverNext Steps
Once the server is running:
- 👉 Configure MCP Clients — Connect Claude Desktop, IBM Bob, or other MCP-compatible clients
- ✅ Test the authentication flow end-to-end with an entitled user
- ✅ Review Encryption & Obfuscation to harden credentials for production
- ✅ Set
allowed_redirect_urisinconfig.yamlto lock down OAuth redirect targets - ✅ Set up monitoring, log retention, and alerting
Additional Resources
- Encryption & Obfuscation — Generate
!obfand!encvalues for use inconfig.yaml - Tenant Setup Prerequisites — IBM Verify application configuration
- Troubleshooting Guide
- IBM Verify Documentation
- Kubernetes Documentation
- Docker Documentation
- MCP Protocol Specification
Support
For issues or questions:
- Review the Troubleshooting Guide
- Consult IBM Verify support documentation
- Contact your IBM Verify administrator
Updated 4 days ago
