# 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) │
└──────────────────┘ └──────────────────────────────────────┘
```
## Prerequisites
- **AWS CLI** v2 configured with credentials
- **Terraform** >= 1.0
- **Docker** with Buildx
- **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 (one-time)
Create the S3 bucket and DynamoDB table for remote state:
```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
```
Key variables to fill in:
| Variable | Description | How to find it |
|---|---|---|
| `vpc_id` | Existing VPC ID | `aws ec2 describe-vpcs --region us-east-1` |
| `private_subnet_ids` | Subnets associated with your Client VPN | `aws ec2 describe-client-vpn-target-networks --client-vpn-endpoint-id <id>` |
| `vpc_cidr` | VPC CIDR block | From VPC console or describe-vpcs output |
| `vpn_client_cidr` | Client VPN CIDR | `aws ec2 describe-client-vpn-endpoints --region us-east-1` |
| `okta_domain` | Okta org URL | Okta admin console |
| `okta_client_id` | OIDC app client ID | Okta admin → Applications → your app → General |
| `github_repo` | GitHub repo (org/repo format) | Your GitHub repo URL |
Store the GitHub PAT separately (this file is gitignored):
```bash
cat > secrets.auto.tfvars <<EOF
github_pat = "github_pat_XXXXXXXXXXXX"
EOF
```
### 3. Initial Deploy (local, one-time)
The first apply must be run locally to bootstrap the GitHub Actions IAM role:
```bash
export AWS_PROFILE=<your-profile>
export AWS_REGION=us-east-1
terraform -chdir=terraform init
terraform -chdir=terraform plan
terraform -chdir=terraform apply
```
Note the `github_actions_role_arn` output — you'll need it for CI/CD setup.
### 4. Set Up GitHub Actions Secrets
Go to your repo → Settings → Secrets and variables → Actions, and add:
| Secret | Value |
|---|---|
| `OKTA_DOMAIN` | Your Okta domain (e.g. `yourorg.okta.com`) |
| `OKTA_CLIENT_ID` | Your Okta OIDC client ID |
| `GH_PAT` | Your GitHub Personal Access Token |
Note: GitHub reserves the `GITHUB_` prefix, so the PAT secret is named `GH_PAT`.
Non-sensitive values (VPC IDs, CIDRs, region, repo name) are inlined in the workflow file.
### 5. Ongoing Deploys (CI/CD)
After the initial local apply, all subsequent changes are deployed via GitHub Actions on push to `main`. The workflow:
1. Authenticates to AWS via OIDC federation (no stored credentials)
2. Builds the Docker image and pushes to ECR
3. Runs `terraform plan` and `terraform apply`
## Client Configuration
See [docs/client-config.md](docs/client-config.md) for IDE-specific setup instructions.
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
```bash
LOG_GROUP=$(terraform -chdir=terraform output -raw log_group_name)
aws logs tail "$LOG_GROUP" --since 30m --follow --region us-east-1
```
### 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 in AWS Console → CloudWatch → X-Ray traces.
## Project Structure
```
├── .github/workflows/
│ └── deploy.yml # Build, push, and Terraform apply on main
├── docker/
│ ├── Dockerfile # Supergateway + github-mcp-server
│ └── start.sh # Fetch secret from Secrets Manager, start Supergateway
├── terraform/
│ ├── main.tf # AgentCore runtime resource (VPC mode)
│ ├── 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
│ └── mcp-client-config.md # MCP client configuration
└── README.md
```