diff --git a/.github/workflows/chart-preview.yml b/.github/workflows/chart-preview.yml index 7b45d60f..3af6d438 100644 --- a/.github/workflows/chart-preview.yml +++ b/.github/workflows/chart-preview.yml @@ -13,6 +13,11 @@ on: required: false default: false type: boolean + plane-mcp-server: + description: "Plane MCP Server" + required: false + default: false + type: boolean env: PREVIEW_BUILD_FOLDER: helm-preview @@ -28,6 +33,7 @@ env: CHART_PREFIX: ${{ github.run_id }} BUILD_PLANE_CE: ${{ github.event.inputs.plane-ce }} BUILD_PLANE_EE: ${{ github.event.inputs.plane-enterprise }} + BUILD_PLANE_MCP_SERVER: ${{ github.event.inputs.plane-mcp-server }} jobs: build: @@ -117,8 +123,28 @@ jobs: cp charts/${{env.CHART_REPO}}/README.md ${{ env.EXPORT_DIR }}/${{env.CHART_REPO}}/${{env.CHART_REPO}}.md helm repo index ${{ env.EXPORT_DIR }}/${{env.CHART_REPO}} + - id: build-plane-mcp-server + if: ${{ env.BUILD_PLANE_MCP_SERVER == 'true' }} + name: Build Plane-MCP-Server + working-directory: code + env: + EXPORT_DIR: ${{env.PREVIEW_BUILD_FOLDER}} + CHART_REPO: plane-mcp-server + CR_KEY: ${{ env.GPG_KEY_NAME }} + CR_PASSPHRASE_FILE: ${{env.GNUPGHOME}}/gpg-passphrase + CR_KEYRING: ${{env.GNUPGHOME}}/secring.gpg + run: | + flatBranchName=$(echo "${{ github.ref_name}}" | sed 's/\//\-/g') + sed -i "s/name: ${{env.CHART_REPO}}/name: ${{ env.CHART_PREFIX }}-${{env.CHART_REPO}}/" charts/${{env.CHART_REPO}}/Chart.yaml + sed -i "s/description: .*/description: ${flatBranchName}/g" charts/${{env.CHART_REPO}}/Chart.yaml + # sed -i "s/version: \(.*\)/version: \1-${flatBranchName}/" charts/${{env.CHART_REPO}}/Chart.yaml + + helm package --sign --key "$CR_KEY" --keyring $CR_KEYRING --passphrase-file "$CR_PASSPHRASE_FILE" charts/$CHART_REPO -u -d ${{ env.EXPORT_DIR }}/${{env.CHART_REPO}}/charts + cp charts/${{env.CHART_REPO}}/README.md ${{ env.EXPORT_DIR }}/${{env.CHART_REPO}}/${{env.CHART_REPO}}.md + helm repo index ${{ env.EXPORT_DIR }}/${{env.CHART_REPO}} + - name: Publish - if: ${{ env.BUILD_PLANE_CE == 'true' || env.BUILD_PLANE_EE == 'true' }} + if: ${{ env.BUILD_PLANE_CE == 'true' || env.BUILD_PLANE_EE == 'true' || env.BUILD_PLANE_MCP_SERVER == 'true' }} working-directory: code run: | @@ -139,6 +165,11 @@ jobs:
  • Plane-Enterprise
  • " fi + if [ "${{ env.BUILD_PLANE_MCP_SERVER }}" == "true" ]; then + HTML_CONTENT="$HTML_CONTENT +
  • Plane-MCP-Server
  • " + fi + HTML_CONTENT="$HTML_CONTENT " echo $HTML_CONTENT >> ${{env.PREVIEW_BUILD_FOLDER}}/index.html diff --git a/.github/workflows/chart-releaser.yml b/.github/workflows/chart-releaser.yml index 30d45e4d..498c0a84 100644 --- a/.github/workflows/chart-releaser.yml +++ b/.github/workflows/chart-releaser.yml @@ -11,6 +11,10 @@ on: description: 'Build Plane EE' type: boolean default: false + plane-mcp-server: + description: 'Build Plane MCP Server' + type: boolean + default: false env: CR_CONFIGFILE: "${{ github.workspace }}/cr.yaml" @@ -23,13 +27,14 @@ env: TARGET_BRANCH: "${{ github.ref_name }}" CHART_NAME_CE: "plane-ce" CHART_NAME_ENTERPRISE: "plane-enterprise" + CHART_NAME_MCP_SERVER: "plane-mcp-server" MARK_AS_LATEST: true MARK_AS_PRERELASE: false PAGES_INDEX_PATH: "" jobs: setup: - if: ${{ github.event.inputs.plane-ce == 'true' || github.event.inputs.plane-ee == 'true' }} + if: ${{ github.event.inputs.plane-ce == 'true' || github.event.inputs.plane-ee == 'true' || github.event.inputs.plane-mcp-server == 'true' }} runs-on: ubuntu-22.04 permissions: contents: write @@ -81,6 +86,9 @@ jobs: if [ "${{ github.event.inputs.plane-ee }}" = "false" ]; then rm -rf charts/${{ env.CHART_NAME_ENTERPRISE }} fi + if [ "${{ github.event.inputs.plane-mcp-server }}" = "false" ]; then + rm -rf charts/${{ env.CHART_NAME_MCP_SERVER }} + fi - name: Rename Chart if: github.ref_name != 'master' @@ -94,6 +102,10 @@ jobs: sed -i "s/name: \(.*\)/name: \1-${flatBranchName}/" charts/${{ env.CHART_NAME_ENTERPRISE }}/Chart.yaml fi + if [ "${{ github.event.inputs.plane-mcp-server }}" = "true" ]; then + sed -i "s/name: \(.*\)/name: \1-${flatBranchName}/" charts/${{ env.CHART_NAME_MCP_SERVER }}/Chart.yaml + fi + echo "MARK_AS_LATEST=false" >> $GITHUB_ENV echo "MARK_AS_PRERELASE=true" >> $GITHUB_ENV echo "PAGES_INDEX_PATH=${flatBranchName}" >> $GITHUB_ENV @@ -149,6 +161,9 @@ jobs: if [ "${{ github.event.inputs.plane-ee }}" = "true" ]; then cp code/charts/plane-enterprise/README.md pages/content/plane-ee.md fi + if [ "${{ github.event.inputs.plane-mcp-server }}" = "true" ]; then + cp code/charts/plane-mcp-server/README.md pages/content/plane-mcp-server.md + fi - name: Publish pages working-directory: pages diff --git a/charts/plane-mcp-server/.helmignore b/charts/plane-mcp-server/.helmignore new file mode 100644 index 00000000..2d4a744c --- /dev/null +++ b/charts/plane-mcp-server/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +test-*.yaml \ No newline at end of file diff --git a/charts/plane-mcp-server/Chart.yaml b/charts/plane-mcp-server/Chart.yaml new file mode 100644 index 00000000..7ac88650 --- /dev/null +++ b/charts/plane-mcp-server/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 + +name: plane-mcp-server +description: plane-mcp-server Setup + +type: application + +version: 1.0.0 +appVersion: "v0.2.0" + +home: https://plane.so +icon: https://plane.so/favicon/favicon-32x32.png +sources: + - https://github.com/makeplane/plane-mcp-server \ No newline at end of file diff --git a/charts/plane-mcp-server/README.md b/charts/plane-mcp-server/README.md new file mode 100644 index 00000000..fc41aecd --- /dev/null +++ b/charts/plane-mcp-server/README.md @@ -0,0 +1,113 @@ +# Plane MCP Server Helm Chart + +## Pre-requisite + +- A working Kubernetes cluster +- `kubectl` and `helm` on the client system that you will use to install our Helm charts + +## Installing Plane MCP Server + +1. Open Terminal or any other command-line app that has access to Kubernetes tools on your local system. + +1. Set-up and customization + + For more control over your set-up, extract the Helm chart to access the values file and edit using any editor like Vim or Nano. + + ```bash + # Extract the Helm chart to access the values file + helm show values plane-mcp-server --repo https://private-helm.plane.tools > custom-values.yaml + vi custom-values.yaml + ``` + + > See `Configuration Settings` for more details. + + After saving the `custom-values.yaml` file, continue to be on the same Terminal window as on the previous steps, copy the code below, and paste it on your Terminal screen. + + ```bash + helm upgrade plane-mcp-server-app plane-mcp-server \ + --repo https://private-helm.plane.tools \ + --install \ + --create-namespace \ + --namespace plane-mcp-server \ + -f custom-values.yaml \ + --timeout 10m \ + --wait \ + --wait-for-jobs + ``` + +## Configuration Settings + +### Docker Registry Configuration + +| Setting | Default | Required | Description | +| ---------------------------- | :---------------------------: | :------: | --------------------------------------------------------------------------- | +| dockerRegistry.enabled | true | | Enable Docker registry authentication for pulling images | +| dockerRegistry.loginid | planeengineering | | Docker registry login ID/username | +| dockerRegistry.password | | | Docker registry password or token | +| dockerRegistry.default_tag | latest | | Default image tag for MCP server image | +| services.api.image | makeplane/plane-mcp-server | | MCP Server Docker image name (without tag) | + +### MCP Server Setup + +| Setting | Default | Required | Description | +| ---------------------------- | :--------------------: | :------: | --------------------------------------------------------------------------- | +| services.api.replicas | 1 | | Number of MCP Server replicas | +| services.api.memoryLimit | 1000Mi | | Memory limit for MCP Server pods | +| services.api.cpuLimit | 500m | | CPU limit for MCP Server pods | +| services.api.memoryRequest | 50Mi | | Memory request for MCP Server pods | +| services.api.cpuRequest | 50m | | CPU request for MCP Server pods | + +### Plane OAuth Configuration + +| Setting | Default | Required | Description | +| ------------------------------------- | :---------: | :------: | --------------------------------------------------------------------------- | +| services.api.plane_oauth.client_id | | Yes | Plane OAuth Client ID for authentication | +| services.api.plane_oauth.client_secret| | Yes | Plane OAuth Client Secret for authentication | +| services.api.plane_oauth.redirect_uri | | Yes | OAuth redirect URI for callback handling | +| services.api.plane_oauth.base_url | | Yes | Plane instance base URL for OAuth | + +### Redis/Valkey Setup + +| Setting | Default | Required | Description | +| -------------------------------- | :-------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| services.redis.local_setup | true | | Plane MCP Server uses `redis` to cache session data. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely. Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database | +| services.redis.image | valkey/valkey:7.2.11-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) | +| services.redis.volume_size | 500Mi | | While setting up the stateful deployment, while creating the persistent volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) | +| services.redis.external_redis_url| | | Users can also decide to use the remote hosted database and link to Plane MCP Server deployment. Ignoring all the above keys, set `services.redis.local_setup` to `false` and set this key with remote connection url | + +### Ingress Configuration + +| Setting | Default | Required | Description | +| -------------------------------- | :-----------------------------------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ingress.enabled | true | | Enable ingress for Plane MCP Server | +| ingress.host | mcp.example.com | Yes | Main hostname for Plane MCP Server application | +| ingress.ingressClass | nginx | Yes | Kubernetes cluster setup comes with various options of `ingressClass`. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider | +| ingress.ingressAnnotations | { nginx.ingress.kubernetes.io/proxy-body-size: "10m" } | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required | +| ingress.ssl.enabled | false | | Enable SSL/TLS for ingress | +| ingress.ssl.issuer | cloudflare | | CertManager configuration allows user to create issuers using `http` or any of the other DNS Providers like `cloudflare`, `digitalocean`, etc. As of now Plane MCP Server supports `http`, `cloudflare`, `digitalocean` | +| ingress.ssl.token | | | To create issuers using DNS challenge, set the issuer api token of dns provider like `cloudflare` or `digitalocean` (not required for http) | +| ingress.ssl.server | https://acme-v02.api.letsencrypt.org/directory | | Issuer creation configuration need the certificate generation authority server url. Default URL is the `Let's Encrypt` server | +| ingress.ssl.email | engineering@plane.so | | Certificate generation authority needs a valid email id before generating certificate. Required when `ingress.ssl.enabled=true` | + +## Custom Ingress Routes + +If you are planning to use 3rd party ingress providers, here is the available route configuration + +| Host | Path | Service | Required | +| ----------------------- | :-----------: | ------------------------------------------ | :------: | +| mcp.example.com | / | -api:8000> | Yes | + +## Verify + +- After install, the MCP Server listens on Service `-api` port 8000 +- If ingress is enabled, access the application at `https:///` via Ingress +- Check all pods are running: `kubectl get pods -n ` +- Check services: `kubectl get svc -n ` + +## Troubleshooting + +- Ensure `ingress.host` resolves to your ingress controller +- For TLS issues, check cert-manager events and Issuer/Certificate resources in the install namespace +- If using external Redis, verify `services.redis.external_redis_url` is reachable from the cluster +- Confirm Redis/Valkey pods are Ready; caching depends on it +- Ensure all Plane OAuth configuration values are correctly set (client_id, client_secret, redirect_uri, base_url) diff --git a/charts/plane-mcp-server/questions.yml b/charts/plane-mcp-server/questions.yml new file mode 100644 index 00000000..a33f1bee --- /dev/null +++ b/charts/plane-mcp-server/questions.yml @@ -0,0 +1,139 @@ +questions: + +- variable: dockerRegistry.loginid + label: "Login ID" + type: string + default: "makeplane" + group: "Docker Credentials" + subquestions: + - variable: dockerRegistry.password + label: "Password/Token" + type: password + default: "" + - variable: dockerRegistry.default_tag + label: "Image Tag" + type: string + default: "latest" + - variable: services.api.image + label: "MCP Server Docker Image (without tag)" + type: string + default: "makeplane/plane-mcp-server" + - variable: services.storage_class + label: "Storage Class" + type: storageclass + +- variable: services.api.replicas + label: "MCP Server Replicas" + type: int + default: 1 + group: "MCP Server Setup" + subquestions: + - variable: services.api.memoryLimit + label: "Memory Limit" + type: string + default: "1000Mi" + - variable: services.api.cpuLimit + label: "CPU Limit" + type: string + default: "500m" + - variable: services.api.memoryRequest + label: "Memory Request" + type: string + default: "50Mi" + - variable: services.api.cpuRequest + label: "CPU Request" + type: string + default: "50m" + +- variable: services.api.plane_oauth.enabled + label: "Enable Plane OAuth" + type: boolean + default: true + group: "MCP Server Setup" + show_subquestion_if: true + subquestions: + - variable: services.api.plane_oauth.client_id + label: "Plane OAuth Client ID" + type: string + default: "" + - variable: services.api.plane_oauth.client_secret + label: "Plane OAuth Client Secret" + type: password + default: "" + - variable: services.api.plane_oauth.provider_base_url + label: "Plane OAuth Provider Base URL" + type: string + default: "" + - variable: services.api.plane_base_url + label: "Plane Base URL" + type: string + default: "" + - variable: services.api.plane_internal_base_url + label: "Plane Internal Base URL" + type: string + default: "" + +- variable: services.redis.local_setup + label: "Install Redis" + type: boolean + default: true + group: "Redis Setup" + subquestions: + - variable: services.redis.image + label: "Docker Image" + type: string + default: "valkey/valkey:7.2.11-alpine" + show_if: "services.redis.local_setup=true" + - variable: services.redis.volume_size + label: "Volume Size" + type: string + default: "500Mi" + show_if: "services.redis.local_setup=true" + - variable: services.redis.external_redis_url + label: "Remote Redis URL" + type: string + default: "redis://" + show_if: "services.redis.local_setup=false" + +- variable: ingress.host + label: "MCP Server Hostname" + type: string + required: true + default: "mcp.example.com" + group: "Ingress Setup" + subquestions: + - variable: ingress.ingressClass + label: "Ingress Classname" + type: string + required: true + default: "nginx" + +- variable: ingress.ssl.enabled + label: "Enable SSL" + type: boolean + default: false + group: "Ingress Setup" + show_subquestion_if: true + subquestions: + - variable: ingress.ssl.issuer + label: "SSL Issuer" + type: enum + options: + - "http" + - "cloudflare" + - "digitalocean" + default: "cloudflare" + - variable: ingress.ssl.server + label: "SSL Server" + type: string + default: "https://acme-v02.api.letsencrypt.org/directory" + - variable: ingress.ssl.email + label: "SSL Email" + type: string + default: "engineering@plane.so" + - variable: ingress.ssl.token + label: "Provider API Token" + type: password + default: "" + description: "Not required for 'http' issuer" + show_if: "ingress.ssl.issuer=cloudflare || ingress.ssl.issuer=digitalocean" diff --git a/charts/plane-mcp-server/templates/_helpers.tpl b/charts/plane-mcp-server/templates/_helpers.tpl new file mode 100644 index 00000000..0aa026f6 --- /dev/null +++ b/charts/plane-mcp-server/templates/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "imagePullSecret" }} +{{- printf "{\"auths\":{\"index.docker.io/v1/\":{\"username\":\"%s\",\"password\":\"%s\"}}}" .Values.dockerRegistry.loginid .Values.dockerRegistry.password | b64enc }} +{{- end }} \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/config-secrets/app-env.yaml b/charts/plane-mcp-server/templates/config-secrets/app-env.yaml new file mode 100644 index 00000000..20185d9a --- /dev/null +++ b/charts/plane-mcp-server/templates/config-secrets/app-env.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-app-secrets +stringData: + PLANE_OAUTH_PROVIDER_CLIENT_ID: {{ .Values.services.api.plane_oauth.client_id | quote }} + PLANE_OAUTH_PROVIDER_CLIENT_SECRET: {{ .Values.services.api.plane_oauth.client_secret | quote }} + PLANE_OAUTH_PROVIDER_BASE_URL: {{ .Values.services.api.plane_oauth.provider_base_url | quote }} + PLANE_BASE_URL: {{ .Values.services.api.plane_base_url | quote }} + PLANE_INTERNAL_BASE_URL: {{ .Values.services.api.plane_internal_base_url | quote }} + REDIS_HOST: {{ .Release.Name }}-redis + REDIS_PORT: "6379" + {{- if .Values.services.redis.local_setup }} + REDIS_URL: "redis://{{ .Release.Name }}-redis.{{ .Release.Namespace }}.svc.cluster.local:6379/" + {{- else }} + REDIS_URL: {{ .Values.services.redis.external_redis_url | default "" | quote }} + {{- end }} + PORT: "8211" \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/config-secrets/docker-registry.yaml b/charts/plane-mcp-server/templates/config-secrets/docker-registry.yaml new file mode 100644 index 00000000..14e6b3dc --- /dev/null +++ b/charts/plane-mcp-server/templates/config-secrets/docker-registry.yaml @@ -0,0 +1,12 @@ +{{- if .Values.dockerRegistry.enabled }} + +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-docker-registry-credentials +data: + .dockerconfigjson: {{ include "imagePullSecret" .}} +type: kubernetes.io/dockerconfigjson + +{{- end }} \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/ingress/ingress.yaml b/charts/plane-mcp-server/templates/ingress/ingress.yaml new file mode 100644 index 00000000..3a00be35 --- /dev/null +++ b/charts/plane-mcp-server/templates/ingress/ingress.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.ingress.enabled .Values.ingress.host }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.ingress.clientMaxBodySize | default "5m" | quote}} +spec: + ingressClassName: {{ .Values.ingress.ingressClass }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - backend: + service: + port: + number: 8211 + name: {{ .Release.Name }}-api + path: / + pathType: Prefix + + {{- if .Values.ingress.ssl.enabled }} + tls: + - hosts: + - {{ .Values.ingress.host | quote }} + secretName: {{ .Release.Name }}-ssl-cert + {{- end }} + +{{- end }} \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/ingress/issuers-certs.yaml b/charts/plane-mcp-server/templates/ingress/issuers-certs.yaml new file mode 100644 index 00000000..979e66d7 --- /dev/null +++ b/charts/plane-mcp-server/templates/ingress/issuers-certs.yaml @@ -0,0 +1,59 @@ +{{- if and .Values.ingress.enabled .Values.ingress.ssl.enabled .Values.ingress.host }} + +{{- if ne .Values.ingress.ssl.issuer "http" }} +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-issuer-api-token-secret +type: Opaque +stringData: + api-token: {{ .Values.ingress.ssl.token | default "default-api-token" | quote }} + +--- +{{- end }} + +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-cert-issuer +spec: + acme: + email: {{ .Values.ingress.ssl.email }} + server: {{ .Values.ingress.ssl.server }} + privateKeySecretRef: + name: {{ .Release.Name }}-cert-issuer-key + solvers: + {{- if eq .Values.ingress.ssl.issuer "cloudflare" }} + - dns01: + cloudflare: + apiTokenSecretRef: + name: {{ .Release.Name }}-issuer-api-token-secret + key: api-token + {{- else if eq .Values.ingress.ssl.issuer "digitalocean" }} + - dns01: + digitalocean: + tokenSecretRef: + name: {{ .Release.Name }}-issuer-api-token-secret + key: api-token + {{- else if eq .Values.ingress.ssl.issuer "http" }} + - http01: + ingress: + ingressClassName: {{ .Values.ingress.ingressClass }} + {{- end }} + +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-ssl-cert +spec: + dnsNames: + - {{ .Values.ingress.host | quote }} + issuerRef: + name: {{ .Release.Name }}-cert-issuer + secretName: {{ .Release.Name }}-ssl-cert + +{{- end}} diff --git a/charts/plane-mcp-server/templates/service-account.yaml b/charts/plane-mcp-server/templates/service-account.yaml new file mode 100644 index 00000000..6eed2f5f --- /dev/null +++ b/charts/plane-mcp-server/templates/service-account.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-srv-account +{{- if .Values.dockerRegistry.enabled }} +imagePullSecrets: + - name: {{ .Release.Name }}-docker-registry-credentials +{{- end}} \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/workloads/plane-mcp-server.deployment.yaml b/charts/plane-mcp-server/templates/workloads/plane-mcp-server.deployment.yaml new file mode 100644 index 00000000..f06f42d1 --- /dev/null +++ b/charts/plane-mcp-server/templates/workloads/plane-mcp-server.deployment.yaml @@ -0,0 +1,58 @@ + +apiVersion: v1 +kind: Service +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-api + labels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-api +spec: + clusterIP: None + ports: + - name: api-8211 + port: 8211 + protocol: TCP + targetPort: 8211 + selector: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-api + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-api-wl +spec: + replicas: {{ .Values.services.api.replicas | default 1}} + selector: + matchLabels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-api + template: + metadata: + namespace: {{ .Release.Namespace }} + labels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-api + annotations: + timestamp: {{ now | quote }} + spec: + containers: + - name: {{ .Release.Name }}-api + imagePullPolicy: Always + image: {{ .Values.services.api.image | default "makeplane/plane-mcp-server" }}:{{ .Values.dockerRegistry.default_tag | default "latest" }} + stdin: true + tty: true + resources: + requests: + memory: {{ .Values.services.api.memoryRequest | default "50Mi" | quote }} + cpu: {{ .Values.services.api.cpuRequest | default "50m" | quote }} + limits: + memory: {{ .Values.services.api.memoryLimit | default "1000Mi" | quote }} + cpu: {{ .Values.services.api.cpuLimit | default "500m" | quote }} + envFrom: + - secretRef: + name: {{ .Release.Name }}-app-secrets + optional: false + serviceAccount: {{ .Release.Name }}-srv-account + serviceAccountName: {{ .Release.Name }}-srv-account +--- \ No newline at end of file diff --git a/charts/plane-mcp-server/templates/workloads/redis-statefulset.yaml b/charts/plane-mcp-server/templates/workloads/redis-statefulset.yaml new file mode 100644 index 00000000..84e9b7e2 --- /dev/null +++ b/charts/plane-mcp-server/templates/workloads/redis-statefulset.yaml @@ -0,0 +1,66 @@ +{{- if .Values.services.redis.local_setup }} + +apiVersion: v1 +kind: Service +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-redis + labels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-redis +spec: + clusterIP: None + ports: + - name: redis-6379 + port: 6379 + protocol: TCP + targetPort: 6379 + selector: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-redis +--- + +# REDIS WORKLOAD + +apiVersion: apps/v1 +kind: StatefulSet +metadata: + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-redis-wl +spec: + selector: + matchLabels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-redis + serviceName: {{ .Release.Name }}-redis + template: + metadata: + labels: + app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-redis + spec: + containers: + - image: {{ .Values.services.redis.image }} + imagePullPolicy: Always + name: {{ .Release.Name }}-redis + stdin: true + tty: true + volumeMounts: + - mountPath: /data + name: pvc-{{ .Release.Name }}-redis-vol + subPath: '' + serviceAccount: {{ .Release.Name }}-srv-account + serviceAccountName: {{ .Release.Name }}-srv-account + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + creationTimestamp: null + namespace: {{ .Release.Namespace }} + name: pvc-{{ .Release.Name }}-redis-vol + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.services.redis.volume_size | default "1Gi" | quote }} + storageClassName: {{ .Values.services.storage_class | quote }} + volumeMode: Filesystem + +{{- end }} \ No newline at end of file diff --git a/charts/plane-mcp-server/values.yaml b/charts/plane-mcp-server/values.yaml new file mode 100644 index 00000000..a9659178 --- /dev/null +++ b/charts/plane-mcp-server/values.yaml @@ -0,0 +1,40 @@ +dockerRegistry: + enabled: true + loginid: makeplane + password: '' + default_tag: latest + +ingress: + enabled: true + host: 'mcp.example.com' + ingressClass: 'nginx' + ingressAnnotations: { nginx.ingress.kubernetes.io/proxy-body-size: "10m" } + ssl: + enabled: false + issuer: cloudflare # Allowed : cloudflare, digitalocean, http + token: '' # not required for http + server: https://acme-v02.api.letsencrypt.org/directory + email: engineering@plane.so + +services: + storage_class: '' + api: + image: makeplane/plane-mcp-server + replicas: 1 + memoryLimit: 1000Mi + cpuLimit: 500m + memoryRequest: 50Mi + cpuRequest: 50m + plane_base_url: '' + plane_internal_base_url: '' + plane_oauth: + enabled: false + client_id: '' + client_secret: '' + provider_base_url: '' + + redis: + local_setup: true + image: valkey/valkey:7.2.11-alpine + volume_size: 500Mi + external_redis_url: '' # INCASE OF REMOTE REDIS ONLY diff --git a/test.sh b/test.sh index 683cfb9a..f2879b72 100755 --- a/test.sh +++ b/test.sh @@ -29,6 +29,7 @@ HELM_CHART=$(dialog \ --menu "Select the Helm Chart to test" 25 50 20 \ "1" "Plane-CE" \ "2" "Plane-Enterprise" \ + "3" "Plane-MCP-Server" \ 3>&1 1>&2 2>&3) @@ -49,6 +50,15 @@ elif [ "$HELM_CHART" == "2" ]; then else printFailed fi +elif [ "$HELM_CHART" == "3" ]; then + helm template plane-mcp-server-app-$(date +%s) charts/plane-mcp-server -n myns > test-mcp-server.yaml + if [ $? -eq 0 ]; then + clear + printSuccess + code test-mcp-server.yaml + else + printFailed + fi else exit 0 fi