MCP in a docker world

I followed penpot/mcp at develop · penpot/penpot · GitHub to run the MCP-Server and the plugin locally and it works like a charm.

And I saw on penpot/docker/images/docker-compose.yaml at develop · penpot/penpot · GitHub that a `penpot-mcp` service was added. So I’ve tried to check it out and updated my `docker-compose.yaml` but it doesn’t work. Neither can I install the MCP-Plugin nor can I connect the MCP-Server. I think there are missing some port mappings and environment variables. And it seems that this service is server-only.

So my question is: how can it be used? Is there something missing? Any help appreciated.

Hi, @rwam, the MCP runs on self-hosted instances in both Docker and Kubernetes (Helm).

Have you added the service?
Have you enabled the MCP flag?
Have you tried using the docker-compose.yml file from the repository to set it up locally?
Can you share the contents of your docker-compose.yml file?

Best regards

Hi @davidbarragan,

I use a customized `docker-compose.yaml` and adapted latest changes from the repository. So I’ve ended up here:

## Common flags:
# demo-users
# email-verification
# log-emails
# log-invitation-tokens
# login-with-github
# login-with-gitlab
# login-with-google
# login-with-ldap
# login-with-oidc
# login-with-password
# prepl-server
# registration
# secure-session-cookies
# smtp
# smtp-debug
# telemetry
# webhooks
##
## You can read more about all available flags and other
## environment variables here:
## https://help.penpot.app/technical-guide/configuration/#penpot-configuration
#
# WARNING: if you're exposing Penpot to the internet, you should remove the flags
# 'disable-secure-session-cookies' and 'disable-email-verification'
x-flags: &penpot-flags
  PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp enable-registration enable-login-with-password

x-uri: &penpot-public-uri
  PENPOT_PUBLIC_URI: ${PENPOT_PUBLIC_URI:-http://penpot.our-internal-vm:9001}

x-body-size: &penpot-http-body-size
  # Max body size
  PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
  # Deprecation warning: this variable is deprecated. Use PENPOT_HTTP_SERVER_MAX_BODY (defaults to 367001600)
  PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600

## Penpot SECRET KEY. It serves as a master key from which other keys for subsystems
## (eg http sessions, or invitations) are derived.
##
## We recommend to use a trully randomly generated
## 512 bits base64 encoded string here. You can generate one with:
##
## python3 -c "import secrets; print(secrets.token_urlsafe(64))"
x-secret-key: &penpot-secret-key
  PENPOT_SECRET_KEY: A-Very-secret-key1_11

networks:
  penpot:

volumes:
  penpot_postgres_v15:
  penpot_assets:
  # penpot_traefik:

services:
  ## Traefik service declaration example. Consider using it if you are going to expose
  ## penpot to the internet, or a different host than `localhost`.

  # traefik:
  #   image: traefik:v3.3
  #   networks:
  #     - penpot
  #   command:
  #     - "--api.insecure=true"
  #     - "--entryPoints.web.address=:80"
  #     - "--providers.docker=true"
  #     - "--providers.docker.exposedbydefault=false"
  #     - "--entryPoints.websecure.address=:443"
  #     - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
  #     - "--certificatesresolvers.letsencrypt.acme.email=<EMAIL_ADDRESS>"
  #     - "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json"
  #   volumes:
  #     - "penpot_traefik:/traefik"
  #     - "/var/run/docker.sock:/var/run/docker.sock"
  #   ports:
  #     - "80:80"
  #     - "443:443"

  penpot-frontend:
    image: "penpotapp/frontend:latest"
    restart: always
    ports:
      - 9001:8080

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp

    networks:
      - penpot

    # labels:
      # - "traefik.enable=true"

      # ## HTTPS: example of labels for the case where penpot will be exposed to the
      # ## internet with HTTPS using traefik.

      # - "traefik.http.routers.penpot-https.rule=Host(`<DOMAIN_NAME>`)"
      # - "traefik.http.routers.penpot-https.entrypoints=websecure"
      # - "traefik.http.routers.penpot-https.tls.certresolver=letsencrypt"
      # - "traefik.http.routers.penpot-https.tls=true"

    environment:
      << : [*penpot-flags, *penpot-http-body-size, *penpot-public-uri]
      # Set to "true" on hosts where IPv6 is disabled at kernel boot level.
      # PENPOT_DISABLE_IPV6_LISTEN: "true"

  penpot-backend:
    image: "penpotapp/backend:latest"
    restart: always

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      penpot-postgres:
        condition: service_healthy
      penpot-valkey:
        condition: service_healthy

    networks:
      - penpot

    ## Configuration environment variables for the backend container.

    environment:
      << : [*penpot-flags, *penpot-public-uri, *penpot-http-body-size, *penpot-secret-key]

      ## Database connection parameters. Don't touch them unless you are using custom
      ## postgresql connection parameters.

      PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
      PENPOT_DATABASE_USERNAME: penpot
      PENPOT_DATABASE_PASSWORD: penpot

      ## Valkey (or previously redis) is used for the websockets notifications. Don't touch
      ## unless the valkey container has different parameters or different name.

      PENPOT_REDIS_URI: redis://penpot-valkey/0

      ## Default configuration for assets storage: using filesystem based with all files
      ## stored in a docker volume.

      PENPOT_OBJECTS_STORAGE_BACKEND: fs
      PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets

      ## Also can be configured to to use a S3 compatible storage.

      # AWS_ACCESS_KEY_ID: <KEY_ID>
      # AWS_SECRET_ACCESS_KEY: <ACCESS_KEY>
      # PENPOT_OBJECTS_STORAGE_BACKEND: s3
      # PENPOT_OBJECTS_STORAGE_S3_ENDPOINT: <ENDPOINT>
      # PENPOT_OBJECTS_STORAGE_S3_BUCKET: <BUKET_NAME>

      ## Telemetry. When enabled, a periodical process will send anonymous data about this
      ## instance. Telemetry data will enable us to learn how the application is used,
      ## based on real scenarios. If you want to help us, please leave it enabled. You can
      ## audit what data we send with the code available on github.

      PENPOT_TELEMETRY_ENABLED: "true"
      PENPOT_TELEMETRY_REFERER: compose

      ## Example SMTP/Email configuration. By default, emails are sent to the mailcatch
      ## service, but for production usage it is recommended to setup a real SMTP
      ## provider. Emails are used to confirm user registrations & invitations. Look below
      ## how the mailcatch service is configured.

      PENPOT_SMTP_DEFAULT_FROM: Penpot <penpot@example.com>
      PENPOT_SMTP_DEFAULT_REPLY_TO: Penpot <penpot@example.com>
      PENPOT_SMTP_HOST: 1.2.3.4
      PENPOT_SMTP_PORT: 25
      PENPOT_SMTP_USERNAME:
      PENPOT_SMTP_PASSWORD:
      PENPOT_SMTP_TLS: "false"
      PENPOT_SMTP_SSL: "false"

  penpot-mcp:
    image: "penpotapp/mcp:${PENPOT_VERSION:-2.16}"
    restart: always
    networks:
      - penpot

  penpot-exporter:
    image: "penpotapp/exporter:latest"
    restart: always

    depends_on:
      penpot-valkey:
        condition: service_healthy

    networks:
      - penpot

    environment:
      << : [*penpot-secret-key]

      # Don't touch it; this uses an internal docker network to
      # communicate with the frontend.
      PENPOT_PUBLIC_URI: http://penpot-frontend:8080

      ## Valkey (or previously Redis) is used for the websockets notifications.
      PENPOT_REDIS_URI: redis://penpot-valkey/0

  penpot-postgres:
    image: "postgres:15"
    restart: always
    stop_signal: SIGINT

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U penpot"]
      interval: 2s
      timeout: 10s
      retries: 5
      start_period: 2s

    volumes:
      - penpot_postgres_v15:/var/lib/postgresql/data

    networks:
      - penpot

    environment:
      - POSTGRES_INITDB_ARGS=--data-checksums
      - POSTGRES_DB=penpot
      - POSTGRES_USER=penpot
      - POSTGRES_PASSWORD=penpot

  penpot-valkey:
    image: valkey/valkey:8.1
    restart: always

    healthcheck:
      test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
      interval: 1s
      timeout: 3s
      retries: 5
      start_period: 3s

    networks:
      - penpot

    environment:
      # You can increase the max memory size if you have sufficient resources,
      # although this should not be necessary.
      - VALKEY_EXTRA_FLAGS=--maxmemory 128mb --maxmemory-policy volatile-lfu

  ## A mailcatch service, used as temporal SMTP server. You can access via HTTP to the
  ## port 1080 for read all emails the penpot platform has sent. Should be only used as a
  ## temporal solution while no real SMTP provider is configured.

  penpot-mailcatch:
    image: sj26/mailcatcher:latest
    restart: always
    expose:
      - '1025'
    ports:
      - "1080:1080"
    networks:
      - penpot

And everything works locally and on our VM but how can I install resp. serve the MCP plugin and connect to the MCP server?

You just have to add the new service and the flag, the plugin to use it is included in the frontend image:

x-flags: &penpot-flags
  PENPOT_FLAGS: ... enable-mcp

...

services:
  ...

  penpot-frontend:
    image: "penpotapp/frontend:${PENPOT_VERSION:-2.16}"
    restart: always
    ports:
      - 9001:8080

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp

    ...

  ...

  penpot-mcp:
    image: "penpotapp/mcp:${PENPOT_VERSION:-2.16}"
    restart: always
    networks:
      - penpot

  ...

You only need to install a plugin if you want to use your custom MCP (not the oficial image)

Regards.

Oh, sorry. my fault. I forgot to rebuild the containers. Everything seems to be fine with my YAML. So I have to run

docker compose -p penpot -f docker-compose.yaml up -d --build --force-recreate

And now I can see the MCP server on the integrations tab and the plugin on a penpot file.

Great! We use `docker compose up -d --pull always --force-recreat` for upgrades.

1 Like