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.

bundlesazure-data-planeguide.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Azure data-plane API bundle

Use this bundle for Azure-aligned data-plane APIs and for non-Azure APIs that intentionally want Microsoft API Guidelines-style rigor.

Recommended dependencies

{
  "type": "module",
  "private": true,
  "scripts": {
    "build": "tsp compile . --warn-as-error",
    "watch": "tsp compile . --watch",
    "format": "tsp format \"**/*.tsp\""
  },
  "dependencies": {
    "@typespec/compiler": "<verified-compatible-version>",
    "@typespec/http": "<verified-compatible-version>",
    "@typespec/rest": "<verified-compatible-version>",
    "@typespec/openapi3": "<verified-compatible-version>",
    "@typespec/versioning": "0.85.0",
    "@azure-tools/typespec-azure-core": "0.71.0",
    "@azure-tools/typespec-autorest": "0.71.0"
  }
}

Before committing, replace <verified-compatible-version> with versions verified from official TypeSpec/Azure TypeSpec release notes, the repository lockfile, or tsp init https://aka.ms/typespec/azure-init output. Azure TypeSpec packages can move independently.

Recommended imports

import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
import "@azure-tools/typespec-azure-core";

using Http;
using Rest;
using Versioning;
using Azure.Core;

Recommended tspconfig.yaml

warn-as-error: true
emit:
  - "@azure-tools/typespec-autorest"
  - "@typespec/openapi3"
linter:
  extends:
    - "@azure-tools/typespec-azure-core/all"
options:
  "@azure-tools/typespec-autorest":
    emitter-output-dir: "{output-dir}/swagger"
  "@typespec/openapi3":
    openapi-versions:
      - "3.0.0"

Use @azure-tools/typespec-autorest for Azure SDK-facing output. @typespec/openapi3 alone does not emit Azure x-ms-* extensions required by Azure SDK tooling.

Service skeleton

@service(#{ title: "Contoso Widget Service" })
@server("https://{accountName}.widgets.contoso.azure.com", "Service endpoint", {
  /** Azure resource account name. */
  accountName: string;
})
@versioned(Contoso.WidgetService.Versions)
@useAuth(EntraIDToken)
namespace Contoso.WidgetService;

/** Service API versions. */
enum Versions {
  /** Initial stable release. */
  v2026_05_10: "2026-05-10",
}

/** Microsoft Entra ID OAuth2 flow. */
model EntraIDToken is OAuth2Auth<[
  {
    type: OAuth2FlowType.authorizationCode;
    authorizationUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
    tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token";
    scopes: ["https://widgets.contoso.azure.com/.default"];
  }
]>;

Resource and operation templates

alias ServiceTraits = SupportsRepeatableRequests &
  SupportsConditionalRequests &
  SupportsClientRequestId;

alias Operations = Azure.Core.ResourceOperations<ServiceTraits>;

/** A widget resource. */
@resource("widgets")
model Widget {
  /** Widget name. */
  @key("widgetName")
  @maxLength(64)
  @pattern("^[A-Za-z0-9-]+$")
  @visibility(Lifecycle.Read)
  name: string;

  /** User-visible display name. */
  @maxLength(120)
  displayName?: string;

  /** Provisioning state. */
  @visibility(Lifecycle.Read)
  provisioningState?: "Succeeded" | "Failed" | "Canceled" | "Creating" | "Updating" | "Deleting";
}

interface Widgets {
  /** Get a widget. */
  getWidget is Operations.ResourceRead<Widget>;

  /** Create or update a widget. */
  createOrUpdateWidget is Operations.ResourceCreateOrUpdate<Widget>;

  /** Delete a widget. */
  deleteWidget is Operations.ResourceDelete<Widget>;

  /** List widgets. */
  listWidgets is Operations.ResourceList<Widget>;
}

OAuth2 import clarification

OAuth2Auth, OAuth2FlowType, BearerAuth, and NoAuth are all provided by @typespec/http and become available once you do using Http;. They are not Azure-specific. The EntraIDToken symbol shown in the service skeleton above is a user-defined alias built on top of OAuth2Auth - it is not a built-in. You can name your auth model anything; only OAuth2Auth/OAuth2FlowType/BearerAuth/NoAuth are framework symbols.

Repeatability headers

Clients MUST send TWO headers to make a request repeatable:

  • Repeatability-Request-ID - a uuid that uniquely identifies the request attempt.
  • Repeatability-First-Sent - the original send time in HTTP-date format (RFC 7231).

The SupportsRepeatableRequests trait adds both headers to the operation contract automatically. The server MUST return the same response for the same Repeatability-Request-ID within a 24-hour window; conflicting reuse of an ID with a different payload MUST return 409 Conflict.

api-version requirement

Every Azure data-plane operation MUST accept api-version as a required query parameter. Azure Core operation templates (Operations.ResourceRead, ResourceCreateOrUpdate, ResourceList, etc.) inject this parameter automatically. Hand-written op declarations must add it explicitly via @query("api-version") apiVersion: string in the parameter list - omitting it breaks the Azure SDK pipeline.

Standard error model

Azure data-plane services MUST use the standard error envelope. The canonical type is Azure.Core.Foundations.ErrorResponse; if you define it locally for reference, wrap an ErrorDetail inside an ErrorResponse:

model ErrorResponse {
  error: ErrorDetail;
}

model ErrorDetail {
  code: string;
  message: string;
  target?: string;
  details?: ErrorDetail[];
  innererror?: InnerError;
}

Do not emit a flat { code, message } payload at the response root - Azure SDK error parsers expect the wrapper.

Conditional request safety

When an operation accepts If-Match or If-None-Match, the success response MUST include an ETag header, and the contract MUST declare a 412 Precondition Failed response for concurrency conflicts. Without these, clients cannot implement safe optimistic concurrency.

/** Precondition failed - the resource was modified by another caller. */
@error
model PreconditionFailedResponse {
  @statusCode statusCode: 412;
  @header eTag?: string;
  code: "PreconditionFailed";
  message: string;
}

model WidgetReadResponse {
  @header eTag: string;
  @body widget: Widget;
}

Pair this with SupportsConditionalRequests in service traits so Azure Core wires If-Match/If-None-Match parameters consistently.

Security hard rules for Azure data plane

  • All endpoints MUST be served over HTTPS. No HTTP, no exceptions, including dev/test rings.
  • The OAuth2 token audience MUST be the resource-specific audience: the data-plane resource URI (e.g., https://widgets.contoso.azure.com/.default) for data-plane APIs, or https://management.azure.com/.default for ARM. Document the audience explicitly in the OAuth2 flow scopes.
  • Use Microsoft Entra ID (Azure AD) as the identity provider. Do not invent shared-secret, API-key, or custom HMAC schemes for new Azure APIs.
  • Service-level @useAuth declares the default auth. Per-operation @useAuth(NoAuth) overrides are reserved for explicit anonymous endpoints such as a public OpenAPI document endpoint or a liveness probe; each one needs a documented justification.
  • Never embed secrets, connection strings, or tokens in URLs, path segments, query parameters, or error messages.

Avoid Legacy.* templates

The Azure.Core.Legacy.* operation templates exist for migrating older patterns; they ignore @parentResource and miss some traits. Default to modern Operations.Resource* templates for new work.

Azure data-plane review checks

  • @useAuth is present and scopes use the <resource-uri>/.default pattern.
  • API is versioned from the first public version.
  • Resource keys have @key, @visibility(Lifecycle.Read), @maxLength, and @pattern.
  • Standard operation templates are used before custom operations.
  • Mutating create operations support repeatability where appropriate.
  • Conditional requests are supported where concurrency matters.
  • LROs have a status monitor operation and final-state behavior is clear.
  • No explicit @operationId in Azure specs.
  • No open-ended @format when a concrete scalar exists.

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