Smithy command-line application
moonx moonrockz/smithy-cli@latest --help
moonx moonrockz/smithy-cli@latest versionmise use -g github:moonrockz/smithy
smithy versionmoon build apps/smithy --target native --release
_build/native/release/build/moonrockz/smithy-cli/smithy-cli.exe versionsmithy ast model.smithy extra.json
smithy validate model.smithy extra.json
smithy validate --protocol-tests examples/protocol-tests/greeting.smithy
smithy validate value --shape example#Input --value request.json model.smithy
smithy validate rpc --version 2.0 --service example#Calculator --message call.json api.smithy
smithy diff --old previous/ --new current/
smithy build --config smithy-build.json
smithy clean --config smithy-build.json
smithy select --selector 'structure > member > string' model.smithy
smithy convert --to json model.smithy -o model.json
smithy convert --to idl model.json -o model.smithy
smithy convert --to idl one.json two.json --output-dir generated
smithy format model.smithy
smithy format --check model.smithy model.json
smithy format --write model.smithy model.json$version: "2"
namespace example.weather
service Weather {
version: "2026-10-01"
operations: [GetForecast]
}
operation GetForecast {
input := {
@required
city: String
days: Integer
}
output := {
@required
summary: String
celsius: Integer
}
}$version: "2"
namespace example.weather
service Weather {
version: "2026-10-01"
operations: [GetForecast]
}
operation GetForecast {
input := {
@required
city: String
days: Integer
}
output := {
@required
summary: String
celsius: Integer
humidity: Integer
}
}$version: "2"
namespace example.weather
service Weather {
version: "2026-10-01"
operations: [GetForecast]
}
operation GetForecast {
input := {
@required
city: String
}
output := {
@required
summary: String
celsius: Integer
humidity: Integer
}
}$ smithy diff --old old-weather.smithy --new new-weather.smithy
[NOTE] example.weather#GetForecastOutput$humidity: Added member `example.weather#GetForecastOutput$humidity` | AddedShape$ smithy diff --old old-weather.smithy --new new-weather-removed.smithy
[NOTE] example.weather#GetForecastOutput$humidity: Added member `example.weather#GetForecastOutput$humidity` | AddedShape
[ERROR] example.weather#GetForecastInput$days: Removed member `example.weather#GetForecastInput$days` | RemovedShapesmithy diff --old previous/ --new current/ --format jsonl
smithy diff --old previous/ --new current/ --format=jsonl --output changes.jsonl{"id":"ChangedMemberTarget","severity":"ERROR","shape":"example#Value$name","message":"The event message","source":{"file":"current/model.smithy","path":"","span":{"start":42,"length":12}}}| Field | Value |
|---|---|
| id | Event identifier string |
| severity | NOTE, WARNING, DANGER or ERROR |
| shape | Absolute shape ID string, or null for a model-level event |
| message | Complete message string; JSON decoding restores newlines and other escaped characters |
| source | null, or an object containing file, path and span |
| source.file | Physical source identifier, including archive entry paths when present |
| source.path | JSON Pointer for JSON AST input, otherwise usually an empty string |
| source.span | null, or nonnegative integer start and length in zero-based UTF-16 code units |
set -o pipefail
smithy diff --old previous/ --new current/ --format jsonl |
jq --unbuffered -c 'select(.severity == "ERROR" or .severity == "DANGER")'moon run apps/smithy -- build --config examples/openrpc/smithy-build.jsonmise exec -- moon run apps/smithy --target js -- build --config examples/local-resources/smithy-build.jsoncat model.smithy | smithy convert --from idl --to json -$version: "2"
namespace example
use smithy.test#httpRequestTests
use smithy.test#httpResponseTests
@trait
@protocolDefinition
structure fixtureProtocol {}
@httpRequestTests([{
id: "GreetingRequest"
protocol: fixtureProtocol
method: "POST"
uri: "/greeting"
params: {name: "Ada"}
headers: {"Content-Type": "application/json"}
forbidHeaders: ["X-Secret"]
body: "{\"name\":\"Ada\"}"
bodyMediaType: "application/json"
}])
@httpResponseTests([{
id: "GreetingResponse"
protocol: fixtureProtocol
code: 200
params: {greeting: "Hello, Ada"}
}])
operation Greet { input: Input, output: Output }
structure Input { @required name: String }
structure Output { greeting: String }$version: "2"
namespace example
use smithy.test#httpRequestTests
use smithy.test#httpResponseTests
@trait
@protocolDefinition
structure fixtureProtocol {}
@httpRequestTests([{
id: "GreetingRequest"
protocol: fixtureProtocol
method: "POST"
uri: "/greeting"
params: {name: 1}
headers: {"Content-Type": "application/json"}
forbidHeaders: ["X-Secret"]
body: "{\"name\":\"Ada\"}"
bodyMediaType: "application/json"
}])
@httpResponseTests([{
id: "GreetingResponse"
protocol: fixtureProtocol
code: 200
params: {greeting: "Hello, Ada"}
}])
operation Greet { input: Input, output: Output }
structure Input { @required name: String }
structure Output { greeting: String }$ smithy validate --protocol-tests protocol-tests.smithy$ smithy validate --protocol-tests protocol-tests-bad.smithy
[ERROR] example#Greet: smithy.test#httpRequestTests.0.params: Error validating value/name: Expected string value for `smithy.api#String`. | HttpRequestTestsInput$version: "2"
namespace example
structure Greeting {
message: String
}$ smithy convert --to json greeting.smithy
{
"smithy": "2.0",
"shapes": {
"example#Greeting": {
"type": "structure",
"members": {
"message": {
"target": "smithy.api#String"
}
}
}
}
}{
"smithy": "2.0",
"shapes": {
"example#Greeting": {
"type": "structure",
"members": {
"message": {
"target": "smithy.api#String"
}
}
}
}
}$ smithy convert --to idl greeting.json
$version: "2"
namespace example
structure Greeting {
message: smithy.api#String
}$version: "2"
namespace example
service Weather {
version: "2024-01-01"
operations: [GetForecast, PutCity]
}
@readonly
operation GetForecast {}
@idempotent
operation PutCity {}$ smithy select --selector 'operation[trait|readonly]' weather.smithy
example#GetForecast{
"version": "1.0",
"sources": ["forecast.smithy"],
"projections": {
"public": {
"transforms": [
{"name": "excludeShapesByTrait", "args": {"traits": ["internal"]}}
]
}
}
}$version: "2"
namespace example
service Weather {
version: "2024-01-01"
operations: [GetForecast, GetDebugInfo]
}
@readonly
operation GetForecast {}
@internal
@readonly
operation GetDebugInfo {}$ smithy build --config smithy-build.json
$ smithy select --selector operation build/smithy/source/model/model.json
example#GetDebugInfo
example#GetForecast
$ smithy select --selector operation build/smithy/public/model/model.json
example#GetForecast$version: "2"
namespace example
use smithy.contracts#conditions
@conditions({
StartBeforeEnd: {
expression: "start < end"
documentation: "The start time must be before the end time."
}
})
structure TimeRange {
@required
start: Timestamp
@required
end: Timestamp
}$ smithy validate time-range.smithy
$ smithy select --selector '[trait|smithy.contracts#conditions]' time-range.smithy
example#TimeRange{"path":"module.zip","root":"models","paths":["service.smithy"],"globs":["shared/**/*.json"]}{"path":"downloaded-package.tgz","root":"package/models","globs":["**/*.smithy","**/*.json"]}mise exec -- moon run apps/smithy --target js -- build --config examples/npm-archive/smithy-build.json{"url":"https://example.org/models.zip","format":"zip","root":"models","globs":["**/*.smithy"]}mise exec -- moon run apps/smithy --target js -- build --config examples/public-archive/smithy-build.json$version: "2"
namespace example
@range(min: 1, max: 100)
integer Count
structure Input {
@required
count: Count
}smithy validate value --shape example#Input --value request.json model.smithy
smithy validate value --shape example#Input --value request.json --diagnostics json model.smithy$version: "2"
namespace example.orders
/// An order in a shop.
structure Order {
@required
id: String
@range(min: 1, max: 10)
quantity: Integer
@length(max: 20)
note: String
}{
"id": "A-1001",
"quantity": 2,
"note": "Leave at the door"
}{
"id": "A-1001",
"quantity": 11,
"note": "Leave at the door"
}$ smithy validate value --shape example.orders#Order --value order.json order.smithy$ smithy validate value --shape example.orders#Order --value order-too-many.json order.smithy
order-too-many.json/quantity: error[Range] example.orders#Order$quantity: I expected the number to satisfy the modeled range.smithy validate value --shape example#Input --value request.ion --input-format ion-text model.smithy
smithy validate value --shape example#Input --value request.10n --input-format ion-binary --diagnostics json model.smithy$version: "2"
namespace example
structure Reading {
@required
sensor: String
@required
@range(min: -50, max: 60)
celsius: Integer
@required
takenAt: Timestamp
}{sensor: "roof", celsius: 21, takenAt: 2026-10-24T09:30:00Z}{sensor: "roof", celsius: 21, takenAt: "2026-10-24T09:30:00Z"}$ smithy validate value --shape example#Reading --value reading.ion --input-format ion-text reading.smithy$ smithy validate value --shape example#Reading --value reading-text-time.ion --input-format ion-text reading.smithy
reading-text-time.ion/takenAt: error[TypeMismatch] example#Reading$takenAt: I expected a timestamp value, but found string.smithy validate rpc --version 2.0 --service example#Calculator --message call.json api.smithy
smithy validate rpc --version 2.0 --service example#Calculator --message invalid.json --diagnostics json api.smithy
smithy validate rpc --version 2.0 --service example#Calculator --response reply.json --method subtract api.smithy
smithy validate rpc --version 2.0 --service example#Calculator --response reply.json --request call.json --diagnostics json api.smithy$version: "2"
namespace example
use smithy.rpc#jsonRpc
use smithy.rpc#method
@jsonRpc(versions: ["1.0", "2.0"])
service Calculator {
version: "1"
operations: [Subtract, Ping]
}
@method(name: "subtract", params: "either",
positional: ["minuend", "subtrahend"], resultMember: "value")
operation Subtract { input: SubtractInput, output: SubtractOutput }
structure SubtractInput {
@required
minuend: Integer
@required
subtrahend: Integer
}
structure SubtractOutput { value: Integer }
@method(name: "ping", params: "either", positional: [], nullResult: true)
operation Ping {}{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, "23"],
"id": 1
}$ smithy validate rpc --version 2.0 --service example#Calculator --message subtract.json calculator.smithy$ smithy validate rpc --version 2.0 --service example#Calculator --message subtract-string.json calculator.smithy
subtract-string.json/params/1: error[TypeMismatch] example#SubtractInput$subtrahend: I expected a integer value, but found string.smithy validate rpc transcript --protocol example.peers#Editor \
examples/json-rpc/peers.smithy examples/json-rpc/transcripts/editor.json
smithy validate rpc transcript --protocol example.peers#Editor --complete --diagnostics json \
examples/json-rpc/peers.smithy examples/json-rpc/transcripts/editor-negative.json
smithy validate rpc transcript --protocol batch#Link \
examples/json-rpc/batches.smithy examples/json-rpc/transcripts/batches.json
smithy validate rpc transcript --protocol example.lifecycle#Lifecycle \
examples/json-rpc/lifecycle.smithy examples/json-rpc/transcripts/lifecycle-violation.json
smithy validate rpc transcript --protocol example.peers#Editor --version 2.0 \
--direction in=client --direction out=server \
examples/json-rpc/peers.smithy examples/json-rpc/transcripts/editor.jsonl
smithy validate rpc transcript --protocol example.peers#Editor --version 2.0 \
--log-mapping examples/json-rpc/transcripts/mcp-like.mapping.json \
examples/json-rpc/peers.smithy examples/json-rpc/transcripts/mcp-like.jsonl
# my-server is any process that logs dir-message records to stderr.
my-server --stdio 2>&1 >/dev/null | smithy validate rpc transcript --protocol example.peers#Editor \
--version 2.0 --diagnostics json examples/json-rpc/peers.smithy -$version: "2"
namespace example
use smithy.rpc#jsonRpc
use smithy.rpc#jsonRpcProtocol
use smithy.rpc#method
/// The two peers of the weather protocol.
@jsonRpcProtocol(
versions: ["2.0"]
peers: [
{ name: "client", accepts: WeatherClient }
{ name: "server", accepts: WeatherApi }
]
)
structure WeatherProtocol {}
/// The server answers forecast calls.
@jsonRpc(versions: ["2.0"])
service WeatherApi {
version: "2024-01-01"
operations: [GetForecast]
}
/// The client receives alerts from the server.
@jsonRpc(versions: ["2.0"])
service WeatherClient {
version: "2024-01-01"
operations: [Alert]
}
@method(name: "weather/forecast", params: "named", resultMember: "chanceOfRain")
operation GetForecast {
input := {
@required
cityId: String
}
output := {
@required
chanceOfRain: Float
}
}
@method(name: "weather/alert", params: "named", nullResult: true)
operation Alert {
input := {
@required
text: String
}
}{
"version": "2.0",
"complete": true,
"entries": [
{"from": "client", "message": {"jsonrpc": "2.0", "id": 1, "method": "weather/forecast", "params": {"cityId": "oslo"}}},
{"from": "server", "message": {"jsonrpc": "2.0", "method": "weather/alert", "params": {"text": "Storm at 18:00"}}},
{"from": "server", "message": {"jsonrpc": "2.0", "id": 1, "result": 0.8}}
]
}{
"version": "2.0",
"complete": true,
"entries": [
{"from": "client", "message": {"jsonrpc": "2.0", "id": 1, "method": "weather/forecast", "params": {"cityId": "oslo"}}},
{"from": "server", "message": {"jsonrpc": "2.0", "method": "weather/alert", "params": {"text": "Storm at 18:00"}}}
]
}$ smithy validate rpc transcript --protocol example#WeatherProtocol --diagnostics json weather-peers.smithy weather-exchange.json
{"valid":true,"complete":true,"entries":3,"outstanding":0,"correlation":true,"findings":[]}$ smithy validate rpc transcript --protocol example#WeatherProtocol weather-peers.smithy weather-no-reply.json
weather-no-reply.json:5:35: entry 0 client: error MissingResponse: I expected a reply from server to entry 0 from client with id 1; the complete transcript has none.{
"version": "2.0",
"sources": [{
"github": {
"repository": "owner/models",
"tag": "v1.2.3",
"asset": "models.zip"
},
"format": "zip",
"root": "models",
"globs": ["**/*.smithy"]
}]
}{
"version": "2.0",
"sources": [{
"npm": { "package": "@scope/models", "version": "1.2.3" },
"root": "package/models",
"globs": ["**/*.smithy"]
}],
"imports": [{
"maven": {
"group": "org.example",
"artifact": "model-traits",
"version": "1.2.3"
}
}]
}mise exec -- moon run apps/smithy --target js -- build --config examples/npm-registry/smithy-build.json
mise exec -- moon run apps/smithy --target js -- build --config examples/maven-repository/smithy-build.json{
"version": "1.0",
"sources": [
{
"npm": { "package": "@example/weather-models", "version": "1.2.3" },
"root": "package/models",
"globs": ["**/*.smithy"]
}
]
}{
"version": "1.0",
"sources": [
{
"npm": { "package": "@example/weather-models", "version": "^1.2.0" },
"root": "package/models",
"globs": ["**/*.smithy"]
}
]
}$ smithy build --config weather-build-range.json
smithy: npm requires a public registry base, package name and exact semantic version{
"version": "2.0",
"credentials": [
{ "origin": "https://api.github.com", "bearerTokenEnv": "GITHUB_TOKEN" },
{ "origin": "https://registry.npmjs.org", "bearerTokenEnv": "NPM_TOKEN" },
{ "origin": "https://maven.example.com", "usernameEnv": "MAVEN_USER", "passwordEnv": "MAVEN_PASSWORD" }
]
}type InputSourcetype Layouttype OutputSink