# TCGC Monthly Newsletter — `@azure-tools/typespec-client-generator-core` 0.67

Hey everyone! This newsletter covers the latest TCGC releases (0.65.2 through 0.67.1) with a major client hierarchy refactor, full multi-service customization support, new extern functions for easy `@override` usage, significant performance improvements, and important bug fixes. As always, I welcome any and all feedback!

## ⭐ What's New in This Release

- Client Hierarchy Refactor: deprecate `@operationGroup` and consolidate into `@client` ⭐ (Breaking) — [docs](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/03client/)
- Multi-Service Client Customization ⭐ (Major Feature) — [docs](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/03client/#one-client-from-multiple-services)
- Extern Functions for Easy `@override` Customization ⭐ (Major Feature) — [docs](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/04method/#transformation-functions-experimental)
- Performance Improvements
- `crossLanguageVersion` Tracking
- SSE Stream Metadata Support
- `@scope` Support for Parameters — [docs](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.scope)
- Bug Fixes & Improvements

---

## ⚠️ Emitter Behavior Changes

Emitter authors, heads up! This release includes **breaking changes** that require migration. Here's a quick summary — see the detailed sections below for migration guides and examples.

- **SdkOperationGroup consolidated into SdkClient** — The `SdkOperationGroup` interface is removed. All operation groups are now `SdkClient` instances. See _Client Hierarchy Refactor_ below for the full migration table.
- **Multi-service auto-merge now requires explicit opt-in** — Previously, sub clients from multiple services were automatically merged. Now you must set `autoMergeService: true` on `@client`. See _Multi-Service Client Customization_ below.
- **Operations under undecorated nested interfaces are now ignored** — Previously, operations inside a nested interface or namespace without `@client` would be surfaced on the root client. Now, nested interfaces/namespaces under a `@client` that are not themselves decorated with `@client` will be **ignored entirely**. If you relied on those operations appearing on the root client, either add `@client` to the nested interface to make them a sub client, or remove the nesting to place them directly on the root client.
- **Root `@client` now requires explicit `service` config** — Previously, a root `@client` without a `service` property would automatically find a `@service` namespace to bind to. This implicit behavior has been removed to properly support multi-service clients. A diagnostic will now be reported if `service` is missing on a root `@client`. Add `service: MyService` (or `service: [ServiceA, ServiceB]` for multi-service) to your `@client` decorator to fix this.
- **Multi-service API version handling** — `@useDependency` is no longer used to declare each service's API version; the latest version is used instead.

---

## ⭐ Client Hierarchy Refactor: deprecate `@operationGroup` and consolidate into `@client` (0.67.0)

📖 **Docs**: [Client Hierarchy](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/03client/) · [`@client` reference](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.client)

This is the headline change of this release. The `SdkOperationGroup` interface has been **removed entirely**. All operation groups are now represented as `SdkClient` instances, creating a unified, consistent client tree. This dramatically simplifies emitter code — there's no longer a need to handle two separate types for clients and operation groups.

### Migration Guide

| Old (0.65.x)                                 | New (0.67.0)                   |
| -------------------------------------------- | ------------------------------ |
| `SdkOperationGroup`                          | `SdkClient`                    |
| `subOperationGroups`                         | `subClients`                   |
| `groupPath`                                  | `clientPath`                   |
| `SdkClient.service`                          | `SdkClient.services`           |
| `listOperationGroups()`                      | `listSubClients()`             |
| `listOperationsInOperationGroup()`           | `listOperationsInClient()`     |
| `isOperationGroup()` / `getOperationGroup()` | `getClient()` + check `parent` |

What Emitter Authors Gain: A single unified type for the entire client hierarchy. No more branching logic between "is this a client or an operation group?" — it's all `SdkClient` now, with `parent` to walk up the tree and `subClients` to walk down.

### `@operationGroup` Deprecated

As part of this unification, the `@operationGroup` decorator is now **deprecated** in favor of `@client`. Internally, `@operationGroup` already delegates to `@client`. It will be removed in a future release.

What Spec Authors Should Do: Replace `@operationGroup` usage with `@client` to define sub clients.

```typespec
// Before (deprecated)
@client({name: "PetStoreClient", service: PetStore})
namespace Customizations;

@operationGroup
interface Dogs {
  feed is PetStore.feed;
}

// After
@client({name: "PetStoreClient", service: PetStore})
namespace Customizations;

@client
interface Dogs {
  feed is PetStore.feed;
}
```

---

## ⭐ Multi-Service Client Customization (0.67.0)

📖 **Docs**: [Multi-Service Clients](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/03client/#one-client-from-multiple-services)

Building on the multi-service foundation from 0.65, TCGC now provides **full customization support** for multi-service client hierarchies via the new `autoMergeService` property on `@client`.

What Spec Authors Can Now Do: Use `autoMergeService: true` to automatically merge all services' operations and sub clients into a single client. Or, create fully customized client hierarchies using explicit `is` operation mapping for fine-grained control.

```typespec
// Auto-merge: all services merge into one client
@client({
  name: "UnifiedClient",
  service: [ServiceA, ServiceB],
  autoMergeService: true,
})
namespace MyClient;
```

```typespec
// Or: services as direct children with auto-merge on children
@client({ name: "ParentClient", service: [ServiceA, ServiceB] })
namespace MyClient {
  @client({ name: "ServiceAClient", service: ServiceA, autoMergeService: true })
  namespace ClientA;
  @client({ name: "ServiceBClient", service: ServiceB, autoMergeService: true })
  namespace ClientB;
}
```

```typespec
// Or: fully customized hierarchy with explicit operation mapping
// Each child client picks specific operations from specific services
@client({
  name: "CustomClient",
  service: [ServiceA, ServiceB],
})
namespace CustomClient {
  // Child client scoped to ServiceA — pulls ops from ServiceA only
  @client({
    name: "ServiceAClient",
    service: ServiceA,
  })
  interface ServiceAClient {
    opA is ServiceA.Operations.opA;
    subOpA is ServiceA.SubNamespace.subOpA;
  }

  // Child client scoped to ServiceB — pulls ops from ServiceB only
  @client({
    name: "ServiceBClient",
    service: ServiceB,
  })
  interface ServiceBClient {
    opB is ServiceB.Operations.opB;
    subOpB is ServiceB.SubNamespace.subOpB;
  }
}
```

What Emitter Authors Gain: Multi-service scenarios now have a clean, explicit configuration model. Root explicit `@client` must have a `service` config. Only `autoMergeService` will merge the service's inner assets into the client — otherwise the client will remain empty for you to populate with explicit `is` mappings. This gives spec authors full control over client shape while keeping the emitter logic simple.

---

## ⭐ Extern Functions for Easy `@override` Customization (0.67.0)

📖 **Docs**: [Customizing Method Signatures with `@override`](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/04method/#customizing-method-signatures-with-override) · [Transformation Functions](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/04method/#transformation-functions-experimental) · [`@override` reference](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.override)

A set of new experimental **extern functions** that make `@@override` dramatically easier to use. Previously, customizing method signatures required writing verbose manual operation definitions. Now you can compose transformations declaratively.

### New Functions

- `replaceParameter(op, paramName, newParam)` — Replace a parameter in an operation
- `removeParameter(op, paramName)` — Remove a parameter from an operation
- `addParameter(op, newParam)` — Add a new parameter to an operation
- `reorderParameters(op, order)` — Reorder parameters according to a specified order

What Spec Authors Can Now Do: Chain these functions with `@@override` to customize method signatures concisely, without writing an entire duplicate operation.

```typespec
// Before: verbose manual override — requires writing a full duplicate operation
op customGetSecrets(maxResults: int32): void;
@@override(KeyVault.getSecrets, customGetSecrets);
```

```typespec
// After: composable transformation — concise, no duplicate operation needed
model RequiredMaxResults {
  maxResults: int32;
}
#suppress "experimental-feature" "using replaceParameter"
@@override(
  KeyVault.getSecrets,
  replaceParameter(KeyVault.getSecrets, "maxResults", RequiredMaxResults.maxResults)
);

// Reorder parameters (useful for ARM to prevent SDK breaking changes)
#suppress "experimental-feature" "using reorderParameters"
@@override(MyService.myOp, reorderParameters(MyService.myOp, #["c", "a", "b"]));
```

What Emitter Authors Gain: These functions work entirely within the existing `@@override` mechanism — no new emitter-side handling needed. The result is the same transformed method signature, but spec authors can express changes much more concisely. This is particularly impactful for ARM packages where parameter reordering is a frequent requirement to prevent SDK breaking changes.

---

## 🚀 Performance Improvements (0.66.1 + 0.67.0)

Three significant performance optimizations that dramatically reduce TCGC processing time for large specifications.

### Parameter Mapping & Traversal Optimization (0.66.1) — [#4020](https://github.com/Azure/typespec-azure/pull/4020)

Optimized internal traversal algorithms and parameter comparison logic across TCGC, significantly reducing processing time for specs with deeply nested models.

- **Result: AppService TCGC consumption reduced from ~30 mins to ~60 seconds**

### Orphan Model Logic Refinement (0.66.1) — [#4024](https://github.com/Azure/typespec-azure/pull/4024)

Refined how TCGC discovers orphan types (models/enums not directly referenced by operations), reducing unnecessary type calculations for large specs.

### Anonymous Type Naming Optimization (0.67.0) — [#4108](https://github.com/Azure/typespec-azure/pull/4108)

Eliminated redundant DFS traversals in anonymous type naming (`getGeneratedName` / `getCrossLanguageDefinitionId`).

| Spec       | Old TCGC Time | New TCGC Time | Speedup |
| ---------- | ------------- | ------------- | ------- |
| MLServices | ~24,586ms     | 833ms         | ~29x    |
| Compute    | ~1,818ms      | 633ms         | ~3x     |
| NetApp     | ~1,537ms      | 593ms         | ~2.6x   |

What Emitter Authors Gain: Faster TCGC processing across the board, especially noticeable on large specs with many models and deeply nested types.

---

## 🔖 `crossLanguageVersion` on `SdkPackage` (0.67.0)

A new `.crossLanguageVersion` property on `SdkPackage` to track equivalent API surfaces across different language SDKs.

What Emitter Authors Gain: Use `crossLanguageVersion` to coordinate versioning across language-specific SDK releases. This enables tooling to identify when two SDKs (e.g., Python and Java) are at the same API surface level, even if their package versions differ.

---

## 📡 SSE Stream Metadata Support (0.66.0)

TCGC now provides `.streamMetadata` on operations that use SSE (Server-Sent Events) for streaming input or output.

What Emitter Authors Gain: Operations using SSE event streaming now expose structured metadata about the stream, enabling emitters to generate appropriate streaming client code.

---

## 🎯 `@scope` Support for Parameters (0.67.0 + 0.66.4)

📖 **Docs**: [`@scope` reference](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.scope)

You can now use `@scope` to specify that certain parameters should only be generated for specific languages.

What Spec Authors Can Now Do: Control parameter generation per language.

```typespec
op myOperation(@scope("python") @query pythonOnlyParam: string, @query sharedParam: string): void;
```

---

## 🐛 Bug Fixes & Improvements

Related docs: [`@clientNamespace`](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/03client/#renaming-the-client-namespace) · [`@clientOption`](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/12clientoptions/) · [`@access`](https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.access) · [`@clientLocation`](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/04method/#using-clientlocation-to-control-parameter-placement)

**Duplicate Client Name with Generic Templates (0.65.2)**

- Issue: Using generic union or model templates with different type parameters caused duplicate client name errors.
- Fix: [#3945](https://github.com/Azure/typespec-azure/pull/3945)

**Namespace Duplication (0.65.3 + 0.66.0)**

- Issue: `@clientNamespace` extending the namespace flag produced duplicated namespaces (e.g., `Azure.Search.Documents.Azure.Search.Documents.Indexes`).
- Fix: [#3954](https://github.com/Azure/typespec-azure/pull/3954), [#3953](https://github.com/Azure/typespec-azure/pull/3953)

**Cross-Namespace Naming Collision (0.65.4)**

- Issue: Incorrect cross-namespace naming collision detection.
- Fix: [#3977](https://github.com/Azure/typespec-azure/pull/3977)

**`@clientOption` `omitSlashFromEmptyRoute` (0.66.3)**

- Issue: Legacy services with empty routes needed a way to omit the trailing slash.
- Fix: [#4068](https://github.com/Azure/typespec-azure/pull/4068) — Added `@clientOption("omitSlashFromEmptyRoute", true)` support.

**LRO Scalar Result Fix (0.66.4)**

- Issue: `getLroMetadata` didn't handle scalar types (e.g., `string`) as LRO final results.
- Fix: [#4101](https://github.com/Azure/typespec-azure/pull/4101)

**`@clientOption` Diagnostic Target (0.66.4)**

- Issue: `@clientOption` diagnostic reported on the target model instead of the decorator, preventing suppression.
- Fix: [#4103](https://github.com/Azure/typespec-azure/pull/4103)

**`@access` Override for Scoped-Out Parameters (0.66.4)**

- Issue: `@access` overrides weren't allowed for types only used in scoped-out parameters.
- Fix: [#4112](https://github.com/Azure/typespec-azure/pull/4112)

**`@clientLocation` `subscriptionId` Fix (0.67.0)**

- Issue: `@clientLocation` didn't work for `subscriptionId` parameter when another operation had already elevated it to client level.
- Fix: [#4164](https://github.com/Azure/typespec-azure/pull/4164)

**Synthetic Union Response Naming (0.67.0)**

- Issue: Synthetic unions from split HTTP responses didn't get generated names, creating union-of-union with 2+ response types.
- Fix: [#4135](https://github.com/Azure/typespec-azure/pull/4135)

**File Type Content-Type/Accept Header (0.67.0)**

- Issue: File type bodies needed proper constant/enum handling for contentType and accept params.
- Fix: [#4030](https://github.com/Azure/typespec-azure/pull/4030)

**Multi-Service API Version Config (0.67.0)**

- Issue: Multi-service clients incorrectly honored the specific `api-version` set in emitter config.
- Fix: [#4177](https://github.com/Azure/typespec-azure/pull/4177)

**Content-Type Synthesis (0.67.0)**

- Issue: Synthetic content type and accept parameters didn't honor HTTP library results directly.
- Fix: [#4125](https://github.com/Azure/typespec-azure/pull/4125)

---

## 🎉 The Bottom Line

This release **unifies the client hierarchy** — `SdkOperationGroup` is gone, everything is `SdkClient` now. Combined with `autoMergeService`, spec authors have full control over multi-service client shape. Please migrate from `@operationGroup` to `@client` and update to the new APIs (`listSubClients`, `listOperationsInClient`, etc.).

New **extern functions** (`replaceParameter`, `removeParameter`, `addParameter`, `reorderParameters`) make `@override` dramatically easier — no more verbose duplicate operations.

**Performance** is significantly improved: AppService ~30 min → ~60s (#4020), and anonymous type naming up to ~29x faster (#4108).

Special thanks to all emitter authors for your continued collaboration and feedback! 🙌

Thanks, @Chenjie and @Isabella
