From bcb86d72b0c40f3208604e8b328bec3554ada8c0 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 21 May 2026 18:01:52 +0200 Subject: [PATCH] Add openapi-zudoku-review skill Adds a skill for reviewing OpenAPI 3.1 specifications for documentation quality and Zudoku/Zuplo portal rendering. Produces a severity-grouped findings report and offers to apply changes. Includes reference files covering the full review checklist and the eight Zudoku OpenAPI extensions. --- AGENTS.md | 1 + README.md | 1 + skills/openapi-zudoku-review/SKILL.md | 121 ++++++++ .../references/review-checklist.md | 289 ++++++++++++++++++ .../references/zudoku-extensions.md | 189 ++++++++++++ 5 files changed, 601 insertions(+) create mode 100644 skills/openapi-zudoku-review/SKILL.md create mode 100644 skills/openapi-zudoku-review/references/review-checklist.md create mode 100644 skills/openapi-zudoku-review/references/zudoku-extensions.md diff --git a/AGENTS.md b/AGENTS.md index 525af06..34fefcf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,6 +25,7 @@ The **Zuplo Agent Skills** repository provides official agent skills for coding | `skills/zuplo-policies/` | Policy management guide with complete built-in catalog | | `skills/zuplo-handlers/` | Request handler configuration and custom handler guide | | `skills/zuplo-cli/` | Zuplo CLI command reference and usage guide | +| `skills/openapi-zudoku-review/` | OpenAPI 3.1 spec review for documentation quality and Zudoku rendering | ## Specification diff --git a/README.md b/README.md index 136df82..3f3eaa3 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ If local docs aren't available and MCP is not configured, skills fall back to fe | Skill | Description | | ----- | ----------- | | **zudoku-guide** | Comprehensive Zudoku framework guide — setup, configuration, OpenAPI integration, plugins, auth, theming, troubleshooting, migrations. | +| **openapi-zudoku-review** | Review an OpenAPI 3.1 spec for documentation quality and Zudoku portal rendering — produces a severity-grouped findings report and offers to apply the fixes. | ## Contributing diff --git a/skills/openapi-zudoku-review/SKILL.md b/skills/openapi-zudoku-review/SKILL.md new file mode 100644 index 0000000..717bd9d --- /dev/null +++ b/skills/openapi-zudoku-review/SKILL.md @@ -0,0 +1,121 @@ +--- +name: openapi-zudoku-review +description: "Review an OpenAPI 3.1 specification for documentation quality with an eye toward how it will render in Zudoku/Zuplo developer portals. Produces a thorough findings report grouped by severity (covering missing schemas, weak descriptions, schema hygiene, and Zudoku-specific extensions like x-tagGroups, x-code-samples, x-displayName, x-mcp-server, etc.), then offers to apply the changes to the spec. Use whenever a user shares an OpenAPI/Swagger file (.yaml, .yml, .json) and asks for a review, audit, lint, quality check, improvements, \"best practices\", \"docs cleanup\", or anything about making their API reference better — even if they don't mention Zudoku by name, since the underlying quality issues are the same and the Zudoku-specific suggestions are additive." +license: MIT +metadata: + author: Zuplo + version: "1.0.0" + repository: https://github.com/zuplo/tools +--- + +# OpenAPI Zudoku Review + +A thorough review skill for OpenAPI 3.1 specs, optimized for documentation quality and Zudoku/Zuplo portal rendering. + +## What this skill does + +Given an OpenAPI 3.1 spec, walk through it systematically and produce a findings report grouped by severity, then offer to apply the improvements. The review covers two layers: + +1. **Generic OpenAPI documentation quality** — missing schemas, weak descriptions, inconsistent naming, missing examples, unstructured errors, etc. These problems exist regardless of the rendering tool. +2. **Zudoku-specific improvements** — Zudoku extensions (`x-tagGroups`, `x-displayName`, `x-code-samples`, `x-mcp-server`, etc.) and patterns that make the rendered portal noticeably better. See `references/zudoku-extensions.md` for the full reference. + +The two layers are additive. Run layer 1 on every spec; suggest layer 2 enhancements where they would actually help (don't sprinkle extensions onto every operation just because they exist). + +## When to invoke + +Trigger whenever a user shares an OpenAPI spec file and wants any kind of quality assessment — "review", "audit", "lint", "clean up", "improve docs", "make this better", "what's missing". Trigger even if the user doesn't mention Zudoku; generic findings still apply, and you can mention Zudoku-specific opportunities as a bonus layer. + +Do NOT trigger for: spec generation from scratch (different task), runtime debugging of an API, or code review of an API implementation. + +## Workflow + +### 1. Confirm scope and load the spec + +Before reviewing, briefly confirm with the user: +- Which file? (If a single file is attached, just use it.) +- Is this OpenAPI 3.1? If it's 3.0 or Swagger 2.0, mention that the skill is tuned for 3.1 — 3.0 reviews still work but a few findings (like `nullable` vs. `type: ['string', 'null']`, or `webhooks` support) won't apply. +- Are they using Zudoku to render this, or do they just want general quality improvements? This shapes how much weight to give Zudoku-specific suggestions. + +Parse the spec into a structure you can iterate over. YAML and JSON are both common; handle either. + +### 2. Run the review + +Walk through the spec systematically. Don't skip sections, but it's OK to summarize "everything in `components/schemas` looks clean" rather than enumerate the clean ones. + +Cover these areas, in roughly this order — see `references/review-checklist.md` for the detailed checklist with examples of good vs. bad for each item: + +1. **Info block** — title, version, description, contact, license, terms of service +2. **Servers** — at least one, with descriptions; environment variables for variants +3. **Tags** — defined at root, used consistently, with descriptions +4. **Paths and operations** — `operationId`, `summary`, `description`, `tags`, parameter descriptions, request body schemas, response schemas including error responses +5. **Components/schemas** — every schema has a description, properties have descriptions, examples are present, `required` is set correctly, no inline schemas that should be `$ref`'d +6. **Security schemes** — defined and applied; global vs. per-operation +7. **Reusability** — repeated inline shapes that should be extracted into `components` +8. **Zudoku layer** — opportunities to add `x-tagGroups`, `x-displayName`, `x-code-samples`, `x-mcp-server`, `x-zudoku-collapsed`, `x-zudoku-playground-enabled`, `x-mcp` + +### 3. Produce the findings report + +Output a markdown report with this exact structure: + +```markdown +# OpenAPI Review: + +**Spec version:** +**Operations reviewed:** +**Schemas reviewed:** + +## Summary + +<2-3 sentences: overall quality, biggest themes> + +## Critical + + +## High + + +## Medium + + +## Low / Nice-to-have + + +## Zudoku-specific opportunities + +``` + +For each finding, include: +- **Location** — JSON pointer or human-readable path (e.g., `paths./users.get` or `components.schemas.User.properties.email`) +- **Issue** — what's wrong in one sentence +- **Suggestion** — concrete fix, with a code snippet when useful + +Group findings by severity, not by location — readers want to triage by importance first. + +### 4. Offer to apply changes + +After the report, ask the user how they want to proceed. Don't apply automatically — they may want to review first, or apply only a subset. + +Offer these options: +- Apply all suggestions and produce an updated spec file +- Apply only specific severity levels (e.g., critical + high) +- Apply only specific findings (numbered list) +- Just keep the report + +When applying changes, preserve formatting and comments as much as possible (use a YAML library that round-trips well, like `ruamel.yaml` for Python, rather than one that reformats everything). Output the updated spec as a new file (e.g., `spec.improved.yaml`) rather than overwriting, so the user can diff. + +### 5. Summarize what changed + +After applying, briefly summarize the diff at a high level: "Added X descriptions, extracted Y inline schemas into components, added Z code samples for the auth endpoints, introduced x-tagGroups for sidebar organization." + +## Style notes + +- Be specific. "The description is weak" is unhelpful. "The description just repeats the operation name — describe what the endpoint does, when to use it, and any gotchas" is useful. +- Prioritize value. A spec with 200 operations and weak descriptions everywhere shouldn't get 200 line items — group them into a theme ("All operations under `/admin` lack descriptions; treat them collectively"). +- Don't moralize. The user knows their docs aren't perfect, that's why they're asking. +- Quote the user's existing text when suggesting rewrites, so the suggestion is concrete. +- For Zudoku extensions, only suggest them where they earn their keep. Suggesting `x-tagGroups` on a spec with 3 tags is silly; suggesting it on a spec with 25 tags is gold. + +## Reference files + +- `references/zudoku-extensions.md` — concise reference for all 8 Zudoku extensions: what each does, where it goes, when to suggest it, and a minimal example. +- `references/review-checklist.md` — detailed checklist of what to look for, with good/bad examples for each category. Read this when running the review so you don't miss anything. diff --git a/skills/openapi-zudoku-review/references/review-checklist.md b/skills/openapi-zudoku-review/references/review-checklist.md new file mode 100644 index 0000000..0c20965 --- /dev/null +++ b/skills/openapi-zudoku-review/references/review-checklist.md @@ -0,0 +1,289 @@ +# OpenAPI Review Checklist + +Detailed checklist for reviewing an OpenAPI 3.1 spec. Each section lists what to look for, examples of weak vs. strong, and what severity to assign. + +## Table of contents + +1. [Info block](#1-info-block) +2. [Servers](#2-servers) +3. [Tags](#3-tags) +4. [Paths and operations](#4-paths-and-operations) +5. [Parameters](#5-parameters) +6. [Request bodies](#6-request-bodies) +7. [Responses](#7-responses) +8. [Components / schemas](#8-components--schemas) +9. [Security](#9-security) +10. [Reusability and consistency](#10-reusability-and-consistency) +11. [Examples](#11-examples) + +--- + +## 1. Info block + +Check: +- `title` — present and meaningful (not "API") +- `version` — present, semver-style preferred +- `description` — present, more than one line, explains what the API does and who it's for +- `contact` — present with email or URL (helps users when things break) +- `license` — present if the API is meant to be used publicly +- `termsOfService` — present for commercial APIs + +**Weak:** `description: "REST API"` +**Strong:** Multi-paragraph description with overview, key concepts, and a pointer to getting-started docs. + +Severity: missing `title`/`version` = critical (spec invalid). Missing `description` = high. Missing `contact`/`license` = medium. + +--- + +## 2. Servers + +Check: +- At least one `servers` entry +- Each server has a `description` (Production, Staging, etc.) +- Use server variables for tenant/region patterns rather than listing every URL + +**Weak:** +```yaml +servers: + - url: https://api.example.com +``` + +**Strong:** +```yaml +servers: + - url: https://api.example.com + description: Production + - url: https://api.{region}.example.com + description: Regional production + variables: + region: + enum: [us, eu, ap] + default: us +``` + +Severity: no `servers` = high. Missing descriptions = medium. + +--- + +## 3. Tags + +Check: +- All tags used in operations are defined at root +- Each root-level tag has a `description` +- Tag naming is consistent (don't mix `Users`, `user-management`, `Auth`) +- Tags are used to group operations meaningfully + +**Weak:** Operations use `tags: [users]`, `tags: [User]`, `tags: [user-mgmt]` interchangeably. +**Strong:** A single canonical tag name per concept, defined once at root with a description. + +Severity: undefined tags = high (renders as untagged section). Inconsistent naming = medium. Missing descriptions = medium. + +--- + +## 4. Paths and operations + +Per operation, check: +- `operationId` — present, unique, descriptive (`getUserById`, not `op1`) +- `summary` — present, short (under ~50 chars), action-oriented +- `description` — present, explains what, when, and any gotchas +- `tags` — present and references defined tags +- `deprecated` — set when applicable, with explanation in description + +**Weak summary:** `summary: get user` +**Strong summary:** `summary: Get user by ID` + +**Weak description:** Just repeats the summary. +**Strong description:** "Returns the full user profile including embedded organization memberships. Use this when you need the canonical user record; for lightweight existence checks, use HEAD /users/{id}." + +Severity: missing `operationId` = high (breaks SDK gen). Missing `summary` = high (sidebar shows path). Missing/weak `description` = high. Missing `tags` = high (operation falls into "default" group). + +--- + +## 5. Parameters + +Per parameter, check: +- `description` — present +- `required` — set correctly (path params must be required) +- `schema` — present with `type` and any constraints +- `example` or `examples` — present for non-obvious values + +**Weak:** +```yaml +parameters: + - name: id + in: path + required: true + schema: { type: string } +``` + +**Strong:** +```yaml +parameters: + - name: id + in: path + required: true + description: The unique identifier of the user (UUID v4). + schema: + type: string + format: uuid + example: "550e8400-e29b-41d4-a716-446655440000" +``` + +Severity: missing `schema` = critical. Missing `description` = medium. Missing `example` for non-obvious = low. + +--- + +## 6. Request bodies + +Check: +- `description` on the request body itself +- `required: true` set if needed +- `content` has a schema (preferably `$ref` to a named component, not inline) +- Schema has examples +- All supported media types listed + +**Weak:** Inline schema with 15 properties, no description, no example. +**Strong:** `$ref: "#/components/schemas/CreateUserRequest"` to a named schema with full descriptions and examples. + +Severity: missing schema = critical. Inline complex schema that should be reusable = medium. + +--- + +## 7. Responses + +Check: +- Every operation has at least the success status documented (200, 201, 204, etc.) +- Error responses documented (400, 401, 403, 404, 409, 422, 500 as applicable) +- Each response has a `description` +- Each response has a `content` schema (except 204) +- Errors use a consistent shape (e.g., RFC 7807 `application/problem+json`) +- Headers documented where relevant (rate-limit headers, location, etc.) + +**Weak:** Only `200: OK` documented. +**Strong:** Success + all expected error codes, each with a `$ref` to a shared `Error` schema or RFC 7807 `Problem` schema. + +Severity: missing success response = critical. Missing error responses = high. Inconsistent error shapes across operations = high. + +--- + +## 8. Components / schemas + +Per schema, check: +- `description` on the schema itself +- `description` on each property +- `type` and constraints (`format`, `minLength`, `pattern`, `minimum`, etc.) +- `required` array set correctly +- `example` or `examples` present +- `additionalProperties: false` if the schema is closed (prevents accidental extra fields) +- Discriminators set for polymorphic schemas (`oneOf`, `anyOf` with shared key) +- Nullable handled with `type: [string, "null"]` (3.1) not `nullable: true` (3.0) + +**Weak:** +```yaml +User: + type: object + properties: + id: { type: string } + email: { type: string } +``` + +**Strong:** +```yaml +User: + type: object + description: A registered user account. + required: [id, email] + properties: + id: + type: string + format: uuid + description: Stable identifier assigned at creation. + email: + type: string + format: email + description: Primary contact email; also used for login. + example: "alex@example.com" +``` + +Severity: missing top-level description = high. Missing property descriptions = high. Missing `required` = high. Missing examples = medium. `nullable: true` in 3.1 spec = medium (silently ignored). + +--- + +## 9. Security + +Check: +- `securitySchemes` defined under `components` +- Each scheme has a `description` +- Global `security` array set, OR per-operation `security` overrides documented +- Public endpoints explicitly opt out with `security: []` +- For OAuth2, scopes are defined and used per-operation +- For API keys, location (`header`, `query`, `cookie`) is right + +**Weak:** Security scheme defined but never applied anywhere. +**Strong:** Global `security`, with explicit `security: []` on public endpoints like `/health` and `/login`. + +Severity: scheme defined but not applied = high. Missing scope documentation for OAuth2 = high. + +--- + +## 10. Reusability and consistency + +Check: +- Repeated inline shapes pulled into `components/schemas` +- Repeated parameters pulled into `components/parameters` +- Repeated responses pulled into `components/responses` +- Naming conventions consistent across the spec (camelCase vs. snake_case — pick one) +- Pagination, filtering, sorting handled the same way across list endpoints +- Date/time fields all use the same format (ideally `date-time`) +- Money amounts handled consistently (minor units integer vs. decimal string) + +Severity: significant duplication = medium-high. Inconsistent naming = medium. + +--- + +## 11. Examples + +Beyond per-property examples, check: +- Request and response examples on operations +- Multiple `examples` (named) where there's meaningful variety +- Examples are realistic (not `"string"`, `"foo"`, or `0`) +- Examples are actually valid against the schema + +**Weak:** +```yaml +example: "string" +``` + +**Strong:** +```yaml +examples: + individual: + summary: Individual customer + value: + type: individual + firstName: Alex + lastName: Müller + email: alex@example.com + business: + summary: Business customer + value: + type: business + companyName: "Acme GmbH" + vatId: "DE123456789" + contactEmail: billing@acme.example +``` + +Severity: missing examples = medium. Unrealistic examples = low (but call out — they hurt the docs even though the spec validates). + +--- + +## Severity guide + +When in doubt, here's how to think about severity: + +- **Critical**: Spec is invalid or won't render (missing required OpenAPI fields, broken `$ref`s, invalid schemas). +- **High**: Spec is valid but documentation is materially broken — users can't figure out how to use the API from the docs alone. Missing descriptions on key elements, missing error responses, missing security application. +- **Medium**: Polish that meaningfully improves the docs — weak descriptions, missing examples, inconsistent naming, missing tag descriptions. +- **Low**: Nice-to-have — style consistency, minor extension opportunities. + +If a spec has dozens of findings of the same kind, collapse them into a thematic finding ("All 47 operations under `/admin` lack descriptions — treat as a single workstream") rather than enumerating each. diff --git a/skills/openapi-zudoku-review/references/zudoku-extensions.md b/skills/openapi-zudoku-review/references/zudoku-extensions.md new file mode 100644 index 0000000..8bd06d3 --- /dev/null +++ b/skills/openapi-zudoku-review/references/zudoku-extensions.md @@ -0,0 +1,189 @@ +# Zudoku OpenAPI Extensions Reference + +Zudoku supports 8 OpenAPI extensions that affect how the rendered portal looks and behaves. Use this reference to know what to suggest, where it goes in the spec, and when it actually helps versus when it's noise. + +## Cheat sheet + +| Extension | Location | What it does | When to suggest | +|---|---|---|---| +| `x-tagGroups` | Root | Groups tags into sidebar sections | Spec has 8+ tags that fall into themes | +| `x-displayName` | Tag object | Pretty label for a tag (e.g. `ai-ops` → `AI Operations`) | Tag names are kebab-case, snake_case, or otherwise not human-friendly | +| `x-code-samples` (alias: `x-codeSamples`) | Operation | Custom code samples in sidebar | Operation is unusual, auth is non-trivial, or the user has hand-tuned snippets | +| `x-zudoku-collapsed` | Tag object | Initial collapsed state in sidebar | Some tags are advanced/optional and shouldn't dominate the sidebar at first glance | +| `x-zudoku-collapsible` | Tag object | Whether user can collapse a tag at all | "Getting Started" or core sections that should always be visible | +| `x-zudoku-playground-enabled` (alias: `x-explorer-enabled`) | Operation | Show/hide the API playground per operation | Webhooks, destructive ops, or anything that shouldn't be triggered from the docs | +| `x-mcp-server` | Operation | Marks an operation as an MCP endpoint; replaces request/response UI with MCP client setup card | Operation actually serves MCP traffic | +| `x-mcp` | Root | Describes a full MCP server's tools/resources/prompts at the document root | Spec describes an MCP server holistically (rendering support is in development) | + +--- + +## x-tagGroups (root level) + +Organize tags into named sidebar groups. Without this, tags are a flat list. + +```yaml +tags: + - name: Packages + - name: Parcels + - name: Tracking + - name: Billing + +x-tagGroups: + - name: Shipment + tags: [Packages, Parcels] + - name: Management + tags: [Tracking, Billing] +``` + +**Suggest when:** The spec has roughly 8+ tags that naturally cluster. Don't suggest for small specs — flat lists are fine under ~6 tags. + +--- + +## x-displayName (tag object) + +Override the tag's display label without changing the tag name (which operations reference). + +```yaml +tags: + - name: ai-ops + description: AI-powered operations + x-displayName: AI Operations +``` + +**Suggest when:** Tag names use kebab-case, snake_case, abbreviations, or otherwise look ugly in a sidebar. Don't rename the tag itself — that breaks operation references. + +--- + +## x-code-samples (operation level) + +Custom code snippets that appear in the sidecar alongside auto-generated examples. + +```yaml +paths: + /users: + get: + x-code-samples: + - lang: curl + label: cURL + source: | + curl https://api.example.com/users \ + -H "Authorization: Bearer $TOKEN" + - lang: python + label: Python + source: | + import requests + requests.get("https://api.example.com/users", + headers={"Authorization": f"Bearer {token}"}) +``` + +**Suggest when:** Auth flow is non-trivial (custom headers, signing, OAuth dance), or the operation has a tricky payload shape that benefits from a worked example. Don't suggest blanket addition — auto-generated samples are usually fine. + +--- + +## x-zudoku-collapsed (tag object) + +Whether a tag's section starts collapsed in the sidebar. Defaults to `true`. + +```yaml +tags: + - name: Getting Started + x-zudoku-collapsed: false + - name: Advanced + x-zudoku-collapsed: true +``` + +**Suggest when:** There's a clear "primary path" through the docs that should be open by default, and other sections are deep-dive material. + +--- + +## x-zudoku-collapsible (tag object) + +Whether a tag section *can* be collapsed at all. Defaults to `true`. Set `false` to lock a section open. + +```yaml +tags: + - name: Core API + x-zudoku-collapsible: false + x-zudoku-collapsed: false +``` + +**Suggest when:** There's a section the user always wants in front of readers (a `Getting Started`, an `Authentication` section). Use sparingly — taking away user control is friction. + +--- + +## x-zudoku-playground-enabled (operation level) + +Show/hide the interactive playground per operation. Alias: `x-explorer-enabled`. Defaults to whatever the global `disablePlayground` setting is. + +```yaml +paths: + /webhooks/trigger: + post: + x-zudoku-playground-enabled: false +``` + +**Suggest when:** Operation is destructive without confirmation (delete account), triggers external systems (sends real webhook, charges a card), or doesn't make sense interactively (long-running async job). + +--- + +## x-mcp-server (operation level) + +Marks an operation as an MCP endpoint. Zudoku replaces the standard request/response view with an MCP card showing the endpoint URL and client setup tabs (Claude, ChatGPT, Cursor, VS Code, generic). + +Boolean shorthand: +```yaml +paths: + /mcp: + post: + summary: My MCP Server + x-mcp-server: true +``` + +Object form (richer): +```yaml +x-mcp-server: + name: my-mcp-server + version: 1.0.0 + tools: + - name: search_docs + description: Search the documentation + - name: get_page + description: Retrieve a specific documentation page +``` + +**Suggest when:** The operation genuinely serves MCP traffic. Don't add to non-MCP endpoints. + +--- + +## x-mcp (root level) + +Describes an MCP server's protocol version, capabilities, tools, resources, and prompts at the document root. Rendering support is in development; for per-operation MCP UI today, use `x-mcp-server`. + +```yaml +x-mcp: + protocolVersion: "2025-06-18" + capabilities: + tools: + listChanged: true + tools: + - name: clients/get + description: Get a client by ID + inputSchema: + type: object + properties: + clientId: { type: string } + outputSchema: + $ref: "#/components/schemas/Client" +``` + +**Suggest when:** The whole spec describes an MCP server (not just one endpoint). Forward-looking; mention that rendering is in development. + +--- + +## What NOT to suggest + +A few patterns to avoid: + +- Don't suggest extensions just because they exist. If a spec is small and clean, the answer is usually "no extensions needed." +- Don't conflate `x-mcp` and `x-mcp-server`. `x-mcp-server` is per-operation, has full UI today. `x-mcp` is document-level, rendering still in development. +- Don't use `x-zudoku-collapsible: false` casually — it removes user control. Reserve for sections that genuinely should always be visible.