Two real defects, plus the CLI reference claims that go with them. ## 1. ECDSA P-384 login instructions cannot work Every "ECDSA P-384" tab ran the *identical* command to the Ed25519 tab. Since `--algorithm` defaults to ed25519 ([login.go:100](https://github.com/modelcontextprotocol/registry/blob/main/cmd/publisher/commands/login.go#L100)), a 96-hex key is rejected outright. Verified against a freshly generated P-384 key: ``` $ mcp-publisher login dns --domain=example.com --private-key=$KEY Signing in process using key algorithm ed25519 Error: failed to get token: failed to sign timestamp: invalid seed length: expected 32 bytes, got 48 $ mcp-publisher login dns --algorithm ecdsap384 --domain=example.com --private-key=$KEY Signing in process using key algorithm ecdsap384 Expected proof record: # works ``` `--algorithm` is registered at [login.go:129](https://github.com/modelcontextprotocol/registry/blob/main/cmd/publisher/commands/login.go#L129) and described correctly by the CLI's own help, but `grep -rn algorithm docs/` had **zero** hits for it. Anyone following the ECDSA path in the docs hit that error with nothing to tell them why. Fixed at all four in-process ECDSA sites (2 in `authentication.mdx`, 2 in `cli/commands.md`), with a note that cloud signing providers derive the algorithm from the key itself so the flag does not apply to them. ## 2. `mcp-publisher validate --help` was broken ``` $ mcp-publisher validate --help Unknown command: validate ... Use 'mcp-publisher <command> --help' for more information about a command. ``` It printed the very instruction that had just failed. `main.go` intercepted `--help` for all subcommands and called `printCommandHelp`, whose switch had no `validate` case — making `ValidateCommand`'s own help block ([validate.go:120-129](https://github.com/modelcontextprotocol/registry/blob/main/cmd/publisher/commands/validate.go#L120)) unreachable. Rather than paste a third copy of the help text, `printCommandHelp` now returns whether it handled the command, and the caller falls through to normal dispatch when it did not. Commands own their own `--help`, and any future command with this shape works automatically. | Command | Before | After | |---|---|---| | `validate --help` | `Unknown command`, exit 0 | prints its help, exit 0 | | `publish --help` | works | works | | `init -h` | works | works | | `bogus --help` | `Unknown command`, exit 0 | `Unknown command`, **exit 1** | ## 3. CLI reference corrections - **`--registry` is not global.** It exists only on `login`; other commands read the URL from the stored token. `publish --registry=URL` silently treats the flag as the `server.json` path. `cmd/publisher/README.md` had exactly this broken example. - **`--token` was undocumented** despite `github-actions.mdx:123` depending on it. Now documented, noting it is `login github` only. - **`init` takes no flags** and does not prompt; it writes `TODO:` placeholders. The reference said `init [options]` and "Prompts for missing required fields". - **Refreshed the `--help` transcript** in `quickstart.mdx`, which listed only 4 of 6 commands. Now byte-for-byte identical to real output (diffed). - **Added `validate`** to the command list in `cmd/publisher/README.md`. ## Verification `go build` and `go test ./cmd/...` pass. All four help paths exercised with exit codes checked. The 23 `goconst` findings in `validate_test.go` are pre-existing — identical count on pristine `main`, none in `main.go`. Quickstart transcript diffed against real output. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
301 lines
9.9 KiB
Text
301 lines
9.9 KiB
Text
---
|
|
title: "Quickstart: Publish an MCP Server to the MCP Registry"
|
|
sidebarTitle: "Quickstart: Publish a Server"
|
|
---
|
|
|
|
<Note>
|
|
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](https://github.com/modelcontextprotocol/registry/issues).
|
|
</Note>
|
|
|
|
This tutorial will show you how to publish an MCP server written in TypeScript to the MCP Registry using the official `mcp-publisher` CLI tool.
|
|
|
|
<Note>
|
|
If you are publishing a non-npm package (PyPI, NuGet, OCI, MCPB), the overall flow is identical, but the ownership-verification step in [Step 1](#step-1-add-verification-information-to-the-package) is different per package type. See [Package Types](./package-types) for the verification mechanism that applies to your package, then return here and follow the remaining steps.
|
|
</Note>
|
|
|
|
## Prerequisites
|
|
|
|
- **Node.js** — This tutorial assumes the MCP server is written in TypeScript.
|
|
- **npm account** — The MCP Registry only hosts metadata, not artifacts. Before publishing to the MCP Registry, we will publish the MCP server's package to npm, so you will need an [npm](https://www.npmjs.com) account.
|
|
- **GitHub account** — The MCP Registry supports [multiple authentication methods](./authentication). For simplicity, this tutorial will use GitHub-based authentication, so you will need a [GitHub](https://github.com/) account.
|
|
|
|
If you do not have an MCP server written in TypeScript, you can copy the `weather-server-typescript` server from the [`modelcontextprotocol/quickstart-resources` repository](https://github.com/modelcontextprotocol/quickstart-resources) to follow along with this tutorial:
|
|
|
|
```bash
|
|
git clone --depth 1 git@github.com:modelcontextprotocol/quickstart-resources.git
|
|
cp -r quickstart-resources/weather-server-typescript .
|
|
rm -rf quickstart-resources
|
|
cd weather-server-typescript
|
|
```
|
|
|
|
And edit `package.json` to reflect your information:
|
|
|
|
```diff package.json
|
|
{
|
|
- "name": "mcp-quickstart-ts",
|
|
- "version": "1.0.0",
|
|
+ "name": "@my-username/mcp-weather-server",
|
|
+ "version": "1.0.1",
|
|
"main": "index.js",
|
|
```
|
|
|
|
```diff package.json
|
|
"license": "ISC",
|
|
- "description": "",
|
|
+ "repository": {
|
|
+ "type": "git",
|
|
+ "url": "https://github.com/my-username/mcp-weather-server.git"
|
|
+ },
|
|
+ "description": "An MCP server for weather information.",
|
|
"devDependencies": {
|
|
```
|
|
|
|
## Step 1: Add verification information to the package
|
|
|
|
The MCP Registry verifies that a server's underlying package matches its metadata. For npm packages, this requires adding an `mcpName` property to `package.json`:
|
|
|
|
```diff package.json
|
|
{
|
|
"name": "@my-username/mcp-weather-server",
|
|
"version": "1.0.1",
|
|
+ "mcpName": "io.github.my-username/weather",
|
|
"main": "index.js",
|
|
```
|
|
|
|
The value of `mcpName` will be your server's name in the MCP Registry.
|
|
|
|
Because we will be using GitHub-based authentication, `mcpName` **must** start with `io.github.my-username/`.
|
|
|
|
## Step 2: Publish the package
|
|
|
|
The MCP Registry only hosts metadata, not artifacts, so we must publish the package to npm before publishing the server to the MCP Registry.
|
|
|
|
Ensure the distribution files are built:
|
|
|
|
```bash
|
|
# Navigate to project directory
|
|
cd weather-server-typescript
|
|
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Build the distribution files
|
|
npm run build
|
|
```
|
|
|
|
Then follow npm's [publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages). In particular, you will probably need to run the following commands:
|
|
|
|
```bash
|
|
# If necessary, authenticate to npm
|
|
npm adduser
|
|
|
|
# Publish the package
|
|
npm publish --access public
|
|
```
|
|
|
|
You can verify your package is published by visiting its npm URL, such as https://www.npmjs.com/package/@my-username/mcp-weather-server.
|
|
|
|
## Step 3: Install `mcp-publisher`
|
|
|
|
Install the `mcp-publisher` CLI tool using a pre-built binary or [Homebrew](https://brew.sh):
|
|
|
|
<CodeGroup>
|
|
|
|
```bash macOS/Linux
|
|
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher && sudo mv mcp-publisher /usr/local/bin/
|
|
```
|
|
|
|
```powershell Windows
|
|
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") { "arm64" } else { "amd64" }; Invoke-WebRequest -Uri "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_windows_$arch.tar.gz" -OutFile "mcp-publisher.tar.gz"; tar xf mcp-publisher.tar.gz mcp-publisher.exe; rm mcp-publisher.tar.gz
|
|
# Move mcp-publisher.exe to a directory in your PATH
|
|
```
|
|
|
|
```bash
|
|
brew install mcp-publisher
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Verify that `mcp-publisher` is correctly installed by running:
|
|
|
|
```bash
|
|
mcp-publisher --help
|
|
```
|
|
|
|
You should see output like:
|
|
|
|
```text Output
|
|
MCP Registry Publisher Tool
|
|
|
|
Usage:
|
|
mcp-publisher <command> [arguments]
|
|
|
|
Commands:
|
|
init Create a server.json file template
|
|
login Authenticate with the registry
|
|
logout Clear saved authentication
|
|
publish Publish server.json to the registry
|
|
status Update the status of a server version
|
|
validate Validate server.json without publishing
|
|
|
|
Use 'mcp-publisher <command> --help' for more information about a command.
|
|
```
|
|
|
|
## Step 4: Create `server.json`
|
|
|
|
The `mcp-publisher init` command can generate a `server.json` template file with some information derived from your project.
|
|
|
|
In your server project directory, run `mcp-publisher init`:
|
|
|
|
```bash
|
|
mcp-publisher init
|
|
```
|
|
|
|
Open the generated `server.json` file, and you should see contents like:
|
|
|
|
```json server.json
|
|
{
|
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
"name": "io.github.my-username/weather",
|
|
"description": "An MCP server for weather information.",
|
|
"repository": {
|
|
"url": "https://github.com/my-username/mcp-weather-server",
|
|
"source": "github"
|
|
},
|
|
"version": "1.0.0",
|
|
"packages": [
|
|
{
|
|
"registryType": "npm",
|
|
"identifier": "@my-username/mcp-weather-server",
|
|
"version": "1.0.0",
|
|
"transport": {
|
|
"type": "stdio"
|
|
},
|
|
"environmentVariables": [
|
|
{
|
|
"description": "Your API key for the service",
|
|
"isRequired": true,
|
|
"format": "string",
|
|
"isSecret": true,
|
|
"name": "YOUR_API_KEY"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Edit the contents as necessary:
|
|
|
|
```diff server.json
|
|
{
|
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
"name": "io.github.my-username/weather",
|
|
"description": "An MCP server for weather information.",
|
|
"repository": {
|
|
"url": "https://github.com/my-username/mcp-weather-server",
|
|
"source": "github"
|
|
},
|
|
- "version": "1.0.0",
|
|
+ "version": "1.0.1",
|
|
"packages": [
|
|
{
|
|
"registryType": "npm",
|
|
"identifier": "@my-username/mcp-weather-server",
|
|
- "version": "1.0.0",
|
|
+ "version": "1.0.1",
|
|
"transport": {
|
|
"type": "stdio"
|
|
- },
|
|
- "environmentVariables": [
|
|
- {
|
|
- "description": "Your API key for the service",
|
|
- "isRequired": true,
|
|
- "format": "string",
|
|
- "isSecret": true,
|
|
- "name": "YOUR_API_KEY"
|
|
- }
|
|
- ]
|
|
+ }
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The `name` property in `server.json` **must** match the `mcpName` property in `package.json`.
|
|
|
|
## Step 5: Authenticate with the MCP Registry
|
|
|
|
For this tutorial, we will authenticate with the MCP Registry using GitHub-based authentication.
|
|
|
|
Run the `mcp-publisher login` command to initiate authentication:
|
|
|
|
```bash
|
|
mcp-publisher login github
|
|
```
|
|
|
|
You should see output like:
|
|
|
|
```text Output
|
|
Logging in with github...
|
|
|
|
To authenticate, please:
|
|
1. Go to: https://github.com/login/device
|
|
2. Enter code: ABCD-1234
|
|
3. Authorize this application
|
|
Waiting for authorization...
|
|
```
|
|
|
|
Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like:
|
|
|
|
```text Output
|
|
Successfully authenticated!
|
|
✓ Successfully logged in
|
|
```
|
|
|
|
## Step 6: Publish to the MCP Registry
|
|
|
|
Finally, publish your server to the MCP Registry using the `mcp-publisher publish` command:
|
|
|
|
```bash
|
|
mcp-publisher publish
|
|
```
|
|
|
|
You should see output like:
|
|
|
|
```text Output
|
|
Publishing to https://registry.modelcontextprotocol.io...
|
|
✓ Successfully published
|
|
✓ Server io.github.my-username/weather version 1.0.1
|
|
```
|
|
|
|
You can verify that your server is published by searching for it using the MCP Registry API:
|
|
|
|
```bash
|
|
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather"
|
|
```
|
|
|
|
You should see your server's metadata in the search results JSON:
|
|
|
|
```text Output
|
|
{"servers":[{ ... "name":"io.github.my-username/weather" ... }]}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
{/* prettier-ignore-start */}
|
|
|
|
| Error Message | Action |
|
|
| --- | --- |
|
|
|"Registry validation failed for package"|Ensure your package includes the required ownership-verification marker for its package type. For npm this is `mcpName` in `package.json`; for PyPI and NuGet it is an `mcp-name: <server-name>` line (or HTML comment) in the package README; for other types see [Package Types](./package-types).|
|
|
|"Invalid or expired Registry JWT token"|Re-authenticate by running `mcp-publisher login github`.|
|
|
|"You do not have permission to publish this server"|Your authentication method doesn't match your server's namespace format. With GitHub auth, your server name must start with `io.github.your-username/`.|
|
|
|
|
{/* prettier-ignore-end */}
|
|
|
|
## Next Steps
|
|
|
|
- Learn about [support for other package types](./package-types).
|
|
- Learn about [support for remote servers](./remote-servers).
|
|
- Learn how to [use other authentication methods](./authentication), such as [DNS authentication](./authentication#dns-authentication) which enables custom domains for server name prefixes.
|
|
- Learn how to [automate publishing with GitHub Actions](./github-actions).
|