ci(mcp): list e2e mcp in the MCP Registry

Merged
OK
9d ago
+232-5

Lists e2e mcp in the official MCP Registry as io.github.tester-army/e2e, published from CI after each release. Nothing is published by hand: registry versions are immutable.

  • packages/e2e/package.json: mcpName, which the registry checks on npm to prove ownership.
  • packages/e2e/server.json: npm package entry, npx e2e@<version> mcp over stdio.
  • scripts/sync-mcp-server.ts: version-packages keeps server.json on the runner's version, so the release commit carries the entry. spec.yml and pnpm check run mcp-server:check.
  • .github/workflows/mcp-registry.yml: checks out the e2e@<version> tag, skips a version already listed, waits for npm to serve that version with mcpName, then publishes with a pinned, sha256-checked mcp-publisher v1.8.1 over GitHub OIDC. Dispatch it by hand with a version to retry a failed publish.
  • release.yml calls it only when changesets published e2e. Nightlies are never listed.
  • docs/reference/mcp.mdx: a short note on the registry listing.
flowchart LR
  A[release job: changeset publish] -->|e2e in publishedPackages| B[mcp-registry.yml]
  D[workflow_dispatch version] --> B
  B --> C{already listed?}
  C -- yes --> E[skip]
  C -- no --> F[wait for npm mcpName] --> G[mcp-publisher login github-oidc + publish]

First listing is the next e2e release (0.21.1 on npm has no mcpName). Older versions can't be backfilled.

Verified

Ran it locally: yes

Version skew, since a registry client runs npx e2e@<listed version> mcp while the project has its own e2e installed. Scratch project with a web target, a local app, and a project tool built with the project's defineTool. I drove it with an MCP stdio client: open_session, call observe, call ping (project tool), call locate, close_session.

server (npx)project installresult
0.20.0 (own)0.20.0all ok (baseline)
0.21.10.20.0all ok
0.19.00.21.1all ok
this branch, packed tgz0.20.0all ok, locate returns screen.getByRole("button", "Go")

Shared state already goes through Symbol.for slots (packages/e2e/src/internal/realm-slot.ts), so two copies work. That's why this is an npm package listing, not discovery only.

e2e mcp: [info] e2e mcp 0.21.1 serving /private/tmp/mcp-skew
open_session ok  Session ... open on target "web" (platform web, engine web 0.15.1)
call observe ok  #n2 heading "Hello skew" / #n3 button "Go"
call ping ok     pong
call locate ok   1 node matches getByRole("button", name: "Go").

Release plumbing, all without publishing:

  • The packed tarball's package.json has mcpName: io.github.tester-army/e2e. server.json is not in the tarball.
  • pnpm run version-packages in a throwaway worktree bumped server.json to 0.21.2 along with the package. mcp-publisher validate passes on that output and on the committed file.
  • The registry API returns 404 for this server today and 200 for a listed one, so the "already listed" check works.
  • The workflow's jq and bash snippets run locally against sample publishedPackages JSON. actionlint is clean.

Not verified: the real OIDC login and publish. Those only run from Actions after the next release.