@smbcloud / cli / commits / 3b48a84

Add MCP registry config

Seto Elkahfi committed Jul 22, 2026 at 16:36 UTC 3b48a8426b9d3dfb2ccda6bec126b9829387b165
9 files changed +248 -1
.agents/AGENTS.md
+2
@@ -101,6 +101,8 @@ make patch | make minor | make major | make custom VERSION=0.x.y
101
102 It commits `Release <version>` and tags `v<version>` locally; pushing is manual. Crate publishing is separate and manual (`cargo workspaces publish`, see `docs/development.md`). The per-target release workflows (`release-*.yml`) build the distributable artifacts.
103
104 +`server.json` (the MCP Registry listing) is version-synced by the same Makefile step. Run `release-mcp-registry.yml` last, after the npm and NuGet packages for that version are live — the registry validates its metadata against the real packages. See `docs/mcp-registry.md`.
105 +
106 ---
107
108 # Architecture
.github/workflows/release-mcp-registry.yml new
+87
@@ -0,0 +1,87 @@
1 +name: MCP Registry Release
2 +
3 +on:
4 + workflow_dispatch:
5 + inputs:
6 + tag:
7 + description: "Release tag (e.g. v0.4.12)"
8 + required: true
9 +
10 +# OIDC is how we authenticate to the MCP Registry: the registry trusts a GitHub
11 +# Actions token issued for this repository, so no long-lived secret is needed.
12 +permissions:
13 + id-token: write
14 + contents: read
15 +
16 +jobs:
17 + publish:
18 + name: Publish server.json to the MCP Registry
19 + runs-on: ubuntu-latest
20 + steps:
21 + - name: Checkout
22 + uses: actions/checkout@v6
23 + with:
24 + ref: ${{ github.event.inputs.tag || github.ref }}
25 +
26 + - name: Set the release version
27 + shell: bash
28 + run: |
29 + if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
30 + release_version="${GITHUB_REF_NAME#v}"
31 + else
32 + release_version="${{ github.event.inputs.tag }}"
33 + release_version="${release_version#v}"
34 + fi
35 +
36 + echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
37 +
38 + - name: Check the server name is consistent across packages
39 + shell: bash
40 + run: |
41 + server_name="$(jq -r '.name' server.json)"
42 + echo "Server name: ${server_name}"
43 +
44 + # npm proves ownership through `mcpName` in the published package.json,
45 + # which is generated by this script at release time.
46 + grep -q "mcpName: \"${server_name}\"" npm/scripts/render-main-package.cjs
47 +
48 + # NuGet proves ownership through an `mcp-name:` marker in the package README.
49 + grep -q "mcp-name: ${server_name}" nuget/smbcloud-cli/README.md
50 +
51 + - name: Set the version in server.json
52 + shell: bash
53 + run: |
54 + jq --arg v "${RELEASE_VERSION}" \
55 + '.version = $v | .packages |= map(.version = $v)' \
56 + server.json > server.tmp
57 + mv server.tmp server.json
58 + cat server.json
59 +
60 + - name: Verify the packages are published
61 + shell: bash
62 + run: |
63 + # The registry only stores metadata and validates it against the real
64 + # packages, so both must already exist at this version.
65 + npm view "@smbcloud/cli@${RELEASE_VERSION}" version
66 +
67 + nuget_versions="$(curl -fsSL https://api.nuget.org/v3-flatcontainer/smbcloud.cli/index.json)"
68 + echo "${nuget_versions}" | jq -e --arg v "${RELEASE_VERSION}" '.versions | index($v)'
69 +
70 + - name: Install mcp-publisher
71 + shell: bash
72 + run: |
73 + curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
74 +
75 + - name: Authenticate to the MCP Registry
76 + shell: bash
77 + run: ./mcp-publisher login github-oidc
78 +
79 + - name: Publish
80 + shell: bash
81 + run: ./mcp-publisher publish
82 +
83 + - name: Verify the server is listed
84 + shell: bash
85 + run: |
86 + server_name="$(jq -r '.name' server.json)"
87 + curl -fsSL "https://registry.modelcontextprotocol.io/v0.1/servers?search=${server_name}" | jq .
docs/mcp-registry.md new
+84
@@ -0,0 +1,84 @@
1 +# MCP Registry
2 +
3 +The smbCloud CLI's MCP server is listed in the [official MCP Registry](https://registry.modelcontextprotocol.io)
4 +as **`io.github.smbcloudxyz/smbcloud-cli`**. The registry is a metadata index —
5 +it doesn't host binaries — so the listing points at packages we already publish
6 +to npm and NuGet, and clients that browse the registry can offer a one-click
7 +install of `smb --mcp`.
8 +
9 +For running and configuring the server itself, see [MCP Server](./mcp.md).
10 +
11 +## What makes up the listing
12 +
13 +| Piece | Where |
14 +| --- | --- |
15 +| Server metadata | [`server.json`](../server.json) at the repo root |
16 +| npm ownership proof | `mcpName` in the generated `@smbcloud/cli` `package.json` (`npm/scripts/render-main-package.cjs`) |
17 +| NuGet ownership proof | `<!-- mcp-name: ... -->` in `nuget/smbcloud-cli/README.md` |
18 +| Publishing | `.github/workflows/release-mcp-registry.yml` |
19 +
20 +The registry checks each listed package for a marker naming the server. If the
21 +marker is missing or doesn't match `name` in `server.json`, publishing fails
22 +with "Registry validation failed for package". The publish workflow greps for
23 +both markers before it does anything else, so a mismatch shows up as a failed
24 +job rather than a rejected upload.
25 +
26 +### Why the name is `io.github.smbcloudxyz/...`
27 +
28 +The namespace has to match the authentication method. We authenticate with
29 +GitHub OIDC from Actions, which grants the `io.github.smbcloudxyz/*` namespace
30 +(the reverse-DNS form of the `smbcloudXYZ` org). Publishing under
31 +`xyz.smbcloud/*` instead would mean domain-based auth: a `v=MCPv1` TXT record on
32 +`smbcloud.xyz` plus an Ed25519 private key stored as a repo secret. OIDC needs
33 +no secret at all, so that's what we use.
34 +
35 +### Which packages are listed
36 +
37 +- **npm** — `@smbcloud/cli`, launched as `npx @smbcloud/cli --mcp`. The package
38 + declares a single binary (`smb`), so `npx` resolves it despite the name
39 + difference.
40 +- **NuGet** — `SmbCloud.Cli`, launched as `dnx SmbCloud.Cli --mcp`.
41 +
42 +PyPI is deliberately not listed. The `smbcloud-cli` wheel installs its
43 +executable as `smb` (maturin `bindings = "bin"`), and `uvx smbcloud-cli` looks
44 +for an executable matching the distribution name, so the launch command the
45 +registry hands to clients wouldn't work. Adding PyPI would mean shipping an
46 +extra console script named `smbcloud-cli`. Homebrew and the direct GitHub
47 +release binaries have no registry package type at all; those install paths stay
48 +documented in [Install](./cli-install.md).
49 +
50 +## Releasing a new version
51 +
52 +`server.json` carries the version twice — once for the server, once per package
53 +— and both are updated by `make patch | minor | major` along with the rest of
54 +the release metadata (`scripts/sync-release-version.mjs`).
55 +
56 +After tagging and running the npm and NuGet release workflows, run the
57 +**MCP Registry Release** workflow with the same tag:
58 +
59 +```sh
60 +gh workflow run release-mcp-registry.yml -f tag=v0.4.12
61 +```
62 +
63 +Order matters. The registry fetches the package metadata during publish, so
64 +`@smbcloud/cli@<version>` and `SmbCloud.Cli <version>` must already be live —
65 +the workflow checks for both and stops early if they aren't.
66 +
67 +Confirm the listing afterwards:
68 +
69 +```sh
70 +curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.smbcloudxyz/smbcloud-cli"
71 +```
72 +
73 +## Publishing by hand
74 +
75 +Rarely needed, but if Actions is unavailable:
76 +
77 +```sh
78 +brew install mcp-publisher
79 +mcp-publisher login github # device flow, needs push access to smbcloudXYZ
80 +mcp-publisher publish # reads ./server.json
81 +```
82 +
83 +Versions are immutable — republishing the same version is rejected. Fixing a
84 +bad listing means shipping a new patch version.
docs/mcp.md
+3 -1
@@ -9,7 +9,9 @@ tools covering the account, project, tenant, Mail, and Auth surfaces.
9
10 This page is the setup guide and the full tool reference. For how `--mcp`
11 compares to the CLI's other two interfaces (headless and `--tui`), see
12 -[Interfaces](./interfaces.md).
12 +[Interfaces](./interfaces.md). The server is also listed in the official MCP
13 +Registry as `io.github.smbcloudxyz/smbcloud-cli`, so clients that browse the
14 +registry can install it for you — see [MCP Registry](./mcp-registry.md).
15
16 ## Prerequisites
17
npm/package-main.json.tmpl
+1
@@ -1,6 +1,7 @@
1 {
2 "name": "@smbcloud/cli",
3 "version": "${release_version}",
4 + "mcpName": "io.github.smbcloudxyz/smbcloud-cli",
5 "keywords": [
6 "smbcloud",
7 "cli",
npm/scripts/render-main-package.cjs
+2
@@ -12,6 +12,8 @@ if (!outputPath || !version) {
12 const packageJson = {
13 name: "@smbcloud/cli",
14 version,
15 + // Ownership proof for the MCP Registry: must match `name` in ../../server.json.
16 + mcpName: "io.github.smbcloudxyz/smbcloud-cli",
17 keywords: [
18 "smbcloud",
19 "cli",
nuget/smbcloud-cli/README.md
+2
@@ -1,5 +1,7 @@
1 # smbCloud CLI for .NET
2
3 +<!-- mcp-name: io.github.smbcloudxyz/smbcloud-cli -->
4 +
5 `smb` is the command-line interface for [smbCloud](https://smbcloud.xyz/).
6
7 ## Install
scripts/sync-release-version.mjs
+16
@@ -8,6 +8,7 @@ const authGemVersionPath = resolve(repoRoot, "sdk/gems/auth/lib/auth/version.rb"
8 const authGemCargoTomlPath = resolve(repoRoot, "sdk/gems/auth/ext/auth/Cargo.toml");
9 const modelGemVersionPath = resolve(repoRoot, "sdk/gems/model/lib/model/version.rb");
10 const modelGemCargoTomlPath = resolve(repoRoot, "sdk/gems/model/ext/model/Cargo.toml");
11 +const mcpServerJsonPath = resolve(repoRoot, "server.json");
12
13 function readText(path) {
14 return readFileSync(path, "utf8");
@@ -129,6 +130,21 @@ if (
130 updatedPaths.push(modelGemCargoTomlPath);
131 }
132
133 +if (
134 + updateFile(mcpServerJsonPath, (content) => {
135 + const parsed = JSON.parse(content);
136 + parsed.version = releaseVersion;
137 +
138 + for (const pkg of parsed.packages ?? []) {
139 + pkg.version = releaseVersion;
140 + }
141 +
142 + return `${JSON.stringify(parsed, null, 2)}\n`;
143 + })
144 +) {
145 + updatedPaths.push(mcpServerJsonPath);
146 +}
147 +
148 console.log(`Release version: ${releaseVersion}`);
149
150 if (updatedPaths.length === 0) {
server.json new
+51
@@ -0,0 +1,51 @@
1 +{
2 + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3 + "name": "io.github.smbcloudxyz/smbcloud-cli",
4 + "title": "smbCloud CLI",
5 + "description": "Manage smbCloud projects, deployments, tenants, Mail apps, and Auth apps.",
6 + "version": "0.4.12",
7 + "websiteUrl": "https://smbcloud.xyz/",
8 + "repository": {
9 + "url": "https://github.com/smbcloudXYZ/smbcloud-cli",
10 + "source": "github",
11 + "id": "597171611"
12 + },
13 + "packages": [
14 + {
15 + "registryType": "npm",
16 + "registryBaseUrl": "https://registry.npmjs.org",
17 + "identifier": "@smbcloud/cli",
18 + "version": "0.4.12",
19 + "runtimeHint": "npx",
20 + "transport": {
21 + "type": "stdio"
22 + },
23 + "packageArguments": [
24 + {
25 + "type": "named",
26 + "name": "--mcp",
27 + "description": "Run the CLI as an MCP server over stdio instead of executing a one-shot command.",
28 + "isRequired": true
29 + }
30 + ]
31 + },
32 + {
33 + "registryType": "nuget",
34 + "registryBaseUrl": "https://api.nuget.org/v3/index.json",
35 + "identifier": "SmbCloud.Cli",
36 + "version": "0.4.12",
37 + "runtimeHint": "dnx",
38 + "transport": {
39 + "type": "stdio"
40 + },
41 + "packageArguments": [
42 + {
43 + "type": "named",
44 + "name": "--mcp",
45 + "description": "Run the CLI as an MCP server over stdio instead of executing a one-shot command.",
46 + "isRequired": true
47 + }
48 + ]
49 + }
50 + ]
51 +}