Skip to content

Commit 0dbb79a

Browse files
peopleworksclaude
andcommitted
Publish to the MCP registry automatically on release
Ownership is proven two ways and neither needs a secret: GitHub OIDC establishes the io.github.peopleworks/* namespace, and the registry reads the mcp-name line out of the published NuGet package to confirm the same person owns that. The job waits for the package to be downloadable rather than trusting that the sibling NuGet job succeeded. Both start from the same release event, so they run in parallel -- and today's release showed that a push returning Created is not immediately fetchable: NuGet's indexes lag its API by minutes. The registry would reject a server whose package it cannot read. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 0c9da42 commit 0dbb79a

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

.github/workflows/mcp-registry.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Publish the MCP server to the official Model Context Protocol registry.
2+
#
3+
# No secret and no browser: `login github-oidc` proves ownership of the io.github.peopleworks/*
4+
# namespace from this workflow's OIDC token, which is why `id-token: write` below is required
5+
# rather than tidy.
6+
#
7+
# The registry additionally verifies that whoever publishes owns the NuGet package, by reading
8+
# `mcp-name: io.github.peopleworks/xaf-logic-explainer` out of the README inside the published
9+
# package. That line lives in src/XafLogicExplainer.Mcp/README.md and must not be reworded.
10+
name: Publish MCP registry
11+
12+
on:
13+
release:
14+
types: [published]
15+
workflow_dispatch:
16+
17+
permissions:
18+
contents: read
19+
id-token: write # required for github-oidc login
20+
21+
jobs:
22+
publish:
23+
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref_name, 'v')
24+
runs-on: ubuntu-latest
25+
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
# This job and the NuGet one both start from `release: published`, so they run in parallel —
30+
# and the registry rejects a server whose package it cannot download yet. NuGet's indexes
31+
# also lag its API by minutes: a push that returns "Created" is not immediately fetchable.
32+
# So wait for the artifact itself rather than assuming the sibling job's success is enough.
33+
- name: Wait for the package to be downloadable from NuGet
34+
run: |
35+
version=$(jq -r '.packages[0].version' src/XafLogicExplainer.Mcp/.mcp/server.json)
36+
url="https://api.nuget.org/v3-flatcontainer/xaflogicexplainer.mcp/$version/xaflogicexplainer.mcp.$version.nupkg"
37+
echo "Waiting for $url"
38+
39+
for attempt in $(seq 1 40); do
40+
code=$(curl -s -o /dev/null -w "%{http_code}" "$url" || true)
41+
if [ "$code" = "200" ]; then
42+
echo "Available after $attempt attempt(s)."
43+
exit 0
44+
fi
45+
echo " attempt $attempt: HTTP $code"
46+
sleep 30
47+
done
48+
49+
echo "::error::XafLogicExplainer.Mcp $version never became downloadable from NuGet."
50+
echo "The registry validates ownership by reading the package, so publishing would fail."
51+
exit 1
52+
53+
- name: Install mcp-publisher
54+
run: |
55+
tag=$(curl -s https://api.github.com/repos/modelcontextprotocol/registry/releases/latest | jq -r .tag_name)
56+
echo "mcp-publisher $tag"
57+
curl -sL "https://github.com/modelcontextprotocol/registry/releases/download/$tag/mcp-publisher_linux_amd64.tar.gz" \
58+
| tar xz mcp-publisher
59+
./mcp-publisher --version || true
60+
61+
- name: Log in with GitHub OIDC
62+
run: ./mcp-publisher login github-oidc
63+
64+
# `publish` reads server.json from the working directory.
65+
- name: Publish
66+
working-directory: src/XafLogicExplainer.Mcp/.mcp
67+
run: $GITHUB_WORKSPACE/mcp-publisher publish
68+
69+
- name: Confirm the listing
70+
run: |
71+
name="io.github.peopleworks/xaf-logic-explainer"
72+
curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=$name" \
73+
| jq -r '.servers[]? | "\(.name) \(.version)"' || true

0 commit comments

Comments
 (0)