Repository: Azure/typespec-azure
Pull request #2523: [tcgc] add `@clientDoc` decorator
Author: login: iscai-msft
Created: 2025-04-10T21:46:44Z | Merged: 2025-04-14T19:06:27Z

fixes #2490

Linked issue #2490: [TCGC] Add @clientDoc decorator
Quite simple, when used replace the doc of the element it's applied to.

Value is that this one has a scope, and therefore can be changed for a given language.

Note, this replaces https://github.com/Azure/typespec-azure/issues/2127
Linked issue comments: user: login: heaths
created_at: 2025-04-02T20:03:42Z
body: There's no transformation on the content of this decorator, right? I mean, every language can use whatever content format it supports without TCGC or the emitter having to do anything (unless the latter wants to)? That would be ideal.

E.g., .NET supports XML while Rust supports markdown. I don't want anything getting in the way of using what is natural.
author_association: MEMBER
user: login: lmazuel
created_at: 2025-04-04T22:13:11Z
body: > There's no transformation on the content of this decorator, right? I mean, every language can use whatever content format it supports without TCGC or the emitter having to do anything (unless the latter wants to)? That would be ideal.
> 
> E.g., .NET supports XML while Rust supports markdown. I don't want anything getting in the way of using what is natural.

Yes, no transformation, WYSIWYG :)
author_association: MEMBER
user: login: tadelesh
created_at: 2025-04-09T08:37:19Z
body: if typespec could support decorator scope, it seems we do not need to add a similar decorator in tcgc along with decorator in core.
author_association: MEMBER
user: login: lmazuel
created_at: 2025-04-09T20:19:36Z
body: > if typespec could support decorator scope, it seems we do not need to add a similar decorator in tcgc along with decorator in core.

That's true, but TypeSpec don't support it, and it's not in roadmap, so given the low cost of this decorator the ROI is there. It's been asked consistently.
author_association: MEMBER
user: login: iscai-msft
created_at: 2025-04-09T20:20:24Z
body: during our discussions we've talked about whether we could augment instead of replace. @timotheeguerin I know typespec is adding more support for expressions: is there going to be a way to refer to the value of a decorator, i.e. do something like this:

```tsp
// main.tsp
@doc("Service doc")
model A {};

// client.tsp
@@clientDoc(A, ...A.@doc + ". Client information")
```
author_association: CONTRIBUTOR
user: login: timotheeguerin
created_at: 2025-04-09T20:24:14Z
body: not something we had plans for, probably wouldn't expect to see this in the near future. Recommend you either use an extra flag or some known interpolation value like 
```tsp
@@clientDoc(A, "{BASE_DOC}  Client extra");
```

though I would think about how this might make sense if we did support a conditional/scope application of decorators in typespec where the base doc might not make sense.

like if you have this
```tsp
@doc("Base doc")
@doc("Python doc") with Emitter.python
model A {}
```
author_association: MEMBER
user: login: iscai-msft
created_at: 2025-04-10T00:12:06Z
body: Thanks @timotheeguerin 

Following discussions in office hours, we should do this:

```
enum InsertionKind {
  replace, append
}
@clientDoc(doc: string, insertion: InsertionKind, scope: string)
```
author_association: CONTRIBUTOR

Diff:
diff --git a/.chronus/changes/tcgc-addClientDocDecorator-2025-3-10-17-39-23.md b/.chronus/changes/tcgc-addClientDocDecorator-2025-3-10-17-39-23.md
new file mode 100644
index 0000000000..1167569c6d
--- /dev/null
+++ b/.chronus/changes/tcgc-addClientDocDecorator-2025-3-10-17-39-23.md
@@ -0,0 +1,7 @@
+---
+changeKind: feature
+packages:
+  - "@azure-tools/typespec-client-generator-core"
+---
+
+add `@clientDoc` decorator
\ No newline at end of file
diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md
index 3fb474ea33..ec23aca4d1 100644
--- a/packages/typespec-client-generator-core/README.md
+++ b/packages/typespec-client-generator-core/README.md
@@ -111,6 +111,7 @@ Available ruleSets:
 - [`@apiVersion`](#@apiversion)
 - [`@client`](#@client)
 - [`@clientApiVersions`](#@clientapiversions)
+- [`@clientDoc`](#@clientdoc)
 - [`@clientInitialization`](#@clientinitialization)
 - [`@clientName`](#@clientname)
 - [`@clientNamespace`](#@clientnamespace)
@@ -447,6 +448,56 @@ enum ClientApiVersions {
 @@clientApiVersions(Contoso, ClientApiVersions);
 ```
 
+#### `@clientDoc`
+
+Override documentation for a type in client libraries. This allows you to
+provide client-specific documentation that differs from the service-definition documentation.
+
+```typespec
+@Azure.ClientGenerator.Core.clientDoc(documentation: valueof string, mode: EnumMember, scope?: valueof string)
+```
+
+##### Target
+
+`unknown`
+
+##### Parameters
+
+| Name          | Type             | Description                                                                                                                                                                                            |
+| ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| documentation | `valueof string` | The client-specific documentation to apply                                                                                                                                                             |
+| mode          | `EnumMember`     | Specifies how to apply the documentation (append or replace)                                                                                                                                           |
+| scope         | `valueof string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.<br />You can use "!" to specify negation such as "!(java, python)" or "!java, !python". |
+
+##### Examples
+
+###### Replacing documentation
+
+```typespec
+@doc("This is service documentation")
+@clientDoc("This is client-specific documentation", DocumentationMode.replace)
+op myOperation(): void;
+```
+
+###### Appending documentation
+
+```typespec
+@doc("This is service documentation.")
+@clientDoc("This additional note is for client libraries only.", DocumentationMode.append)
+model MyModel {
+  prop: string;
+}
+```
+
+###### Language-specific documentation
+
+```typespec
+@doc("This is service documentation")
+@clientDoc("Python-specific documentation", DocumentationMode.replace, "python")
+@clientDoc("JavaScript-specific documentation", DocumentationMode.replace, "javascript")
+op myOperation(): void;
+```
+
 #### `@clientInitialization`
 
 Customize the client initialization way.
diff --git a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts
index b33ea954bf..fe725c4139 100644
--- a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts
+++ b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts
@@ -688,6 +688,44 @@ export type ResponseAsBoolDecorator = (
   scope?: string,
 ) => void;
 
+/**
+ * Override documentation for a type in client libraries. This allows you to
+ * provide client-specific documentation that differs from the service-definition documentation.
+ *
+ * @param documentation The client-specific documentation to apply
+ * @param mode Specifies how to apply the documentation (append or replace)
+ * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.
+ * You can use "!" to specify negation such as "!(java, python)" or "!java, !python".
+ * @example Replacing documentation
+ * ```typespec
+ * @doc("This is service documentation")
+ * @clientDoc("This is client-specific documentation", DocumentationMode.replace)
+ * op myOperation(): void;
+ * ```
+ * @example Appending documentation
+ * ```typespec
+ * @doc("This is service documentation.")
+ * @clientDoc("This additional note is for client libraries only.", DocumentationMode.append)
+ * model MyModel {
+ *   prop: string;
+ * }
+ * ```
+ * @example Language-specific documentation
+ * ```typespec
+ * @doc("This is service documentation")
+ * @clientDoc("Python-specific documentation", DocumentationMode.replace, "python")
+ * @clientDoc("JavaScript-specific documentation", DocumentationMode.replace, "javascript")
+ * op myOperation(): void;
+ * ```
+ */
+export type ClientDocDecorator = (
+  context: DecoratorContext,
+  target: Type,
+  documentation: string,
+  mode: EnumMember,
+  scope?: string,
+) => void;
+
 export type AzureClientGeneratorCoreDecorators = {
   clientName: ClientNameDecorator;
   convenientAPI: ConvenientAPIDecorator;
@@ -708,4 +746,5 @@ export type AzureClientGeneratorCoreDecorators = {
   clientApiVersions: ClientApiVersionsDecorator;
   deserializeEmptyStringAsNull: DeserializeEmptyStringAsNullDecorator;
   responseAsBool: ResponseAsBoolDecorator;
+  clientDoc: ClientDocDecorator;
 };
diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp
index 6fef333fdf..8e9deb66a1 100644
--- a/packages/typespec-client-generator-core/lib/decorators.tsp
+++ b/packages/typespec-client-generator-core/lib/decorators.tsp
@@ -678,3 +678,57 @@ extern dec deserializeEmptyStringAsNull(target: ModelProperty, scope?: valueof s
  * ```
  */
 extern dec responseAsBool(target: Operation, scope?: valueof string);
+
+/**
+ * Defines how client documentation should be applied
+ */
+enum DocumentationMode {
+  /**
+   * Append client documentation to the existing doc
+   */
+  append: "append",
+
+  /**
+   * Replace the existing doc with client documentation
+   */
+  replace: "replace",
+}
+
+/**
+ * Override documentation for a type in client libraries. This allows you to
+ * provide client-specific documentation that differs from the service-definition documentation.
+ * @param documentation The client-specific documentation to apply
+ * @param mode Specifies how to apply the documentation (append or replace)
+ * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.
+ * You can use "!" to specify negation such as "!(java, python)" or "!java, !python".
+ *
+ * @example Replacing documentation
+ * ```typespec
+ * @doc("This is service documentation")
+ * @clientDoc("This is client-specific documentation", DocumentationMode.replace)
+ * op myOperation(): void;
+ * ```
+ *
+ * @example Appending documentation
+ * ```typespec
+ * @doc("This is service documentation.")
+ * @clientDoc("This additional note is for client libraries only.", DocumentationMode.append)
+ * model MyModel {
+ *   prop: string;
+ * }
+ * ```
+ *
+ * @example Language-specific documentation
+ * ```typespec
+ * @doc("This is service documentation")
+ * @clientDoc("Python-specific documentation", DocumentationMode.replace, "python")
+ * @clientDoc("JavaScript-specific documentation", DocumentationMode.replace, "javascript")
+ * op myOperation(): void;
+ * ```
+ */
+extern dec clientDoc(
+  target: unknown,
+  documentation: valueof string,
+  mode: EnumMember,
+  scope?: valueof string
+);
diff --git a/packages/typespec-client-generator-core/src/clients.ts b/packages/typespec-client-generator-core/src/clients.ts
index f74eecb0d7..3b59f21302 100644
--- a/packages/typespec-client-generator-core/src/clients.ts
+++ b/packages/typespec-client-generator-core/src/clients.ts
@@ -1,4 +1,4 @@
-import { createDiagnosticCollector, Diagnostic, getDoc, getSummary } from "@typespec/compiler";
+import { createDiagnosticCollector, Diagnostic, getSummary } from "@typespec/compiler";
 import { $ } from "@typespec/compiler/experimental/typekit";
 import { getServers, HttpServer } from "@typespec/http";
 import {
@@ -27,6 +27,7 @@ import {
 import {
   createGeneratedName,
   getAvailableApiVersions,
+  getClientDoc,
   getTypeDecorators,
   getValueTypeValue,
   isSubscriptionId,
@@ -188,7 +189,7 @@ export function createSdkClientType<TServiceOperation extends SdkServiceOperatio
     __raw: client,
     kind: "client",
     name: client.kind === "SdkClient" ? client.name : getLibraryName(context, client.type),
-    doc: getDoc(context.program, client.type),
+    doc: getClientDoc(context, client.type),
     summary: getSummary(context.program, client.type),
     methods: [],
     apiVersions: context.getApiVersionsForType(client.type),
diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts
index 0be801e0cc..931223a733 100644
--- a/packages/typespec-client-generator-core/src/decorators.ts
+++ b/packages/typespec-client-generator-core/src/decorators.ts
@@ -27,6 +27,7 @@ import {
   ApiVersionDecorator,
   ClientApiVersionsDecorator,
   ClientDecorator,
+  ClientDocDecorator,
   ClientInitializationDecorator,
   ClientNameDecorator,
   ClientNamespaceDecorator,
@@ -1376,3 +1377,53 @@ export const $responseAsBool: ResponseAsBoolDecorator = (
 export function getResponseAsBool(context: TCGCContext, target: Operation): boolean {
   return getScopedDecoratorData(context, responseAsBoolKey, target);
 }
+
+const clientDocKey = createStateSymbol("clientDoc");
+
+/**
+ * Type representing the client documentation data stored.
+ */
+interface ClientDocData {
+  documentation: string;
+  mode: string;
+}
+
+export const $clientDoc: ClientDocDecorator = (
+  context: DecoratorContext,
+  target: Type,
+  documentation: string,
+  mode: EnumMember,
+  scope?: LanguageScopes,
+) => {
+  const docMode = mode.value as string;
+  // Validate the mode value
+  if (docMode !== "append" && docMode !== "replace") {
+    reportDiagnostic(context.program, {
+      code: "invalid-client-doc-mode",
+      format: { mode: docMode },
+      target: context.decoratorTarget,
+    });
+    return;
+  }
+
+  const docData: ClientDocData = {
+    documentation,
+    mode: docMode,
+  };
+
+  setScopedDecoratorData(context, $clientDoc, clientDocKey, target, docData, scope);
+};
+
+/**
+ * Gets the client documentation data for a type.
+ *
+ * @param context TCGCContext
+ * @param target Type to get client documentation for
+ * @returns ClientDocData or undefined if no client documentation exists
+ */
+export function getClientDocExplicit(
+  context: TCGCContext,
+  target: Type,
+): ClientDocData | undefined {
+  return getScopedDecoratorData(context, clientDocKey, target);
+}
diff --git a/packages/typespec-client-generator-core/src/http.ts b/packages/typespec-client-generator-core/src/http.ts
index 438df615cd..cde5052634 100644
--- a/packages/typespec-client-generator-core/src/http.ts
+++ b/packages/typespec-client-generator-core/src/http.ts
@@ -6,7 +6,6 @@ import {
   Type,
   compilerAssert,
   createDiagnosticCollector,
-  getDoc,
   getEncode,
   getSummary,
   ignoreDiagnostics,
@@ -56,6 +55,7 @@ import {
 import {
   findRootSourceProperty,
   getAvailableApiVersions,
+  getClientDoc,
   getHttpBodySpreadModel,
   getHttpOperationResponseHeaders,
   getLocationOfOperation,
@@ -242,7 +242,7 @@ function getSdkHttpParameters(
         name,
         isGeneratedName: true,
         serializedName: "",
-        doc: getDoc(context.program, tspBody.type),
+        doc: getClientDoc(context, tspBody.type),
         summary: getSummary(context.program, tspBody.type),
         onClient: false,
         contentTypes: [],
diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts
index d1dcbc35dc..4251dda96e 100644
--- a/packages/typespec-client-generator-core/src/internal-utils.ts
+++ b/packages/typespec-client-generator-core/src/internal-utils.ts
@@ -4,6 +4,7 @@ import {
   createDiagnosticCollector,
   Diagnostic,
   getDeprecationDetails,
+  getDoc,
   getLifecycleVisibilityEnum,
   getNamespaceFullName,
   getVisibilityForClass,
@@ -35,7 +36,7 @@ import {
   getVersioningMutators,
   getVersions,
 } from "@typespec/versioning";
-import { getParamAlias } from "./decorators.js";
+import { getClientDocExplicit, getParamAlias } from "./decorators.js";
 import {
   DecoratorInfo,
   SdkBuiltInType,
@@ -700,3 +701,19 @@ export function resolveConflictGeneratedName(context: TCGCContext) {
     }
   }
 }
+
+export function getClientDoc(context: TCGCContext, target: Type): string | undefined {
+  const clientDocExplicit = getClientDocExplicit(context, target);
+  const baseDoc = getDoc(context.program, target);
+  if (clientDocExplicit) {
+    switch (clientDocExplicit.mode) {
+      case "append":
+        return baseDoc
+          ? `${baseDoc}\n${clientDocExplicit.documentation}`
+          : clientDocExplicit.documentation;
+      case "replace":
+        return clientDocExplicit.documentation;
+    }
+  }
+  return baseDoc;
+}
diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts
index 267c995eec..7536116b86 100644
--- a/packages/typespec-client-generator-core/src/lib.ts
+++ b/packages/typespec-client-generator-core/src/lib.ts
@@ -377,6 +377,12 @@ export const $lib = createTypeSpecLibrary({
         default: paramMessage`The @clientApiVersions decorator is missing one or more versions defined in ${"serviceName"}. Client API must support all service versions to ensure compatibility. Missing versions: ${"missingVersions"}. Please update the client API to support all required service versions.`,
       },
     },
+    "invalid-client-doc-mode": {
+      severity: "error",
+      messages: {
+        default: paramMessage`Invalid mode '${"mode"}' for @clientDoc decorator. Valid values are "append" or "replace".`,
+      },
+    },
   },
   emitter: {
     options: TCGCEmitterOptionsSchema,
diff --git a/packages/typespec-client-generator-core/src/methods.ts b/packages/typespec-client-generator-core/src/methods.ts
index 0c8aececec..7a72ae63a6 100644
--- a/packages/typespec-client-generator-core/src/methods.ts
+++ b/packages/typespec-client-generator-core/src/methods.ts
@@ -6,7 +6,6 @@ import {
 import {
   createDiagnosticCollector,
   Diagnostic,
-  getDoc,
   getPagingOperation,
   getSummary,
   isList,
@@ -53,6 +52,7 @@ import {
   findRootSourceProperty,
   getAllResponseBodiesAndNonBodyExists,
   getAvailableApiVersions,
+  getClientDoc,
   getHashForType,
   getLocationOfOperation,
   getTypeDecorators,
@@ -669,7 +669,7 @@ function getSdkBasicServiceMethod<TServiceOperation extends SdkServiceOperation>
     name,
     access: getAccess(context, operation) ?? "public",
     parameters: methodParameters,
-    doc: getDoc(context.program, operation),
+    doc: getClientDoc(context, operation),
     summary: getSummary(context.program, operation),
     operation: serviceOperation,
     response,
diff --git a/packages/typespec-client-generator-core/src/tsp-index.ts b/packages/typespec-client-generator-core/src/tsp-index.ts
index 46b1742ee3..b2c1c8bbad 100644
--- a/packages/typespec-client-generator-core/src/tsp-index.ts
+++ b/packages/typespec-client-generator-core/src/tsp-index.ts
@@ -5,6 +5,7 @@ import {
   $apiVersion,
   $client,
   $clientApiVersions,
+  $clientDoc,
   $clientInitialization,
   $clientName,
   $clientNamespace,
@@ -46,5 +47,6 @@ export const $decorators = {
     clientApiVersions: $clientApiVersions,
     deserializeEmptyStringAsNull: $deserializeEmptyStringAsNull,
     responseAsBool: $responseAsBool,
+    clientDoc: $clientDoc,
   } as AzureClientGeneratorCoreDecorators,
 };
diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts
index 7b5e69259e..05f663588f 100644
--- a/packages/typespec-client-generator-core/src/types.ts
+++ b/packages/typespec-client-generator-core/src/types.ts
@@ -21,7 +21,6 @@ import {
   Union,
   createDiagnosticCollector,
   getDiscriminator,
-  getDoc,
   getEncode,
   getLifecycleVisibilityEnum,
   getSummary,
@@ -92,6 +91,7 @@ import {
   createGeneratedName,
   filterApiVersionsInEnum,
   getAvailableApiVersions,
+  getClientDoc,
   getHttpBodySpreadModel,
   getHttpOperationResponseHeaders,
   getLocationOfOperation,
@@ -248,7 +248,7 @@ function getSdkBuiltInTypeWithDiagnostics(
   const stdType = {
     ...diagnostics.pipe(getSdkTypeBaseHelper(context, type, kind)),
     name: getLibraryName(context, type),
-    doc: getDoc(context.program, type),
+    doc: getClientDoc(context, type),
     summary: getSummary(context.program, type),
     baseType:
       type.baseScalar && !context.program.checker.isStdType(type) // we only calculate the base type when this type has a base type and this type is not a std type because for std types there is no point of calculating its base type.
@@ -316,7 +316,7 @@ function getSdkDateTimeType(
     encode: (encode ?? "rfc3339") as DateTimeKnownEncoding,
     wireType: wireType ?? getTypeSpecBuiltInType(context, "string"),
     baseType: baseType,
-    doc: getDoc(context.program, type),
+    doc: getClientDoc(context, type),
     summary: getSummary(context.program, type),
     crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type),
   });
@@ -414,7 +414,7 @@ function getSdkDurationTypeWithDiagnostics(
     encode: (encode ?? "ISO8601") as DurationKnownEncoding,
     wireType: wireType ?? getTypeSpecBuiltInType(context, "string"),
     baseType: baseType,
-    doc: getDoc(context.program, type),
+    doc: getClientDoc(context, type),
     summary: getSummary(context.program, type),
     crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type),
   });
@@ -818,7 +818,7 @@ export function getSdkModelWithDiagnostics(
       name: name,
       isGeneratedName: !type.name,
       namespace: getClientNamespace(context, type),
-      doc: getDoc(context.program, type),
+      doc: getClientDoc(context, type),
       summary: getSummary(context.program, type),
       properties: [],
       additionalProperties: undefined, // going to set additional properties in the next few lines when we look at base model
@@ -925,7 +925,7 @@ function getSdkEnumValueWithDiagnostics(
     ...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "enumvalue")),
     name: getLibraryName(context, type),
     value: type.value ?? type.name,
-    doc: getDoc(context.program, type),
+    doc: getClientDoc(context, type),
     summary: getSummary(context.program, type),
     enumType,
     valueType: enumType.valueType,
@@ -949,7 +949,7 @@ function getSdkEnumWithDiagnostics(
       name: getLibraryName(context, type),
       isGeneratedName: false,
       namespace: getClientNamespace(context, type),
-      doc: getDoc(context.program, type),
+      doc: getClientDoc(context, type),
       summary: getSummary(context.program, type),
       valueType: diagnostics.pipe(
         getSdkEnumValueType(
@@ -988,7 +988,7 @@ function getSdkUnionEnumValues(
     values.push({
       ...diagnostics.pipe(getSdkTypeBaseHelper(context, member.type, "enumvalue")),
       name: name ? name : `${member.value}`,
-      doc: getDoc(context.program, member.type),
+      doc: getClientDoc(context, member.type),
       summary: getSummary(context.program, member.type),
       value: member.value,
       valueType: enumType.valueType,
@@ -1015,7 +1015,7 @@ export function getSdkUnionEnumWithDiagnostics(
     name,
     isGeneratedName: !type.union.name,
     namespace: getClientNamespace(context, type.union),
-    doc: getDoc(context.program, union),
+    doc: getClientDoc(context, union),
     summary: getSummary(context.program, union),
     valueType:
       diagnostics.pipe(getUnionAsEnumValueType(context, type.union)) ??
@@ -1225,7 +1225,7 @@ export function getSdkModelPropertyTypeBase(
   const onClient = isOnClient(context, type, operation, apiVersions.length > 0);
   return diagnostics.wrap({
     __raw: type,
-    doc: getDoc(context.program, type),
+    doc: getClientDoc(context, type),
     summary: getSummary(context.program, type),
     apiVersions,
     type: propertyType,
diff --git a/packages/typespec-client-generator-core/test/decorators/client-doc.test.ts b/packages/typespec-client-generator-core/test/decorators/client-doc.test.ts
new file mode 100644
index 0000000000..1ab8277222
--- /dev/null
+++ b/packages/typespec-client-generator-core/test/decorators/client-doc.test.ts
@@ -0,0 +1,196 @@
+import { expectDiagnostics } from "@typespec/compiler/testing";
+import { ok, strictEqual } from "assert";
+import { beforeEach, it } from "vitest";
+import { getClientDocExplicit } from "../../src/decorators.js";
+import { SdkTestRunner, createSdkTestRunner } from "../test-host.js";
+
+let runner: SdkTestRunner;
+
+beforeEach(async () => {
+  runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-python" });
+});
+
+it("@clientDoc model with append mode", async () => {
+  await runner.compileWithBuiltInService(`
+      @doc("Original model documentation")
+      @clientDoc("Additional client documentation", DocumentationMode.append)
+      @usage(Usage.input)
+      model TestModel {
+        prop: string;
+      }
+    `);
+
+  const sdkContext = runner.context;
+  const model = sdkContext.sdkPackage.models[0];
+
+  // Check doc data is stored correctly
+  const clientDocInfo = getClientDocExplicit(sdkContext, model.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Additional client documentation");
+  strictEqual(clientDocInfo.mode, "append");
+
+  // Check doc is accessible from model
+  strictEqual(model.doc, "Original model documentation\nAdditional client documentation");
+});
+
+it("@clientDoc method with replace mode", async () => {
+  await runner.compileWithBuiltInService(`
+      @doc("Original operation documentation")
+      @clientDoc("Client-specific documentation", DocumentationMode.replace)
+      @get
+      op test(): string;
+    `);
+
+  const sdkContext = runner.context;
+  const method = sdkContext.sdkPackage.clients[0].methods[0];
+
+  // Check doc data is stored correctly
+  const clientDocInfo = getClientDocExplicit(sdkContext, method.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Client-specific documentation");
+  strictEqual(clientDocInfo.mode, "replace");
+
+  // Check doc is accessible from operation
+  strictEqual(method.doc, "Client-specific documentation");
+});
+
+it("@clientDoc property with append mode", async () => {
+  await runner.compileWithBuiltInService(`
+      @usage(Usage.input)
+      model TestModel {
+        @doc("Original property documentation")
+        @clientDoc("Client property documentation", DocumentationMode.append)
+        prop: string;
+      }
+    `);
+
+  const sdkContext = runner.context;
+  const model = sdkContext.sdkPackage.models[0];
+  const property = model.properties[0];
+
+  // Check doc data is stored correctly
+  const clientDocInfo = getClientDocExplicit(sdkContext, property.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Client property documentation");
+  strictEqual(clientDocInfo.mode, "append");
+
+  // Check doc is accessible from property
+  strictEqual(property.doc, "Original property documentation\nClient property documentation");
+});
+
+it("@clientDoc enum with append mode", async () => {
+  await runner.compileWithBuiltInService(`
+      @doc("Original enum documentation")
+      @clientDoc("Client enum documentation", DocumentationMode.append)
+      @usage(Usage.input)
+      enum Colors {
+        Red,
+        Green,
+        Blue
+      }
+    `);
+
+  const sdkContext = runner.context;
+  const enumType = sdkContext.sdkPackage.enums[0];
+
+  // Check doc data is stored correctly
+  const clientDocInfo = getClientDocExplicit(sdkContext, enumType.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Client enum documentation");
+  strictEqual(clientDocInfo.mode, "append");
+
+  // Check doc is accessible from enum
+  strictEqual(enumType.doc, "Original enum documentation\nClient enum documentation");
+});
+
+it("@clientDoc enum member with replace mode", async () => {
+  await runner.compileWithBuiltInService(`
+    @usage(Usage.input)
+      enum Colors {
+        @doc("Original enum member doc")
+        @clientDoc("Client enum member doc", DocumentationMode.replace)
+        Red,
+        Green,
+        Blue
+      }
+    `);
+
+  const sdkContext = runner.context;
+  const enumType = sdkContext.sdkPackage.enums[0];
+  const enumMember = enumType.values[0];
+
+  // Check doc data is stored correctly
+  const clientDocInfo = getClientDocExplicit(sdkContext, enumMember.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Client enum member doc");
+  strictEqual(clientDocInfo.mode, "replace");
+
+  // Check doc is accessible from enum member
+  strictEqual(enumMember.doc, "Client enum member doc");
+});
+
+it("@clientMode with scope", async () => {
+  await runner.compileWithBuiltInService(`
+      @doc("Original documentation")
+      @clientDoc("Python documentation", DocumentationMode.replace, "python")
+      @clientDoc("JavaScript documentation", DocumentationMode.replace, "javascript")
+      @usage(Usage.input)
+      model TestModel {
+        prop: string;
+      }
+    `);
+
+  const sdkContext = runner.context;
+  const model = sdkContext.sdkPackage.models[0];
+
+  // Since our test runner uses python emitter, we should see the Python documentation
+  const clientDocInfo = getClientDocExplicit(sdkContext, model.__raw!);
+  ok(clientDocInfo, "Client documentation data should exist");
+  strictEqual(clientDocInfo.documentation, "Python documentation");
+  strictEqual(clientDocInfo.mode, "replace");
+
+  // Check doc is accessible from model
+  strictEqual(model.doc, "Python documentation");
+});
+
+it("reports error when an invalid mode is used", async () => {
+  const diagnostics = await runner.diagnose(`
+      enum InvalidMode {
+        invalid: "invalid"
+      }
+      
+      @doc("Original documentation")
+      @clientDoc("Invalid mode", InvalidMode.invalid)
+      model TestModel {
+        prop: string;
+      }
+    `);
+
+  expectDiagnostics(diagnostics, {
+    code: "@azure-tools/typespec-client-generator-core/invalid-client-doc-mode",
+    message:
+      /Invalid mode 'invalid' for @clientDoc decorator. Valid values are "append" or "replace"./,
+  });
+});
+
+it("reports error when an invalid mode value is used", async () => {
+  const diagnostics = await runner.diagnose(`
+      enum CustomMode {
+        custom: "custom"
+      }
+      
+      @doc("Original documentation")
+      @clientDoc("Invalid mode value", CustomMode.custom)
+      model TestModel {
+        prop: string;
+      }
+    `);
+
+  expectDiagnostics(diagnostics, [
+    {
+      code: "@azure-tools/typespec-client-generator-core/invalid-client-doc-mode",
+      message:
+        /Invalid mode 'custom' for @clientDoc decorator. Valid values are "append" or "replace"./,
+    },
+  ]);
+});
diff --git a/website/src/content/docs/docs/howtos/Generate client libraries/07types.mdx b/website/src/content/docs/docs/howtos/Generate client libraries/07types.mdx
index 9defa74f3c..36a457d7f0 100644
--- a/website/src/content/docs/docs/howtos/Generate client libraries/07types.mdx	
+++ b/website/src/content/docs/docs/howtos/Generate client libraries/07types.mdx	
@@ -166,6 +166,252 @@ Foo foo = new Foo("hello");
 </ClientTabItem>
 </ClientTabs>
 
+## Client Documentation
+
+The `@clientDoc` decorator lets you add documentation that will appear in generated client libraries. This is useful for providing additional context, examples, or implementation notes that are only relevant for SDK consumers.
+
+<ClientTabs>
+<ClientTabItem lang="typespec" >
+
+```typespec
+@doc("This internal model is the base class for shapes")
+@clientDoc(
+  "Base class for all shape objects that can be drawn on a canvas",
+  DocumentationMode.replace
+)
+model Shape {
+  @doc("The coordinates of the shape")
+  @clientDoc(
+    "The x,y coordinates where the shape will be positioned on the canvas",
+    DocumentationMode.replace
+  )
+  position: Point;
+}
+
+@doc("A two-dimensional point")
+@clientDoc("Represents a position in a 2D coordinate system", DocumentationMode.replace)
+model Point {
+  @doc("X coordinate")
+  @clientDoc("The horizontal position (increases moving right)", DocumentationMode.replace)
+  x: float32;
+
+  @doc("Y coordinate")
+  @clientDoc("The vertical position (increases moving down)", DocumentationMode.replace)
+  y: float32;
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="python">
+
+```python
+class Point(_model_base.Model):
+    """Represents a position in a 2D coordinate system.
+
+    :param x: The horizontal position (increases moving right)
+    :type x: float
+    :param y: The vertical position (increases moving down)
+    :type y: float
+    """
+
+    x: float = rest_field()
+    """The horizontal position (increases moving right)."""
+    y: float = rest_field()
+    """The vertical position (increases moving down)."""
+
+class Shape(_model_base.Model):
+    """Base class for all shape objects that can be drawn on a canvas.
+
+    :param position: The x,y coordinates where the shape will be positioned on the canvas
+    :type position: Point
+    """
+
+    position: Point = rest_field()
+    """The x,y coordinates where the shape will be positioned on the canvas."""
+```
+
+</ClientTabItem>
+<ClientTabItem lang="csharp" >
+
+```csharp
+/// <summary>
+/// Base class for all shape objects that can be drawn on a canvas
+/// </summary>
+public partial class Shape
+{
+    /// <summary>
+    /// The x,y coordinates where the shape will be positioned on the canvas
+    /// </summary>
+    public Point Position { get; set; }
+}
+
+/// <summary>
+/// Represents a position in a 2D coordinate system
+/// </summary>
+public partial class Point
+{
+    /// <summary>
+    /// The horizontal position (increases moving right)
+    /// </summary>
+    public float X { get; set; }
+
+    /// <summary>
+    /// The vertical position (increases moving down)
+    /// </summary>
+    public float Y { get; set; }
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="typescript" >
+
+```typescript
+/**
+ * Base class for all shape objects that can be drawn on a canvas
+ */
+export interface Shape {
+  /**
+   * The x,y coordinates where the shape will be positioned on the canvas
+   */
+  position: Point;
+}
+
+/**
+ * Represents a position in a 2D coordinate system
+ */
+export interface Point {
+  /**
+   * The horizontal position (increases moving right)
+   */
+  x: number;
+
+  /**
+   * The vertical position (increases moving down)
+   */
+  y: number;
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="java" >
+
+```java
+/**
+ * Base class for all shape objects that can be drawn on a canvas.
+ */
+@Fluent
+public final class Shape {
+    /**
+     * The x,y coordinates where the shape will be positioned on the canvas.
+     */
+    private Point position;
+
+    /**
+     * Get the position property: The x,y coordinates where the shape will be positioned on the canvas.
+     *
+     * @return the position value.
+     */
+    public Point getPosition() {
+        return this.position;
+    }
+
+    /**
+     * Set the position property: The x,y coordinates where the shape will be positioned on the canvas.
+     *
+     * @param position the position value to set.
+     * @return the Shape object itself.
+     */
+    public Shape setPosition(Point position) {
+        this.position = position;
+        return this;
+    }
+}
+
+/**
+ * Represents a position in a 2D coordinate system.
+ */
+@Fluent
+public final class Point {
+    /**
+     * The horizontal position (increases moving right).
+     */
+    private float x;
+
+    /**
+     * The vertical position (increases moving down).
+     */
+    private float y;
+
+    /**
+     * Get the x property: The horizontal position (increases moving right).
+     *
+     * @return the x value.
+     */
+    public float getX() {
+        return this.x;
+    }
+
+    /**
+     * Set the x property: The horizontal position (increases moving right).
+     *
+     * @param x the x value to set.
+     * @return the Point object itself.
+     */
+    public Point setX(float x) {
+        this.x = x;
+        return this;
+    }
+
+    /**
+     * Get the y property: The vertical position (increases moving down).
+     *
+     * @return the y value.
+     */
+    public float getY() {
+        return this.y;
+    }
+
+    /**
+     * Set the y property: The vertical position (increases moving down).
+     *
+     * @param y the y value to set.
+     * @return the Point object itself.
+     */
+    public Point setY(float y) {
+        this.y = y;
+        return this;
+    }
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="go">
+
+```go
+// Shape - Base class for all shape objects that can be drawn on a canvas
+type Shape struct {
+	// Position - The x,y coordinates where the shape will be positioned on the canvas
+	Position *Point
+}
+
+// Point - Represents a position in a 2D coordinate system
+type Point struct {
+	// X - The horizontal position (increases moving right)
+	X *float32
+
+	// Y - The vertical position (increases moving down)
+	Y *float32
+}
+```
+
+</ClientTabItem>
+</ClientTabs>
+
+The `@clientDoc` decorator can be used on everything that the `@doc` decorator can be applied to.
+
+When both `@doc` and `@clientDoc` are present, TCGC outputs the final documentation depending on the mode that you called `@clientDoc` with.
+If called with `replace`, `@clientDoc` replaces `@doc` completely. If called with `append`, `@clientDoc` is appended onto `@doc`.
+
 ## Models
 
 ### Flattening
diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/data-types.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/data-types.md
index d48a726fed..e3cfde45f8 100644
--- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/data-types.md
+++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/data-types.md
@@ -32,6 +32,19 @@ enum Azure.ClientGenerator.Core.Access
 | public   | `"public"`   | Open to user   |
 | internal | `"internal"` | Hide from user |
 
+### `DocumentationMode` {#Azure.ClientGenerator.Core.DocumentationMode}
+
+Defines how client documentation should be applied
+
+```typespec
+enum Azure.ClientGenerator.Core.DocumentationMode
+```
+
+| Name    | Value       | Description                                        |
+| ------- | ----------- | -------------------------------------------------- |
+| append  | `"append"`  | Append client documentation to the existing doc    |
+| replace | `"replace"` | Replace the existing doc with client documentation |
+
 ### `InitializedBy` {#Azure.ClientGenerator.Core.InitializedBy}
 
 InitializedBy value.
diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md
index 114976c711..25cdb9d77d 100644
--- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md
+++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md
@@ -327,6 +327,56 @@ enum ClientApiVersions {
 @@clientApiVersions(Contoso, ClientApiVersions);
 ```
 
+### `@clientDoc` {#@Azure.ClientGenerator.Core.clientDoc}
+
+Override documentation for a type in client libraries. This allows you to
+provide client-specific documentation that differs from the service-definition documentation.
+
+```typespec
+@Azure.ClientGenerator.Core.clientDoc(documentation: valueof string, mode: EnumMember, scope?: valueof string)
+```
+
+#### Target
+
+`unknown`
+
+#### Parameters
+
+| Name          | Type             | Description                                                                                                                                                                                            |
+| ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| documentation | `valueof string` | The client-specific documentation to apply                                                                                                                                                             |
+| mode          | `EnumMember`     | Specifies how to apply the documentation (append or replace)                                                                                                                                           |
+| scope         | `valueof string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.<br />You can use "!" to specify negation such as "!(java, python)" or "!java, !python". |
+
+#### Examples
+
+##### Replacing documentation
+
+```typespec
+@doc("This is service documentation")
+@clientDoc("This is client-specific documentation", DocumentationMode.replace)
+op myOperation(): void;
+```
+
+##### Appending documentation
+
+```typespec
+@doc("This is service documentation.")
+@clientDoc("This additional note is for client libraries only.", DocumentationMode.append)
+model MyModel {
+  prop: string;
+}
+```
+
+##### Language-specific documentation
+
+```typespec
+@doc("This is service documentation")
+@clientDoc("Python-specific documentation", DocumentationMode.replace, "python")
+@clientDoc("JavaScript-specific documentation", DocumentationMode.replace, "javascript")
+op myOperation(): void;
+```
+
 ### `@clientInitialization` {#@Azure.ClientGenerator.Core.clientInitialization}
 
 Customize the client initialization way.
diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx
index 547327f0e2..1b95be4a94 100644
--- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx
+++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx
@@ -45,6 +45,7 @@ npm install --save-peer @azure-tools/typespec-client-generator-core
 - [`@apiVersion`](./decorators.md#@Azure.ClientGenerator.Core.apiVersion)
 - [`@client`](./decorators.md#@Azure.ClientGenerator.Core.client)
 - [`@clientApiVersions`](./decorators.md#@Azure.ClientGenerator.Core.clientApiVersions)
+- [`@clientDoc`](./decorators.md#@Azure.ClientGenerator.Core.clientDoc)
 - [`@clientInitialization`](./decorators.md#@Azure.ClientGenerator.Core.clientInitialization)
 - [`@clientName`](./decorators.md#@Azure.ClientGenerator.Core.clientName)
 - [`@clientNamespace`](./decorators.md#@Azure.ClientGenerator.Core.clientNamespace)


Reviews:
user: login: tadelesh
body: current code lgtm. just one concern. we only add it for `@doc`, no ask for `@summary`, right? i think dotnet and java use them both.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-11T09:05:33Z
user: login: srnagar
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-14T17:55:44Z
user: login: iscai-msft
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T18:02:33Z

Review comments:
diff_hunk: @@ -663,3 +663,57 @@ extern dec deserializeEmptyStringAsNull(target: ModelProperty, scope?: valueof s
  * ```
  */
 extern dec responseAsBool(target: Operation, scope?: valueof string);
+
+/**
+ * Defines how client documentation should be applied
+ */
+enum DocumentationMode {
+  /**
+   * Append client documentation to the existing doc
+   */
+  append: "append",
path: packages/typespec-client-generator-core/lib/decorators.tsp
user: login: srnagar
body: To address @tadelesh's comment, we can either introduce a new decorator `@clientSummary` or the other option is to use `appendToDoc`, `replaceDoc` values and then we can add `appendToSummary` and `replaceSummary` later if needed.

Also, `@summary` is used in 48 tsp files while `@doc` is used in 700+ tsp files in the rest-api-spec repo. `summary` is not used a lot so, we can add support for it when needed.

created_at: 2025-04-14T17:36:24Z
author_association: MEMBER
diff_hunk: @@ -166,6 +166,259 @@ Foo foo = new Foo("hello");
 </ClientTabItem>
 </ClientTabs>
 
+## Client Documentation
+
+The `@clientDoc` decorator lets you add documentation that will appear in generated client libraries. This is useful for providing additional context, examples, or implementation notes that are only relevant for SDK consumers.
+
+<ClientTabs>
+<ClientTabItem lang="typespec" >
+
+```typespec
+@doc("This internal model is the base class for shapes")
+@clientDoc(
+  "Base class for all shape objects that can be drawn on a canvas",
+  DocumentationMode.replace
+)
+model Shape {
+  @doc("The coordinates of the shape")
+  @clientDoc(
+    "The x,y coordinates where the shape will be positioned on the canvas",
+    DocumentationMode.replace
+  )
+  position: Point;
+}
+
+@doc("A two-dimensional point")
+@clientDoc("Represents a position in a 2D coordinate system", DocumentationMode.replace)
+model Point {
+  @doc("X coordinate")
+  @clientDoc("The horizontal position (increases moving right)", DocumentationMode.replace)
+  x: float32;
+
+  @doc("Y coordinate")
+  @clientDoc("The vertical position (increases moving down)", DocumentationMode.replace)
+  y: float32;
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="python">
+
+```python
+class Point(_model_base.Model):
+    """Represents a position in a 2D coordinate system.
+
+    :param x: The horizontal position (increases moving right)
+    :type x: float
+    :param y: The vertical position (increases moving down)
+    :type y: float
+    """
+
+    x: float = rest_field()
+    """The horizontal position (increases moving right)."""
+    y: float = rest_field()
+    """The vertical position (increases moving down)."""
+
+class Shape(_model_base.Model):
+    """Base class for all shape objects that can be drawn on a canvas.
+
+    :param position: The x,y coordinates where the shape will be positioned on the canvas
+    :type position: Point
+    """
+
+    position: Point = rest_field()
+    """The x,y coordinates where the shape will be positioned on the canvas."""
+```
+
+</ClientTabItem>
+<ClientTabItem lang="csharp" >
+
+```csharp
+/// <summary>
+/// Base class for all shape objects that can be drawn on a canvas
+/// </summary>
+public partial class Shape
+{
+    /// <summary>
+    /// The x,y coordinates where the shape will be positioned on the canvas
+    /// </summary>
+    public Point Position { get; set; }
+}
+
+/// <summary>
+/// Represents a position in a 2D coordinate system
+/// </summary>
+public partial class Point
+{
+    /// <summary>
+    /// The horizontal position (increases moving right)
+    /// </summary>
+    public float X { get; set; }
+
+    /// <summary>
+    /// The vertical position (increases moving down)
+    /// </summary>
+    public float Y { get; set; }
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="typescript" >
+
+```typescript
+/**
+ * Base class for all shape objects that can be drawn on a canvas
+ */
+export interface Shape {
+  /**
+   * The x,y coordinates where the shape will be positioned on the canvas
+   */
+  position: Point;
+}
+
+/**
+ * Represents a position in a 2D coordinate system
+ */
+export interface Point {
+  /**
+   * The horizontal position (increases moving right)
+   */
+  x: number;
+
+  /**
+   * The vertical position (increases moving down)
+   */
+  y: number;
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="java" >
+
+```java
+/**
+ * Base class for all shape objects that can be drawn on a canvas.
+ */
+@Fluent
+public final class Shape {
+    /**
+     * The x,y coordinates where the shape will be positioned on the canvas.
+     */
+    private Point position;
+
+    /**
+     * Get the position property: The x,y coordinates where the shape will be positioned on the canvas.
+     *
+     * @return the position value.
+     */
+    public Point getPosition() {
+        return this.position;
+    }
+
+    /**
+     * Set the position property: The x,y coordinates where the shape will be positioned on the canvas.
+     *
+     * @param position the position value to set.
+     * @return the Shape object itself.
+     */
+    public Shape setPosition(Point position) {
+        this.position = position;
+        return this;
+    }
+}
+
+/**
+ * Represents a position in a 2D coordinate system.
+ */
+@Fluent
+public final class Point {
+    /**
+     * The horizontal position (increases moving right).
+     */
+    private float x;
+
+    /**
+     * The vertical position (increases moving down).
+     */
+    private float y;
+
+    /**
+     * Get the x property: The horizontal position (increases moving right).
+     *
+     * @return the x value.
+     */
+    public float getX() {
+        return this.x;
+    }
+
+    /**
+     * Set the x property: The horizontal position (increases moving right).
+     *
+     * @param x the x value to set.
+     * @return the Point object itself.
+     */
+    public Point setX(float x) {
+        this.x = x;
+        return this;
+    }
+
+    /**
+     * Get the y property: The vertical position (increases moving down).
+     *
+     * @return the y value.
+     */
+    public float getY() {
+        return this.y;
+    }
+
+    /**
+     * Set the y property: The vertical position (increases moving down).
+     *
+     * @param y the y value to set.
+     * @return the Point object itself.
+     */
+    public Point setY(float y) {
+        this.y = y;
+        return this;
+    }
+}
+```
+
+</ClientTabItem>
+<ClientTabItem lang="go">
+
+```go
+// Shape - Base class for all shape objects that can be drawn on a canvas
+type Shape struct {
+	// Position - The x,y coordinates where the shape will be positioned on the canvas
+	Position *Point
+}
+
+// Point - Represents a position in a 2D coordinate system
+type Point struct {
+	// X - The horizontal position (increases moving right)
+	X *float32
+
+	// Y - The vertical position (increases moving down)
+	Y *float32
+}
+```
+
+</ClientTabItem>
+</ClientTabs>
+
+The `@clientDoc` decorator can be used on:
+
+- Models
+- Model properties
+- Operations
+- Operation parameters
+- Enums
+- Enum members
path: website/src/content/docs/docs/howtos/Generate client libraries/07types.mdx
user: login: srnagar
body: We should also make it available for anything that can have a `@doc` decorator. This includes `Namespace`, `Interface`, `Union` etc. I think the decorator's target is `unknown` which implies it works for all of the above. So, we should need to update this documentation.
created_at: 2025-04-14T17:55:23Z
author_association: MEMBER
diff_hunk: @@ -663,3 +663,57 @@ extern dec deserializeEmptyStringAsNull(target: ModelProperty, scope?: valueof s
  * ```
  */
 extern dec responseAsBool(target: Operation, scope?: valueof string);
+
+/**
+ * Defines how client documentation should be applied
+ */
+enum DocumentationMode {
+  /**
+   * Append client documentation to the existing doc
+   */
+  append: "append",
path: packages/typespec-client-generator-core/lib/decorators.tsp
user: login: iscai-msft
body: i think it would be better to add a `@clientSummary` decorator instead if we went down that route for greater symmetry, so I think we can keep the enum options the same for now
created_at: 2025-04-14T18:02:33Z
author_association: CONTRIBUTOR

PR comments:
user: login: iscai-msft
created_at: 2025-04-10T21:47:44Z
body: I don't htink we need any specs tests bc as long as tcgc does the right thing here, all emitters should be fine. There's also not a great testserver way of testing this, since it is ultimately client-level doc generation
author_association: CONTRIBUTOR
user: login: azure-sdk
created_at: 2025-04-10T21:48:40Z
body: <!--chronus-github-change-commenter-->
All changed packages have been documented.
 - :white_check_mark: `@azure-tools/typespec-client-generator-core`

<details>
<summary>Show changes</summary>

### `@azure-tools/typespec-client-generator-core` - _feature_ [✏️](https://github.com/iscai-msft/typespec-azure/edit/tcgc/addClientDocDecorator/.chronus/changes/tcgc-addClientDocDecorator-2025-3-10-17-39-23.md?pr=/Azure/typespec-azure/pull/2523)
> add `@clientDoc` decorator
</details>

author_association: COLLABORATOR
user: login: azure-sdk
created_at: 2025-04-10T22:00:08Z
body: <!-- _TSP_TRYIT_COMMENT_ -->
You can try these changes here

| [🛝 Playground]( https://cadlplayground.z22.web.core.windows.net/typespec-azure/prs/2523/)|[🌐 Website](https://tspwebsitepr.z22.web.core.windows.net/typespec-azure/prs/2523/) |
| ---|--- |
author_association: COLLABORATOR
user: login: iscai-msft
created_at: 2025-04-14T15:02:08Z
body: currently we're just adding for `doc`. I think if we get an ask for `@clientSummary` we can add it. @srnagar what are your thoughts?
author_association: CONTRIBUTOR