Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ validate-schemas
.idea/
coverage.out
coverage.html

deploy/infra/infra
registry
28 changes: 28 additions & 0 deletions deploy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Pulumi
*.pyc
.pulumi/
Pulumi.*.yaml.bak

# Go
*.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
vendor/

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Build artifacts
infra
47 changes: 47 additions & 0 deletions deploy/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.PHONY: help init preview deploy destroy clean dev-init prod-init dev-deploy prod-deploy

help: ## Show this help message
@echo "Usage: make [target]"
@echo ""
@echo "Targets:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'

init: ## Initialize Pulumi stack
pulumi stack init

preview: ## Preview infrastructure changes
pulumi preview

deploy: ## Deploy infrastructure
pulumi up --yes

destroy: ## Destroy infrastructure
pulumi destroy --yes

clean: ## Clean up temporary files
rm -rf .pulumi/
rm -f Pulumi.*.yaml.bak

dev-init: ## Initialize development stack
pulumi stack init dev
pulumi stack select dev

prod-init: ## Initialize production stack
pulumi stack init prod
pulumi stack select prod

dev-deploy: ## Deploy to development environment
pulumi stack select dev
pulumi up --yes

prod-deploy: ## Deploy to production environment
pulumi stack select prod
pulumi up --yes

dev-destroy: ## Destroy development environment
pulumi stack select dev
pulumi destroy --yes

prod-destroy: ## Destroy production environment
pulumi stack select prod
pulumi destroy --yes
7 changes: 7 additions & 0 deletions deploy/Pulumi.local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
config:
mcp-registry:environment: local
mcp-registry:provider: local
mcp-registry:githubClientId: test-client-id
mcp-registry:githubClientSecret:
secure: v1:gt5MBuW7QPiJymkh:1+I2eFChsrUH18cLELq9OAIN94MLH0SldbOuPp2C
encryptionsalt: v1:ijIHaqhbXVA=:v1:7voX1Kv+Bunz33iN:fyVHMOhlGIymzJ+ILgUBy3ExTwUUnA==
6 changes: 6 additions & 0 deletions deploy/Pulumi.prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
config:
mcp-registry:environment: prod
mcp-registry:provider: aks
mcp-registry:githubClientId: test-client-id
mcp-registry:githubClientSecret: some-secret-here
encryptionsalt: v1:oDjcEdLMFwM=:v1:FdgMU4r7kVWUbaUt:1aTSUxrNy+JNW51KRXzoMACuAXruUg==
6 changes: 6 additions & 0 deletions deploy/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: mcp-registry-infra
runtime:
name: go
options:
binary: ./infra
description: MCP Registry Kubernetes Infrastructure
115 changes: 115 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# MCP Registry Kubernetes Deployment

This directory contains Pulumi infrastructure code to deploy the MCP Registry service to a Kubernetes cluster. It supports multiple Kubernetes providers: Azure Kubernetes Service (AKS) and local (using existing kubeconfig).

## Quick Start

### Local Development

Pre-requisites:
- [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/)
- Access to a Kubernetes cluster via kubeconfig. You can run a cluster locally with [minikube](https://minikube.sigs.k8s.io/docs/start/).

1. Set Pulumi's backend to local: `pulumi login --local`
2. Init the local stack: `pulumi stack init local` (fine to leave `password` blank)
3. Set your config:
```bash
# General environment
pulumi config set mcp-registry:environment local

# To use your local kubeconfig (default)
pulumi config set mcp-registry:provider local
# Alternative: To use AKS
# pulumi config set mcp-registry:provider aks

# GitHub OAuth
pulumi config set mcp-registry:githubClientId <your-github-client-id>
pulumi config set --secret mcp-registry:githubClientSecret <your-github-client-secret>
```
4. Deploy: `go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes`
5. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx` (with minikube, if it's 'pending' you might need `minikube tunnel`). Then run `curl -H "Host: mcp-registry-local.example.com" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.

### Production Deployment (AKS)

**Note:** This is how the production deployment will be set up once. But then the plan will be future updates are effectively a login + `pulumi up` from GitHub Actions.

Pre-requisites:
- [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/)
- A Microsoft Azure account
- [Azure CLI](https://learn.microsoft.com/en-gb/cli/azure/get-started-with-azure-cli) installed

1. Login to Azure: `az login`
2. Create a resource group: `az group create --name official-mcp-registry-prod --location eastus`
3. Add the storage resource provider: `az provider register --namespace Microsoft.Storage`
4. Create a storage account: `az storage account create --name officialmcpregistryprod --resource-group official-mcp-registry-prod --location eastus --sku Standard_LRS`
5. Add the 'Storage Blob Data Contributor' role assignment for yourself on the storage account: `az role assignment create --assignee $(az ad signed-in-user show --query id -o tsv) --role "Storage Blob Data Contributor" --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/official-mcp-registry-prod"`
6. Create a container: `az storage container create --name pulumi-state --account-name officialmcpregistryprod`
7. Set Pulumi's backend to Azure: `pulumi login 'azblob://pulumi-state?storage_account=officialmcpregistryprod'`
8. Init the production stack: `pulumi stack init prod`
- TODO: This has a password that maybe needs to be shared with select contributors?
9. Deploy: `go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes`
10. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx` or view it in the Pulumi outputs. Then run `curl -H "Host: mcp-registry-prod.example.com" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.

## Structure

```
├── main.go # Pulumi program entry point
├── Pulumi.yaml # Project configuration
├── Pulumi.local.yaml # Local stack configuration
├── Pulumi.prod.yaml # Production stack configuration
├── Makefile # Build and deployment targets
├── go.mod # Go module dependencies
├── go.sum # Go module checksums
└── pkg/ # Infrastructure packages
├── k8s/ # Kubernetes deployment components
│ ├── cert_manager.go # SSL certificate management
│ ├── deploy.go # Deployment orchestration
│ ├── ingress.go # Ingress controller setup
│ ├── mongodb.go # MongoDB deployment
│ └── registry.go # MCP Registry deployment
└── providers/ # Kubernetes cluster providers
├── types.go # Provider interface definitions
├── aks/ # Azure Kubernetes Service provider
└── local/ # Local kubeconfig provider
```

### Architecture Overview

#### Deployment Flow
1. Pulumi program starts in `main.go`
2. Configuration is loaded from Pulumi config files
3. Provider factory creates appropriate cluster provider (AKS or local)
4. Cluster provider sets up Kubernetes access
5. `k8s.DeployAll()` orchestrates complete deployment:
- Certificate manager for SSL/TLS
- Ingress controller for external access
- MongoDB for data persistence
- MCP Registry application

## Configuration

| Parameter | Description | Required |
|-----------|-------------|----------|
| `environment` | Deployment environment (dev/prod) | Yes |
| `provider` | Kubernetes provider (local/aks) | No (default: local) |
| `githubClientId` | GitHub OAuth Client ID | Yes |
| `githubClientSecret` | GitHub OAuth Client Secret | Yes |

## Troubleshooting

### Check Status

```bash
kubectl get pods
kubectl get deployment
kubectl get svc
kubectl get ingress
kubectl get svc -n ingress-nginx
```

### View Logs

```bash
kubectl logs -l app=mcp-registry
kubectl logs -l app=mongodb
```
98 changes: 98 additions & 0 deletions deploy/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
module github.com/modelcontextprotocol/registry/deploy/infra

go 1.23.0

toolchain go1.24.1

require (
github.com/pulumi/pulumi-azure-native-sdk/containerservice v1.104.0
github.com/pulumi/pulumi-azure-native-sdk/resources v1.104.0
github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.2
github.com/pulumi/pulumi/sdk/v3 v3.158.0
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/charmbracelet/bubbles v0.16.1 // indirect
github.com/charmbracelet/bubbletea v0.25.0 // indirect
github.com/charmbracelet/lipgloss v0.7.1 // indirect
github.com/cheggaaa/pb v1.0.29 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/djherbis/times v1.5.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.1 // indirect
github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.4 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/opentracing/basictracer-go v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pgavlin/fx v0.1.6 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/term v1.1.0 // indirect
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
github.com/pulumi/esc v0.10.0 // indirect
github.com/pulumi/pulumi-azure-native-sdk v1.104.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/texttheater/golang-levenshtein v1.0.1 // indirect
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/zclconf/go-cty v1.13.2 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/frand v1.4.2 // indirect
)
Loading
Loading