From speakeasy
Guides OpenAPI spec writing with best practices on naming conventions, complex patterns like polymorphism, enums, file uploads, server-sent events, and reusability for quality SDKs.
npx claudepluginhub speakeasy-api/skills --plugin speakeasyThis skill uses the workspace's default tool permissions.
Reference for OpenAPI best practices and conventions. This skill provides guidance on taste, conventions, and grey areas not covered by the OpenAPI specification itself.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Reference for OpenAPI best practices and conventions. This skill provides guidance on taste, conventions, and grey areas not covered by the OpenAPI specification itself.
Operation IDs: Use lowercase with underscores, following resource_action pattern:
# Good
operationId: users_list
operationId: users_get
operationId: users_create
# Avoid
operationId: GetApiV1Users # Auto-generated, not semantic
Component Names: Use PascalCase for schemas, parameters, and other reusable components:
components:
schemas:
UserProfile: # PascalCase
OrderHistory: # PascalCase
parameters:
PageLimit: # PascalCase
Tags: Use lowercase with hyphens for machine-friendly tags:
tags:
- name: user-management
description: Operations for managing users
- name: order-processing
description: Operations for processing orders
For more details, see reference/operations.md and reference/components.md.
Use CommonMark: All description fields support CommonMark syntax for rich formatting:
description: |
Retrieves a user by ID.
## Authorization
Requires `users:read` scope.
## Rate Limits
- 100 requests per minute per API key
- 1000 requests per hour per IP
Be Specific: Provide actionable information, not generic descriptions:
# Good
description: Returns a paginated list of active users, ordered by creation date (newest first)
# Avoid
description: Gets users
Use examples over example: The plural examples field provides better SDK generation:
# Good
examples:
basic_user:
value:
id: 123
name: "John Doe"
admin_user:
value:
id: 456
name: "Jane Admin"
role: admin
# Avoid single example
example:
id: 123
name: "John Doe"
For more details, see reference/examples.md.
Create components for:
Keep inline for:
# Reusable schema
components:
schemas:
User:
type: object
properties:
id: {type: integer}
name: {type: string}
# Reference it
paths:
/users/{id}:
get:
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/User'
For more details, see reference/components.md.
These patterns are commonly challenging. Brief examples below with links to detailed guidance.
Use string enums with clear, semantic values:
type: string
enum:
- pending
- approved
- rejected
- cancelled
Avoid:
See reference/schemas.md#enums for more.
oneOf: Value matches exactly one schema (type discrimination)
PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/BankAccount'
- $ref: '#/components/schemas/PayPal'
discriminator:
propertyName: type
mapping:
credit_card: '#/components/schemas/CreditCard'
bank_account: '#/components/schemas/BankAccount'
paypal: '#/components/schemas/PayPal'
allOf: Value matches all schemas (composition/inheritance)
AdminUser:
allOf:
- $ref: '#/components/schemas/User'
- type: object
properties:
permissions:
type: array
items: {type: string}
anyOf: Value matches one or more schemas (flexible union)
SearchFilter:
anyOf:
- $ref: '#/components/schemas/TextFilter'
- $ref: '#/components/schemas/DateFilter'
- $ref: '#/components/schemas/NumericFilter'
See reference/schemas.md#polymorphism for detailed guidance.
Use discriminators with oneOf for efficient type identification:
Pet:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
discriminator:
propertyName: petType
mapping:
dog: '#/components/schemas/Dog'
cat: '#/components/schemas/Cat'
Dog:
type: object
required: [petType, bark]
properties:
petType:
type: string
enum: [dog]
bark:
type: string
Cat:
type: object
required: [petType, meow]
properties:
petType:
type: string
enum: [cat]
meow:
type: string
See reference/schemas.md#discriminators for more.
Handle null values differently based on OpenAPI version:
OpenAPI 3.1 (JSON Schema 2020-12 compliant):
type: [string, "null"]
# or
type: string
nullable: true # Still supported for compatibility
OpenAPI 3.0:
type: string
nullable: true
For optional fields, use required array:
type: object
properties:
name: {type: string} # Can be omitted
email: {type: string} # Can be omitted
required: [name] # email is optional
See reference/schemas.md#nullable for more.
Use multipart/form-data for file uploads:
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
metadata:
type: object
properties:
description: {type: string}
tags:
type: array
items: {type: string}
required: [file]
For base64-encoded files in JSON:
requestBody:
content:
application/json:
schema:
type: object
properties:
filename: {type: string}
content:
type: string
format: byte # base64-encoded
See reference/request-bodies.md#file-uploads for more.
Express streaming responses with text/event-stream:
responses:
'200':
description: Stream of events
content:
text/event-stream:
schema:
type: string
description: |
Server-sent events stream. Each event follows the format:
```
event: message
data: {"type": "update", "content": "..."}
```
examples:
notification_stream:
value: |
event: message
data: {"type": "notification", "message": "New order received"}
event: message
data: {"type": "notification", "message": "Order processing complete"}
See reference/responses.md#streaming for more patterns.
Detailed guidance for each major OpenAPI field:
When writing specs for SDK generation:
operationId: Required for meaningful method namesExample SDK-friendly operation:
paths:
/users:
get:
operationId: users_list
summary: List all users
description: Returns a paginated list of users
parameters:
- name: limit
in: query
schema: {type: integer, default: 20}
- name: offset
in: query
schema: {type: integer, default: 0}
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
required: [data, pagination]
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/PaginationInfo'
examples:
success:
value:
data: [{id: 1, name: "Alice"}, {id: 2, name: "Bob"}]
pagination: {total: 100, limit: 20, offset: 0}
This generates: sdk.users.list({limit: 20, offset: 0})
Don't:
operationId (causes auto-generated names)example when you mean examples (plural is better)Do:
operationId for every operationexamples (plural) with named examples