@smbcloud / cli / commits / 9bbe641

Reposition the MCP registry listing around Mail and Auth

The title said CLI, which names the delivery vehicle rather than the products, and the description spent its 100 characters on internal words like tenants and Mail apps. Title, description, websiteUrl, and the publisher-provided keywords now lead with email and authentication, and the listing carries the smbCloud logo for clients that render icons. The README never mentioned MCP at all. Its About section now names both products and links them, and a new MCP section carries the client config people copy. On the docs site, the Mail page described routing and forwarding without mentioning that smbcloud-email-sdk documents a live send API with verified domains, delivery status, and idempotency keys. It opens with sending now, keeps inbound routing below it, and links the business mailbox product so the two stay distinct. Auth and the index get meta descriptions, and the service cards lead with Mail and Auth. Publishing is chained off the release. release-nuget.yml triggers release-mcp-registry.yml once its publish job succeeds, and both package checks poll for up to ten minutes, because nuget.org validates a package before it reaches the flat container. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Seto Elkahfi committed Jul 22, 2026 at 18:18 UTC 9bbe641e56053b3c444de23c3c7bfb3fcf2f839b
9 files changed +229 -52
.agents/AGENTS.md
+1 -1
@@ -101,7 +101,7 @@ 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`.
104 +`server.json` (the MCP Registry listing) is version-synced by the same Makefile step, and `release-nuget.yml` triggers `release-mcp-registry.yml` once its publish job succeeds — the listing needs both npm and NuGet live at that version, and the registry validates its metadata against the real packages. See `docs/mcp-registry.md`.
105
106 ---
107
.github/workflows/release-mcp-registry.yml
+33 -7
@@ -18,12 +18,10 @@ jobs:
18 name: Publish server.json to the MCP Registry
19 runs-on: ubuntu-latest
20 steps:
21 - # Unlike the other release workflows, this one builds nothing from source:
22 - # it uploads metadata. So it checks out the branch it was dispatched on
23 - # rather than the tag — `server.json` may have landed after the tag was
24 - # cut — and uses the tag input only to pick the version to publish.
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
@@ -64,7 +62,21 @@ jobs:
62 # landed will still be missing it. Both comparisons are case-sensitive.
63 server_name="$(jq -r '.name' server.json)"
64
67 - npm view "@smbcloud/cli@${RELEASE_VERSION}" version
65 + # npm is queryable within seconds of publishing, but the npm release
66 + # workflow runs in parallel with the NuGet one that triggers this, so
67 + # it may still be building its six platform packages.
68 + for attempt in $(seq 1 20); do
69 + if npm view "@smbcloud/cli@${RELEASE_VERSION}" version; then
70 + break
71 + fi
72 + if [ "${attempt}" -eq 20 ]; then
73 + echo "@smbcloud/cli@${RELEASE_VERSION} is still not on npm after 10 minutes." >&2
74 + exit 1
75 + fi
76 + echo "Waiting for @smbcloud/cli@${RELEASE_VERSION} on npm (attempt ${attempt}/20)..."
77 + sleep 30
78 + done
79 +
80 npm_mcp_name="$(npm view "@smbcloud/cli@${RELEASE_VERSION}" mcpName)"
81 if [ "${npm_mcp_name}" != "${server_name}" ]; then
82 echo "@smbcloud/cli@${RELEASE_VERSION} declares mcpName '${npm_mcp_name}', expected '${server_name}'." >&2
@@ -72,8 +84,22 @@ jobs:
84 exit 1
85 fi
86
75 - nuget_versions="$(curl -fsSL https://api.nuget.org/v3-flatcontainer/smbcloud.cli/index.json)"
76 - echo "${nuget_versions}" | jq -e --arg v "${RELEASE_VERSION}" '.versions | index($v)'
87 + # nuget.org validates a package before it reaches the flat container,
88 + # so a version pushed minutes ago is not queryable yet. Poll instead
89 + # of failing the release on that race.
90 + for attempt in $(seq 1 20); do
91 + nuget_versions="$(curl -fsSL "https://api.nuget.org/v3-flatcontainer/smbcloud.cli/index.json")"
92 + if echo "${nuget_versions}" | jq -e --arg v "${RELEASE_VERSION}" '.versions | index($v)' >/dev/null; then
93 + echo "SmbCloud.Cli ${RELEASE_VERSION} is indexed."
94 + break
95 + fi
96 + if [ "${attempt}" -eq 20 ]; then
97 + echo "SmbCloud.Cli ${RELEASE_VERSION} is still not indexed on nuget.org after 10 minutes." >&2
98 + exit 1
99 + fi
100 + echo "Waiting for nuget.org to index ${RELEASE_VERSION} (attempt ${attempt}/20)..."
101 + sleep 30
102 + done
103
104 curl -fsSL -o package.nupkg \
105 "https://api.nuget.org/v3-flatcontainer/smbcloud.cli/${RELEASE_VERSION}/smbcloud.cli.${RELEASE_VERSION}.nupkg"
.github/workflows/release-nuget.yml
+23
@@ -288,3 +288,26 @@ jobs:
288 --api-key "${NUGET_API_KEY}" \
289 --source https://api.nuget.org/v3/index.json \
290 --skip-duplicate
291 +
292 + dispatch-mcp-registry:
293 + name: Trigger MCP Registry release
294 + runs-on: ubuntu-latest
295 + needs: publish-nuget
296 + permissions:
297 + actions: write
298 + steps:
299 + # The MCP Registry listing needs both npm and NuGet live at this version,
300 + # and NuGet is the slower of the two, so it hangs off this workflow rather
301 + # than the parallel fan-out in release-crate.yml.
302 + - name: Trigger MCP Registry release workflow
303 + uses: actions/github-script@v7
304 + with:
305 + script: |
306 + const tag = '${{ github.event.inputs.tag }}' || context.ref.replace('refs/tags/', '')
307 + await github.rest.actions.createWorkflowDispatch({
308 + owner: context.repo.owner,
309 + repo: context.repo.repo,
310 + workflow_id: 'release-mcp-registry.yml',
311 + ref: tag,
312 + inputs: { tag }
313 + })
README.MD
+36 -3
@@ -5,7 +5,7 @@
5 <br>smbCloud CLI
6 </h1>
7 <p align="center">
8 - smbCloud Deploy, Auth, Mail, and GresIQ interface from the terminal.
8 + smbCloud Mail, Auth, Deploy, and GresIQ from your terminal or your AI agent.
9 <br />
10 <br />
11 <a href="https://smbcloud.xyz/">Website</a>
@@ -28,9 +28,13 @@
28
29 ## About
30
31 -**`smb`** is the command-line interface for [smbCloud](https://smbcloud.xyz/).
31 +**`smb`** is the command-line interface for [smbCloud](https://smbcloud.xyz/). It is
32 +also an MCP server, so Claude, Cursor, and other AI agents can run the same operations
33 +you do.
34
33 -Deploy Rust, Node.js, Ruby, or Swift apps from your terminal.
35 +Set up email domains and inbox routing on [smbCloud Mail](https://smbcloudmail.com),
36 +manage authentication apps on [smbCloud Auth](https://smbcloudauth.com), and deploy
37 +Rust, Node.js, Ruby, or Swift apps. One binary, no runtime to install.
38
39 ## Install
40
@@ -85,6 +89,35 @@ smb deploy
89
90 That gets you from login to first deploy.
91
92 +## MCP server
93 +
94 +`smb --mcp` speaks the [Model Context Protocol](https://modelcontextprotocol.io) over
95 +stdio, exposing 30 tools across smbCloud Mail, Auth, projects, and deployments. It is
96 +listed in the official MCP Registry as `io.github.smbcloudXYZ/smbcloud-cli`.
97 +
98 +Claude Code:
99 +
100 +```sh
101 +claude mcp add smbcloud -- smb --mcp
102 +```
103 +
104 +Claude Desktop, Cursor, and anything else that takes a JSON config:
105 +
106 +```json
107 +{
108 + "mcpServers": {
109 + "smbcloud": {
110 + "command": "npx",
111 + "args": ["@smbcloud/cli", "--mcp"]
112 + }
113 + }
114 +}
115 +```
116 +
117 +Auth comes from `smb login`, so a terminal session and an agent session share the same
118 +token and the same selected project. See [`docs/mcp.md`](./docs/mcp.md) for the full tool
119 +reference.
120 +
121 ## CI / non-interactive mode
122
123 Pass `--ci` (or set `SMB_CI=1`, or run under any provider that sets `CI`) to
docs/mcp-registry.md
+15 -12
@@ -64,21 +64,24 @@ documented in [Install](./cli-install.md).
64 — and both are updated by `make patch | minor | major` along with the rest of
65 the release metadata (`scripts/sync-release-version.mjs`).
66
67 -After tagging and running the npm and NuGet release workflows, run the
68 -**MCP Registry Release** workflow with the same tag:
67 +Publishing is chained off the release: pushing a `v*` tag runs the crates.io
68 +workflow, which fans out to the distribution workflows, and **NuGet CLI
69 +Release** triggers **MCP Registry Release** once its publish job succeeds. It
70 +hangs off NuGet rather than the fan-out because the listing needs both npm and
71 +NuGet live at that version, and NuGet is the slower of the two.
72
70 -```sh
71 -gh workflow run release-mcp-registry.yml --ref development -f tag=v0.4.12
72 -```
73 +The registry fetches the package metadata during publish, so
74 +`@smbcloud/cli@<version>` and `SmbCloud.Cli <version>` must already exist. npm
75 +is queryable within seconds of publishing; nuget.org validates first and takes
76 +roughly 5–15 minutes to reach the flat container. The workflow polls both for up
77 +to ten minutes rather than failing on that gap.
78
74 -The workflow publishes the `server.json` on the branch you dispatch from, not
75 -the one at the tag — it uploads metadata rather than building from source, and
76 -the metadata can change after a tag is cut. The tag input only selects which
77 -version to publish.
79 +To publish by hand — a re-run after a transient failure, say — dispatch it with
80 +the tag:
81
79 -Order matters. The registry fetches the package metadata during publish, so
80 -`@smbcloud/cli@<version>` and `SmbCloud.Cli <version>` must already be live —
81 -the workflow checks for both and stops early if they aren't.
82 +```sh
83 +gh workflow run release-mcp-registry.yml -f tag=v0.4.13
84 +```
85
86 Confirm the listing afterwards:
87
server.json
+30 -4
@@ -1,10 +1,17 @@
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.",
4 + "title": "smbCloud Mail & Auth",
5 + "description": "Email infrastructure and authentication for developers: domains, inbox routes, auth apps, deploys.",
6 "version": "0.4.13",
7 - "websiteUrl": "https://smbcloud.xyz/",
7 + "websiteUrl": "https://docs.smbcloud.xyz/developer/cli",
8 + "icons": [
9 + {
10 + "src": "https://smbcloud.xyz/logo.png",
11 + "mimeType": "image/png",
12 + "sizes": ["866x866"]
13 + }
14 + ],
15 "repository": {
16 "url": "https://github.com/smbcloudXYZ/smbcloud-cli",
17 "source": "github",
@@ -47,5 +54,24 @@
54 }
55 ]
56 }
50 - ]
57 + ],
58 + "_meta": {
59 + "io.modelcontextprotocol.registry/publisher-provided": {
60 + "categories": ["email", "authentication", "developer-tools"],
61 + "keywords": [
62 + "transactional email",
63 + "email api",
64 + "email domains",
65 + "inbox routing",
66 + "authentication",
67 + "oauth",
68 + "identity",
69 + "deployment"
70 + ],
71 + "products": {
72 + "smbCloud Mail": "https://smbcloudmail.com",
73 + "smbCloud Auth": "https://smbcloudauth.com"
74 + }
75 + }
76 + }
77 }
website/content/auth.mdx
+8 -2
@@ -1,7 +1,13 @@
1 +---
2 +title: Auth
3 +description: smbCloud Auth is the authentication service for your product. Run hosted sign-in, accounts, and sessions for your end users without building identity yourself.
4 +---
5 +
6 # Auth
7
3 -Auth is the identity and account service. It handles signing users in and
4 -managing their accounts, so you don't have to build that part yourself.
8 +Auth is the authentication and identity service for your product. It handles signing
9 +users in, their accounts, and their sessions, so you don't have to build or run that
10 +part yourself.
11
12 ## What you can do
13
website/content/index.mdx
+9 -8
@@ -1,5 +1,6 @@
1 ---
2 title: Introduction
3 +description: smbCloud is the platform you build your product on. Transactional email, authentication, app deploys, and a managed PostgreSQL database in one place.
4 ---
5
6 # smbCloud
@@ -7,23 +8,23 @@ title: Introduction
8 **Four services. One platform.** Enterprise power at startup velocity.
9
10 smbCloud is the platform you build your product on. It bundles the pieces most
10 -teams stitch together by hand — shipping apps, signing users in, routing mail,
11 -and running a database — into one place, so you can stay focused on what you're
12 -actually building.
11 +teams stitch together by hand, meaning email your users actually receive, sign-in
12 +you don't have to build, app hosting, and a database, into one place, so you can
13 +stay focused on what you're actually building.
14
15 ## The services
16
17 import { Cards } from 'nextra/components'
18
19 <Cards>
19 - <Cards.Card title="Deploy" href="/deploy" arrow>
20 - Ship NodeJS, Swift, and Ruby apps with one command.
20 + <Cards.Card title="Mail" href="/mail" arrow>
21 + Transactional email from your domain, plus inbound routing.
22 </Cards.Card>
23 <Cards.Card title="Auth" href="/auth" arrow>
24 Sign-in, accounts, and hosted auth for your users.
25 </Cards.Card>
25 - <Cards.Card title="Mail" href="/mail" arrow>
26 - Domain mail routing, forwarding, and stored messages.
26 + <Cards.Card title="Deploy" href="/deploy" arrow>
27 + Ship NodeJS, Swift, and Ruby apps with one command.
28 </Cards.Card>
29 <Cards.Card title="GresIQ" href="/gresiq" arrow>
30 A managed PostgreSQL database for your app.
@@ -36,6 +37,6 @@ import { Cards } from 'nextra/components'
37 - Building against smbCloud? The [Developer](/developer) section covers the CLI,
38 tools, and SDKs.
39
39 -Looking for product help by service, head to **Deploy**, **Auth**, **Mail**, or
40 +Looking for product help by service, head to **Mail**, **Auth**, **Deploy**, or
41 **GresIQ** above. For the command-line tool and everything you install locally,
42 see [Developer](/developer).
website/content/mail.mdx
+74 -15
@@ -1,30 +1,89 @@
1 +---
2 +title: Mail
3 +description: smbCloud Mail is the transactional email service. Send from your own verified domain over the API, read delivery status for every message, and route inbound mail where it needs to go.
4 +---
5 +
6 # Mail
7
3 -Mail is the domain mail-routing product. It's forwarding-first today, and it can
4 -now store inbound message history too.
8 +Mail is the email service for your product. Send transactional email from your own
9 +domain over the API, then read what happened to each message. The same domain also
10 +routes inbound mail, so receipts go out and replies come back through one place.
11 +
12 +## Send transactional email
13 +
14 +Mint an API key for your Mail app in the console. Keys look like `smb_mail_…`, and
15 +sending is scoped to the domain that Mail app has verified.
16 +
17 +```sh
18 +curl -X POST https://api.smbcloud.xyz/v1/email/messages \
19 + -H "Authorization: Bearer smb_mail_your_key" \
20 + -H "Content-Type: application/json" \
21 + -d '{
22 + "from": "billing@example.com",
23 + "to": ["customer@acme.com"],
24 + "subject": "Your receipt",
25 + "html": "<h1>Thanks!</h1>",
26 + "idempotency_key": "receipt-2026-0001"
27 + }'
28 +```
29 +
30 +At least one of `html` or `text` is required, and `from` must sit on the verified
31 +domain. Messages also take `cc`, `bcc`, `reply_to`, base64 attachments, custom
32 +headers, and tags.
33 +
34 +Reuse an `idempotency_key` and you get the original message back instead of a second
35 +send, which is what you want when a retry fires after a timeout.
36 +
37 +From Rust, use [`smbcloud-email-sdk`](https://crates.io/crates/smbcloud-email-sdk):
38 +
39 +```rust
40 +let message = SendEmail::new("billing@example.com", ["customer@acme.com"])
41 + .subject("Your receipt")
42 + .html("<h1>Thanks!</h1>")
43 + .idempotency_key("receipt-2026-0001");
44 +
45 +let sent = client.send(&message).await?;
46 +```
47
6 -## What you can do
48 +## Track delivery
49 +
50 +Every message carries a status: queued, sent, delivered, bounced, complained, or
51 +failed. Fetch one message for its full event timeline, or list recent messages
52 +filtered by status when you want to see what bounced.
53 +
54 +```sh
55 +curl "https://api.smbcloud.xyz/v1/email/messages?status=bounced&limit=20" \
56 + -H "Authorization: Bearer smb_mail_your_read_key"
57 +```
58 +
59 +Reading messages needs a read-scope key. Sending keys cannot read.
60 +
61 +## Receive and route
62 +
63 +A Mail app maps to one domain and holds many inbox routes. Each route forwards
64 +inbound mail where it needs to go, and stores the message so you keep the history.
65
66 - Run one Mail app per domain
67 - Set up many inbox routes on a Mail app
68 - Forward inbound mail where it needs to go
69 - Store inbound message history
70
13 -## How it fits together
14 -
15 -- A project can have many Mail apps.
16 -- One Mail app maps to one domain.
17 -- One Mail app can have many inbox routes.
18 -
19 -Day to day, you work with **inboxes**, **forwarding**, and **stored messages**.
20 -The domain setup — DNS, bucket, prefix, and shared routing — lives on the Mail
21 -app's setup page, out of the way of everyday inbox work.
71 +A project can have many Mail apps. The domain setup, meaning DNS, bucket, prefix,
72 +and shared routing, lives on the Mail app's setup page, out of the way of everyday
73 +inbox work.
74
75 ## Getting started
76
77 1. Create a Mail app for your domain.
78 2. Complete the domain setup (DNS and routing) on the Mail app's setup page.
27 -3. Add inbox routes and forwarding.
79 +3. Mint an API key to send, or add inbox routes to receive.
80 +
81 +Mail handles transactional email and domain routing. It is not a hosted mailbox
82 +suite: for business mailboxes you read in an app, see
83 +[smbCloud Mail for business](https://smbcloudmail.com).
84 +
85 +## From the CLI or an AI agent
86
29 -Mail is forwarding-first — it's not a full hosted mailbox suite. Stored messages
30 -add history on top of routing.
87 +`smb mail` manages Mail apps and inbox routes from the terminal, and the same
88 +operations are available to Claude, Cursor, and other agents through the
89 +[MCP server](/developer/cli/mcp).