From b85ab903c04a4116dc07d0e59789fbe3682c5112 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 19:49:39 +0000
Subject: [PATCH 1/7] Initial plan
From 86f98698ee9c0b2d24d43145a1e9276c4c54fded Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 19:53:29 +0000
Subject: [PATCH 2/7] Add plugins documentation for C# emitter
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
---
packages/http-client-csharp/docs/emitter.md | 2 +-
packages/http-client-csharp/docs/index.mdx | 4 +
packages/http-client-csharp/docs/plugins.md | 103 ++++++++++++++++++++
3 files changed, 108 insertions(+), 1 deletion(-)
create mode 100644 packages/http-client-csharp/docs/plugins.md
diff --git a/packages/http-client-csharp/docs/emitter.md b/packages/http-client-csharp/docs/emitter.md
index f20d6edee33..1639b5c4a60 100644
--- a/packages/http-client-csharp/docs/emitter.md
+++ b/packages/http-client-csharp/docs/emitter.md
@@ -112,7 +112,7 @@ Allows emitter authors to specify the path to a custom emitter package, allowing
**Type:** `array`
-Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends GeneratorPlugin.
+Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends `GeneratorPlugin`. See [Generator plugins](./plugins.md) for a walkthrough on authoring and using plugins.
### `license`
diff --git a/packages/http-client-csharp/docs/index.mdx b/packages/http-client-csharp/docs/index.mdx
index ca373e97796..d2cbde176f7 100644
--- a/packages/http-client-csharp/docs/index.mdx
+++ b/packages/http-client-csharp/docs/index.mdx
@@ -32,6 +32,10 @@ npm install --save-peer @typespec/http-client-csharp
[See documentation](./emitter.md)
+## Generator plugins
+
+[See documentation](./plugins.md)
+
## TypeSpec.HttpClient
## TypeSpec.HttpClient.CSharp
diff --git a/packages/http-client-csharp/docs/plugins.md b/packages/http-client-csharp/docs/plugins.md
new file mode 100644
index 00000000000..5f21e696944
--- /dev/null
+++ b/packages/http-client-csharp/docs/plugins.md
@@ -0,0 +1,103 @@
+---
+title: "Generator plugins"
+---
+
+## Generator plugins
+
+Plugins are a lightweight way to customize the C# generator without authoring a full custom generator. A plugin can register additional visitors, rewriters, metadata references, or shared source directories that participate in the normal generation pipeline. This is useful when you want to tweak the generated output (for example, rename types, add attributes, or post-process models) while still using the built-in `ScmCodeModelGenerator`.
+
+The generator discovers plugins in two ways:
+
+1. **Automatically**, from the `dist` folder of any package listed in your project's `node_modules`.
+2. **Explicitly**, from the paths supplied via the [`plugins`](./emitter.md#plugins) emitter option.
+
+This document focuses on the `plugins` option, which lets emitter authors point the generator at one or more plugin assemblies (or directories/projects containing them).
+
+## Authoring a plugin
+
+A plugin is a C# class that extends `GeneratorPlugin` and overrides `Apply`. The base class is exported via [MEF](https://learn.microsoft.com/dotnet/framework/mef/) using `[InheritedExport]`, so any subclass is discovered automatically once its assembly is loaded — you do not need to add an `[Export]` attribute yourself.
+
+```csharp
+using Microsoft.TypeSpec.Generator;
+
+public class MyPlugin : GeneratorPlugin
+{
+ public override void Apply(CodeModelGenerator generator)
+ {
+ // Register a custom visitor that transforms the output library.
+ generator.AddVisitor(new MyLibraryVisitor());
+ }
+}
+```
+
+The `CodeModelGenerator` passed to `Apply` exposes the extension points a plugin can use, including:
+
+- `AddVisitor(LibraryVisitor visitor)` — add a visitor that traverses and modifies the output library.
+- `AddRewriter(LibraryRewriter rewriter)` — add a rewriter to transform generated members.
+- `AddMetadataReference(MetadataReference reference)` — make additional assemblies available to the generated code.
+- `AddSharedSourceDirectory(string sharedSourceDirectory)` — include a directory of shared source files.
+
+A visitor lets you hook into specific parts of the output. For example, to transform every model:
+
+```csharp
+using Microsoft.TypeSpec.Generator;
+using Microsoft.TypeSpec.Generator.Providers;
+
+public class MyLibraryVisitor : LibraryVisitor
+{
+ protected override TypeProvider? VisitType(TypeProvider type)
+ {
+ // Inspect or modify the generated type here.
+ return base.VisitType(type);
+ }
+}
+```
+
+### Plugin project file
+
+Build your plugin as a class library that references the generator package. When run from a TypeSpec project, the generator assemblies are restored into the emitter's `dist/generator` directory, so you can reference them directly. A minimal `.csproj` looks like this:
+
+```xml
+
+
+
+ net8.0
+ enable
+
+
+
+
+ path/to/@typespec/http-client-csharp/dist/generator/Microsoft.TypeSpec.Generator.dll
+
+
+ path/to/@typespec/http-client-csharp/dist/generator/Microsoft.TypeSpec.Generator.ClientModel.dll
+
+
+
+
+```
+
+## Using the `plugins` option
+
+Once you have a plugin, point the emitter at it using the `plugins` option in `tspconfig.yaml`. Each entry is a path, relative to the emitter output directory, to either:
+
+- a **directory** containing a pre-built plugin assembly (`*.dll`), or
+- a **directory** (or project) containing a `.csproj` — the generator builds it (`dotnet build -c Release`) before loading the resulting assembly.
+
+```yaml
+emit:
+ - "@typespec/http-client-csharp"
+options:
+ "@typespec/http-client-csharp":
+ plugins:
+ - "../plugins/MyPlugin"
+```
+
+Notes:
+
+- Paths are resolved relative to the emitter output directory.
+- When a directory contains a `.csproj`, it is built automatically; otherwise the directory is scanned for pre-built `*.dll` files.
+- A plugin path that does not point to an existing directory causes the generator to fail.
+- Plugins loaded via the `plugins` option are applied in addition to any plugins discovered through `node_modules`.
+
+After the assemblies are loaded, every discovered `GeneratorPlugin` has its `Apply` method invoked on the selected generator before generation runs, so all of your registered visitors, rewriters, and references take effect.
From 201b7928ae48ed945080b9d80090f1aed6a24cb8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 20:13:45 +0000
Subject: [PATCH 3/7] Apply plugins option inline examples from #10908
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
---
...ent-plugins-option-examples-2026-6-5-20-0-0.md | 7 +++++++
packages/http-client-csharp/docs/emitter.md | 13 ++++++++++++-
packages/http-client-csharp/docs/plugins.md | 15 +++++++++------
.../http-client-csharp/emitter/src/options.ts | 12 +++++++++++-
packages/http-client-csharp/readme.md | 11 ++++++++++-
.../http-client-csharp/reference/emitter.md | 11 ++++++++++-
6 files changed, 59 insertions(+), 10 deletions(-)
create mode 100644 .chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
diff --git a/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md b/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
new file mode 100644
index 00000000000..49cae4dc5ad
--- /dev/null
+++ b/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
@@ -0,0 +1,7 @@
+---
+changeKind: internal
+packages:
+ - "@typespec/http-client-csharp"
+---
+
+Document the `plugins` emitter option path-resolution behavior with examples.
diff --git a/packages/http-client-csharp/docs/emitter.md b/packages/http-client-csharp/docs/emitter.md
index 1639b5c4a60..1f968bcce9f 100644
--- a/packages/http-client-csharp/docs/emitter.md
+++ b/packages/http-client-csharp/docs/emitter.md
@@ -112,7 +112,18 @@ Allows emitter authors to specify the path to a custom emitter package, allowing
**Type:** `array`
-Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends `GeneratorPlugin`. See [Generator plugins](./plugins.md) for a walkthrough on authoring and using plugins.
+Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends `GeneratorPlugin`. Paths may be absolute or relative to the resolved `emitter-output-dir`. For example, to load plugins that live in a `codegen` folder under the output directory:
+
+```yaml
+options:
+ "@typespec/http-client-csharp":
+ plugins:
+ - "codegen/MyPlugin.dll" # file relative to emitter-output-dir
+ - "codegen" # directory containing plugin assemblies
+ - "/abs/path/to/MyPlugin.dll" # absolute path used as-is
+```
+
+See [Generator plugins](./plugins.md) for a walkthrough on authoring and using plugins.
### `license`
diff --git a/packages/http-client-csharp/docs/plugins.md b/packages/http-client-csharp/docs/plugins.md
index 5f21e696944..d22727b2e2b 100644
--- a/packages/http-client-csharp/docs/plugins.md
+++ b/packages/http-client-csharp/docs/plugins.md
@@ -79,10 +79,12 @@ Build your plugin as a class library that references the generator package. When
## Using the `plugins` option
-Once you have a plugin, point the emitter at it using the `plugins` option in `tspconfig.yaml`. Each entry is a path, relative to the emitter output directory, to either:
+Once you have a plugin, point the emitter at it using the `plugins` option in `tspconfig.yaml`. Each entry is a path that may be **absolute** or **relative to the resolved `emitter-output-dir`**. A path can point to either:
-- a **directory** containing a pre-built plugin assembly (`*.dll`), or
-- a **directory** (or project) containing a `.csproj` — the generator builds it (`dotnet build -c Release`) before loading the resulting assembly.
+- a **file** — a pre-built plugin assembly (`*.dll`), or
+- a **directory** containing pre-built plugin assemblies (`*.dll`) or a `.csproj` — when a `.csproj` is present the generator builds it (`dotnet build -c Release`) before loading the resulting assembly.
+
+For example, to load plugins that live in a `codegen` folder under the output directory:
```yaml
emit:
@@ -90,14 +92,15 @@ emit:
options:
"@typespec/http-client-csharp":
plugins:
- - "../plugins/MyPlugin"
+ - "codegen/MyPlugin.dll" # file relative to emitter-output-dir
+ - "codegen" # directory containing plugin assemblies
+ - "/abs/path/to/MyPlugin.dll" # absolute path used as-is
```
Notes:
-- Paths are resolved relative to the emitter output directory.
+- Absolute paths are used as-is; relative paths are anchored to the resolved `emitter-output-dir`.
- When a directory contains a `.csproj`, it is built automatically; otherwise the directory is scanned for pre-built `*.dll` files.
-- A plugin path that does not point to an existing directory causes the generator to fail.
- Plugins loaded via the `plugins` option are applied in addition to any plugins discovered through `node_modules`.
After the assemblies are loaded, every discovered `GeneratorPlugin` has its `Apply` method invoked on the selected generator before generation runs, so all of your registered visitors, rewriters, and references take effect.
diff --git a/packages/http-client-csharp/emitter/src/options.ts b/packages/http-client-csharp/emitter/src/options.ts
index b980b4121f8..9b9529c9ce2 100644
--- a/packages/http-client-csharp/emitter/src/options.ts
+++ b/packages/http-client-csharp/emitter/src/options.ts
@@ -120,7 +120,17 @@ export const CSharpEmitterOptionsSchema: JSONSchemaType =
nullable: true,
description:
"Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. " +
- "Each plugin must contain a class that extends GeneratorPlugin.",
+ "Each plugin must contain a class that extends `GeneratorPlugin`. " +
+ "Paths may be absolute or relative to the resolved `emitter-output-dir`. " +
+ "For example, to load plugins that live in a `codegen` folder under the output directory:\n\n" +
+ "```yaml\n" +
+ "options:\n" +
+ ' "@typespec/http-client-csharp":\n' +
+ " plugins:\n" +
+ ' - "codegen/MyPlugin.dll" # file relative to emitter-output-dir\n' +
+ ' - "codegen" # directory containing plugin assemblies\n' +
+ ' - "/abs/path/to/MyPlugin.dll" # absolute path used as-is\n' +
+ "```",
},
license: {
type: "object",
diff --git a/packages/http-client-csharp/readme.md b/packages/http-client-csharp/readme.md
index 2acc3d7cbaf..beb26cda25e 100644
--- a/packages/http-client-csharp/readme.md
+++ b/packages/http-client-csharp/readme.md
@@ -129,7 +129,16 @@ Allows emitter authors to specify the path to a custom emitter package, allowing
**Type:** `array`
-Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends GeneratorPlugin.
+Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends `GeneratorPlugin`. Paths may be absolute or relative to the resolved `emitter-output-dir`. For example, to load plugins that live in a `codegen` folder under the output directory:
+
+```yaml
+options:
+ "@typespec/http-client-csharp":
+ plugins:
+ - "codegen/MyPlugin.dll" # file relative to emitter-output-dir
+ - "codegen" # directory containing plugin assemblies
+ - "/abs/path/to/MyPlugin.dll" # absolute path used as-is
+```
### `license`
diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md
index f20d6edee33..10b52769c33 100644
--- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md
+++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md
@@ -112,7 +112,16 @@ Allows emitter authors to specify the path to a custom emitter package, allowing
**Type:** `array`
-Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends GeneratorPlugin.
+Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. Each plugin must contain a class that extends `GeneratorPlugin`. Paths may be absolute or relative to the resolved `emitter-output-dir`. For example, to load plugins that live in a `codegen` folder under the output directory:
+
+```yaml
+options:
+ "@typespec/http-client-csharp":
+ plugins:
+ - "codegen/MyPlugin.dll" # file relative to emitter-output-dir
+ - "codegen" # directory containing plugin assemblies
+ - "/abs/path/to/MyPlugin.dll" # absolute path used as-is
+```
### `license`
From cb9e9355495121cb5393e215d277696b1d19790b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 20:17:56 +0000
Subject: [PATCH 4/7] Reference generator via NuGet PackageReference in plugin
csproj example
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
---
packages/http-client-csharp/docs/plugins.md | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/packages/http-client-csharp/docs/plugins.md b/packages/http-client-csharp/docs/plugins.md
index d22727b2e2b..86065efd43a 100644
--- a/packages/http-client-csharp/docs/plugins.md
+++ b/packages/http-client-csharp/docs/plugins.md
@@ -55,7 +55,7 @@ public class MyLibraryVisitor : LibraryVisitor
### Plugin project file
-Build your plugin as a class library that references the generator package. When run from a TypeSpec project, the generator assemblies are restored into the emitter's `dist/generator` directory, so you can reference them directly. A minimal `.csproj` looks like this:
+Build your plugin as a class library that references the generator's published NuGet package. Referencing `Microsoft.TypeSpec.Generator.ClientModel` transitively brings in `Microsoft.TypeSpec.Generator`, which contains the `GeneratorPlugin` base class. Use the package version that matches the version of `@typespec/http-client-csharp` you are generating with. A minimal `.csproj` looks like this:
```xml
@@ -66,12 +66,7 @@ Build your plugin as a class library that references the generator package. When
-
- path/to/@typespec/http-client-csharp/dist/generator/Microsoft.TypeSpec.Generator.dll
-
-
- path/to/@typespec/http-client-csharp/dist/generator/Microsoft.TypeSpec.Generator.ClientModel.dll
-
+
From 40ecc462c9d6688be44384431f7c7ed4215925ca Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 20:18:31 +0000
Subject: [PATCH 5/7] Use placeholder version in plugin csproj example
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
---
packages/http-client-csharp/docs/plugins.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/http-client-csharp/docs/plugins.md b/packages/http-client-csharp/docs/plugins.md
index 86065efd43a..a27530e3206 100644
--- a/packages/http-client-csharp/docs/plugins.md
+++ b/packages/http-client-csharp/docs/plugins.md
@@ -66,7 +66,7 @@ Build your plugin as a class library that references the generator's published N
-
+
From 9121f26f4e7ca9209de20c9489abb82b6e10d24b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Jun 2026 20:21:43 +0000
Subject: [PATCH 6/7] Remove plugins option changeset file per review feedback
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
---
...lot-document-plugins-option-examples-2026-6-5-20-0-0.md | 7 -------
1 file changed, 7 deletions(-)
delete mode 100644 .chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
diff --git a/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md b/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
deleted file mode 100644
index 49cae4dc5ad..00000000000
--- a/.chronus/changes/copilot-document-plugins-option-examples-2026-6-5-20-0-0.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-changeKind: internal
-packages:
- - "@typespec/http-client-csharp"
----
-
-Document the `plugins` emitter option path-resolution behavior with examples.
From 4607a4fa89ca0ea754dc28a6f0284efbbf1b1b47 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Jun 2026 03:47:02 +0000
Subject: [PATCH 7/7] Use net10.0 target framework in plugin csproj sample
Co-authored-by: JonathanCrd <17486462+JonathanCrd@users.noreply.github.com>
---
packages/http-client-csharp/docs/plugins.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/http-client-csharp/docs/plugins.md b/packages/http-client-csharp/docs/plugins.md
index a27530e3206..66834b76ef9 100644
--- a/packages/http-client-csharp/docs/plugins.md
+++ b/packages/http-client-csharp/docs/plugins.md
@@ -61,7 +61,7 @@ Build your plugin as a class library that references the generator's published N
- net8.0
+ net10.0
enable