Skip to main content
Glama
madlinux768

GitHub MCP Server on Amazon Bedrock AgentCore

by madlinux768
README.md
# GitHub MCP Server on Amazon Bedrock AgentCore

Private [GitHub MCP Server](https://github.com/github/github-mcp-server) hosted on Amazon Bedrock AgentCore Runtime, accessible via AWS Client VPN with Okta OAuth authentication.

## Overview

The official GitHub MCP Server runs as a managed container on AgentCore Runtime. AgentCore handles TLS termination, JWT authorization, and container lifecycle. Developers connect from any MCP-compatible IDE (Kiro, VS Code, Cursor) using `mcp-remote`, which handles OAuth login via Okta.

```
Developer Machine                        AWS
┌──────────────────┐                    ┌──────────────────────────────────────┐
│ IDE (Kiro/etc)   │                    │ VPC Endpoint (PrivateLink)           │
│  └─ mcp-remote   │─── Client VPN ───▶│  └─ AgentCore Runtime (managed TLS)  │
│      ├─ OAuth    │                    │      ├─ JWT Authorizer (Okta OIDC)   │
│      │  discovery│                    │      └─ Container                    │
│      ├─ Okta     │                    │           ├─ Supergateway (:8000)    │
│      │  PKCE     │                    │           │  stateless Streamable HTTP│
│      └─ Bearer   │                    │           └─ github-mcp-server       │
│         token    │                    │              (stdio subprocess)       │
└──────────────────┘                    └──────────────────────────────────────┘
```

### What AgentCore Replaces

- ECS Cluster, Service, Task Definition
- Application Load Balancer + self-signed certificate
- NAT Gateway + Internet Gateway + Public Subnet
- Custom JWT Proxy (Node.js sidecar)

## Prerequisites

- **AWS CLI** v2 configured with credentials
- **Terraform** >= 1.0
- **Docker** with Buildx (for ARM64 builds)
- **AWS Client VPN** connected to the target VPC
- **Okta** Native application (Authorization Code + PKCE) with client ID
- **GitHub Personal Access Token** with appropriate scopes

## Deployment

### 1. Bootstrap Terraform State

Create the S3 bucket and DynamoDB table for remote state (one-time setup):

```bash
aws s3api create-bucket \
  --bucket github-mcp-agentcore-tfstate \
  --region us-east-1

aws s3api put-bucket-versioning \
  --bucket github-mcp-agentcore-tfstate \
  --versioning-configuration Status=Enabled

aws s3api put-bucket-encryption \
  --bucket github-mcp-agentcore-tfstate \
  --server-side-encryption-configuration \
    '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'

aws s3api put-public-access-block \
  --bucket github-mcp-agentcore-tfstate \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

aws dynamodb create-table \
  --table-name github-mcp-agentcore-tflock \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1
```

### 2. Configure Variables

```bash
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your values
```

Store the GitHub PAT separately (this file is gitignored):

```bash
cat > secrets.auto.tfvars <<EOF
github_pat = "github_pat_XXXXXXXXXXXX"
EOF
```

### 3. Deploy Infrastructure

```bash
terraform init
terraform plan
terraform apply
```

### 4. Build and Push Container

```bash
# Get ECR repository URL from Terraform output
ECR_URL=$(terraform output -raw ecr_repository_url)

# Authenticate to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin "$ECR_URL"

# Build ARM64 image and push
docker buildx build --platform linux/arm64 -f docker/Dockerfile -t "$ECR_URL:latest" --push .
```

On subsequent pushes, the GitHub Actions workflow (`.github/workflows/docker-build.yml`) handles builds automatically when `docker/` files change on the main branch.

## Client Configuration

See [docs/client-config.md](docs/client-config.md) for IDE-specific setup instructions (Kiro, VS Code, Cursor).

Quick example — add to your MCP config and connect to VPN:

```json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://<AGENTCORE_ENDPOINT>",
        "--static-oauth-client-info",
        "{\"client_id\":\"<OKTA_CLIENT_ID>\"}"
      ]
    }
  }
}
```

No `NODE_TLS_REJECT_UNAUTHORIZED=0` needed — AgentCore uses AWS-managed TLS.

## Operations

### Logs

Application logs are delivered to CloudWatch via vended log delivery:

```bash
# Get log group name from Terraform output
LOG_GROUP=$(terraform -chdir=terraform output -raw log_group_name)

# Tail logs
aws logs tail "$LOG_GROUP" --since 30m --follow
```

### Health Check

From a machine connected to the VPN:

```bash
curl -s https://<AGENTCORE_ENDPOINT>/healthz
```

### X-Ray Traces

Traces are delivered to X-Ray automatically. View them in the AWS Console under CloudWatch > X-Ray traces.

## CI/CD

Two GitHub Actions workflows handle automation:

- **`terraform.yml`** — Runs `terraform fmt`, `validate`, and `plan` on PRs. Applies on merge to main with environment protection rules.
- **`docker-build.yml`** — Builds ARM64 Docker image and pushes to ECR on changes to `docker/` on main.

Both workflows use OIDC federation for AWS authentication (no stored credentials).

## Project Structure

```
├── .github/workflows/
│   ├── terraform.yml          # Terraform plan/apply CI/CD
│   └── docker-build.yml       # ARM64 Docker build + ECR push
├── docker/
│   ├── Dockerfile             # Supergateway + github-mcp-server (ARM64)
│   └── start.sh               # Fetch secret, start Supergateway
├── terraform/
│   ├── main.tf                # AgentCore runtime resource
│   ├── vpc-endpoint.tf        # PrivateLink VPC endpoint + security group
│   ├── iam.tf                 # Agent execution role, GitHub Actions OIDC role
│   ├── ecr.tf                 # ECR repository + lifecycle policy
│   ├── secrets.tf             # Secrets Manager for GitHub PAT
│   ├── observability.tf       # CloudWatch vended logs + X-Ray traces
│   ├── backend.tf             # S3 remote state configuration
│   ├── variables.tf           # Input variables
│   ├── outputs.tf             # Output values
│   ├── versions.tf            # Provider version constraints
│   └── terraform.tfvars.example
├── docs/
│   └── client-config.md       # IDE configuration examples
├── tests/                     # Property-based and unit tests
├── .gitignore
└── README.md
```