Split by domain and serve every operation over MCP - #2
Merged
Conversation
The certificate work landed inside the metadata client, where it did not belong, and adding an MCP server was about to make that worse by depending on both halves at once. Six crates now: core carries the transport (JWT auth, the HTTP client, JSON:API envelopes, errors), aso carries App Metadata, signing carries certificates and CSR generation, and frontend holds what the two front ends share so they cannot disagree. aso and signing know nothing about each other, and CI builds each library on its own, because a workspace build compiles them with the server's dependencies already in the graph and proves nothing. core exists because both domains need Client and ApiKey. Calling that shared transport "aso" would have meant signing depending on App Store Optimization for its HTTP client, a misnomer that only gets worse when provisioning profiles and devices land. This breaks anyone embedding the library. Rust allows inherent impls only in the crate that defines a type, so every resource that used to be a method on Client is an extension trait now: AppsApi, CertificatesApi, and the rest, with a prelude per domain crate. The methods do not exist until the trait is in scope. The MCP server exposes 28 tools, every command line operation except certificate revocation, following the xcrs contract in smbcloud-cli: tool names come from the embedder through a macro, so a host can re-expose the same tools under its own namespace without forking the bodies. Contract tests assert the exact tool set, titles under 40 characters, all six description sections, annotation consistency, and a description on every input schema property. Revocation is not a tool, and a test fails the build if it becomes one. Revoking a signing certificate invalidates every provisioning profile embedding it, for every teammate and every CI job, at once and irreversibly, and no confirmation string a model types on a human's behalf makes that safe. Scoped deletes are exposed and annotated destructive, because a version, a localization, or a screenshot can each be recreated by re-running the tool that made it. Tool results carry no key material. certificate_create returns the path it wrote the private key to, never the key, since results are read by a model and end up in transcripts. Credentials resolve per call rather than at startup, so an unconfigured server still answers tools/list and then fails naming what is missing; one that refuses to start can tell nobody what it needs. The MCP Registry marker lives in the CLI crate's README rather than the library's, because the registry fetches the package named in server.json and greps that crate's README for it. Co-Authored-By: siGit Code <sigit@sigit.si> siGit-Code-Cloud-Agent-Session: https://code.sigit.si/cloud/sessions/d8b72f7e-fb25-45ee-a7f7-e2bf826baff3
keypair34
approved these changes
Aug 6, 2026
paydii
approved these changes
Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The certificate work landed inside the metadata client, where it did not belong, and adding an MCP server was about to make that worse by depending on both halves at once. Six crates now: core carries the transport (JWT auth, the HTTP client, JSON:API envelopes, errors), aso carries App Metadata, signing carries certificates and CSR generation, and frontend holds what the two front ends share so they cannot disagree. aso and signing know nothing about each other, and CI builds each library on its own, because a workspace build compiles them with the server's dependencies already in the graph and proves nothing.
core exists because both domains need Client and ApiKey. Calling that shared transport "aso" would have meant signing depending on App Store Optimization for its HTTP client, a misnomer that only gets worse when provisioning profiles and devices land.
This breaks anyone embedding the library. Rust allows inherent impls only in the crate that defines a type, so every resource that used to be a method on Client is an extension trait now: AppsApi, CertificatesApi, and the rest, with a prelude per domain crate. The methods do not exist until the trait is in scope.
The MCP server exposes 28 tools, every command line operation except certificate revocation, following the xcrs contract in smbcloud-cli: tool names come from the embedder through a macro, so a host can re-expose the same tools under its own namespace without forking the bodies. Contract tests assert the exact tool set, titles under 40 characters, all six description sections, annotation consistency, and a description on every input schema property.
Revocation is not a tool, and a test fails the build if it becomes one. Revoking a signing certificate invalidates every provisioning profile embedding it, for every teammate and every CI job, at once and irreversibly, and no confirmation string a model types on a human's behalf makes that safe. Scoped deletes are exposed and annotated destructive, because a version, a localization, or a screenshot can each be recreated by re-running the tool that made it.
Tool results carry no key material. certificate_create returns the path it wrote the private key to, never the key, since results are read by a model and end up in transcripts. Credentials resolve per call rather than at startup, so an unconfigured server still answers tools/list and then fails naming what is missing; one that refuses to start can tell nobody what it needs.
The MCP Registry marker lives in the CLI crate's README rather than the library's, because the registry fetches the package named in server.json and greps that crate's README for it.
siGit-Code-Cloud-Agent-Session: https://code.sigit.si/cloud/sessions/d8b72f7e-fb25-45ee-a7f7-e2bf826baff3