Generate
alirezarezvani/claude-skills
Generate Playwright tests. An agent skill from alirezarezvani/claude-skills.
What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.
$ npx skills add aws/aws-sdk-net --skill marshalling -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install aws/aws-sdk-net marshalling --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .claude/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .claude/skills/marshalling && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .claude/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshallingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add aws/aws-sdk-net --skill marshalling -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install aws/aws-sdk-net marshalling --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .agents/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .agents/skills/marshalling && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .agents/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aws/aws-sdk-net --skill marshalling -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install aws/aws-sdk-net marshalling --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .cursor/skills/marshalling && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .cursor/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/aws/aws-sdk-net.git --path generator/SmithyDotNet/skills/marshalling--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add aws/aws-sdk-net --skill marshalling -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install aws/aws-sdk-net marshalling --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .gemini/skills/marshalling && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .gemini/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install aws/aws-sdk-net marshallingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add aws/aws-sdk-net --skill marshalling -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .github/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .github/skills/marshalling && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .github/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aws/aws-sdk-net --skill marshalling -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install aws/aws-sdk-net marshalling --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .opencode/skills/marshalling && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "marshalling" agent skill from https://github.com/aws/aws-sdk-net/tree/main/generator/SmithyDotNet/skills/marshalling into .opencode/skills/marshalling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "marshalling", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
marshallingWhat the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.
Marshalling is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization. What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol. Use when writing or reviewing any marshaller/unmarshaller writer.
Its SKILL.md is about 8.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
The repository describes itself as: The official AWS SDK for .NET. For more information on the AWS SDK for .NET, see our web site:. The licence is Apache-2.0.
6 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit f36df89. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md.
From the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
smithy.ioFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Marshalling loads about 8.2k tokens when it runs. Until then it costs about 54 tokens; SKILL.md has 3,425 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from aws/aws-sdk-net at commit f36df89, republished under its Apache-2.0 licence (© aws). 3,425 words, ~8,192 tokens.
.claude/skills/marshalling/SKILL.md (or your agent's skills folder).This skill states the emitted output. Where a rule exists only for parity with the legacy C2J generator, it says so; everything else follows from the Smithy spec.
Under Generated/Model/Internal/MarshallTransformations/, all partial:
| File | Class | Notes |
|---|---|---|
{Operation}RequestMarshaller.cs | IMarshaller<IRequest, {Operation}Request> | |
{Operation}ResponseUnmarshaller.cs | JsonResponseUnmarshaller (or protocol equivalent) | Dispatches errors |
{Shape}Marshaller.cs | IRequestMarshaller<{Shape}, JsonMarshallerContext> (or protocol equivalent) | Nested structures in the request path |
{Shape}Unmarshaller.cs | IJsonUnmarshaller<{Shape}, JsonUnmarshallerContext> (or protocol equivalent) | Nested structures in the response path |
{Exception}Unmarshaller.cs | IJsonErrorResponseUnmarshaller<{Exception}, JsonUnmarshallerContext> (or protocol equivalent) | |
{Operation}EndpointDiscoveryMarshaller.cs | IMarshaller<EndpointDiscoveryDataBase, {Operation}Request> | Endpoint discovery only; see sdk-conventions |
An operation output already named {Operation}Response that a member also targets is typed with the
{Operation}Response class itself (no separate model class); its structure unmarshaller is
{Shape}StructureUnmarshaller, since {Shape}Unmarshaller is the operation's response unmarshaller.
No C2J service has this shape, so there is no parity target.
Structure (un)marshaller class names come only from GenerationContext.StructureMarshallerName /
StructureUnmarshallerName. TypeDescriptor.MarshallerName / UnmarshallerName carry them for a
structure target; writers read those (or the context, when declaring the class or naming the file)
and never compose {Shape}Marshaller / {Shape}Unmarshaller themselves, so a naming rule needs no
per-protocol code.
Structure marshallers expose public readonly static {Shape}Marshaller Instance = new {Shape}Marshaller();.
Operation marshallers/unmarshallers expose a private static instance behind a public Instance property.
Every request marshaller emits, in this order:
new DefaultRequest(publicRequest, "Amazon.{ServiceName}"), then @requestCompression (below).Content-Type (protocol-dependent, or the overrideContentType customization when set — below),
omitted for GET/DELETE and for operations with no body.HeaderKeys.XAmzApiVersion (the service shape's version) and HttpMethod.@httpQuery members, then @httpQueryParams; @httpPrefixHeaders, then @httpHeader members.@httpLabel members as AddPathResource calls, then request.ResourcePath set to the @http uri
template (labels left as {name} for the runtime to substitute).@httpChecksumRequired, @unsignedPayload, UseQueryString, @endpoint host
prefix, return request.An operation that requires HTTP/2 pins request.HttpProtocolVersion = System.Net.HttpVersion.Version20
(under #if NET8_0_OR_GREATER) right after the DefaultRequest. Which operations require it comes from
the protocol trait's version lists, where eventStreamHttp defaults to http when absent or empty:
http | eventStreamHttp | Operations pinned to h2 |
|---|---|---|
no h2 | any | none |
h2 without http/1.1 | any | all |
h2 and http/1.1 | without http/1.1 | those with an output event stream |
h2 and http/1.1 | with http/1.1 | those with both an input and an output event stream |
overrideContentType customizationThe service-level overrideContentType customization (a top-level string in *.customizations.json,
read into GenerationContext.Customizations.OverrideContentType) hard-codes the request Content-Type:
request.Headers["Content-Type"] = "{value}";. On restJson1 it replaces the per-operation value
(including the blob-payload default) but is still skipped for input event streams and GET/DELETE; it's
emitted even for body-less operations. On awsJson it replaces only the versioned Content-Type;
X-Amz-Target is unchanged. A handful of restJson1 services use it to send
application/x-amz-json-1.1 instead of the default application/json (finspace, finspace-data,
lex.v2), matching C2J.
| Smithy trait | Where | SDK pattern |
|---|---|---|
@httpQuery("name") scalar | Query string | request.Parameters.Add("name", StringUtils.FromString(...)) |
@httpQuery("name") list<string> | Query string | request.ParameterCollection.Add("name", publicRequest.Prop) (repeated params, ordinal-sorted at runtime) |
@httpQuery("name") list<value-type> | Query string | request.ParameterCollection.Add("name", publicRequest.Prop.ConvertAll<string>(item => StringUtils.FromX(item))) |
@httpQueryParams map | Query string | Loop entries into query params (see below); @httpQuery wins on key collision |
@httpLabel | URI segment | if (!publicRequest.IsSetProp()) throw new Amazon{BaseName}Exception(...), then request.AddPathResource("{name}", <conversion>). A greedy label keeps {name+} as the key and passes the value through .TrimStart('/') |
@httpHeader("name") scalar | Header | request.Headers["name"] = ... |
@httpHeader("name") @mediaType string | Header | Base64: Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(...)); the read side decodes (C2J "jsonvalue"). Body-bound @mediaType strings are plain |
@httpHeader("name") list<string> | Header | request.Headers["name"] = StringUtils.FromList(publicRequest.Prop) (comma join, RFC-7230 quoting) |
@httpHeader("name") list<value-type> | Header | request.Headers["name"] = StringUtils.FromValueTypeList(publicRequest.Prop) (the List<T> overload lowercases bool and forces DateTime to RFC822, so no per-element or per-format branch is emitted) |
@httpPrefixHeaders("prefix") map | Multiple headers | Loop map<string,string>, emit {prefix}{key} headers (see below); request & response |
@httpPayload | Entire body | Direct stream/string (skips body serialization) |
@hostLabel | Endpoint host prefix | request.HostPrefix label in addition to the member's normal binding (see below) |
@httpResponseCode | (response only) | unmarshalledObject.{Prop} = (int)context.ResponseData.StatusCode; (see below) |
| No HTTP trait | Body | Protocol-specific serialization |
Every query/header/label member is IsSet-guarded. A @required query member (an idempotency token
excepted) first throws Amazon{BaseName}Exception("Request object does not have required field {Prop} set")
when unset: string.IsNullOrEmpty for a string/enum, == null for anything else. Body members get no
required check.
List query/header bindings accept list<string>/list<enum> (an enum element is a plain string, see
type-mapping) and value-type element lists (int, long, bool, double, float, timestamp, intEnum), matching
C2J. A non-scalar element (blob, structure, nested list/map) fails loud in both positions, as does a
@sparse value-type list (a sparse string/enum list is accepted). A header list<timestamp> is only
supported with the default/http-date format (the runtime helper always emits RFC822).
A single member may carry @httpQueryParams: a map<string, string> or map<string, list<string>>
whose entries each become query params under the @httpQuery rules (a list<string> value repeats the
key via request.ParameterCollection; a map<string, string> adds StringUtils.FromString(kvp.Value)
via request.Parameters). It's emitted after the explicit @httpQuery members, each entry guarded
by a ContainsKey check that skips keys already set, so an explicit @httpQuery wins the collision per
the Smithy precedence rule. The guard intentionally does NOT cover query literals in the @http uri
(those live in request.SubResources), matching C2J so migration doesn't change wire behavior. The map
is request-only and never enters the body; its presence sets request.UseQueryString = true.
A single member may carry @httpPrefixHeaders: a map<string, string> whose entries each become a
header {prefix}{key} (an IsSet guard, then a foreach assigning
request.Headers[$"{prefix}{kvp.Key}"] = kvp.Value;). It's emitted before the explicit @httpHeader
members, so a colliding name (only possible with an empty prefix) is overwritten by the later
@httpHeader assignment, per the Smithy precedence rule. Valid on request, response and error
structures. The map value must be a string (anything else fails loud).
@jsonName if present, else the Smithy member name verbatim. awsJson1.x ignores @jsonName (see below).
When any member is body-bound, or the @httpPayload is a structure or document, the marshaller writes
a Utf8JsonWriter body over a PooledContentStream (#if NETFRAMEWORK: a MemoryStream copied to
request.Content). Each body member is IsSet-guarded, then written per Type → Marshal/Unmarshal under
its wire name.
{Shape}Marshaller.Instance; lists/maps loop and recurse to any depth. Map keys
are always strings..Value unwrap and no
float/double NaN guard (both are member-only). @timestampFormat is honored on leaves.@sparse leaves are nullable and JSON nulls are written, matching C2J. A sparse list null-guards its
value-type and blob elements (a null string/structure/document already serializes as null); a sparse
map null-guards every value kind.StringUtils.IsSpecial{Float,Double}Value so NaN/±Infinity
serialize as strings.Structure marshallers loop the structure's own members with the same rules. A request event-stream
event is the one exception: the publisher marshaller calls {Event}Marshaller with the JSON object
already open, then reads context.Request.EventHeaders and context.Request.Content. Event members
route by trait:
@eventHeader → IsSet-guarded EventStreamHeader("{memberName}") (never @jsonName) with the typed
setter for the target shape (SetString string/enum, SetBool/SetInt32/SetInt64/SetTimestamp
with .Value, SetByteBuf(….ToArray()) blob), added to EventHeaders. Any other target fails loud.@eventPayload → IsSet-guarded: blob Request.Content = ….ToArray(), string/enum
Encoding.UTF8.GetBytes(…) (publisher sends octet-stream / text/plain, C2J parity); structure/union
{Type}Marshaller.Instance.Marshall(…, context) into the open object (the protocol tests expect the bare
structure as the payload, not one wrapped under the member name). List/map fail loud.@httpPayload (request)A @httpPayload member IS the entire body: no wrapping object/property name, no other member in the body
(Smithy: all others are header/query/label). No IsSet guard (matches C2J), except string/enum.
String (enum too) → text/plain (or the target's @mediaType value when present), no writer scaffold: if (publicRequest.IsSet{Prop}()) { request.Content = System.Text.Encoding.UTF8.GetBytes(publicRequest.{Prop}); }. The guard diverges from C2J, whose unguarded GetBytes throws on an unset optional payload (DOTNET-8852).
Structure (a union too) → application/json; the scaffold, then the target's marshaller as the body object (WriteStartObject → {Type}Marshaller.Instance.Marshall(publicRequest.{Prop}, context) → WriteEndObject).
Document → application/json; the scaffold, then Amazon.Runtime.Documents.Internal.Transform.DocumentMarshaller.Instance.Write(writer, publicRequest.{Prop}); with NO WriteStartObject/WriteEndObject wrapping (the document is the whole JSON value). No C2J precedent.
Blob (MemoryStream, or Stream when @streaming) → application/octet-stream (or the target's @mediaType value; overrides the top application/json). Always assigns request.ContentStream = publicRequest.{Prop} ?? new MemoryStream(); first and ends with the Content-Type override, except when the input also has an @httpHeader("Content-Type") member: that header is emitted before the blob block and must win, so the blob's type moves to the top Content-Type line and the trailing override is dropped (restJson1 TestPayloadBlob). The Content-Length handling in between branches on the operation's aws.auth#unsignedPayload and the blob's @requiresLength (C2J parity):
@streaming + @unsignedPayload, no @requiresLength → seek to start and set Content-Length when the stream is seekable, else Transfer-Encoding: chunked.@streaming + @requiresLength → the stream MUST be seekable (throws InvalidOperationException otherwise), then always sets Content-Length. @requiresLength wins over the unsigned chunked path.Separately, aws.auth#unsignedPayload on the operation emits request.DisablePayloadSigning = true; after the body block, for any body kind.
List and map payloads fail loud.
@requestCompression and @httpChecksumRequired (request)Both are one emitted call to a runtime helper in Amazon.Runtime.Internal.Util, at opposite ends of the body.
@requestCompression → CompressionAlgorithmUtils.SetCompressionAlgorithm(request, CompressionEncodingAlgorithm.{encoding});
right after new DefaultRequest(...). encodings is a preference list: the first supported entry
wins and unsupported ones are skipped (["br", "gzip"] emits gzip); gzip is the whole supported set
(CompressionEncodingAlgorithm has only NONE and gzip). When nothing in the list is supported we
throw, where C2J warns and emits no call: silently dropping compression changes wire behavior with no
signal. A @streaming @requiresLength payload is rejected (Smithy forbids it; the compressed length
isn't known until the stream has been read).@httpChecksumRequired → ChecksumUtils.SetChecksumData(request); after body serialization (the
checksum covers the body), before the DisablePayloadSigning line. This is the legacy MD5-only trait.
C2J also treats the flexible aws.protocols#httpChecksum as MD5-required; we deliberately don't (that
trait is rejected as unsupported), so flexible-checksum operations stay off the MD5 path.@endpoint host prefix (request)An operation's @endpoint trait sets request.HostPrefix (the resolver prepends it to the endpoint host). Emitted last, after UseQueryString, before return.
request.HostPrefix = $"data.";@hostLabel member is captured into an anonymous hostPrefixLabels object (field = modeled member name, value = StringUtils.FromString(publicRequest.{Prop})), validated with HostPrefixUtils.IsValidLabelValue (throws Amazon{Service}Exception naming the label and the 1–63 alphanumeric/dash rule), then interpolated into the prefix ({name} → {hostPrefixLabels.name}).The body loop is while (context.ReadAtDepth(targetDepth, ref reader)) with a
context.TestExpression("{wireName}", targetDepth, ref reader) guard per member assigning
unmarshalledObject.{Prop} = {Unmarshaller}.Instance.Unmarshall(context, ref reader) then continue.
A response whose members are all headers (or empty) emits no reader loop at all.
new JsonListUnmarshaller<T, TUnmarshaller>(TUnmarshaller.Instance)new JsonDictionaryUnmarshaller<string, V, StringUnmarshaller, VU>(StringUnmarshaller.Instance, VU.Instance)
(key is always string/StringUnmarshaller).JsonDictionaryUnmarshaller<string, List<T>, StringUnmarshaller, JsonListUnmarshaller<T, TU>>(...). An
enum leaf (and an enum key) is string/StringUnmarshaller, never a ConstantClass generic argument; a
non-streaming blob leaf is MemoryStream/MemoryStreamUnmarshaller.@httpPayload (response)A @httpPayload output member IS the whole body (replaces the named-field loop; other members are header-bound), into unmarshalledObject:
using (var sr = new StreamReader(context.Stream)) { unmarshalledObject.Body = sr.ReadToEnd(); }if (reader.Reader.IsFinalBlock) return unmarshalledObject; + {Type}Unmarshaller.Instance.Unmarshall(context, ref reader).Amazon.Runtime.Documents.Internal.Transform.DocumentUnmarshaller.Instance. C2J models a document as a structure flagged document: true, so its output takes the structure-payload branch; we emit the same (bedrock-agentcore GetAgentCardResponse).MemoryStream) → Amazon.Util.AWSSDKUtils.CopyStream(context.Stream, ms) into a new MemoryStream; assigned only when ms.Length > 0, so an empty body leaves the property null (matches C2J).@streaming, Stream) → assigns the raw context.Stream unbuffered and the unmarshaller class overrides public override bool HasStreamingProperty => true (matches C2J, see Polly SynthesizeSpeech).@httpHeader members read from context.ResponseData after. An @httpPayload error member fails
loud: C2J never bound an error body to a payload, and emitting a never-populated property would be worse.
@httpResponseCode (response)An @httpResponseCode output member (an integer) is populated from the HTTP status code itself,
unmarshalledObject.{Prop} = (int)context.ResponseData.StatusCode;, not read from the body or a
header. Matches C2J. On an error the trait "is simply ignored" (Smithy spec) and the member falls through
to the body like any ordinary member.
An output member targeting a @streaming union/structure is an event stream. It IS the body: the
unmarshaller emits unmarshalledObject.{Prop} = new {UnionClass}(context.Stream); instead of a JSON
reader loop (C2J parity), and the class overrides HasStreamingProperty => true and
ShouldReadEntireResponse(...) => false so Core never buffers the body. The {UnionClass} itself, and
everything else event streams emit, is in sdk-conventions → Event Streams.
Each non-error union member targets an event structure that gets its own {Event}Unmarshaller,
invoked per event from the event-stream class's EventMapping. A member carries at most one of
@eventPayload/@eventHeader:
@eventPayload (≤1 per event) → the raw message payload, no JSON body loop for it. Blob →
unmarshalledObject.{Prop} = context.Stream as MemoryStream;; string →
using (var sr = new StreamReader(context.Stream)) { unmarshalledObject.{Prop} = sr.ReadToEnd(); }; structure →
unmarshalledObject.{Prop} = {Type}Unmarshaller.Instance.Unmarshall(context, ref reader);. The SEP
requires all other members to carry @eventHeader when a payload member exists.@eventHeader → read from the event-message header via context.ResponseData, guarded by
IsEventHeaderPresent("{ModeledName}") (the wire header key is the member name verbatim). The accessor on
GetEventStreamHeader("{ModeledName}") follows the target shape: string/enum → AsString(), boolean →
AsBool(), integer/intEnum → AsInt32(), long → AsInt64(), timestamp → AsTimestamp(), blob →
new MemoryStream(...AsByteBuf()). Any other target fails loud.An event can carry @eventHeader members without an @eventPayload member: a headers-only event (only
header reads, no reader loop) or an implicit-payload event (headers from headers, the rest from the JSON
body). This is not an all-or-nothing branch (C2J gets this wrong; the Smithy protocol tests
HeadersEvent/HeadersAndImplicitPayloadEvent cover it). The reader loop is skipped only for events
with an explicit payload or headers alone; an ordinary structure with no members still runs it, so the
{} tokens are consumed and the parent's remaining members are not misread.
A request event stream adds a publisher marshaller on top of the marker interface and per-event partials described in sdk-conventions. The union gets no plain model class or structure marshaller, but its event members still get marshallers.
{Union}PublisherMarshaller: NextEventAsync pulls the consumer's next event, dispatches on
evnt is {Event}, marshals it with {Event}Marshaller.Instance, and sets eventType to the union
member name verbatim (the wire :event-type, not the shape name). The wire :content-type follows the
event's @eventPayload member: a blob payload → application/octet-stream, a string payload →
text/plain, both read from context.Request.Content; a structure payload or an implicit body →
application/json from the JSON writer's stream. (C2J emits text/plain for a structure payload too,
but the event marshaller writes it as JSON, so JSON is kept here.) CBOR is not handled: restJson1 only.Func<Task<I{Union}Event>> {Member}Publisher property (keeps any modeled
[AWSProperty]/[Obsolete], no IsSet). The request marshaller wires it:
request.EventStreamPublisher = new {Union}PublisherMarshaller(publicRequest.{Member}Publisher) with
Content-Type: application/vnd.amazon.eventstream, replacing body serialization.Output and error members bound with @httpHeader are read from the HTTP response headers via
context.ResponseData, not the body reader; they're extracted after the reader loop, into
unmarshalledObject on both the response and the exception path.
Each member is guarded by context.ResponseData.IsHeaderPresent("x-foo") and assigned a <conversion>:
| Member target | <conversion> (with value = context.ResponseData.GetHeaderValue("x-foo")) |
|---|---|
| string / enum | value (direct; enum rides the string path via implicit ConstantClass conversion) |
| boolean | bool.Parse(value) (no culture: its two literals are culture-invariant) |
| integer / intEnum | int.Parse(value, CultureInfo.InvariantCulture) |
| long | long.Parse(value, CultureInfo.InvariantCulture) |
| float | float.Parse(value, CultureInfo.InvariantCulture) |
| double | double.Parse(value, CultureInfo.InvariantCulture) |
| timestamp, date-time / http-date | DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal) |
| timestamp, epoch-seconds | Amazon.Util.AWSSDKUtils.ConvertFromUnixEpochSeconds(int.Parse(value, CultureInfo.InvariantCulture)) |
list<string> / list<enum> | MultiValueHeaderParser.ToStringList(value) |
list<value-type> (int/long/bool/double/float) | MultiValueHeaderParser.ToValueTypeList<T>(value) (T = the non-nullable element type, e.g. int) |
list<timestamp> | MultiValueHeaderParser.ToDateTimeList(value, "FMT"), where FMT is the runtime's format name (RFC822/ISO8601/UnixTimestamp, header default RFC822), not the Smithy token |
A non-scalar list element fails loud, as does a @sparse value-type element.
MultiValueHeaderParser lives in Amazon.Runtime.Internal.Util; CultureInfo/DateTimeStyles come from
System.Globalization. Both namespaces are imported unconditionally.
@httpPrefixHeaders (response / error)A map<string, string> member bound with @httpPrefixHeaders collects every response header whose name
starts with the prefix into a local dictionary named headersFor{Property} (matching C2J), stripping the
prefix from each key. An empty prefix collects all headers. Assigned only when non-empty (matches C2J).
Same output on the response and exception unmarshallers.
| Smithy target | .NET | JSON Marshal | JSON Unmarshal |
|---|---|---|---|
| string / enum | string / ConstantClass | WriteStringValue | StringUnmarshaller |
| integer / intEnum | int? | WriteNumberValue | IntUnmarshaller |
| long | long? | WriteNumberValue | LongUnmarshaller |
| boolean | bool? | WriteBooleanValue | BoolUnmarshaller |
| float | float? | WriteNumberValue (NaN/∞ as strings, members only) | FloatUnmarshaller |
| double | double? | WriteNumberValue (NaN/∞ as strings, members only) | DoubleUnmarshaller |
| timestamp | DateTime? | Format-dependent (see below) | DateTimeUnmarshaller |
| blob | MemoryStream | StringUtils.WriteBase64StringValue(context.Writer, ...) | MemoryStreamUnmarshaller |
| document | Amazon.Runtime.Documents.Document | DocumentMarshaller.Instance.Write | DocumentUnmarshaller.Instance |
| list | List<T> | Array loop | JsonListUnmarshaller<ElementType, ElementUnmarshaller> |
| map | Dictionary<string,V> | Object loop | JsonDictionaryUnmarshaller<string, V, StringUnmarshaller, ValueUnmarshaller> |
| structure / union | Generated class | {Shape}Marshaller.Instance | {Shape}Unmarshaller.Instance |
A value type that is nullable in its position (a member, or a @sparse element; see type-mapping →
Nullability Rules) unwraps with .Value before the write and takes the Nullable*Unmarshaller; a
non-sparse element writes bare and takes the plain unmarshaller.
An explicit @timestampFormat (on the member or its target) always wins. When unset, the default is
per binding, not one per protocol.
@timestampFormat | Marshal (body) | Marshal (header/query/label) |
|---|---|---|
date-time | WriteStringValue(StringUtils.FromDateTimeToISO8601WithOptionalMs(value)) | StringUtils.FromDateTimeToISO8601WithOptionalMs(value) |
http-date | WriteStringValue(StringUtils.FromDateTimeToRFC822(value)) | StringUtils.FromDateTimeToRFC822(value) |
epoch-seconds | WriteNumberValue(Amazon.Util.AWSSDKUtils.ConvertToUnixEpochSecondsDecimal(value.Value)) | StringUtils.FromDateTimeToUnixTimestamp(value) |
restJson1 defaults when @timestampFormat is unset (matches C2J's output). They apply when marshalling
and when reading headers; body reads auto-detect the wire format.
| Binding | Default |
|---|---|
| Body / structure member | epoch-seconds (restJson1's document-timestamp default per the Smithy spec) |
@httpHeader | http-date |
@httpQuery, @httpLabel | date-time |
String forms pass the nullable DateTime? straight to the StringUtils overload; the epoch form
unwraps with .Value. epoch-seconds in a body is a JSON number that may carry a fraction, so it
goes through ConvertToUnixEpochSecondsDecimal (millisecond precision, decimal for identical digits on
every TFM), not the whole-second StringUtils.FromDateTimeToUnixTimestamp. Header/query/label positions
are whole seconds, matching C2J.
A dataTypeSwap member (see type-mapping → Data Type Swaps) carries MarshallerOverride /
UnmarshallerOverride on its TypeDescriptor. Only the shared scalar helpers honor them, so a new protocol
writer gets swaps by reusing those helpers; an omitted override keeps the modeled conversion:
JsonScalarMarshaller.WriteScalar): context.Writer.WriteStringValue({Marshaller}(x)), or
WriteNumberValue when the marshaller is Amazon.Util.AWSSDKUtils.ConvertToUnixEpochMilliseconds (C2J
keys on the name, assuming every other returns a string). .Value is unwrapped first when the modeled type
is a value type and the swapped type is nullable.JsonRequestMarshallerWriter.StringConversion, and the header branch that assigns strings
directly): {Marshaller}(publicRequest.X), passed the property as-is (no .Value). A required swapped
query member is checked with == null, since its type may not be a string.JsonBodyMemberUnmarshaller.ScalarUnmarshaller): var unmarshaller = {Unmarshaller}.Instance;,
emitted verbatim.Unmarshaller fails loud (not supported yet).In {Operation}ResponseUnmarshaller.UnmarshallException, each error is matched with
errorResponse.Code != null && errorResponse.Code.Equals("{smithyShapeName}") (the Smithy shape name,
not the .NET exception name) and dispatched to
{Exception}Unmarshaller.Instance.Unmarshall(contextCopy, errorResponse, ref readerCopy).
Fallback: new Amazon{Service}Exception(errorResponse.Message, ...).
Unmarshall(JsonUnmarshallerContext context, ErrorResponse errorResponse, ref StreamingUtf8JsonReader reader)
constructs the exception from the six errorResponse fields (message, inner exception, type, code,
request id, status code). When the error has body-bound members beyond message, a single
if (context.Stream.Length > 0) wraps the first-token read and the body loop, so an empty error body
skips both. @httpHeader members are then read from context.ResponseData (see Response Header
Unmarshalling).
Same JSON body (un)marshalling as restJson1. Every operation is POST / with
X-Amz-Target: {ServiceShapeName}.{OperationName} (the service's shape name, not its sdkId) and
Content-Type: application/x-amz-json-1.0|1.1; an operation with no input members sends {}. HTTP binding
traits and @jsonName are ignored when a model carries them. Spec:
protocol behaviors.
1.0 and 1.1 differ only in the Content-Type version and the __type form of errors
(differences);
Core handles the __type difference transparently, so the generator only varies the version string.
Every one of those decisions is gated on GenerationContext.UsesHttpBindings; AwsJsonCodegenTests
pins the emitted code and the JSONRPC10/JsonProtocol protocol tests verify it end to end.
Spec: Smithy RPC v2 CBOR. Every
operation is POST service/{ServiceShapeName}/operation/{OperationName} with smithy-protocol: rpc-v2-cbor,
Accept: application/cbor, and a CBOR map body with Content-Type: application/cbor unless the input is
Unit. HTTP binding traits, @jsonName and @timestampFormat are ignored: keys are member names and
timestamps are tag 1. The output is wire-compatible with C2J's CBOR templates and formatted like the JSON
writers' output; the Cbor* writers mirror the JSON ones and are pinned by RpcV2CborCodegenTests.
A @sparse element writes CBOR null per the spec; C2J writes {} for a null structure element in a sparse
list, but no CBOR service model uses @sparse. Documents and streaming blobs are rejected by
UnsupportedTraitValidator; event streams are not supported.
Service trait for awsJson1.0 and rpcv2Cbor services that moved off awsQuery (spec): the request announces query mode and errors still dispatch on the shape name, not the rewritten query error code.
restXml, awsQuery and ec2Query: the target output is defined by the C2J templates
(generator/ServiceClientGeneratorLib/Generators/Marshallers/*.tt). awsQuery/ec2Query route via an
Action={Operation} param with URL-encoded bodies; restXml keeps the HTTP binding traits with an XML
body (@xmlName/@xmlFlattened/@xmlAttribute/@xmlNamespace) and, per the Smithy spec, defaults
body timestamps to date-time. The XML-response family uses XmlResponseUnmarshaller. Wire names:
restXml @xmlName, awsQuery the member name verbatim, ec2Query @ec2QueryName or the member name with
its first letter upper-cased.
When implementing one, replace this note with the real patterns and pin the emitted code in codegen tests.
© aws, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in generator/SmithyDotNet/skills/marshalling of aws/aws-sdk-net.
Open the folder on GitHubat commit f36df89
Marshalling next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Marshalling this skillaws/aws-sdk-net | 145 | — | ~8.2k | Automated safety check: Pass | Apache-2.0 | |
| Generatealirezarezvani/claude-skills | 28k | 1 repos | ~1.1k | Automated safety check: Pass | MIT | |
| Responsive Unitsthedaviddias/Front-End-Checklist | 74k | — | ~472 | Automated safety check: Pass | MIT | |
| Fal Generatenexu-io/open-design | 100k | — | ~306 | Automated safety check: Pass | Apache-2.0 | |
| Runbook Generatoralirezarezvani/claude-skills | 28k | — | ~535 | Automated safety check: Pass | MIT | |
| Video Generationbytedance/deer-flow | 83k | 3 repos | ~1.4k | Automated safety check: Pass | MIT |
alirezarezvani/claude-skills
Generate Playwright tests. An agent skill from alirezarezvani/claude-skills.
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing stylesheets, component styles, and responsive behavior related to Use relative units for responsive layouts.
nexu-io/open-design
Generate images and videos using fal.ai AI models. An agent skill from nexu-io/open-design.
alirezarezvani/claude-skills
Generate operational runbooks from a service name — deployment, incident response, maintenance, and rollback workflows.
bytedance/deer-flow
Generates short videos from a structured JSON prompt, optionally guided by a reference image used as the first or last frame.
onyx-dot-app/onyx
Generate or edit raster images (photos, illustrations, textures, sprites, mockups, logos, infographics) using the workspace's configured image-generation provider via onyx-cli image.
aws/aws-sdk-net
A skill your agent uses when validating that a C2J-to-Smithy migrated AWS SDK service builds, packages, and stays API-compatible with the shipping SDK, or before reporting such a migration as done…
aws/aws-sdk-net
A skill your agent uses when working on the AWS SDK for .NET source code itself, including Core runtime changes, service client implementations, generator or model changes, repo-specific build and…
aws/aws-sdk-net
The Smithy JSON AST facts and model invariants the SmithyDotNet generator relies on.
aws/aws-sdk-net
Smithy shape to .NET type mapping, nullability, collection element rules, and error-shape naming/member rules.
aws/aws-sdk-net
The public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs.
What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol. Marshalling is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization. What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.
Marshalling fits situations like: reviewing any marshaller/unmarshaller writer.
Run `npx skills add aws/aws-sdk-net --skill marshalling -a claude-code`. Or copy the skill folder (generator/SmithyDotNet/skills/marshalling in aws/aws-sdk-net) into .claude/skills/marshalling in your project. Claude Code loads it when a task matches its description.
Run `npx skills add aws/aws-sdk-net --skill marshalling -a codex`. Or copy the skill folder (generator/SmithyDotNet/skills/marshalling in aws/aws-sdk-net) into .agents/skills/marshalling in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add aws/aws-sdk-net --skill marshalling -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/marshalling, .gemini/skills/marshalling, .github/skills/marshalling and .opencode/skills/marshalling in your project.
SKILL.md names no scripts, command-line tools or credentials: Marshalling is instructions for the agent only.
SKILL.md names 1 domain. As links in the text: smithy.io. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Marshalling is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.2k tokens (SKILL.md is roughly 33k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Marshalling: Generate (alirezarezvani/claude-skills, 28k stars), Responsive Units (thedaviddias/Front-End-Checklist, 74k stars), Fal Generate (nexu-io/open-design, 100k stars) and Runbook Generator (alirezarezvani/claude-skills, 28k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
aws (a GitHub organization, an official publisher) maintains it in aws/aws-sdk-net, which has 145 GitHub stars. The repository holds 6 skills in this directory. The repository was last updated on October 6, 2026.
Source: aws/aws-sdk-net on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.