diff --git a/cmd/publisher/README.md b/cmd/publisher/README.md index 235d18952..3a5a79297 100644 --- a/cmd/publisher/README.md +++ b/cmd/publisher/README.md @@ -14,9 +14,12 @@ make publisher make dev-compose # Start local registry ./bin/mcp-publisher init ./bin/mcp-publisher login none --registry=http://localhost:8080 -./bin/mcp-publisher publish --registry=http://localhost:8080 +./bin/mcp-publisher publish ``` +`publish` takes the registry URL from the token saved by `login`, so it accepts no `--registry` +flag — passing one would be read as the `server.json` path. + ## Architecture ### Commands @@ -24,6 +27,7 @@ make dev-compose # Start local registry - **`login`** - Handle authentication (github, dns, http, none) - **`publish`** - Validate and upload servers to registry - **`status`** - Update server lifecycle status (active, deprecated, deleted) +- **`validate`** - Validate server.json without publishing - **`logout`** - Clear stored credentials ### Authentication Providers diff --git a/cmd/publisher/main.go b/cmd/publisher/main.go index 20cea2bdf..8f7de090c 100644 --- a/cmd/publisher/main.go +++ b/cmd/publisher/main.go @@ -27,10 +27,12 @@ func main() { os.Exit(1) } - // Check for help flag for subcommands + // Check for help flag for subcommands. Commands without an entry in + // printCommandHelp handle --help themselves, so fall through to dispatch. if len(os.Args) >= 3 && (os.Args[2] == "--help" || os.Args[2] == "-h") { - printCommandHelp(os.Args[1]) - return + if printCommandHelp(os.Args[1]) { + return + } } var err error @@ -81,7 +83,10 @@ func printUsage() { _, _ = fmt.Fprintln(os.Stdout, "Use 'mcp-publisher --help' for more information about a command.") } -func printCommandHelp(command string) { +// printCommandHelp prints help for a specific command and reports whether it had +// an entry here. Commands without one parse --help themselves, so the caller +// should fall through to normal dispatch when this returns false. +func printCommandHelp(command string) bool { switch command { case "init": _, _ = fmt.Fprintln(os.Stdout, "Create a server.json file template") @@ -165,7 +170,8 @@ func printCommandHelp(command string) { _, _ = fmt.Fprintln(os.Stdout, "You must be logged in before updating status. Run 'mcp-publisher login' first.") default: - fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) - printUsage() + return false } + + return true } diff --git a/docs/modelcontextprotocol-io/authentication.mdx b/docs/modelcontextprotocol-io/authentication.mdx index 9812fcc13..59d7218d5 100644 --- a/docs/modelcontextprotocol-io/authentication.mdx +++ b/docs/modelcontextprotocol-io/authentication.mdx @@ -174,7 +174,7 @@ mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" -mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" +mcp-publisher login dns --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash Google KMS @@ -298,7 +298,7 @@ mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ```bash ECDSA P-384 MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" -mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" +mcp-publisher login http --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash Google KMS diff --git a/docs/modelcontextprotocol-io/quickstart.mdx b/docs/modelcontextprotocol-io/quickstart.mdx index 41bc8fc3f..69886898e 100644 --- a/docs/modelcontextprotocol-io/quickstart.mdx +++ b/docs/modelcontextprotocol-io/quickstart.mdx @@ -135,6 +135,10 @@ Commands: 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 --help' for more information about a command. ``` ## Step 4: Create `server.json` diff --git a/docs/reference/cli/commands.md b/docs/reference/cli/commands.md index e077019b8..c8d761579 100644 --- a/docs/reference/cli/commands.md +++ b/docs/reference/cli/commands.md @@ -16,7 +16,10 @@ $ brew install mcp-publisher All commands support: - `--help`, `-h` - Show command help -- `--registry` - Registry URL (default: `https://registry.modelcontextprotocol.io`) + +`--registry` is a flag on `login` only (default: `https://registry.modelcontextprotocol.io`). The +other commands read the registry URL from the stored login token, so passing `--registry` to +`publish` would be interpreted as the `server.json` path. ## Commands @@ -26,14 +29,14 @@ Generate a `server.json` template with automatic detection. **Usage:** ```bash -mcp-publisher init [options] +mcp-publisher init ``` **Behavior:** - Creates `server.json` in current directory - Auto-detects package managers (`package.json`, `setup.py`, etc.) - Pre-fills fields where possible -- Prompts for missing required fields +- Writes `TODO:` placeholders for fields it cannot detect — it is non-interactive and takes no flags **Example output:** ```json @@ -59,10 +62,13 @@ Authenticate with the registry. #### GitHub Interactive ```bash -mcp-publisher login github [--registry=URL] +mcp-publisher login github [--token=PAT] [--registry=URL] ``` - Opens browser for GitHub OAuth flow - Grants access to `io.github.{username}/*` and `io.github.{org}/*` namespaces +- `--token` supplies a GitHub Personal Access Token instead of the interactive flow, which is how + [publishing from GitHub Actions](../../modelcontextprotocol-io/github-actions.mdx) authenticates + without a browser. This flag is accepted by `login github` only. #### GitHub OIDC (CI/CD) ```bash @@ -83,12 +89,13 @@ Also see [the guide to publishing from GitHub Actions](../../modelcontextprotoco #### DNS Verification ```bash -mcp-publisher login dns --domain=example.com --private-key=HEX_KEY [--registry=URL] +mcp-publisher login dns --domain=example.com --private-key=HEX_KEY [--algorithm=ed25519|ecdsap384] [--registry=URL] ``` - Verifies domain ownership via DNS TXT record - Grants access to `com.example.*` namespaces - Requires Ed25519 private key (64-character hex) or ECDSA P-384 private key (96-character hex) - - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. + - `--algorithm` defaults to `ed25519`. **For an ECDSA P-384 key you must pass `--algorithm ecdsap384`**, otherwise the key is rejected with `invalid seed length: expected 32 bytes, got 48`. + - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. Cloud providers derive the algorithm from the key itself, so `--algorithm` does not apply to them. **Setup:** (for Ed25519, recommended) ```bash @@ -118,6 +125,9 @@ openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | ta # Extract private key for login openssl ec -in -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n' + +# Log in, selecting the ECDSA P-384 algorithm explicitly +mcp-publisher login dns --algorithm ecdsap384 --domain=example.com --private-key=HEX_KEY ``` **Setup:** (for Google KMS signing) @@ -178,12 +188,13 @@ mcp-publisher login dns azure-key-vault --domain=example.com --vault MyKeyVault #### HTTP Verification ```bash -mcp-publisher login http --domain=example.com --private-key=HEX_KEY [--registry=URL] +mcp-publisher login http --domain=example.com --private-key=HEX_KEY [--algorithm=ed25519|ecdsap384] [--registry=URL] ``` - Verifies domain ownership via HTTPS endpoint - Grants access to `com.example.*` namespaces - Requires Ed25519 private key (64-character hex) or ECDSA P-384 private key (96-character hex) - - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. + - `--algorithm` defaults to `ed25519`. **For an ECDSA P-384 key you must pass `--algorithm ecdsap384`**. + - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. Cloud providers derive the algorithm from the key itself, so `--algorithm` does not apply to them. **Setup:** (for Ed25519, recommended) ```bash @@ -203,6 +214,9 @@ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem # Host public key at: # https://example.com/.well-known/mcp-registry-auth # Content: v=MCPv1; k=ecdsap384; p=PUBLIC_KEY + +# Log in, selecting the ECDSA P-384 algorithm explicitly +mcp-publisher login http --algorithm ecdsap384 --domain=example.com --private-key=HEX_KEY ``` Cloud signing is also supported for HTTP authentication, similar to the DNS examples above. Just swap out the `dns` positional argument for `http`.