Guide · AWS Networking · Global Accelerator

AWS Global Accelerator Routing for MCP Server Endpoints

Global Accelerator's routing model — listeners, endpoint groups, traffic dials, endpoint weights, and client affinity — is the layer where you control which MCP server receives which client, across how many regions, and at what traffic fraction. Getting this configuration wrong costs you either availability (too aggressive a failover) or sticky-session correctness (wrong affinity setting). This guide covers the full routing object model, SOURCE_IP affinity for stateful SSE connections, traffic dial patterns for blue/green and canary deployments, and when to use a custom routing accelerator instead of a standard one. For GA overview and CDK setup see Global Accelerator for MCP Servers; for health checks and failover timing see Global Accelerator Health Checks; for multi-region active-active see Multi-Region MCP Servers with GA.

TL;DR

Set client affinity to SOURCE_IP on any listener that carries SSE sessions — without it, reconnecting clients may land on a different endpoint and lose session state. Use traffic dials at 0% for standby regions and ramp slowly during blue/green. Use endpoint weights within a group for canary (5% to new ECS task version). For large-scale MCP deployments where each client session must reach a specific process, evaluate custom routing accelerators — they provide deterministic IP:port → EC2 mapping that standard GA load balancing cannot guarantee.

Listener configuration for MCP SSE

A GA listener specifies which ports to accept and how to handle client affinity. For MCP over SSE, you typically create one listener per protocol port:

// listener for MCP SSE (HTTPS/443) with SOURCE_IP affinity
const mcpListener = accelerator.addListener('McpSseListener', {
  portRanges: [{ fromPort: 443, toPort: 443 }],
  protocol: ga.ConnectionProtocol.TCP,
  clientAffinity: ga.ClientAffinity.SOURCE_IP,
});

// separate listener for MCP management API (8443) — no affinity needed
// because management calls are stateless
const mgmtListener = accelerator.addListener('McpMgmtListener', {
  portRanges: [{ fromPort: 8443, toPort: 8443 }],
  protocol: ga.ConnectionProtocol.TCP,
  clientAffinity: ga.ClientAffinity.NONE,
});

SOURCE_IP affinity is a 5-tuple consistent hash on client IP (not the full 5-tuple) — GA uses the source IP address to deterministically route new connections from the same client to the same endpoint. The affinity persists for the duration of the TCP connection, which for SSE means the entire session. When the SSE connection drops and reconnects, the same client IP re-hashes to the same endpoint.

Caveat for clients behind corporate NAT: if multiple MCP clients share an outbound IP (common in enterprise environments), all of them hash to the same endpoint. If you have 500 corporate users behind one NAT IP, they all hit the same backend. Combine SOURCE_IP affinity with adequate capacity on each endpoint — don't rely on affinity as a load distribution mechanism.

Endpoint group and traffic dial patterns

Each listener can have one endpoint group per AWS region. The traffic dial (0–100%) controls what fraction of traffic the listener routes to each group. GA distributes traffic proportionally across groups by dial — a group at 50% with a group at 50% splits traffic evenly regardless of geographic proximity (proximity still affects POP routing, but dial controls the total fraction).

PatternPrimary group dialSecondary group dialUse case
Single region (normal) 100% — Default for startups; simplest setup
Active-passive DR 100% 0% DR region warm and healthy but receiving no traffic; failover by setting primary to 0% + secondary to 100%
Blue/green deployment 100% (blue) 0% → 100% (green) New region with new version at 0%; smoke test; ramp green to 100%; set blue to 0%; delete blue
Canary (5%) 95% 5% New region with new MCP version receives 5% of traffic for validation before full ramp
Active-active (geo split) 50% (us-east-1) 50% (eu-west-1) Both regions fully active; GA routes each client to nearest POP then to nearest group; requires shared session state across regions

Traffic dials are a blunt instrument — they divide the total traffic pool, not the geographic bucket. A 50/50 dial sends roughly half of all global clients to each region, regardless of whether those clients are geographically near one region. GA's POP routing still routes clients through the nearest edge, but the endpoint group selection respects the dial percentages. For true latency-based routing where EU clients always go to eu-west-1 and US clients always go to us-east-1, you need proximity routing — which GA provides automatically when both groups are at 100% and GA detects the closer group's endpoints as healthy.

Proximity-based routing: how GA picks the group

When multiple endpoint groups are present and all have non-zero traffic dials, GA routes each incoming connection to the geographically nearest healthy endpoint group. "Nearest" is measured by GA's internal latency model from the client's ingress POP, not by physical distance. This means:

Endpoint weights within a group

Within a single endpoint group, you can assign weights (0–255) to individual endpoints (NLBs, ALBs, EC2 instances, EIPs). GA distributes traffic proportionally to weight values. This enables within-region canary deployments: add a new NLB (pointing to your new ECS task set) at weight 5, keep the existing NLB at weight 100, and gradually increase the new NLB's weight as you verify the new version.

listener.addEndpointGroup('UsEast1Group', {
  region: 'us-east-1',
  trafficDialPercentage: 100,
  endpoints: [
    // existing NLB — 95% of weight-proportional traffic
    new gaEndpoints.NetworkLoadBalancerEndpoint(existingNlb, {
      weight: 100,
      preserveClientIp: true,
    }),
    // new NLB with updated MCP version — 5% of weight-proportional traffic
    // increase this weight as validation passes; remove old NLB when done
    new gaEndpoints.NetworkLoadBalancerEndpoint(newNlb, {
      weight: 5,
      preserveClientIp: true,
    }),
  ],
});

Setting an endpoint's weight to 0 effectively removes it from the pool without deleting it — useful for graceful draining. Unlike endpoint group traffic dial (which affects the whole region), per-endpoint weight allows in-region A/B testing without DNS changes or ALB weighted target groups.

Standard vs custom routing accelerator

GA offers two accelerator types. Standard accelerators (what we've discussed so far) let GA decide which endpoint receives each connection — it picks from the endpoint group based on health and weight. The connection is routed to any healthy endpoint, with optional SOURCE_IP affinity for session stickiness.

Custom routing accelerators work differently: you define a port range on the listener (e.g., 10000–20000), and GA deterministically maps each (accelerator_ip, port) combination to exactly one (ec2_instance_ip, port). Every connection to 1.2.3.4:10042 always goes to 10.0.0.5:8042. No load balancing decision is made — the mapping is fixed until you update it via the API.

Custom routing is useful for MCP architectures where each client session must bind to a specific MCP server process — for example, an agent orchestration platform where each orchestrator process owns a set of tool-server connections and those connections cannot migrate to a different process without session teardown. Custom routing gives you deterministic addressing without relying on SOURCE_IP affinity (which can break if client IP changes).

PropertyStandard acceleratorCustom routing accelerator
Endpoint selection GA selects based on health + weight Deterministic: each IP:port maps to one EC2 instance:port
Load balancing Yes — GA distributes across healthy endpoints No — you manage port:instance assignment externally
Target type NLB, ALB, EC2, EIP EC2 instance only (one instance per port mapping)
Session affinity SOURCE_IP hash (optional) Guaranteed — same port always reaches same instance
MCP use case Standard MCP SSE endpoints with NLB backend Stateful MCP orchestrators where clients must reach specific process
Pricing $0.025/hr + data premium $0.025/hr + data premium (same)

For most MCP server deployments — a single stateless tool server with SSE transport — standard accelerator + SOURCE_IP affinity is the right choice. Custom routing is for agent orchestration platforms that implement their own connection mapping layer.

Combining GA traffic dials with NLB health checks

GA and NLB are independent health check layers. GA health-checks the NLB endpoint (TCP port open, or HTTP 200 at the GA health check URL). NLB health-checks the ECS tasks behind it. A failure can occur at either layer:

AliveMCP's probe design accounts for this: when we monitor an MCP endpoint that's fronted by GA, we probe both the origin endpoint and the GA endpoint separately. GA masking a partial failure is a known blind spot — AliveMCP raises an alert on origin health degradation even if GA is still reporting the endpoint as healthy from the client's perspective.