Skip to main content

Using private npm packages in hosted servers

Organizations often maintain internal npm packages for shared utilities, proprietary integrations, or security-sensitive code. Hosted connectors run self-contained container images and install nothing at startup, so install private packages when you build the image. Pass the registry token as a Docker build secret, which keeps it out of the image layers and out of Mintive entirely.

1. Identify required credentials​

Determine what authentication your private registry requires:

  • npm Enterprise or GitHub Packages: Authentication token
  • JFrog Artifactory: Token or username/password
  • Azure Artifacts: Personal access token
  • Custom registry: Check registry documentation

2. Write an .npmrc for the build​

Create an .npmrc outside your build context (for example in your home directory) that maps your package scope to the registry and holds the token:

@myorg:registry=https://registry.mycompany.com/
//registry.mycompany.com/:_authToken=<auth-token>

Add one registry line per scope and one _authToken line per registry host if you pull from several registries. See Common registry configurations for exact values.

3. Install the package in the Dockerfile​

Mount the .npmrc as a build secret only for the install step:

# syntax=docker/dockerfile:1
FROM node:22-slim
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install -g @myorg/private-mcp-server

Then build for the hosted platform, passing the secret:

docker build --platform linux/amd64 \
--secret id=npmrc,src=$HOME/.npmrc \
-t private-mcp-server:1.0.0 .

The secret exists only while that RUN step executes, so the token isn't written into any image layer.

4. Push the image to Mintive and deploy it​

Hosted connectors can only pull public images, and this image contains your private packages, so don't publish it to Docker Hub or a public GHCR repository. Push it to Mintive's managed registry with the Admin MCP instead. Images there are scoped to your organization.

  1. In an AI client connected to the Admin MCP, call create_registry_push_session. It returns an imageRef, a registryHost, a username, a short-lived credentialUrl, and a finalizeToken.

  2. Tag the image you built in step 3, log in, and push it:

    docker tag private-mcp-server:1.0.0 <imageRef>
    curl -fsSL '<credentialUrl>' | docker login <registryHost> -u <username> --password-stdin
    docker push <imageRef>

    Piping the credential URL into docker login keeps the registry password out of your shell history and the chat.

  3. Call deploy_pushed_image with the finalizeToken and a config for the new connector. For a stdio server, set the transport to stdio and the command to the package's executable name (for example private-mcp-server). For an HTTP server whose image sets its own entrypoint, leave the command unset.

You can also ask the assistant to run these steps for you. Tell it the image is already built locally, so it pushes private-mcp-server:1.0.0 instead of rebuilding without the build secret.

npx @mintmcp/hosted-cli build-and-push also pushes to the managed registry, but it can't pass a build secret, so use the Admin MCP for this image.

See Deploy Docker-based hosted connectors for the full image requirements.

Security considerations​

  1. Token permissions: Use tokens with minimal required permissions (read-only package access)
  2. Token rotation: Establish a rotation schedule for shared tokens
  3. No token in the image: Build secrets aren't stored in image layers, so anyone who can pull the image can't recover the registry token
  4. Least privilege: Prefer scoped package authentication over global registry credentials

Common registry configurations​

GitHub Packages​

@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<github-pat>

Required GitHub token scopes: read:packages

Azure Artifacts​

@myorg:registry=https://pkgs.dev.azure.com/myorg/_packaging/myfeed/npm/registry/
//pkgs.dev.azure.com/myorg/_packaging/myfeed/npm/registry/:_authToken=<azure-pat>

Required Azure DevOps token scopes: Packaging (read)

JFrog Artifactory​

@myorg:registry=https://mycompany.jfrog.io/artifactory/api/npm/npm-local/
//mycompany.jfrog.io/artifactory/api/npm/npm-local/:_authToken=<artifactory-token>

Troubleshooting​

Build fails with 401, 404, or invalid token/password errors​

  • Verify the registry URL is correct
  • Confirm the package name and scope match your registry configuration
  • Check that the token has read access to the package
  • Check that you passed --secret id=npmrc,... and that the id matches the --mount in the Dockerfile

Authentication fails​

  • Verify the token is not expired
  • Check that the registry path in the _authToken line matches the registry URL exactly (including trailing slashes)
  • For scoped packages, ensure the @scope:registry line names the right scope

Existing connectors​

Hosted connectors created earlier from a pasted STDIO JSON config, which install packages at startup using npm_config_ environment variables, keep running unchanged.

Next steps​