Skip to main content
The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on GitHub.

Step 1: Create a Workflow File

In your server project directory, create a .github/workflows/publish-mcp.yml file. Here is an example for npm-based local server, but the MCP Registry publishing steps are the same for all package types: The examples pin mcp-publisher to a specific release and verify its Sigstore bundle before extracting it. When upgrading, update MCP_PUBLISHER_VERSION to the intended release tag deliberately; do not switch back to the mutable releases/latest URL.

Step 2: Add Secrets

You may need to add a secret depending on which authentication method you choose:
  • GitHub OIDC Authentication: No dedicated secret necessary.
  • GitHub PAT Authentication: Add a MCP_GITHUB_TOKEN secret with a GitHub Personal Access Token. The registry only needs to read your organization role to confirm you are an Owner before granting its namespace, so grant the minimum for that and no repository scopes (the registry never reads or writes your code):
    • Classic PAT: the read:org scope (read:user is not needed; a bare token still authenticates and publishes to your personal namespace).
    • Fine-grained PAT: the Organization permissions → Members → Read-only permission (the equivalent of read:org). Without it you’ll get your personal namespace only. A fine-grained PAT is scoped to one resource owner, so create it for the organization you’re publishing to.
    For an organization token, store it as an Environment secret on the mcp-registry-publish environment rather than a plain repository secret — see “Securing your registry token in CI” below for why this matters and how to configure the environment.
  • DNS Authentication: Add a MCP_PRIVATE_KEY secret with your Ed25519 private key.
You may also need to add secrets for your package registry. For example, the workflow above needs an NPM_TOKEN secret with your npm token. For information about how to add secrets to a repository, see Using secrets in GitHub Actions.

Securing your registry token in CI

Whichever token you use to authenticate, treat it as a high-value credential. When you authenticate to an organization namespace, the resulting registry token can publish — and overwrite — any server under io.github.<org>/*, not just the one in this repository. The relevant question for your threat model is therefore: who can cause code to run in a job where this secret is exposed? By default that is every repository writer (via branch or tag pushes), not only org Owners — so a plain repo secret quietly widens publish access to everyone with write access. GitHub gives you the tools to close that gap. We recommend, in increasing order of strength:
  1. Store the token as an Environment secret, not a repository or organization secret. A job can only read an environment secret when it declares environment: (as the PAT example above does). Note that the environment: key alone protects nothing: if the environment does not exist yet, GitHub auto-creates it with no protection rules on first run, and a job that declares environment: still also receives ordinary repository and organization secrets. The protection comes from storing the token as a secret on that environment and configuring the environment (steps 2–3 below) — not from the environment: key by itself.
  2. Restrict that environment to your default branch and/or release tags with a deployment branch rule, and protect that branch with required pull request reviews. Now only reviewed, maintainer-approved code can ever reach the token.
  3. (Strongest) Add a required reviewer to the environment so each publish pauses for an explicit human approval.
Two pitfalls to avoid regardless of the above:
  • Never publish from a pull_request_target workflow that checks out PR-head code. That trigger runs with your secrets in the base-repo context, so an untrusted fork PR could exfiltrate the token. Environment branch rules do not protect you here (the ref is still your base branch) — only required reviewers do.
  • Avoid self-hosted runners for the publish job on public repositories, where fork PRs may be able to schedule jobs onto them.
By default, pull requests from forks receive no secrets and cannot push branches, so external contributors cannot reach the token without one of the misconfigurations above.

Step 3: Tag and Release

Create and push a version tag to trigger the workflow:
The workflow will run tests, build the package, publish the package to npm, and publish the server to the MCP Registry.

Troubleshooting