diff --git a/CHANGELOG.md b/CHANGELOG.md index 9714878..b10c3dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes are listed here. The format follows [Keep a Changelog](https - `references/mcp-install-and-metadata.md`: package types and ownership checks, `server.json` fields, README install routes per client, listing files, org visibility. - Audit checks for `server.json` (description length, `websiteUrl`, namespace) and the OCI label. - Docker/OCI registry quality eval. +- MCP publishing learnings from a real run (2026-11-02): org namespace auth, OCI build and verify flow, no `validate` command, listing check, Docker MCP Registry route, leaked-history scrub, README install block generation, closing "who built this" section. ## [0.1.0] - 2026-10-29 diff --git a/skills/github-seo/SKILL.md b/skills/github-seo/SKILL.md index a969210..e9802e2 100644 --- a/skills/github-seo/SKILL.md +++ b/skills/github-seo/SKILL.md @@ -86,7 +86,8 @@ others each have their own ownership check), complete `server.json` including `w lay out the README install routes per client. Then use `references/mcp-registry-checklist.md`. It covers the Official MCP Registry (`server.json` plus the `mcp-publisher` CLI), Smithery, Glama, mcp.so, mcpservers.org, Cursor Directory, which registries auto-ingest from the Official one, and common gotchas (stale listings under a personal -account, publish order, domain presence). +account, publish order, domain presence). Pitfalls from real runs (org namespace 403, OCI +build and label, leaked-history scrub) are in the install reference. ## What not to do diff --git a/skills/github-seo/references/mcp-install-and-metadata.md b/skills/github-seo/references/mcp-install-and-metadata.md index 447b386..7f97761 100644 --- a/skills/github-seo/references/mcp-install-and-metadata.md +++ b/skills/github-seo/references/mcp-install-and-metadata.md @@ -25,13 +25,36 @@ OCI details [verified]: supported hosts are Docker Hub, `ghcr.io`, Google Artifa (a digest also works). Pin a version tag, not `latest`. Put the `LABEL` in the Dockerfile and rebuild before the final push, because the label has to be in the pushed image. +OCI route end to end [verified, 2026-11-02, observed from real runs]: + +1. Add `LABEL io.modelcontextprotocol.server.name=""` to the Dockerfile. +2. Build multi-arch and push, using a `docker-container` builder: + `docker buildx build --builder --push --platform linux/amd64,linux/arm64 -t /: -t /:latest .` +3. Verify with `docker buildx imagetools inspect /:`. Extra `unknown/unknown` + entries are buildx provenance and SBOM attestations, not platforms. Check the label on each + platform with `--format '{{json .Image}}'`. +4. Smoke test: pull the image and import the server module. +5. Only then run `mcp-publisher publish`. + +Order: make the repo public first, push the image second, publish third. The `server.json` +`identifier` still pins the version tag even though `latest` is also pushed. + ## 2. Namespace and auth [verified] -- GitHub login gives `io.github./`. A live org-owned entry exists - (`io.github.devkindhq/swell`) [observed], so org namespaces work in practice. +- GitHub login gives `io.github./`. Org-owned entries are live in + the registry [observed], so org namespaces work in practice. - Domain auth (DNS TXT record or an HTTP file at `/.well-known/mcp-registry-auth`) gives a reverse-DNS name such as `com.example.*/`. - `mcp-publisher login github` is an interactive device-code flow. The repo owner runs it. +- Org namespace gotcha [verified, 2026-11-02, observed from a real run]: the device flow grants + only the user's personal namespace. Publishing `io.github./` returned 403 with + "You have permission to publish: io.github./*". The fix that worked: + `mcp-publisher login github --token "$(gh auth token)"`, where the `gh` token has the + `read:org` scope and the user is an org Owner or admin. The token is never printed or pasted. + Alternative: the GitHub Actions OIDC route (section 7). Never paste a PAT into a chat. +- `mcp-publisher` has no `validate` command [verified, 2026-11-02]. Its commands are `init`, + `login`, `logout`, `publish` and `status`. The registry validates on publish, and the 100 char + description limit is enforced by the schema. ## 3. `server.json` fields that matter for discovery @@ -58,9 +81,12 @@ Environment variables go in `packages[].environmentVariables` with `name`, `desc Verify after publishing: ```bash -curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=" +curl -s "https://registry.modelcontextprotocol.io/v0.1/servers?search=" ``` +A good result shows status `active`, `isLatest` true and `websiteUrl` populated [verified, +2026-11-02, observed from a real run]. + The registry is in preview, so breaking changes or data resets can happen [verified]. ## 4. README install routes @@ -86,6 +112,9 @@ readers, and the collapsed blocks keep the README short [general]. Keep to the 4 target by linking out to the write-up for anything long, and cap the client list at the clients you have actually tested. +Generate every README install block (standard config, Cursor deeplink, per-client blocks) from +one script so they stay consistent [general]. Hand-edited copies drift. + Docker-only servers: the standard config is `{"command":"docker","args":["run","--rm","-i",":"]}`. Add resource limits only if the server needs them. @@ -99,6 +128,9 @@ server needs them. | `manifest.json` | MCPB bundle, desktop installs | Carries `author` with name and URL, `repository`, and `user_config` prompts for credentials [observed]. | | README badges | Humans | "Built by " badge linking to the homepage. | +`smithery.yaml` with a docker `commandFunction` was copied from another published server README and is +[unverified] against Smithery's current docs. Check them before reusing it. + Keep one source of truth for the description, tool list and homepage, and copy it into each file. A version drift between `package.json`, `server.json` and `manifest.json` is easy to introduce [observed: one repo had `1.1.0` in two files and `1.0.9` in the third]. @@ -117,3 +149,44 @@ A version drift between `package.json`, `server.json` and `manifest.json` is eas A manual publish drifts out of date. The registry documents a GitHub Actions route using OIDC [verified: documented, not tested here]. Wire `mcp-publisher publish` into the release job so the registry version tracks the package version. + +## 8. Docker MCP Registry, a second distribution route + +`docker/mcp-registry` lists containerised servers for Docker's MCP tooling. Facts below are from +its `CONTRIBUTING.md` [verified, 2026-11-02]: https://github.com/docker/mcp-registry/blob/main/CONTRIBUTING.md + +- Open a PR that adds `servers//server.yaml`. `task wizard` or `task create` generate it. +- `server.yaml` pins a source commit SHA (`source.project` and `source.commit`). +- Every PR needs a review from the Docker team, and CI must pass. +- Without `--image`, Docker builds and hosts the image under its `mcp/` namespace, with + signatures, provenance and SBOMs. With `--image /`, your own image is used and + those extras do not apply. +- The contributing guide asks that the server license allow consumption (MIT or Apache 2 named as + good, GPL as not). +- Review turnaround and listing criteria beyond the above are [unverified]. + +## 9. Scrubbing a leaked repo history + +If the secret scan finds leaks in history and the token lacks `delete_repo` +[verified, 2026-11-02, observed from a real run]: + +1. Rename the old repo. +2. Create a new repo under the original name. +3. Push only `main`. Do not mirror refs, because leaked content can sit in `refs/pull/*`. +4. Re-scan the new repo with `gitleaks`. +5. Have the owner delete the old repo in the GitHub UI. + +Rotate any leaked credential regardless. History scrubbing does not un-leak it. + +## 10. Earn mentions with a closing "who built this" section [general] + +End the README with a short, related and non-pushy section that tells readers who maintains the +server and where to get help or related work. It earns a branded mention and a link back from every +directory that renders the README. Keep it specific to the server's audience, not generic. + +- Heading that names the reader's need, for example "Need this wired into your stack?". +- Two or three sentences on what you or your organisation offers that relates to the server. +- One link to a specific page on a domain you own (a services, consulting or product page, not + only the home page) plus a contact address. +- Request each linked URL first and confirm it returns 200. +- Use the same organisation name and homepage as in `server.json`, `smithery.yaml` and the About box. diff --git a/skills/github-seo/references/mcp-registry-checklist.md b/skills/github-seo/references/mcp-registry-checklist.md index 76a3407..2125fee 100644 --- a/skills/github-seo/references/mcp-registry-checklist.md +++ b/skills/github-seo/references/mcp-registry-checklist.md @@ -30,7 +30,8 @@ Write the fields every listing asks for, then reuse them verbatim so listings st | Registry | How it works | Action | |---|---|---| -| **Official MCP Registry** | A `server.json` in your project, published with the `mcp-publisher` CLI. Names take the form `io.github./` and you must authenticate as that GitHub account [verified, 2026-10-29, registry README]. A pull request to the registry repo does not publish a server [verified, 2026-10-29]. | Direct. Do this first, because other registries ingest from it. | +| **Official MCP Registry** | A `server.json` in your project, published with the `mcp-publisher` CLI. Names take the form `io.github./` and you must authenticate with access to that namespace [verified, 2026-10-29, registry README]. For an org namespace the device flow is not enough, see `mcp-install-and-metadata.md` section 2 [verified, 2026-11-02]. A pull request to the registry repo does not publish a server [verified, 2026-10-29]. | Direct. Do this first, because other registries ingest from it. | +| **Docker MCP Registry** | PR adding `servers//server.yaml`, Docker team review [verified, 2026-11-02]. | Optional second route, see `mcp-install-and-metadata.md` section 8. | | **Smithery** | Listed under an organisation namespace. Shows a quality score that depends on the live tool surface [observed]. | Direct. Check for stale listings under a personal account. | | **PulseMCP** | Ingests from the Official Registry [observed]. | None once the Official entry is live. | | **mcpbench.ai** | Indexes from the Official Registry, can lag [observed]. | Verify it appears. | @@ -46,7 +47,6 @@ Write the fields every listing asks for, then reuse them verbatim so listings st - **Empty `websiteUrl`.** Put the homepage in the official `websiteUrl` field, not only under `_meta`. See `mcp-install-and-metadata.md`. - **Version drift.** Keep `package.json`, `server.json` and `manifest.json` on the same version. - - **Stale listings under a personal account.** If the server was ever published from a personal GitHub account before the org account existed, that listing can persist on registries such as Smithery. Find it and have it removed, through the registry's support if there is no self-service delete.