From 44d7ac0d2040a8fcd7bebffbc124cd3a946246f6 Mon Sep 17 00:00:00 2001 From: Fieldproof <3labsio@gmail.com> Date: Sun, 16 Aug 2026 11:01:50 -0500 Subject: [PATCH] docs(api): document the raw publish flow for non-CLI publishers The auth endpoints are listed by name but their request and response shapes are not documented, and the CLI covers every worked example. Adds the signed-timestamp exchange (hex, not base64), the registry_token response field, and the flat publish body with $schema. Also records three error messages that point away from their causes: a wrapped body reporting a missing $schema that is present, Bearer undefined reported as a malformed token, and duplicate-version on republish. --- docs/reference/api/official-registry-api.md | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/docs/reference/api/official-registry-api.md b/docs/reference/api/official-registry-api.md index 47e461df..2e64be32 100644 --- a/docs/reference/api/official-registry-api.md +++ b/docs/reference/api/official-registry-api.md @@ -77,6 +77,64 @@ The `GET /v0.1/servers/{serverName}/versions` endpoint returns all versions of a - POST `/v0.1/auth/github-oidc` - Exchange GitHub OIDC token for auth token - POST `/v0.1/auth/oidc` - Exchange Google OIDC token for auth token (for admins) +##### Publishing without `mcp-publisher` + +[Publisher Commands](../cli/commands.md) covers the CLI, which is the recommended path. If you +publish from a script, a CI step, or a language without the Go binary, the request and response +shapes for the domain-proof flow are below. + +Exchange a signed timestamp for a token. The signature is over the RFC3339 timestamp string, +and is **hex-encoded, not base64**: + +```bash +curl -s -X POST https://registry.modelcontextprotocol.io/v0.1/auth/http \ + -H 'content-type: application/json' \ + -d '{ + "domain": "example.com", + "timestamp": "2026-08-16T15:53:00Z", + "signed_timestamp": "" + }' +``` + +The response field is **`registry_token`**: + +```json +{ "registry_token": "", "expires_at": 1786893600 } +``` + +The token is short-lived, so authenticate and publish in the same run. + +Then publish. The body is the [server.json](../server-json/generic-server-json.md) object +**flat at the top level** — not wrapped in `{"server": {...}}` — and `$schema` is required: + +```bash +curl -s -X POST https://registry.modelcontextprotocol.io/v0.1/publish \ + -H "authorization: Bearer ${REGISTRY_TOKEN}" \ + -H 'content-type: application/json' \ + -d '{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json", + "name": "com.example/my-server", + "description": "Does a useful thing.", + "version": "1.0.0", + "repository": { "url": "https://github.com/example/my-server", "source": "github" }, + "remotes": [{ "type": "streamable-http", "url": "https://example.com/mcp" }] + }' +``` + +Three responses worth recognising: + +- Wrapping the body in `{"server": {...}}` returns `422` with + `expected required property $schema to be present`. The validator is describing the outer + object, so the message names fields that are plainly present one level down. +- Reading the auth response's `token` instead of `registry_token` sends `Bearer undefined`, + which returns `401` with `token is malformed: token contains an invalid number of segments` + — an error that reads like a signing fault and is not one. +- Re-publishing an existing `version` returns `400` with + `invalid version: cannot publish duplicate version`. There is no upsert; bump the version. + +If you are unsure whether a server was already published, query the full name rather than +searching for part of it — `?search=` matches loosely and can rank other servers above yours. + #### Status endpoints ##### Update Single Version Status