CI/CD Platform Automation
Create and update collections, proxies, and workflow configuration from a CI/CD pipeline using the Management MCP. Every change made via the pipeline uses the same OIDC identity and role-based access as the admin UI — one audit trail for both human and automated changes.
Typical scenario: Your team promotes API configuration from a development environment to production as part of a release pipeline. A GitHub Actions job creates a new collection and proxy in production, validates the configuration, then exits with a success or failure signal.
How it works
The Management MCP exposes platform management operations as MCP tools over Streamable HTTP. Your pipeline authenticates with an OIDC token from your identity provider, initializes a session, and calls tools to read or write platform configuration.
Supported operations include:
list_collections,get_collection,create_collection,update_collection,toggle_collection_statuslist_proxies,get_proxy,create_proxy,update_proxyget_proxy_workflow,update_proxy_workflow,validate
Prerequisites
- Your identity provider (e.g. Okta, Auth0, Azure AD) is configured as an OIDC issuer for the platform
- A service account or machine identity is registered with the
modifierrole in the platform - The Management MCP endpoint is accessible from your CI runner (e.g.
https://api.example.com/api/v1/mcp)
Step 1 — Obtain an OIDC token in the pipeline
In GitHub Actions, use the built-in OIDC token exchange to get a token from your IdP:
# .github/workflows/deploy-api-config.yml
name: Deploy API Configuration
on:
push:
branches: [main]
paths:
- 'api-config/**'
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get OIDC token
id: oidc
run: |
TOKEN=$(curl -s -X POST \
"${{ vars.OIDC_TOKEN_URL }}" \
-d "grant_type=client_credentials" \
-d "client_id=${{ secrets.MCP_CLIENT_ID }}" \
-d "client_secret=${{ secrets.MCP_CLIENT_SECRET }}" \
-d "scope=openid" \
| jq -r '.access_token')
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Deploy API config
env:
MCP_TOKEN: ${{ steps.oidc.outputs.token }}
MCP_URL: ${{ vars.MCP_MANAGEMENT_URL }}
run: ./scripts/deploy-api-config.sh
Store MCP_CLIENT_ID and MCP_CLIENT_SECRET as GitHub Actions secrets. Never hard-code credentials in workflow files.
Step 2 — Initialize an MCP session
Every Management MCP interaction starts with an initialize call. Store the returned Mcp-Session-Id for subsequent requests.
#!/bin/bash
# scripts/deploy-api-config.sh
set -euo pipefail
MCP_URL="${MCP_URL:-https://api.example.com/api/v1/mcp}"
# Initialize session
INIT_RESPONSE=$(curl -s -i https://"${MCP_URL}" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "init-1",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "github-actions", "version": "1.0" }
}
}')
SESSION_ID=$(echo "$INIT_RESPONSE" | grep -i "Mcp-Session-Id" | awk '{print $2}' | tr -d '\r')
echo "Session ID: $SESSION_ID"
Step 3 — Read existing state (plan phase)
Before making any changes, read the current state to understand what already exists:
# List existing collections
list_collections() {
curl -s "$MCP_URL" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"read-1","method":"tools/call","params":{"name":"list_collections","arguments":{}}}' \
| jq '.result.content[0].text | fromjson'
}
EXISTING=$(list_collections)
echo "Existing collections: $(echo $EXISTING | jq length)"
Step 4 — Create or update a collection
# Create a new collection (idempotent — check if exists first)
create_or_update_collection() {
local NAME="$1"
local BASE_PATH="$2"
curl -s "$MCP_URL" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": \"create-col-1\",
\"method\": \"tools/call\",
\"params\": {
\"name\": \"create_collection\",
\"arguments\": {
\"name\": \"$NAME\",
\"basePath\": \"$BASE_PATH\",
\"description\": \"Deployed by CI pipeline on $(date -u +%Y-%m-%dT%H:%M:%SZ)\"
}
}
}" | jq '.result'
}
COLLECTION=$(create_or_update_collection "payments-api-v2" "/payments/v2")
COLLECTION_ID=$(echo $COLLECTION | jq -r '.content[0].text | fromjson | .id')
echo "Collection ID: $COLLECTION_ID"
Step 5 — Add a proxy to the collection
# Create a proxy within the collection
create_proxy() {
local COLLECTION_ID="$1"
curl -s "$MCP_URL" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": \"create-proxy-1\",
\"method\": \"tools/call\",
\"params\": {
\"name\": \"create_proxy\",
\"arguments\": {
\"collectionId\": \"$COLLECTION_ID\",
\"name\": \"Process Payment\",
\"path\": \"/process\",
\"method\": \"POST\",
\"targetUrl\": \"https://payments.internal/v2/process\",
\"description\": \"Payment processing endpoint\"
}
}
}" | jq '.result'
}
create_proxy "$COLLECTION_ID"
Step 6 — Validate and publish
# Validate the collection configuration
validate_collection() {
local COLLECTION_ID="$1"
curl -s "$MCP_URL" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": \"validate-1\",
\"method\": \"tools/call\",
\"params\": {
\"name\": \"validate\",
\"arguments\": { \"collectionId\": \"$COLLECTION_ID\" }
}
}" | jq '.result.content[0].text | fromjson'
}
VALIDATION=$(validate_collection "$COLLECTION_ID")
STATUS=$(echo $VALIDATION | jq -r '.status')
if [ "$STATUS" != "valid" ]; then
echo "Validation failed: $VALIDATION"
exit 1
fi
# Toggle collection to active (published)
curl -s "$MCP_URL" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": \"publish-1\",
\"method\": \"tools/call\",
\"params\": {
\"name\": \"toggle_collection_status\",
\"arguments\": { \"collectionId\": \"$COLLECTION_ID\", \"active\": true }
}
}" | jq '.result'
echo "Collection published successfully."
Step 7 — Smoke test after deploy
After publishing, send a test request through the gateway to confirm the proxy is live:
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://api.example.com/payments/v2/process \
-X POST \
-H "Authorization: Bearer $SMOKE_TEST_TOKEN" \
-H "X-Client-ID: $SMOKE_TEST_CLIENT_ID" \
-H "X-Profile-ID: $SMOKE_TEST_PROFILE_ID" \
-H "Content-Type: application/json" \
-d '{"amount": 1, "currency": "USD"}')
if [ "$HTTP_STATUS" != "200" ] && [ "$HTTP_STATUS" != "201" ]; then
echo "Smoke test failed with status $HTTP_STATUS"
exit 1
fi
echo "Smoke test passed: $HTTP_STATUS"
CI/CD safety gates
Follow these practices for safe automated changes:
| Gate | How to implement |
|---|---|
| Read before write | Always call list_collections before create_collection |
| Validate before publish | Run validate before toggle_collection_status |
| Smoke test after publish | Test through gateway with a smoke test client |
| Separate environments | Use different OIDC service accounts per environment (dev/staging/prod) |
| Fail fast | Exit non-zero on any unexpected error; do not continue a partial deploy |
Audit trail
Every MCP tool call is recorded in the platform audit log. Open Logs → Audit Logs and filter by the service account identity to see the full history of pipeline-created changes alongside UI-created changes — one unified audit trail.
Next steps
- Management MCP Overview — full Management MCP reference
- Management MCP Tool Catalog — all available tools
- RBAC and Audit — role requirements and audit events
- Setup OIDC — connect your identity provider