# System Patterns
## Architecture
```
Developer IDE → mcp-remote (local) → VPN → Internal ALB (:443)
→ JWT Proxy (:8000) → Supergateway (:8001) → github-mcp-server (stdio)
```
## Component Relationships
### Container Stack (single ECS Fargate task)
- **JWT Proxy** (Node.js, port 8000) — front door, handles OAuth metadata + JWT validation
- **Supergateway** (port 8001) — Streamable HTTP ↔ stdio bridge, stateful multi-session
- **github-mcp-server** — one stdio process per session, spawned by Supergateway
### Infrastructure
- **Internal ALB** — HTTPS :443, self-signed cert, sticky sessions (lb_cookie, 24h)
- **ECS Fargate** — private subnets, NAT Gateway for outbound
- **Secrets Manager** — GitHub PAT injected at runtime
## Key Design Decisions
1. **Streamable HTTP over SSE** — SSE only supported 1 connection per process; Streamable HTTP supports many concurrent sessions
2. **Token endpoint proxy** — `mcp-remote` sends RFC 8707 `resource` param that Okta rejects; JWT proxy strips it
3. **Authorization endpoint NOT proxied** — browser needs to reach Okta directly; internal ALB is unreachable from browser
4. **Self-signed cert** — internal-only ALB, clients use `NODE_TLS_REJECT_UNAUTHORIZED=0`
5. **Process supervisor (start.sh)** — if either JWT proxy or Supergateway dies, kills the other and exits so ECS restarts the task
## Terraform Module Structure
```
terraform/
├── main.tf, variables.tf, outputs.tf, backend.tf
├── modules/infrastructure/ # ALB, SGs, NAT Gateway, public subnet
└── modules/application/ # ECR, ECS, Secrets Manager, IAM
```
## Request Flow
| Path | Auth | Behavior |
|------|------|----------|
| `/healthz` | No | Returns `ok` (ALB health check) |
| `/.well-known/oauth-*` | No | OAuth metadata (token_endpoint rewritten to proxy) |
| `/oauth/token` | No | Proxies to Okta, strips `resource` param |
| `/mcp` | Bearer JWT | Validates JWT, proxies to Supergateway |