Skip to content

Commit 10aa52c

Browse files
committed
feat(mcp): add MCP registry publishing for yap-mcp + skill (#195)
- server.json manifest (io.github.m0rf30/yap, OCI package) - build/mcp/Dockerfile: dedicated distroless yap-mcp image - .github/workflows/mcp-publish.yml: build OCI image + publish to MCP registry via GitHub OIDC on tags - README: OCI/registry install option + PUBLISHING reference - PUBLISHING.md: channels (registry, aggregators, skill) + release checklist
1 parent 090e78a commit 10aa52c

6 files changed

Lines changed: 292 additions & 3 deletions

File tree

.github/workflows/mcp-publish.yml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
name: 📡 Publish MCP Server
2+
3+
# Builds the dedicated yap-mcp OCI image and publishes the server to the
4+
# official MCP registry (registry.modelcontextprotocol.io).
5+
#
6+
# Runs on version tags after release artifacts exist. The OCI image is the
7+
# package referenced by server.json (registryType: oci).
8+
9+
on:
10+
push:
11+
tags: ["v*.*.*"]
12+
workflow_dispatch:
13+
inputs:
14+
version:
15+
description: "Version to publish (without v prefix, e.g. 2.2.1)"
16+
required: true
17+
type: string
18+
19+
permissions:
20+
contents: read
21+
packages: write
22+
id-token: write # required for MCP registry GitHub OIDC auth
23+
24+
env:
25+
REGISTRY: ghcr.io
26+
IMAGE: ghcr.io/m0rf30/yap-mcp
27+
28+
jobs:
29+
image:
30+
name: 🐳 Build & push yap-mcp image
31+
runs-on: ubuntu-latest
32+
timeout-minutes: 20
33+
steps:
34+
- name: 📂 Checkout
35+
uses: actions/checkout@v6
36+
37+
- name: 🏷️ Resolve version
38+
id: ver
39+
run: |
40+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
41+
V="${{ github.event.inputs.version }}"
42+
else
43+
V="${GITHUB_REF#refs/tags/v}"
44+
fi
45+
echo "version=${V}" >> "$GITHUB_OUTPUT"
46+
47+
- name: 🔧 Set up Buildx
48+
uses: docker/setup-buildx-action@v3
49+
50+
- name: 🔑 Log in to GHCR
51+
uses: docker/login-action@v4
52+
with:
53+
registry: ${{ env.REGISTRY }}
54+
username: ${{ github.actor }}
55+
password: ${{ secrets.GITHUB_TOKEN }}
56+
57+
- name: 🐳 Build & push
58+
uses: docker/build-push-action@v6
59+
with:
60+
context: .
61+
file: build/mcp/Dockerfile
62+
platforms: linux/amd64,linux/arm64
63+
push: true
64+
build-args: |
65+
VERSION=v${{ steps.ver.outputs.version }}
66+
COMMIT=${{ github.sha }}
67+
tags: |
68+
${{ env.IMAGE }}:${{ steps.ver.outputs.version }}
69+
${{ env.IMAGE }}:latest
70+
71+
publish:
72+
name: 📡 Publish to MCP registry
73+
runs-on: ubuntu-latest
74+
timeout-minutes: 10
75+
needs: image
76+
steps:
77+
- name: 📂 Checkout
78+
uses: actions/checkout@v6
79+
80+
- name: 🏷️ Resolve version
81+
id: ver
82+
run: |
83+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
84+
V="${{ github.event.inputs.version }}"
85+
else
86+
V="${GITHUB_REF#refs/tags/v}"
87+
fi
88+
echo "version=${V}" >> "$GITHUB_OUTPUT"
89+
90+
- name: 🔄 Inject tag version into server.json
91+
run: |
92+
V="${{ steps.ver.outputs.version }}"
93+
# server.json ships with a 0.0.0 placeholder; the git tag is the
94+
# single source of truth. Update server version + OCI package version.
95+
jq --arg v "$V" \
96+
'.version = $v | .packages[].version = $v' \
97+
server.json > server.json.tmp
98+
mv server.json.tmp server.json
99+
cat server.json
100+
101+
- name: 📥 Install mcp-publisher
102+
run: |
103+
curl -fsSL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" \
104+
| tar -xz mcp-publisher
105+
sudo mv mcp-publisher /usr/local/bin/
106+
107+
- name: 🔐 Authenticate (GitHub OIDC)
108+
run: mcp-publisher login github-oidc
109+
110+
- name: 📡 Publish
111+
run: mcp-publisher publish

PUBLISHING.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Publishing YAP's MCP server & skill
2+
3+
This document covers how `yap-mcp` and the YAP agent skill are distributed to
4+
the popular discovery channels.
5+
6+
## Artifacts
7+
8+
| Artifact | Source | Channel |
9+
| --- | --- | --- |
10+
| `yap-mcp` binary | goreleaser (`.goreleaser.yml`, id `yap-mcp`) | GitHub Releases |
11+
| `ghcr.io/m0rf30/yap-mcp` OCI image | `build/mcp/Dockerfile` + `.github/workflows/mcp-publish.yml` | GHCR + MCP registry |
12+
| `server.json` | repo root | MCP registry manifest |
13+
| `skills/yap/SKILL.md` | repo | Anthropic Agent Skills layout |
14+
15+
## 1. MCP Registry (registry.modelcontextprotocol.io)
16+
17+
The official registry is the highest-signal channel. It cannot ingest a raw
18+
`.tar.gz` release asset — `registryType` only accepts `npm | pypi | oci |
19+
nuget | mcpb`. We publish the **OCI image** and reference it from
20+
`server.json`.
21+
22+
Automated on every `v*.*.*` tag by `.github/workflows/mcp-publish.yml`:
23+
24+
1. Builds + pushes `ghcr.io/m0rf30/yap-mcp:{version,latest}` (amd64 + arm64).
25+
2. Syncs the version into `server.json`.
26+
3. Authenticates via **GitHub OIDC** (`mcp-publisher login github-oidc`).
27+
4. `mcp-publisher publish`.
28+
29+
Manual publish:
30+
31+
```sh
32+
# from repo root, server.json present
33+
mcp-publisher login github-oidc
34+
mcp-publisher publish
35+
```
36+
37+
Namespace `io.github.M0Rf30/*` is owned automatically via the GitHub OIDC
38+
identity (the repo is under the `M0Rf30` GitHub account).
39+
40+
### Naming
41+
42+
- Server name: `io.github.M0Rf30/yap` (reverse-DNS, exactly one `/`). The
43+
namespace is **case-sensitive** and must match the GitHub username casing
44+
(`M0Rf30`). The GHCR image path stays lowercase (OCI requirement).
45+
- OCI image carries the `io.modelcontextprotocol.server.name` label so the
46+
registry can verify ownership of the image ↔ server mapping.
47+
48+
## 2. GitHub Releases (binaries)
49+
50+
Already handled by `.goreleaser.yml` (`yap-mcp` build + per-binary archive).
51+
Users install via:
52+
53+
```sh
54+
curl -fsSL https://raw.githubusercontent.com/M0Rf30/yap/main/scripts/install.sh | sh
55+
```
56+
57+
This is the path for manual / direct client config (no container).
58+
59+
## 3. Aggregators & awesome-lists (PR-based discovery)
60+
61+
Submit once; they index from GitHub thereafter. Each wants the repo URL +
62+
`server.json` (or their own manifest):
63+
64+
| Channel | How |
65+
| --- | --- |
66+
| `modelcontextprotocol/servers` (Community Servers) | PR adding a list entry |
67+
| `punkpeye/awesome-mcp-servers` | PR adding a list entry |
68+
| Glama (`glama.ai/mcp`) | auto-indexes public GitHub MCP repos |
69+
| PulseMCP (`pulsemcp.com`) | submission form |
70+
| mcp.so | submission form |
71+
| Smithery (`smithery.ai`) | add `smithery.yaml` (optional, for hosted deploy) |
72+
73+
## 4. Agent Skill channels
74+
75+
`skills/yap/SKILL.md` already follows the Anthropic Agent Skills layout.
76+
77+
| Channel | How |
78+
| --- | --- |
79+
| This repo | canonical source (`skills/yap/`) |
80+
| `anthropics/skills` | PR to contribute as a community skill |
81+
| Claude Code plugin marketplaces | bundle skill in a plugin repo with `plugin.json` |
82+
| awesome-claude-skills lists | PR adding an entry |
83+
84+
## Release checklist
85+
86+
1. Tag `vX.Y.Z``release.yml` builds binaries, `mcp-publish.yml` builds the
87+
OCI image and publishes to the MCP registry.
88+
2. Verify the image: `docker run -i --rm ghcr.io/m0rf30/yap-mcp:latest` (should
89+
block on stdio).
90+
3. Verify the registry entry at
91+
`https://registry.modelcontextprotocol.io/v0/servers?search=yap`.
92+
4. (First release only) open the aggregator + skill PRs above.

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,17 +283,28 @@ over the [Model Context Protocol](https://modelcontextprotocol.io), so any
283283
MCP-compatible LLM client (Claude Desktop, opencode, Cursor, Zed, Continue,
284284
Goose, …) can drive yap directly.
285285

286-
Quickstart:
286+
Quickstart (release binary):
287287

288288
```sh
289289
curl -fsSL https://raw.githubusercontent.com/M0Rf30/yap/main/scripts/install.sh | sh
290290
# then add { "mcpServers": { "yap": { "command": "yap-mcp" } } } to your client config
291291
```
292292

293+
Or pull the standalone OCI image (also listed on the
294+
[MCP registry](https://registry.modelcontextprotocol.io) as `io.github.M0Rf30/yap`):
295+
296+
```sh
297+
docker pull ghcr.io/m0rf30/yap-mcp:latest
298+
# client config:
299+
# { "mcpServers": { "yap": { "command": "docker",
300+
# "args": ["run","-i","--rm","ghcr.io/m0rf30/yap-mcp:latest"] } } }
301+
```
302+
293303
The full tool surface, per-client config snippets, security model, and
294304
troubleshooting are in **[`MCP.md`](MCP.md)**. The shipped skill card at
295305
[`skills/yap/SKILL.md`](skills/yap/SKILL.md) follows the Anthropic Agent
296-
Skills layout and gives agents a workflow cheatsheet for free.
306+
Skills layout and gives agents a workflow cheatsheet for free. Distribution
307+
channels and release steps are documented in **[`PUBLISHING.md`](PUBLISHING.md)**.
297308

298309
## CLI reference
299310

build/mcp/Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# syntax=docker/dockerfile:1
2+
# Minimal OCI image shipping only the yap-mcp stdio server.
3+
# Published as ghcr.io/m0rf30/yap-mcp and referenced by server.json for the
4+
# Model Context Protocol registry (registryType: oci).
5+
6+
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS build
7+
WORKDIR /src
8+
RUN apk add --no-cache git
9+
COPY go.mod go.sum ./
10+
RUN go mod download
11+
COPY . .
12+
# Cross-compile natively on the build platform (no QEMU). CGO is disabled, so
13+
# GOARCH switching is clean and needs no C toolchain.
14+
ARG TARGETOS
15+
ARG TARGETARCH
16+
ARG VERSION=dev
17+
ARG COMMIT=none
18+
ARG BUILD_TIME=unknown
19+
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \
20+
-ldflags "-s -w \
21+
-X github.com/M0Rf30/yap/v2/pkg/buildinfo.Version=${VERSION} \
22+
-X github.com/M0Rf30/yap/v2/pkg/buildinfo.Commit=${COMMIT} \
23+
-X github.com/M0Rf30/yap/v2/pkg/buildinfo.BuildTime=${BUILD_TIME}" \
24+
-o /out/yap-mcp ./cmd/yap-mcp
25+
26+
FROM gcr.io/distroless/static-debian12:nonroot
27+
LABEL org.opencontainers.image.title="yap-mcp" \
28+
org.opencontainers.image.description="MCP server exposing YAP package-build capabilities" \
29+
org.opencontainers.image.source="https://github.com/M0Rf30/yap" \
30+
org.opencontainers.image.vendor="M0Rf30" \
31+
io.modelcontextprotocol.server.name="io.github.M0Rf30/yap"
32+
COPY --from=build /out/yap-mcp /usr/local/bin/yap-mcp
33+
ENTRYPOINT ["/usr/local/bin/yap-mcp"]

examples/yap/PKGBUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pkgname=yap
2-
pkgver=2.0.0
2+
pkgver=2.2.2
33
pkgrel=1
44
pkgdesc="Package software with ease"
55
pkgdesc__alpine="Package software with ease for Alpine"

server.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3+
"name": "io.github.M0Rf30/yap",
4+
"title": "YAP — Yet Another Packager",
5+
"description": "Build native Linux packages (.deb/.rpm/.apk/.pkg.tar.zst) from a single PKGBUILD via MCP.",
6+
"version": "0.0.0",
7+
"websiteUrl": "https://github.com/M0Rf30/yap",
8+
"repository": {
9+
"url": "https://github.com/M0Rf30/yap",
10+
"source": "github"
11+
},
12+
"packages": [
13+
{
14+
"registryType": "oci",
15+
"identifier": "ghcr.io/m0rf30/yap-mcp:0.0.0",
16+
"transport": {
17+
"type": "stdio"
18+
},
19+
"environmentVariables": [
20+
{
21+
"name": "YAP_VERBOSE",
22+
"description": "Set to 1 for debug-level JSON-RPC frame logging on stderr.",
23+
"isRequired": false,
24+
"isSecret": false
25+
}
26+
]
27+
}
28+
],
29+
"_meta": {
30+
"io.modelcontextprotocol.registry/publisher-provided": {
31+
"tool": "yap-mcp",
32+
"tools": [
33+
"validate", "parse_pkgbuild", "graph", "build", "build_status",
34+
"build_wait", "build_logs", "build_cancel", "list_artifacts",
35+
"inspect", "install", "prepare", "pull", "zap", "list_distros",
36+
"list_images", "resolve_distro", "status"
37+
],
38+
"prompts": ["build_single_pkg", "cross_compile", "sign_and_release"],
39+
"resources": ["yap://distros", "yap://pkgbuild/{path}"]
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)