Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ make patch | make minor | make major | make custom VERSION=0.x.y

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.

`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`.
`server.json` and `server-xcrs.json` (the MCP Registry listings) are version-synced by the same Makefile step, and `release-nuget.yml` triggers `release-mcp-registry.yml` once its publish job succeeds — the listings need the npm, NuGet, and Cargo packages live at their recorded versions, and the registry validates their metadata against the real packages. See `docs/mcp-registry.md`.

---

Expand Down
48 changes: 45 additions & 3 deletions .github/workflows/release-mcp-registry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permissions:

jobs:
publish:
name: Publish server.json to the MCP Registry
name: Publish MCP servers to the MCP Registry
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down Expand Up @@ -44,14 +44,28 @@ jobs:
# NuGet proves ownership through an `mcp-name:` marker in the package README.
grep -q "mcp-name: ${server_name}" nuget/smbcloud-cli/README.md

- name: Set the version in server.json
xcrs_server_name="$(jq -r '.name' server-xcrs.json)"
grep -q "mcp-name: ${xcrs_server_name}" crates/xcrs/README.md

- name: Set release versions in server metadata
shell: bash
run: |
jq --arg v "${RELEASE_VERSION}" \
'.version = $v | .packages |= map(.version = $v)' \
server.json > server.tmp
mv server.tmp server.json

xcrs_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/xcrs/Cargo.toml | head -n 1)"
if [ "${xcrs_version}" != "${RELEASE_VERSION}" ]; then
echo "xcrs version ${xcrs_version} does not match release version ${RELEASE_VERSION}." >&2
exit 1
fi
jq --arg v "${xcrs_version}" \
'.version = $v | .packages |= map(.version = $v)' \
server-xcrs.json > server-xcrs.tmp
mv server-xcrs.tmp server-xcrs.json
cat server.json
cat server-xcrs.json

- name: Verify the published packages carry the ownership marker
shell: bash
Expand Down Expand Up @@ -109,6 +123,29 @@ jobs:
fi
rm package.nupkg

xcrs_server_name="$(jq -r '.name' server-xcrs.json)"
xcrs_version="$(jq -r '.version' server-xcrs.json)"
for attempt in $(seq 1 20); do
if curl -fsSL "https://crates.io/api/v1/crates/xcrs/${xcrs_version}" >/dev/null; then
echo "xcrs ${xcrs_version} is indexed on crates.io."
break
fi
if [ "${attempt}" -eq 20 ]; then
echo "xcrs ${xcrs_version} is still not indexed on crates.io after 10 minutes." >&2
exit 1
fi
echo "Waiting for crates.io to index xcrs ${xcrs_version} (attempt ${attempt}/20)..."
sleep 30
done

curl -fsSL -o xcrs.crate \
"https://crates.io/api/v1/crates/xcrs/${xcrs_version}/download"
if ! tar -xOf xcrs.crate "xcrs-${xcrs_version}/README.md" | grep -q "mcp-name: ${xcrs_server_name}"; then
echo "xcrs ${xcrs_version} README is missing the 'mcp-name: ${xcrs_server_name}' marker." >&2
exit 1
fi
rm xcrs.crate

- name: Install mcp-publisher
shell: bash
run: |
Expand All @@ -120,10 +157,15 @@ jobs:

- name: Publish
shell: bash
run: ./mcp-publisher publish
run: |
./mcp-publisher publish
cp server-xcrs.json server.json
./mcp-publisher publish

- name: Verify the server is listed
shell: bash
run: |
server_name="$(jq -r '.name' server.json)"
curl -fsSL "https://registry.modelcontextprotocol.io/v0.1/servers?search=${server_name}" | jq .
xcrs_server_name="$(jq -r '.name' server-xcrs.json)"
curl -fsSL "https://registry.modelcontextprotocol.io/v0.1/servers?search=${xcrs_server_name}" | jq .
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[workspace]
resolver = "2"
members = ["crates/cli", "crates/smbcloud-*"]
members = ["crates/cli", "crates/smbcloud-*", "crates/xcrs"]

[workspace.dependencies]
anyhow = "1.0.58"
async-trait = "0.1.51"
base64 = "0.22"
chrono = "0.4"
clap = "4.1.6"
clap_mangen = "0.2.10"
Expand All @@ -28,17 +29,17 @@ schemars = "1"
serde = "1"
serde_json = "1.0.82"
serde_repr = "0.1"
smbcloud-auth = { version = "0.4.9", path = "crates/smbcloud-auth" }
smbcloud-auth-sdk = { version = "0.4.9", path = "crates/smbcloud-auth-sdk" }
smbcloud-deploy = { version = "0.4.9", path = "crates/smbcloud-deploy" }
smbcloud-email-sdk = { version = "0.4.9", path = "crates/smbcloud-email-sdk" }
smbcloud-gresiq-sdk = { version = "0.4.9", path = "crates/smbcloud-gresiq-sdk" }
smbcloud-mail = { version = "0.4.9", path = "crates/smbcloud-mail" }
smbcloud-model = { version = "0.4.9", path = "crates/smbcloud-model" }
smbcloud-network = { version = "0.4.9", path = "crates/smbcloud-network" }
smbcloud-networking = { version = "0.4.9", path = "crates/smbcloud-networking" }
smbcloud-networking-project = { version = "0.4.9", path = "crates/smbcloud-networking-project" }
smbcloud-utils = { version = "0.4.9", path = "crates/smbcloud-utils" }
smbcloud-auth = { version = "0.4.13", path = "crates/smbcloud-auth" }
smbcloud-auth-sdk = { version = "0.4.13", path = "crates/smbcloud-auth-sdk" }
smbcloud-deploy = { version = "0.4.13", path = "crates/smbcloud-deploy" }
smbcloud-email-sdk = { version = "0.4.13", path = "crates/smbcloud-email-sdk" }
smbcloud-gresiq-sdk = { version = "0.4.13", path = "crates/smbcloud-gresiq-sdk" }
smbcloud-mail = { version = "0.4.13", path = "crates/smbcloud-mail" }
smbcloud-model = { version = "0.4.13", path = "crates/smbcloud-model" }
smbcloud-network = { version = "0.4.13", path = "crates/smbcloud-network" }
smbcloud-networking = { version = "0.4.13", path = "crates/smbcloud-networking" }
smbcloud-networking-project = { version = "0.4.13", path = "crates/smbcloud-networking-project" }
smbcloud-utils = { version = "0.4.13", path = "crates/smbcloud-utils" }
spinners = "4.1.0"
strum = "0.27"
strum_macros = "0.27"
Expand All @@ -55,3 +56,4 @@ tsync = "2"
uniffi = "0.31"
url-builder = "0.1.1"
wasm-bindgen = "0.2"
xcrs = { version = "0.4.13", path = "crates/xcrs" }
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.PHONY: help release patch minor major custom sync-release-metadata regenerate-release-lockfiles
.PHONY: help release patch minor major custom sync-release-metadata regenerate-release-lockfiles check-release-versions

help:
@echo "Release commands:"
@echo " make patch"
@echo " make minor"
@echo " make major"
@echo " make custom VERSION=0.3.40"
@echo " make custom VERSION=0.4.14"

release:
@test -n "$(BUMP)" || (echo "BUMP is required" && exit 1)
Expand All @@ -19,6 +19,7 @@ release:
fi
@$(MAKE) sync-release-metadata
@$(MAKE) regenerate-release-lockfiles
@$(MAKE) check-release-versions
@release_version=$$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1); \
if [ -z "$$release_version" ]; then echo "Unable to determine release version from crates/cli/Cargo.toml"; exit 1; fi; \
git add -A; \
Expand All @@ -32,9 +33,14 @@ sync-release-metadata:

regenerate-release-lockfiles:
@cargo generate-lockfile --manifest-path sdk/gems/auth/Cargo.toml
@cargo generate-lockfile --manifest-path sdk/gems/email/Cargo.toml
@cargo generate-lockfile --manifest-path sdk/gems/model/Cargo.toml
@bundle lock --gemfile sdk/gems/auth/Gemfile
@bundle lock --gemfile sdk/gems/model/Gemfile
@BUNDLE_GEMFILE=sdk/gems/auth/Gemfile bundle lock
@BUNDLE_GEMFILE=sdk/gems/email/Gemfile bundle lock
@BUNDLE_GEMFILE=sdk/gems/model/Gemfile bundle lock

check-release-versions:
@node ./scripts/check-release-versions.mjs

patch:
@$(MAKE) release BUMP=patch
Expand Down
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ tracing = { workspace = true, features = ["log"] }
tracing-bunyan-formatter = { workspace = true }
tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] }
url-builder = { workspace = true }
xcrs = { workspace = true }

[target.'cfg(not(windows))'.dependencies]
openssl = { workspace = true }
51 changes: 51 additions & 0 deletions crates/cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use {
clap::{Parser, Subcommand},
smbcloud_network::environment::Environment,
spinners::Spinner,
std::path::PathBuf,
};

pub struct CommandResult {
Expand Down Expand Up @@ -96,9 +97,59 @@ pub enum Commands {
#[clap(subcommand)]
command: cloud_auth::cli::Commands,
},
#[clap(about = "Run and control XCRS ControlKit on Apple devices.")]
ControlKit {
#[clap(subcommand)]
command: ControlKitCommands,
},
#[clap(
about = "Migrate local .smb/config.toml deploy fields to the smbCloud server.",
display_order = 4
)]
Migrate {},
}

#[derive(Subcommand)]
pub enum ControlKitCommands {
#[clap(about = "Build a signed ControlKit XCTest runner for a physical device.")]
Build {
#[arg(long)]
project_path: PathBuf,
#[arg(long, default_value = "ControlKit")]
scheme: String,
#[arg(long, default_value = "Release")]
configuration: String,
#[arg(long)]
device_udid: String,
#[arg(long)]
derived_data_path: PathBuf,
},
#[clap(about = "Start a built ControlKit XCTest runner on a physical device.")]
Start {
#[arg(long)]
device_udid: String,
#[arg(long)]
xctestrun_path: PathBuf,
#[arg(long, default_value = "::")]
listen_host: String,
#[arg(long, default_value_t = 12004)]
listen_port: u16,
#[arg(long, default_value_t = 120)]
timeout_seconds: u64,
#[arg(long)]
log_path: Option<PathBuf>,
},
#[clap(about = "Call a running ControlKit JSON-RPC method.")]
Call {
#[arg(long, required_unless_present = "host", conflicts_with = "host")]
device_udid: Option<String>,
#[arg(long, conflicts_with = "device_udid")]
host: Option<String>,
#[arg(long, default_value_t = 12004)]
port: u16,
#[arg(long)]
method: String,
#[arg(long, default_value = "{}")]
params: String,
},
}
Loading
Loading