All skills
lukemurraynz avatar

/typespec-api-design

@2cc2455

Design new API-first, contract-first contracts with TypeSpec, OpenAPI/swagger output, Azure data-plane and ARM patterns, versioning, pagination, LROs, CI validation, and agent-ready API boundaries. WHEN: create API spec, API-first design, contract-first, scaffold TypeSpec project, generate OpenAPI or swagger, define REST contract, add API versioning, define LRO, migrate OpenAPI to TypeSpec, Azure API review, SpecKit API.

Use this Skill: https://skilld.dev/gh/lukemurraynz/hve-agent-skills/typespec-api-design

This session only. Nothing lands on disk.

SKILL.md

≈111 tokens always: the name and description. ≈8.9k when used: this file. ≈33k more on demand in 12 files.

TypeSpec API design

Local toolchain ground truth (re-verified 2026-08-26): the locally installed tsp is v1.12.0, which LAGS npm latest stable (1.15.0 as of 2026-08-26). Never copy the local version into package.json pins - resolve every pin with npm view <package> version (see Modern baseline).

Use this skill to design and maintain API contracts where TypeSpec is the source of truth and generated OpenAPI/Swagger/SDK artifacts are build outputs.

Assume new projects unless the task explicitly says otherwise. Prefer modern TypeSpec patterns, Azure-aligned guidance where appropriate, and clean REST contracts that can later be consumed by portals, SDKs, agents, APIM, MCP, or Microsoft 365 Copilot extensions.

Use When

Should trigger Should NOT trigger Nearby skill collision
The task is to design, review, scaffold, or migrate an API contract where TypeSpec should be the source of truth The user only wants code-first controller or handler implementation with no contract-design intent Hand off to the framework skill for pure implementation work once the contract is stable
You need OpenAPI generation, Azure API patterns, versioning, pagination, LROs, or client-generation-ready contracts The target is a non-HTTP contract such as GraphQL or gRPC Use transport-specific skills for non-REST surfaces
The API must be agent-consumable, SDK-friendly, or Azure-review-ready before downstream tooling work The request is manifest/plugin metadata without a need to stabilize the REST contract Coordinate with app/plugin skills after this skill defines the HTTP contract

Anti-Hallucination Rule (MANDATORY before writing TypeSpec)

TypeSpec (formerly Cadl) has a rapidly evolving decorator surface across @typespec/compiler, @typespec/http, @typespec/rest, @typespec/openapi3, @typespec/versioning, and the Azure ecosystem libraries (@azure-tools/typespec-azure-core, @azure-tools/typespec-azure-resource-manager, @azure-tools/typespec-client-generator-core). Decorator names (@route, @get, @post, @error, @statusCode, @autoRoute), template parameters, model inheritance via extends vs is, and emitter options drift between TypeSpec minors. Azure data-plane vs ARM resource patterns use different libraries entirely.

Before writing any TypeSpec decorator, model definition, operation signature, emitter option, or tspconfig.yaml field, ground each identifier using one of these methods:

  1. Installed TypeSpec compiler and library versions: tsp --version and cat package.json for @typespec/* and @azure-tools/typespec-* versions. Read the library's lib/main.tsp for available decorators.
  2. TypeSpec compiler output: tsp compile <file.tsp> --emit @typespec/openapi3 and inspect the generated OpenAPI. If a decorator was wrong, compile will error; if it was silently ignored, the generated OpenAPI will be missing fields.
  3. @azure-tools/typespec-azure-core library (npm latest 0.71.0 as of 2026-08-26 - verify npm ls @azure-tools/typespec-azure-core before writing) for Azure data-plane APIs: ResourceList, ResourceCreateOrUpdate, ResourceDelete, Page<T> are library-defined; do not invent variants.
  4. @azure-tools/typespec-azure-resource-manager (npm latest 0.71.0 as of 2026-08-26) for ARM templates: TrackedResource, ProxyResource, ResourceOperations come from this library only.
  5. @azure-tools/typespec-client-generator-core (TCGC) for client/SDK shaping: SdkOperationGroup was consolidated into SdkClient (operation groups are now SdkClient instances) and @operationGroup is deprecated in favor of @client for sub-clients (Azure/typespec-azure#3997). These are TCGC concepts - NOT @azure-tools/typespec-azure-core. Verify with npm ls @azure-tools/typespec-client-generator-core (npm latest 0.71.2 as of 2026-08-26 - the npm package version is independent of the typespec-azure@X.Y.Z GitHub monorepo tag; the two are not the same number).
  6. tspconfig.yaml schema for the pinned compiler version: the emit and options.* shape is version-specific.

Forbidden shortcuts:

  • Do not write @autoRoute if the project pinned a TypeSpec version where it was renamed or removed. Verify against installed @typespec/http.
  • Do not invent @error vs @errorResponse vs @statusCode semantics. The library defines exactly one.
  • Do not mix Azure data-plane (@azure-tools/typespec-azure-core) and ARM (@azure-tools/typespec-azure-resource-manager) patterns in the same service.
  • Do not write LRO (LongRunningOperation / LongRunningResourceCreateOrReplace) signatures from memory; the Azure libraries provide canonical templates.
  • Do not run tsp compile with --no-emit only; the emitter is where wrong identifiers produce silent OpenAPI omissions.

Known asymmetry traps (illustrative, verify each):

  • @route vs @autoRoute: @route is explicit; @autoRoute infers from parameter decorators. Mixing them produces conflicting routes.
  • extends vs is for model composition: extends is inheritance with merged properties; is is alias-like and changes the spread/include semantics.
  • Page<T> is the canonical Azure-Core pagination model; Cursor and PagedResultOf are not. Invented pagination types break Azure SDK generation.
  • ARM Resource requires id, name, type, systemData from the ARM library - do not write these properties manually.
  • @server decorator host parameter formatting follows OpenAPI 3.x server template variables; invented {baseUrl} placeholders break Azure SDK client gen.
  • TypeSpec compiler - latest stable 1.15.0 (2026-08-11; verified 2026-08-26): FilterVisibility template replaces @withVisibilityFilter (deprecated); @withLifecycleUpdate and @applyMergePatch are deprecated. Recent stable releases (1.14/1.15) added project-scoped config and feature flags, graduated the internal modifier, extended enum-strategy: annotated, and changed file-level using resolution - no decorator-surface break to the patterns this skill teaches, but re-run compile after any upgrade. Note that @typespec/* packages ship on split version tracks - @typespec/compiler/http/openapi3 are 1.15.0 while @typespec/rest/versioning are 0.85.0; see bundles/emitter-gotchas/guide.md. Always verify against the installed @typespec/compiler version before writing these decorators.
  • Client-generator (TCGC) and ARM deprecations (verified 2026-08-26): In @azure-tools/typespec-client-generator-core, SdkOperationGroup was consolidated into SdkClient (operation groups are now SdkClient instances) and @operationGroup is deprecated in favor of @client for sub-clients (Azure/typespec-azure#3997). In @azure-tools/typespec-azure-resource-manager, @locationResource is deprecated - use @parentResource(ArmLocationResource<...>) (Azure/typespec-azure#4132). Current npm latest versions (as of 2026-08-26, verify before pinning): @azure-tools/typespec-azure-core 0.71.0, -azure-resource-manager 0.71.0, -autorest 0.71.0, -client-generator-core (TCGC) 0.71.2. The Azure packages currently sit on a shared 0.7x family but version independently of each other and of the TypeSpec core track (1.15.0) - always resolve each package's real npm version with npm view <package> version; never copy a number from the typespec-azure@X.Y.Z GitHub monorepo release tag (a tag groups a coordinated set but is not itself any package's npm version).

Safe degraded output when verification is blocked

If you cannot run tsp compile, read installed @typespec/* lib/main.tsp files, or check tspconfig.yaml schema, do not produce production-shaped TypeSpec from memory. Wrong decorator silently omits an OpenAPI property; wrong Azure library template generates an SDK client that calls the wrong endpoint.

Instead, return:

  1. A labelled skeleton with [VERIFY] markers on every decorator, library import, model template, operation template, and tspconfig.yaml field.
  2. Verification commands: npm ls @typespec/compiler @typespec/http @typespec/rest @typespec/openapi3, tsp compile <file> --emit @typespec/openapi3, the library lib/main.tsp path, the Azure-Core/ARM library README URL.
  3. Identifier guess list grouped by failure mode (compile failure / silently omitted OpenAPI property / wrong SDK client generation / ARM vs data-plane mismatch / LRO contract not recognized by client gen).
  4. A design-level artifact offer: API surface inventory (resources, operations, error model), versioning plan (@added / @removed / @versioned), pagination strategy, LRO contract decisions, client-gen target language list, OpenAPI emitter options decision record.

Do not produce speculative TypeSpec under blocked verification. The cost of an omitted decorator is a silently incomplete OpenAPI, which downstream SDK generators do not catch.

Quick start

From an empty directory, four commands take you to a compiled spec:

npx -y -p @typespec/compiler tsp init
npm install
npx tsp compile . --warn-as-error
npx tsp format "**/*.tsp"

Choose rest (interactive label "Generic REST API service") from the prompt - template names vary by compiler version.

tsp init scaffolds main.tsp, tspconfig.yaml, and package.json from the selected template. npm install resolves the pinned TypeSpec packages, tsp compile produces emitter output and fails on any warning, and tsp format normalizes all .tsp files in place. Open tsp-output/@typespec/openapi3/openapi.yaml to inspect the generated OpenAPI document.

Routing

Use this skill when the task involves any of the following:

  • Creating or reviewing a TypeSpec project.
  • Designing a REST API contract before implementation.
  • Generating OpenAPI 3.0/3.1, Swagger, or SDK-facing specs. (AutoRest retired July 1, 2026 - do not generate AutoRest inputs for new work.)
  • Azure data-plane or Azure Resource Manager API design.
  • API versioning, LROs, pagination, repeatability, conditional requests, or API review readiness.
  • Migrating a hand-written OpenAPI 3.x spec into TypeSpec.
  • Making an API contract agent-consumable before another skill creates tools, manifests, MCP servers, or plugins.

Do not use this skill as the primary skill for:

  • Code-first API implementation unless the task asks to derive or validate the contract.
  • Microsoft 365 Copilot declarative agents, plugin manifests, or Copilot-specific decorators. Stabilize the REST contract here, then route to the Microsoft 365 Copilot/App-as-Skill skill.
  • Existing stable internal APIs where a validated code-first OpenAPI pipeline already exists and no redesign is planned.

Stop conditions

Hand off or refuse when:

  • User wants code-first scaffolding (controllers, handlers) without API design intent - use a language/framework skill instead.
  • Target is M365 Copilot declarative agent metadata or plugin manifest - hand to the Microsoft 365 Copilot/App-as-Skill skill after the REST contract is stable.
  • Target is a non-REST transport (gRPC, WebSocket). TypeSpec ships first-party emitters for protobuf and (since July 2026) GraphQL (@typespec/graphql), but this skill scopes to REST/OpenAPI contract design; route GraphQL-emitter work to its own documentation.
  • User has a stable code-first OpenAPI pipeline and no redesign is planned - do not force a TypeSpec rewrite.
  • User wants AutoRest-annotated Swagger 2.0 for new work - deprecated; redirect to bundles/migration-from-openapi/.

Hard rules

Bundles inherit these hard rules; load this section first when working from any bundle.

  1. Write TypeSpec first; emit OpenAPI/Swagger/SDK inputs from TypeSpec.
  2. Never hand-edit tsp-output/ or generated OpenAPI/Swagger files.
  3. Commit .tsp, tspconfig.yaml, package.json, lockfiles, examples, and CI. Treat generated outputs as artifacts unless the repository policy requires checked-in swagger.
  4. Every public model, property, operation, parameter, response, and error needs durable documentation.
  5. Declare authentication at the service namespace with @useAuth(...) unless the API is explicitly public and unauthenticated.
  6. Add versioning from the first service version for Azure APIs and for any external or partner-facing API.
  7. Use Azure Core standard operation templates for Azure data-plane APIs unless there is a documented exception.
  8. Do not use @operationId for Azure APIs. Let operation IDs derive from interface and operation names. Operations MUST live inside an interface whose name is the resource (PascalCase, plural). This produces operation IDs in the form <Resource>_<Verb> (e.g., Widgets_Get). Bare op declarations outside an interface produce inconsistent IDs.
  9. Do not use @format(...) for Azure APIs when a concrete scalar exists. Prefer uuid, url, eTag, ipV4Address, ipV6Address, utcDateTime, and explicit numeric widths.
  10. Constrain resource keys and path parameters with @maxLength and @pattern.
  11. Validate with npx tsp compile . --warn-as-error; do not rely on a separate tsp lint command.
  12. Use official docs or the TypeSpec/Azure TypeSpec MCP server before emitting version-sensitive decorators, templates, or package versions.
  13. AutoRest retired on July 1, 2026. Do not generate or recommend autorest or @autorest/* inputs, annotations, or pipelines - the retirement date has passed. TypeSpec with its built-in emitters is the approved replacement. For migration tasks, use the bundles/migration-from-openapi/ bundle and flag any remaining AutoRest dependency as a live blocker requiring immediate resolution. This refers to the retiring autorest CLI and @autorest/* npm packages. The TypeSpec emitter @azure-tools/typespec-autorest (which produces Swagger 2.0 + x-ms-* extensions for Azure SDK pipelines) is a separate, supported package - do NOT confuse the two.
  14. All @server(...) URLs MUST use https:// for non-local environments. http:// is only acceptable for localhost development servers.
  15. Pin TypeSpec packages to exact patch versions in package.json (1.15.0, not ^1.15.0) and commit the lockfile. Use npm ci in CI, never npm install.
  16. Every error response model includes a code string field (machine-readable, stable across versions) alongside the human-readable message.
  17. Never put PII (email, name, SSN, phone, account number) in URL path or query parameters. PII travels in the request body, or in headers if a header is unavoidable.
  18. Paginated list operations declare @maximum on top/limit/pageSize parameters to prevent pagination DoS. Cap at a documented sane maximum (typically 100).
  19. Server-assigned fields (id, createdAt, updatedAt, eTag, provisioningState) MUST carry @visibility(Lifecycle.Read) so they cannot be set via Create/Update payloads (mass-assignment defense).
  20. Correlation headers (traceparent per W3C Trace Context, or x-ms-client-request-id) MUST be modelled on operations exposed to external or partner callers, on both request and response. Correlation IDs never travel in URL paths or query strings.
  21. Deprecated operations MUST carry both the #deprecated directive AND the runtime header trio (Deprecation, Sunset, Link rel="successor-version"). The directive alone is insufficient - live clients see headers, not source code.
  22. Any API that delivers webhooks or fetches a client-supplied URL MUST document its SSRF runtime policy on the URL field's @doc: HTTPS-only, no private/loopback/link-local/cloud-metadata IPs, port allow-list, redirect cap, response-size cap, total timeout. See bundles/webhooks/guide.md.
  23. Design the contract so any replica can serve any request: no server-affinity semantics (no "call the same instance that handled your last request"), no client-visible instance/session IDs, no state implied by prior calls beyond what a shared store (DB/cache) or an opaque continuation/cursor token carries.
  24. Every POST/PATCH that creates or mutates a resource MUST accept a client-supplied idempotency key header (Idempotency-Key for generic/non-Azure REST APIs, Repeatability-Request-ID/Repeatability-First-Sent from @azure-tools/typespec-azure-core for Azure data-plane APIs), not just as a documented example. This applies regardless of API style - generic REST, Azure data-plane, or ARM. Apply exactly ONE scheme per API surface - do not mix Idempotency-Key and Repeatability-* on the same operations. Retries after a dropped connection are the normal case under horizontal scaling, not an edge case.
  25. Rate-limited and overloaded responses (429, 503) MUST include Retry-After. Any API with a request quota MUST expose RateLimit-Limit/RateLimit-Remaining/RateLimit-Reset (IETF RateLimit header fields draft) so clients get consistent backpressure regardless of which replica answered.
  26. Every service exposes unauthenticated GET /health (liveness) and GET /ready (readiness, checks dependencies) endpoints outside the versioned API surface, for load balancer and orchestrator probes.

Default output contract

When applying this skill, produce these sections unless the user asks for a narrower output:

  1. API design summary and assumptions.
  2. Recommended project structure.
  3. package.json with pinned compatible TypeSpec packages.
  4. tspconfig.yaml with emitters, linter rules, and warn-as-error.
  5. main.tsp and any split model/operation files.
  6. Example request/response and error shapes.
  7. Expected emitted artifact paths.
  8. CI validation commands or pipeline snippet.
  9. API review checklist.
  10. Known open design decisions.

Bundle routing

Load only the bundles needed for the task:

Task Bundle Covers
Generic non-Azure REST API bundles/generic-rest/guide.md service scaffold, models, routes, errors, OpenAPI emitter wiring
Azure data-plane API bundles/azure-data-plane/guide.md Entra auth, ResourceOperations templates, conditional requests, repeatability
Azure Resource Manager API bundles/azure-arm/guide.md ARM tracked/proxy resources, identity, SystemData, RPC contract
LROs, pagination, versioning, repeatability bundles/lro-pagination-versioning/guide.md @pollingOperation, Page<T>, @versioned, Repeatability-* headers
Existing OpenAPI 3.x migration bundles/migration-from-openapi/guide.md tsp-openapi3 conversion, cleanup, AutoRest retirement path
CI, validation, package hygiene bundles/ci-validation/guide.md npm ci, --warn-as-error, lockfile policy, emitter artifacts
Microsoft 365 Copilot/App-as-Skill boundary bundles/m365-copilot-boundary/guide.md stable operation IDs, doc coverage, agent-safe response shapes
Webhooks and SSRF defense for outbound/inbound URLs bundles/webhooks/guide.md subscription resource, HMAC signing + replay window, SSRF runtime checks, retry/back-off, verification handshake
Emitter/versioning gotchas, package version-tracks, unsupported emitters bundles/emitter-gotchas/guide.md issue-grounded emit-time traps (@statusCode via alias, unions, versioned examples, Record keys), split @typespec/* version tracks, shipped-vs-absent emitters (GraphQL shipped July 2026; AsyncAPI/WebSocket absent)
Final quality gate bundles/review-checklist/guide.md API review checklist, breaking-change scan, sign-off gates

Naming conventions

  • Models: PascalCase singular (Widget, not Widgets).
  • Properties: camelCase (createdAt, not created_at).
  • Enum members: PascalCase (Active, Maintenance).
  • Interfaces (resource collections): PascalCase plural (Widgets).
  • URL path segments: camelCase plural matching the interface (/widgets).
  • Operation verbs in the interface: camelCase (list, read, create, update, delete).

Modern baseline

Use the following baseline for new projects unless a repository already pins versions:

  • Node 22 LTS minimum, Node 24 LTS recommended for Azure work.
  • @typespec/compiler and all @typespec/* libraries on the same stable release family.
  • Azure packages matched to the current Azure/typespec-azure release family when using Azure libraries.
  • tspconfig.yaml, not tspconfig.yml.
  • npx tsp compile . --warn-as-error for CI validation.
  • tsp-output/ ignored locally and published as a build artifact.

Latest verified baseline at skill update time, 2026-08-26:

  • TypeSpec stable family (split version tracks): @typespec/compiler, @typespec/http, @typespec/openapi3 are 1.15.0 (2026-08-11); @typespec/rest and @typespec/versioning are 0.85.0 (same release train). These are individual npm package versions; the typespec-stable@<tag> GitHub release tag groups a coordinated set.
  • Azure TypeSpec packages (npm latest, 2026-08-26): @azure-tools/typespec-azure-core 0.71.0, @azure-tools/typespec-azure-resource-manager 0.71.0, @azure-tools/typespec-autorest 0.71.0, @azure-tools/typespec-client-generator-core (TCGC) 0.71.2. The Azure packages currently share a 0.7x family but version independently of each other and of the TypeSpec core track - never assume one number covers all.
  • The typespec-azure@X.Y.Z GitHub monorepo release tag is NOT a package's npm version (a typespec-azure@0.70.x tag can ship @azure-tools/typespec-azure-core@0.71.0). Always resolve the real npm version with npm view <package> version (or the package's npm page), not from the monorepo tag.
  • Package-level Azure releases move independently within the release family; verify each @azure-tools/typespec-* package version at https://github.com/Azure/typespec-azure/releases or via npm view before pinning.
  • OpenAPI 3 conversion uses the tsp-openapi3 CLI from @typespec/openapi3.

Core project shape

my-api/
  package.json
  tspconfig.yaml
  main.tsp
  models/
    *.tsp
  operations/
    *.tsp
  examples/
    *.json
  .gitignore
  .github/workflows/typespec.yml   # or azure-pipelines.yml

The examples/ directory holds JSON example payloads referenced from @example(...) decorators on operations and models; they flow into the generated OpenAPI examples fields.

Minimum .gitignore:

tsp-output/
node_modules/

Generic service example

Minimal example

import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi3";

using Http;
using Rest;

@service(#{ title: "Contoso Widget API" })
@server("https://api.contoso.example", "Production endpoint")
@useAuth(BearerAuth)
namespace Contoso.Widgets;

/** A widget managed by the service. */
model Widget {
  @visibility(Lifecycle.Read) id: string;
  @minLength(1) @maxLength(120) name: string;
  status: "active" | "inactive" | "maintenance";
  @visibility(Lifecycle.Read) createdAt: utcDateTime;
}

/** Standard error response. */
@error
model ErrorResponse {
  @statusCode statusCode: 400 | 401 | 403 | 404 | 409 | 429 | 500;
  code: string;
  message: string;
}

@route("/widgets")
interface Widgets {
  /** List widgets. */
  @get list(@query filter?: string): Widget[] | ErrorResponse;
  /** Get a widget by identifier. */
  @get read(@path id: string): Widget | ErrorResponse;
  /** Create a widget. */
  @post create(@body widget: Widget): Widget | ErrorResponse;
}

Production-ready example

import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
import "@typespec/openapi3";

using Http;
using Rest;
using Versioning;

@service(#{ title: "Contoso Widget API" })
@server("https://api.contoso.example", "Production endpoint")
@useAuth(BearerAuth)
@versioned(Versions)
namespace Contoso.Widgets;

enum Versions {
  v2026_01_01: "2026-01-01",
}

/** Single page of results. */
// This `Page<T>` is a generic-API pattern. For Azure data-plane work use `Azure.Core.Page<T>` from `@azure-tools/typespec-azure-core` instead — it has shape `{ @pageItems value: T[]; @nextLink nextLink?: ResourceLocation<T>; }`.
model Page<T> {
  @doc("Items in the current page.")
  items: T[];

  @doc("Opaque cursor for the next page; absent on the last page.")
  nextLink?: url;
}

/** One field-level error detail. */
model ErrorDetail {
  code: string;
  message: string;
  target?: string;
}

/** Standard error envelope. */
@error
model ErrorResponse {
  @statusCode statusCode: 400 | 401 | 403 | 404 | 409 | 412 | 429 | 500;
  code: string;
  message: string;
  target?: string;
  details?: ErrorDetail[];
}

/** A widget managed by the service. */
model Widget {
  @visibility(Lifecycle.Read)
  @maxLength(64)
  @pattern("^[a-zA-Z0-9-]+$")
  id: string;

  @minLength(1)
  @maxLength(120)
  name: string;

  status: "active" | "inactive" | "maintenance";

  @visibility(Lifecycle.Read)
  createdAt: utcDateTime;

  @visibility(Lifecycle.Read)
  @header("ETag")
  etag: string;
}

@route("/widgets")
interface Widgets {
  /** List widgets (paginated). */
  // `@maximum(100)` caps page size — prevents pagination DoS (hard rule #18).
  @get list(@query filter?: string, @query @maximum(100) top?: int32): Page<Widget> | ErrorResponse;

  /** Get a widget by identifier. */
  @get read(
    @path @maxLength(64) @pattern("^[a-zA-Z0-9-]+$") id: string,
  ): Widget | ErrorResponse;

  /** Create a widget. */
  @post create(
    @header("Repeatability-Request-ID") repeatabilityRequestId: string,
    @body widget: Widget,
  ): Widget | ErrorResponse;

  /** Replace a widget. */
  @put update(
    @path @maxLength(64) @pattern("^[a-zA-Z0-9-]+$") id: string,
    @header("If-Match") ifMatch: string,
    @body widget: Widget,
  ): Widget | ErrorResponse;

  /** Delete a widget. */
  @delete delete(
    @path @maxLength(64) @pattern("^[a-zA-Z0-9-]+$") id: string,
    @header("If-Match") ifMatch: string,
  ): void | ErrorResponse;

  /** Archive a widget. Added in 2026-01-01. */
  @added(Versions.v2026_01_01)
  @route("/{id}:archive")
  @post archive(
    @path @maxLength(64) @pattern("^[a-zA-Z0-9-]+$") id: string,
  ): Widget | ErrorResponse;
}

Common commands

npm ci
npx tsp compile . --warn-as-error
npx tsp compile . --watch
npx tsp format "**/*.tsp"
npx tsp info

Do not prescribe tsp lint . for new projects unless the repository has proven that command exists in its pinned compiler version. Configure linter rules in tspconfig.yaml and fail CI with tsp compile . --warn-as-error.

Troubleshooting

  • Cannot find name 'BearerAuth' - Missing using Http;. Add it (and import "@typespec/http";) at the top of the file.
  • Diagnostics were reported during compilation with --warn-as-error - Run npx tsp compile . without the flag first to see the underlying warnings, then fix them and re-run with --warn-as-error.
  • tsp-output/ empty after compile - The emit: list in tspconfig.yaml is missing or wrong. Confirm @typespec/openapi3 (and any other emitters) are listed and installed.
  • tsp init fails behind a proxy - Set npm_config_registry (and HTTPS_PROXY if required) before running, or pre-fetch the template into a local registry mirror.
  • Mixed Azure + non-Azure libraries with version conflicts - Align all @azure-tools/typespec-* packages to the same typespec-azure release family; do not mix release lines.
  • Node version mismatch - TypeSpec 1.x now requires Node 22+ (microsoft/typespec CONTRIBUTING.md bumped to Node 22). Azure TypeSpec recommends Node 24 LTS. Check node --version and upgrade if older.
  • Corporate proxy SSL errors (UNABLE_TO_GET_ISSUER_CERT_LOCALLY) - Set NODE_EXTRA_CA_CERTS=/path/to/corporate-bundle.pem or npm config set cafile /path/to/corporate-bundle.pem. Setting only HTTPS_PROXY is not enough when the proxy MITMs TLS.
  • VS Code TypeSpec extension not activating in a monorepo subproject - Set typespec.tsp-server.path in workspace settings to point at the project-local compiler (e.g., ./node_modules/.bin/tsp-server). The extension's default lookup misses nested tspconfig.yaml files.
  • duplicate-type-name errors - Usually caused by @friendlyName collisions or by @parameterVisibility producing sibling shapes the emitter names identically. Run npx tsp compile . (no flags) to see the diagnostic location; rename one of the colliding constructs.
  • Stale tsp-output/ - If output looks stale after edits, delete tsp-output/ and recompile; emitters cache aggressively.
  • VS Code TypeSpec extension not activating - Install the official TypeSpec extension; ensure your workspace folder contains tspconfig.yaml at the root.
  • Peer-dep warnings during npm ci - Align all @typespec/* and @azure-tools/typespec-* packages to the same release family (see Modern baseline).
  • Windows path issues with tsp format - Use forward slashes in glob patterns: tsp format "**/*.tsp", not tsp format "**\*.tsp".

Glossary

  • TypeSpec - Microsoft's API description language that compiles to OpenAPI, JSON Schema, and SDK inputs.
  • Emitter - A TypeSpec compiler plugin that turns the compiled program into output files (e.g., @typespec/openapi3).
  • Decorator - A @name(...) annotation that attaches metadata or behavior to a model, operation, parameter, or namespace.
  • LRO - Long-running operation; an async REST pattern that returns 202 plus a polling URL.
  • ARM - Azure Resource Manager; the control-plane API surface for Azure resources.
  • Data plane - The runtime API surface a service exposes to its customers, distinct from ARM control-plane operations.
  • Repeatability - The Repeatability-Request-ID/Repeatability-First-Sent header convention that makes POST/PATCH safely retriable.
  • Visibility - A model property modifier (Lifecycle.Read, Lifecycle.Create, etc.) that controls which payloads include the property.
  • Scalar - A TypeSpec primitive or named refinement of a primitive (e.g., uuid, eTag, utcDateTime).
  • Augment decorator (@@) - A top-level form (@@doc(Target, "...")) used to attach a decorator to a target declared elsewhere, including imported libraries.
  • @key - Marks a model property as the resource key; required by resource operation templates and used to derive path parameters.
  • interface - A grouping of related operations on a resource; produces the prefix of generated operation IDs (Widgets_Get).
  • namespace - A logical container for models, operations, and the service declaration; the root namespace decorated with @service defines the service surface.
  • model is X vs model extends X - is creates an alias/copy with the same shape (used with templates like is ResourceOperation<...>); extends creates a subtype that inherits properties and supports polymorphism.
  • Template - A parameterized type (model Page<T>, op StandardResourceOperation<...>) instantiated with concrete type arguments; the primary reuse mechanism in TypeSpec.
  • Union - A type composed of alternatives ("active" | "inactive" or Cat | Dog); named unions support discriminators for polymorphism.
  • op - Declares an operation; prefer placing operations inside an interface so operation IDs derive from <Interface>_<op>.

Agent-readiness baseline

Any API intended for Copilot, MCP, or other agent consumption MUST satisfy these minimums before handoff:

  • Stable operation IDs derived from interface and operation names (do not use @operationId).
  • @doc on every operation, parameter, and response model.
  • Explicit error responses for every operation, with a typed error envelope.
  • No anyOf / oneOf polymorphism without a discriminator; agents cannot reliably route untagged unions.
  • Bounded response shapes: paginated lists, @maxLength/@maxItems on user-facing strings and arrays, no open-ended Record<unknown> payloads.

See bundles/m365-copilot-boundary/guide.md for the full handoff checklist used when crossing into agent/manifest territory.

Final Output Contract

Every engagement produces:

  • A TypeSpec-first contract recommendation or scaffold aligned to the requested API style.
  • Pinned package, emitter, and validation guidance needed to compile and inspect the contract safely.
  • Example shapes for operations, errors, versioning, and other behaviors that materially affect the API surface.
  • Explicit open design decisions or [VERIFY] markers for any version-sensitive identifier that could not be confirmed.

Quality Gate

Do not finish with speculative decorators, mixed Azure library tracks, or unvalidated emitter assumptions. The result must either compile under the pinned toolchain or clearly identify what still needs verification before production use.

Hidden Decorator Surface

TypeSpec has 50+ decorators. These are present in the @typespec/http, @typespec/rest, and @typespec/openapi libraries but easily missed:

Decorator Impact
@server(url: string, description?: string, variables?: Record<string, ...>) Server URL with templated variables and defaults
@useAuth(MyAuth) / `@useAuth(MyAuth OtherAuth)`
@route(path: string, ?shared: boolean) Route template with {pathParam} variables
@query("paramName", ?format: "csv" | "multi") Query parameter serialization format
@header(name: string, ?format: "csv" | "multi") Header parameter with format options
@visibility("read", "create", "update", "delete") Per-visibility property exposure — Azure data-plane pattern
@withVisibility("read") Apply visibility filter to access specific properties
@extension("x-custom", value) OpenAPI extension — arbitrary spec extensions
@encode("base64", typeof bytes) Encoding directives base64, base64url, date, rfc1123, unixTimestamp
@minValue(N) / @maxValue(N) / @minLength(N) / @maxLength(N) Validation constraints on scalar types
@pattern(regex) String pattern validation
@format("email" | "uri" | "uuid") Known format annotations
@added(Version) / @removed(Version) / @madeOptional(Version) API versioning lifecycle decorators
@opExample / @opResponseExample Operation and response examples in spec
@statusCode(N) Explicit HTTP status code on return model

References to verify before version-sensitive work

  • TypeSpec docs: https://typespec.io/docs
  • TypeSpec CLI docs: https://typespec.io/docs/handbook/cli/
  • TypeSpec configuration docs: https://typespec.io/docs/handbook/configuration/configuration/
  • OpenAPI 3 emitter guide: https://typespec.io/docs/emitters/openapi3/openapi/
  • TypeSpec releases: https://github.com/microsoft/typespec/releases
  • Azure TypeSpec docs: https://azure.github.io/typespec-azure/docs/intro/
  • Azure TypeSpec style guide: https://azure.github.io/typespec-azure/docs/reference/azure-style-guide/
  • Azure TypeSpec releases: https://github.com/Azure/typespec-azure/releases
  • Microsoft 365 Copilot TypeSpec overview: https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/overview-typespec

Source: SKILL.md on GitHub

No alerts8d3 checks · Risk SAFE
  • Gen Agent Trust Hub8d

    This skill is a comprehensive and legitimate tool for designing API-first contracts using TypeSpec and Azure patterns. It includes extensive security best practices, particularly regarding SSRF defense, PII handling, and idempotency. The only detected concern is a standard vulnerability surface for indirect prompt injection when processing external OpenAPI specifications for migration.

  • Socket8d

    No alerts

  • Snyk8d

    Risk: LOW · No issues

Signed by skilld at 2cc2455. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated last month
metadata
{
  "last_verified": "2026-08-26",
  "version": "1.3.1"
}

README badge

README badge for lukemurraynz/hve-agent-skills/typespec-api-design