ConcordIO.Tool
0.8.7
dotnet tool install --global ConcordIO.Tool --version 0.8.7
dotnet new tool-manifest
dotnet tool install --local ConcordIO.Tool --version 0.8.7
#tool dotnet:?package=ConcordIO.Tool&version=0.8.7
nuke :add-package ConcordIO.Tool --version 0.8.7
ConcordIO.Tool
A .NET CLI tool for managing API contract packages. Generate, publish, and govern OpenAPI, Protocol Buffers, and AsyncAPI contracts as NuGet packages — with automatic client generation and breaking-change detection.
Installation
dotnet tool install --global ConcordIO.Tool
Or as a local tool:
dotnet new tool-manifest
dotnet tool install ConcordIO.Tool
Once installed, the tool is available as concordio.
Commands
concordio generate
Generate contract and client NuGet packages from specification files.
concordio generate \
--spec <path[:kind]> \
--package-id <id> \
--version <semver>
Required options:
| Option | Description |
|---|---|
--spec |
Specification file(s) with optional kind suffix (format: path[:kind]). Kind defaults to openapi. Valid kinds: openapi, proto, asyncapi. Can be specified multiple times. |
--package-id |
Package ID for the generated NuGet package. |
--version |
SemVer version for the package. |
Optional options:
| Option | Default | Description |
|---|---|---|
--authors |
ConcordIO |
Package authors. |
--description |
Auto-generated | Package description. |
--output |
. |
Output directory for generated files. |
--client |
true |
Also generate a client package. |
--client-package-id |
{PackageId}.Client |
Client package ID. |
--client-class-name |
Derived from PackageId | Client class name (OpenAPI only). |
--nswag-options |
— | Additional NSwag options as key=value (OpenAPI only, repeatable). Values may contain '=' characters. |
--client-options |
— | Additional client options as key=value (AsyncAPI only, repeatable). Values may contain '=' characters. |
--package-properties |
— | Additional NuSpec metadata as key=value (repeatable). Values may contain '=' characters. |
For OpenAPI client generation, ConcordIO applies these NSwag defaults unless you override them via --nswag-options:
NSwagJsonLibrary=SystemTextJsonNSwagJsonPolymorphicSerializationStyle=SystemTextJsonNSwagGenerateNullableReferenceTypes=falseNSwagGenerateExceptionClasses=true
Customizing Generated Clients
Client packages generated from OpenAPI contracts wire specs to NSwag at build time. You can customize the generated client by overriding OpenApiReference metadata in your consuming project.
Standard Metadata (No Prefix)
These properties control MSBuild orchestration and don't require the NSwag prefix:
Namespace— Generated code namespaceClassName— Generated client class nameOutputPath— Location of generated fileCodeGenerator— Code generator to use (e.g.,NSwagCSharp)
NSwag Generator Options (Prefix Required)
NSwag-specific code generation settings must be prefixed with NSwag:
NSwagJsonLibrary— JSON serializer (SystemTextJsonorNewtonsoftJson)NSwagGenerateNullableReferenceTypes— Enable nullable reference types (trueorfalse)NSwagGenerateExceptionClasses— Generate typed exception classes (trueorfalse)NSwagInjectHttpClient— Use dependency injection for HttpClient (trueorfalse)NSwagGenerateClientInterfaces— Generate client interfaces (trueorfalse)
Example: Override Namespace and Disable Nullable Reference Types
<Target Name="CustomizeOpenApiReference" AfterTargets="ConcordIOAddOpenApiReferenceForNSwag">
<ItemGroup>
<OpenApiReference Update="@(OpenApiReference)">
<Namespace>MyApp.Generated.Clients</Namespace>
<NSwagGenerateNullableReferenceTypes>false</NSwagGenerateNullableReferenceTypes>
</OpenApiReference>
</ItemGroup>
</Target>
Why the difference? The unprefixed properties (Namespace, ClassName, etc.) are defined by Microsoft.Extensions.ApiDescription.Client — the MSBuild infrastructure that NSwag builds upon. The NSwag-prefixed properties are passed directly to NSwag's code generator.
Examples:
# OpenAPI contract + client package
concordio generate \
--spec petstore.yaml \
--package-id Contoso.PetStore.Api \
--version 1.0.0
# Multiple specs with explicit kinds
concordio generate \
--spec api.yaml:openapi \
--spec events.yaml:asyncapi \
--package-id Contoso.PetStore.Api \
--version 2.0.0
# Proto contract, no client package
concordio generate \
--spec service.proto:proto \
--package-id Contoso.Grpc.Api \
--version 1.0.0 \
--client false
# With custom NSwag options
concordio generate \
--spec api.json \
--package-id Contoso.Api \
--version 1.0.0 \
--nswag-options GenerateClientInterfaces=true \
--nswag-options InjectHttpClient=true
Output:
The generate command produces two NuGet package scaffolds:
- Contract package (
{PackageId}) — contains the specification files and.targetsfiles in bothbuild/andbuildTransitive/soConcordIOContractitems flow to direct and transitive consumers (for example, when only a client package is referenced). - Client package (
{PackageId}.Client) — contains a.targetsfile that wires contract specs to code generators (NSwag for OpenAPI, ConcordIO.AsyncApi.Client for AsyncAPI) at build time. For AsyncAPI, the generated client package depends onConcordIO.AsyncApi.Clientwith a minimum version equal to the currentConcordIO.Toolversion (NuGet range[toolVersion,)).
concordio breaking
Compare a local specification against the latest published version in a NuGet package and report breaking changes. Uses oasdiff under the hood.
concordio breaking \
--spec <path> \
--package-id <id>
Required options:
| Option | Description |
|---|---|
--spec |
Path to the local specification file. |
--package-id |
Package ID of the published contract NuGet package to compare against. |
Optional options:
| Option | Default | Description |
|---|---|---|
--version |
Latest | Version of the NuGet package to compare against. |
--prerelease |
false |
Include prerelease versions when resolving the package. |
--kind |
openapi |
Contract kind: openapi or proto. |
--working-directory |
Temp directory | Working directory for downloading the package. |
--cli-options |
— | Additional oasdiff CLI options as key=value (repeatable). |
Examples:
# Check for breaking changes against latest published version
concordio breaking \
--spec petstore.yaml \
--package-id Contoso.PetStore.Api
# Compare against a specific version
concordio breaking \
--spec petstore.yaml \
--package-id Contoso.PetStore.Api \
--version 1.2.0
# Include prerelease versions
concordio breaking \
--spec petstore.yaml \
--package-id Contoso.PetStore.Api \
--prerelease
Exit codes:
| Code | Meaning |
|---|---|
0 |
No breaking changes detected. |
1 |
Breaking changes detected. |
> 1 |
Tool error (e.g., invalid arguments, missing files). |
concordio get-spec
Retrieve a specification file from a published contract NuGet package.
concordio get-spec \
--package-id <id>
Required options:
| Option | Description |
|---|---|
--package-id |
Package ID of the NuGet package to retrieve the spec from. |
Optional options:
| Option | Default | Description |
|---|---|---|
--version |
Latest | Version of the NuGet package. |
--prerelease |
false |
Include prerelease versions. |
--kind |
openapi |
Contract kind: openapi, proto, or asyncapi. |
--output-path |
Current directory | Output path for the retrieved file. |
--overwrite-output |
true |
Overwrite the output file if it exists. |
--working-directory |
Temp directory | Working directory for downloading the package. |
Examples:
# Get the spec from the latest version
concordio get-spec --package-id Contoso.PetStore.Api
# Get a specific version and save to a custom path
concordio get-spec \
--package-id Contoso.PetStore.Api \
--version 1.2.0 \
--output-path ./specs/petstore.yaml
# Get a Proto specification
concordio get-spec \
--package-id Contoso.Grpc.Api \
--kind proto
# Get an AsyncAPI specification
concordio get-spec \
--package-id Contoso.Events.Api \
--kind asyncapi
concordio pack
Generate and pack contract NuGet packages (.nupkg) from specification files. Combines generate functionality with nuget pack to produce ready-to-publish packages.
concordio pack \
--spec <path[:kind]> \
--package-id <id> \
--version <semver>
Required options:
| Option | Description |
|---|---|
--spec |
Specification file(s) with optional kind suffix (format: path[:kind]). Kind defaults to openapi. Valid kinds: openapi, proto, asyncapi. Can be specified multiple times. |
--package-id |
Package ID for the generated NuGet package. |
--version |
SemVer version for the package. |
Optional options:
| Option | Default | Description |
|---|---|---|
--authors |
ConcordIO |
Package authors. |
--description |
Auto-generated | Package description. |
--output |
. |
Output directory for generated files and .nupkg packages. |
--client |
true |
Also generate and pack a client package. |
--client-package-id |
{PackageId}.Client |
Client package ID. |
--client-class-name |
Derived from PackageId | Client class name (OpenAPI only). |
--nswag-options |
— | Additional NSwag options as key=value (OpenAPI only, repeatable). |
--client-options |
— | Additional client options as key=value (AsyncAPI only, repeatable). |
--package-properties |
— | Additional NuSpec metadata as key=value (repeatable). |
For OpenAPI client generation, ConcordIO applies these NSwag defaults unless you override them via --nswag-options:
NSwagJsonLibrary=SystemTextJsonNSwagJsonPolymorphicSerializationStyle=SystemTextJsonNSwagGenerateNullableReferenceTypes=falseNSwagGenerateExceptionClasses=true
Examples:
# Pack a single OpenAPI spec
concordio pack \
--spec petstore.yaml \
--package-id Contoso.PetStore.Api \
--version 1.0.0
# Pack with client package to a specific output directory
concordio pack \
--spec api.yaml \
--package-id Contoso.Api \
--version 2.0.0 \
--output ./packages
# Pack multiple specs of different kinds
concordio pack \
--spec api.yaml:openapi \
--spec events.yaml:asyncapi \
--package-id Contoso.Service \
--version 1.0.0
# Pack contract only (no client)
concordio pack \
--spec service.proto:proto \
--package-id Contoso.Grpc.Api \
--version 1.0.0 \
--client false
Output:
The pack command produces .nupkg files:
- Contract package (
{PackageId}.{Version}.nupkg) — ready-to-publish NuGet package containing spec files and MSBuild targets. - Client package (
{PackageId}.Client.{Version}.nupkg) — if--clientis enabled, a development dependency package for code generation.
Exit codes:
| Code | Meaning |
|---|---|
0 |
Packages created successfully. |
1 |
Error (e.g., invalid arguments, missing spec files, nuget pack failure). |
How It Works
Contract Packages
The generate command creates a NuGet package that:
- Bundles the specification files as content.
- Includes a
.targetsfile that exposes specs asConcordIOContract(OpenAPI/Proto) orConcordIOAsyncApiContract(AsyncAPI) MSBuild items. - Consuming projects automatically see the contract files after installing the package — no file copying needed.
Client Packages
The client package is a development dependency that:
- Declares a dependency on the corresponding contract package.
- Wires the contract specs to code generators at build time:
- OpenAPI → NSwag (generates C# client classes)
- AsyncAPI → ConcordIO.AsyncApi.Client (generates messaging client code)
- For AsyncAPI, updates
ConcordIOAsyncApiContractitem metadata in-place (MSBuildUpdate) so consuming builds do not duplicate the same contract path. - Consuming projects just install the client package and get strongly-typed clients generated automatically on build.
Breaking-Change Detection
The breaking command:
- Downloads the published contract NuGet package.
- Extracts the specification file.
- Runs oasdiff to compare the local spec against the published one.
- Reports breaking changes with a non-zero exit code, suitable for CI/CD gates.
Known Limitation: OpenAPI Client Generation in Multi-TFM Consumers
OpenAPI client generation currently relies on the NSwag + Microsoft.Extensions.ApiDescription.Client MSBuild orchestration path. In some projects that use <TargetFrameworks> (multi-targeting), NSwag can skip generation due to target-ordering behavior in the outer/inner build dispatch.
- Affected scenario: Consumer projects using multi-targeting with generated OpenAPI client packages.
- Observed failure: Missing generated client types at compile time (for example
CS0246). - Current recommendation: Prefer single-target projects (
<TargetFramework>) when consuming generated OpenAPI client packages. - Tracking issue: #61.
For automated validation, the E2E suite now uses single-TFM projects with separate coverage for net8.0, net9.0, and net10.0.
Prerequisites
- .NET 10 SDK or later
- nuget CLI on
PATH(required forbreakingandget-speccommands)
Supported Platforms
The tool bundles oasdiff binaries for:
- Windows x64 / ARM64
- Linux x64 / ARM64
- macOS (universal)
License
Licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.8.7 | 144 | 2/21/2026 |