Deploying Remote Containerized MCP Servers on IBM Cloud Code Engine: Overcoming 422 Gateway Errors & SSE Handshakes

WXO Labs Series Icon By Markus van Kempen | Series: WXO Labs & Tutorial Guide (Part 15 of 15)
Interactive Lab Portal: https://markusvankempen.github.io/wxo-labs/
GitHub Repository: https://github.com/markusvankempen/wxo-labs

Deploying Remote Containerized MCP Servers on IBM Cloud Code Engine: Overcoming 422 Gateway Errors & SSE Handshakes

Why This Pattern Matters

In production, Model Context Protocol servers run as microservices on serverless platforms like IBM Cloud Code Engine, Kubernetes, or OpenShift. When connecting Watsonx Orchestrate to a remote MCP server, developers often encounter 'Gateway creation failed: 422' due to protocol mismatches or authentication header stripping. This lab provides the battle-tested blueprint.


πŸ“‹ Prerequisites & Setup

Ensure your environment has the required dependencies:

pip install ibm-watsonx-orchestrate

Verify active environment:

orchestrate env list

πŸ› οΈ Step 1: Implement the Logic (server.js)

import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { z } from "zod";

const app = express();
const server = new McpServer({
  name: "enterprise-ticket-mcp",
  version: "1.0.0"
});

server.tool(
  "query_incident",
  "Lookup incident details from enterprise ITSM",
  { incident_id: z.string().describe("Incident ticket identifier, e.g. INC-1029") },
  async ({ incident_id }) => {
    return {
      content: [{
        type: "text",
        text: `Incident ${incident_id}: High CPU Alert in cluster ca-tor. Status: Investigating.`
      }]
    };
  }
);

let transport = null;

app.get("/sse", async (req, res) => {
  console.log("πŸ‘‰ [WxO Handshake] SSE Connection established");
  transport = new SSEServerTransport("/message", res);
  await server.connect(transport);
});

app.post("/message", async (req, res) => {
  if (transport) {
    await transport.handlePostMessage(req, res);
  } else {
    res.status(400).send("No active transport");
  }
});

app.get("/health", (req, res) => res.json({ status: "healthy", timestamp: new Date() }));

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => console.log(`πŸš€ Remote MCP server listening on port ${PORT}`));

πŸ“¦ Step 2: Declare Dependencies (requirements.txt)

express>=4.18.2
@modelcontextprotocol/sdk>=1.0.1
zod>=3.22.0

πŸ€– Step 3: Configure the AI Agent (agent.yaml)

spec_version: v1
kind: native
name: remote_incident_agent
title: "Remote Incident Commander"
description: "Connects to containerized MCP microservices running on IBM Cloud Code Engine."
model: ibm/granite-3-8b-instruct
tools:
  - query_incident

πŸš€ Step 4: Import & Test Live in Terminal

Import tool and test conversationally:

curl -i https://mcp-ticket-demo.ca-tor.codeengine.appdomain.cloud/health

Live Response:

HTTP/2 200 
content-type: application/json; charset=utf-8
{
  "status": "healthy",
  "timestamp": "2026-09-24T20:30:00.000Z"
}

πŸ’‘ Pro Tips & Gotchas


🏷️ Recommended Community Tags

When publishing this blog post, select or enter the following tags in the community editor:

watsonx Orchestrate, Remote MCP, IBM Cloud Code Engine, Server-Sent Events, SSE, Gateway 422, Microservices


πŸ—ΊοΈ Series Roadmap (15 Hands-On Labs)

This tutorial is Part 15 of 15 in the complete enterprise hands-on series:

Explore the interactive curriculum and code repository: