К основному содержанию
скилл CX Codex

openapi · finom — скилл для Claude Code: генерация OpenAPI и установка

Скилл объясняет, как Vovk.ts собирает спецификацию OpenAPI из процедур и схем валидации. Пригодится, чтобы описать API и показать его документацию через Scalar.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add finom/vovk --skill openapi -a claude-code
Открыть на GitHub

Что делает openapi

Скилл разбирает, как поля процедур Vovk.ts превращаются в параметры, тело запроса и ответы OpenAPI. Метаданные операции задаются декоратором @operation; @operation.error добавляет описания ошибок, а общие сведения о API настраиваются в vovk.config.mjs. Отдельную конфигурацию можно задать для каждого сегмента.

Спецификацию можно получить из кода и отдавать как JSON через контроллер. Для документации описан Scalar, а также упомянуты Redoc и Swagger UI. Важно: процедура без декоратора @operation, @operation.tool или @operation.error в спецификацию не попадает, поэтому нужные операции придётся пометить явно.

Какие задачи решает

  • сгенерировать OpenAPI из процедур и схем валидации
  • задать общие сведения об API и серверы
  • описать ожидаемые ошибки эндпоинта
  • разделить спецификации по сегментам
  • подготовить JSON-спецификацию для страницы документации

Примеры запросов агенту

  • Настрой генерацию OpenAPI для процедур Vovk.ts и добавь метаданные к нужным операциям.
  • Покажи, как описать ошибки эндпоинта через @operation.error.
  • Настрой отдельную спецификацию OpenAPI для сегмента admin.

Как установить openapi

1Через Skills CLI (Vercel)

npx skills add finom/vovk --skill openapi -a claude-code

2Вручную: положить файлы

~/.claude/skills/openapi/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/openapi/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о openapi

Как установить openapi в Claude Code?

Выполните npx skills add finom/vovk --skill openapi -a claude-code. Файл скилла будет доступен по пути ~/.claude/skills/openapi/SKILL.md.

Что делает скилл openapi?

Он помогает настроить генерацию спецификации OpenAPI из процедур и схем валидации Vovk.ts, описать операции и ошибки, а также настроить документацию API.

Почему процедура не появляется в OpenAPI?

Процедура включается в спецификацию, если на ней есть @operation, @operation.tool или @operation.error. Без такого декоратора она пропускается.

Исходный текст

SKILL.md · MIT
---
name: openapi
description: Vovk.ts OpenAPI generation — how the OpenAPI 3.x spec is derived from procedures + validation schemas, the `@operation` decorator family (`@operation({...})` for summary/description/tags/security/deprecated, `@operation.error(status, message)` for error responses, `@operation.tool({...})` for AI tool metadata), top-level `outputConfig.openAPIObject` configuration (info, servers, license, tags, security, components), per-segment overrides, accessing the spec from code (`@/client/openapi`), serving the spec JSON from a controller, and rendering it with **Scalar** (the primary / recommended docs UI — picks up the TypeScript / Python / Rust code samples that Vovk generates via `x-codeSamples`) or alternatives like Redoc / Swagger UI. Use whenever the user asks for "API docs", "Scalar docs", "Swagger", "OpenAPI spec", "document my API", "generate OpenAPI from my controllers", "add an info/title/servers block", "segment-specific docs", "expose OpenAPI at /api/docs", "bearer auth in the spec", "document possible errors", "deprecate an endpoint". Does NOT cover procedure authoring → hand off to `procedure` skill. Does NOT cover AI tool derivation via `deriveTools` → hand off to `tools` skill (though `@operation.tool` is mentioned here since it's part of the decorator family). Does NOT cover third-party OpenAPI imports → hand off to `mixins` skill.
---

# Vovk.ts OpenAPI

OpenAPI 3.x spec is byproduct of code you already write. Procedures + validation schemas + `@operation` metadata → spec. No separate doc authoring.

## How procedures map to OpenAPI

| Procedure field | OpenAPI output |
|---|---|
| `params` schema | `parameters` with `in: 'path'` |
| `query` schema | `parameters` with `in: 'query'` |
| `body` schema | `requestBody` (content type from `contentType`, default `application/json`) |
| `output` schema | `responses.200` with `application/json`; other statuses via `@operation({ responses: { 201: … } })`, merged over it |
| `iteration` schema | `responses.200` with `application/jsonl` |
| `@operation` fields | operation-level metadata (`summary`, `description`, `tags`, etc.) |

Anything expressible in `procedure()` call — request shape, response shape, content type — lands in spec automatically.

## `@operation` decorator family

Three decorators, same namespace, all attached to `operation`:

| Decorator | Writes to | Purpose |
|---|---|---|
| `@operation({...})` | `operationObject` | Standard OpenAPI metadata (`summary`, `description`, `tags`, `deprecated`, `security`, …). Accepts any field from OpenAPI 3.1's `OperationObject`. |
| `@operation.error(status, message)` | `operationObject.responses[status]` | Document a known error shape. Generates response referencing `#/components/schemas/VovkErrorResponse` with `message` pinned to a literal. |
| `@operation.tool({...})` | `operationObject['x-tool']` | AI-tool metadata read by `deriveTools` — `name`, `description`, `title`, `hidden`, `meta`. See **`tools` skill** for full usage. |

### `@operation({...})`

```ts
import { operation, put, procedure } from 'vovk';
import { z } from 'zod';

@operation({
  summary: 'Update user',
  description: 'Update user profile by ID.',
  tags: ['users'],
  // plus any other OpenAPI OperationObject field:
  // deprecated, externalDocs, security, ...
})
@put('{id}')
static updateUser = procedure({
  params: z.object({ id: z.string().uuid() }),
  body: z.object({ email: z.string().email() }),
  output: z.object({ success: z.boolean() }),
}).handle(async (req) => { /* ... */ });
```

Without an operation decorator (`@operation`, `@operation.tool` or `@operation.error`), a procedure is left out of the spec, and `deriveTools` makes no tool of it. Add `@operation` to every procedure the spec or the tools should list.

### `@operation.error(status, message)`

Document errors your endpoint can throw so they show up in generated docs and client type narrowing:

```ts
import { operation, post, procedure, HttpStatus, HttpException } from 'vovk';

@operation({ summary: 'Create user' })
@operation.error(HttpStatus.BAD_REQUEST, 'Email is already taken')
@operation.error(HttpStatus.BAD_REQUEST, 'Invalid email format')
@operation.error(HttpStatus.NOT_FOUND, 'Organization not found')
@post()
static createUser = procedure({
  body: z.object({ email: z.string(), orgId: z.string() }),
}).handle(async (req) => {
  const { email, orgId } = await req.vovk.body();
  if (!await orgExists(orgId)) throw new HttpException(HttpStatus.NOT_FOUND, 'Organization not found');
  if (await emailTaken(email)) throw new HttpException(HttpStatus.BAD_REQUEST, 'Email is already taken');
  // ...
});
```

Same status can be declared multiple times with different messages — they merge into `enum` of allowed messages under that status code. Throw `HttpException(status, message)` with message from declared set.

### `@operation.tool({...})`

Brief shape; **`tools` skill** covers derivation. Sets `x-tool` on operation:

```ts
@operation.tool({
  name: 'get_user_by_id',
  title: 'Get user by ID',
  description: 'Retrieves a user by their unique ID.',
  // hidden: true to exclude from derived tools
})
@operation({ summary: 'Get user by ID' })
@get('{id}')
static getUser = /* ... */;
```

Mentioned here only because part of `operation.*` namespace. Any operation decorator makes a procedure a derived tool, with or without `summary`/`description`: the tool description is `x-tool.description`, else `summary` and `description`, else the method name. `@operation.tool({ hidden: true })` excludes it.

## Global OpenAPI object

Top-level fields (`info`, `servers`, `license`, `tags`, `security`) configured in `vovk.config.mjs`:

```ts
/** @type {import('vovk').VovkConfig} */
const config = {
  outputConfig: {
    openAPIObject: {
      info: {
        title: 'My App API',
        description: 'API for my app.',
        license: { name: 'MIT', url: 'https://opensource.org/licenses/MIT' },
        version: '1.0.0',
      },
      servers: [
        { url: 'https://myapp.example.com', description: 'Production' },
        { url: 'http://localhost:3000', description: 'Local' },
      ],
      tags: [
        { name: 'users', description: 'User management' },
      ],
      security: [{ bearerAuth: [] }],
    },
  },
};
export default config;
```

Put shared metadata here. Per-operation details stay on `@operation`.

## Per-segment overrides

Separate spec per segment with own `info` / `servers`:

```ts
const config = {
  outputConfig: {
    segments: {
      admin: {
        openAPIObject: {
          info: { title: 'Admin API', version: '1.0.0' },
          servers: [{ url: 'https://admin.example.com' }],
        },
      },
    },
  },
};
```

Key (`admin`) is `segmentName` from `initSegment` (root segment uses `""`). Each segment's spec is served and exported independently — useful when segments have different audiences (public SDK vs internal admin).

## Accessing the spec from code

Import path follows the client layout; same rules as generated RPC modules (see **`rpc` skill** for full picture).

### Composed (one merged spec)

```ts
import { openapi } from '@/client/openapi';
// Full OpenAPI 3.x object merged from all segments
```

This is what `vovk init` scaffolds. Composed client generates into `composedClient.outDir` (default `src/client`, or `client/` when the app isn't in `src/app`); `@/client` is the standard Next.js alias for that dir. Custom `outDir` shifts the path; projects without the alias use a relative import.

### Segmented (one spec per segment)

When `segmentedClient.enabled: true`, each segment writes its own `openapi.(ts|json)` under `<segmentedClient.outDir>/<segmentName>/`:

```ts
import { openapi as adminOpenAPI } from '@/client/admin/openapi';
import { openapi as rootOpenAPI }  from '@/client/root/openapi'; // root segment folder is named "root"
```

If `segmentedClient.outDir` is `./sdk` the path becomes `@/sdk/admin/openapi`, etc. Per-segment spec files exist **only** under segmented client dirs; composed client emits just the merged `@/client/openapi`. Both clients enabled share the dir: composed files at the root, segments in subdirs.

Use these exports directly — no fetch required at runtime.

## Serving the spec at runtime

Cleanest Vovk-idiomatic way is plain controller method returning imported `openapi` object:

```ts
// src/modules/openapi/openapi-controller.ts
import { get, prefix, operation } from 'vovk';
import { openapi } from '@/client/openapi';

@prefix('openapi')
export default class OpenApiController {
  @get()
  @operation({ summary: 'OpenAPI spec', tags: ['meta'] })
  static getOpenAPI() {
    return openapi; // return the spec as-is, not { openapi }
  }
}
```

Register in segment's `route.ts` like any other controller. JSON now reachable at `GET /api/openapi` — any docs viewer (Scalar, Redoc, Swagger UI) can point at it.

For just raw JSON without going through controller, Next.js route handler works too:

```ts
// src/app/api/openapi/route.ts
import { openapi } from '@/client/openapi';

export const GET = () => Response.json(openapi);
```

### Rendering it with a UI

**Scalar is the recommended renderer.** Vovk emits per-operation `x-codeSamples` (TypeScript via the generated TS client, Python via `vovk-python`, Rust via `vovk-rust`), and Scalar renders them inline next to each endpoint — resulting docs look like real SDK reference, not stripped-down Swagger page.

Install `@scalar/api-reference-react` and point at URL exposed above:

```tsx
// src/app/api-docs/page.tsx
'use client';
import { ApiReferenceReact } from '@scalar/api-reference-react';
import '@scalar/api-reference-react/style.css';

export default function ApiDocs() {
  return (
    <ApiReferenceReact
      configuration={{
        url: '/api/openapi', // or '/api/openapi.json'
      }}
    />
  );
}
```

For non-React page just drop in `@scalar/api-reference` CDN bundle and pass same URL — same story, different transport.

Alternatives exist but don't surface `x-codeSamples` as prominently:

- **Redoc** (`redoc-cli` or `<RedocStandalone>` component) — does read `x-codeSamples` (it's the Redocly convention Vovk emits). Works fine.
- **Swagger UI** (`swagger-ui-react`, `spec={openapi}`) — works, but no multi-language request examples.

All three need just spec JSON — endpoint above is the integration point. Check renderer's current docs for exact prop name; those APIs change faster than Vovk does.

## Schema artifacts

`vovk dev` writes `.vovk-schema/` (one JSON file per segment, plus `_meta.json`); `vovk generate`, `vovk bundle`, the client, the OpenAPI document and AI tools read it. Commit it.

## Flows

### "Add title and version to my API docs"

```ts
// vovk.config.mjs
outputConfig: {
  openAPIObject: {
    info: { title: 'My API', version: '1.0.0' },
  },
}
```

Run `npx vovk generate`. Done.

### "Describe every endpoint for Scalar"

Add `@operation({ summary, description, tags })` on every procedure. Run `vovk dev` so `.vovk-schema/` and the client pick it up. Scalar picks it up.

### "Expose API docs at /api/openapi"

Mount `OpenApiController` returning `openapi` (snippet above), then point **Scalar** at that URL — recommended renderer because surfaces Vovk's per-language code samples (TS / Python / Rust) inline. Redoc works too; Swagger UI works but skips code-sample block.

### "Bearer auth in the spec"

```ts
openAPIObject: {
  components: {
    securitySchemes: {
      bearerAuth: { type: 'http', scheme: 'bearer' },
    },
  },
  security: [{ bearerAuth: [] }],
}
```

Per-operation `security` lives on `@operation` if you want to override.

### "Admin API should have its own docs and server URL"

Per-segment override under `outputConfig.segments.admin.openAPIObject`. Import per-segment spec from the segmented client dir: `import { openapi } from '@/client/admin/openapi'` (path follows `segmentedClient.outDir`). Requires `segmentedClient.enabled: true`; composed client emits only the merged `@/client/openapi`.

### "Deprecate an endpoint"

```ts
@operation({ deprecated: true })
@get('old-endpoint')
static oldThing = procedure().handle(/* ... */);
```

### "Document the errors my endpoint throws"

Stack `@operation.error(status, message)` per error case. Keep message string identical between decorator and `HttpException` you throw — spec pins each status's `message` field to enum of declared values.

```ts
@operation.error(HttpStatus.NOT_FOUND, 'User not found')
@operation.error(HttpStatus.CONFLICT, 'Email already exists')
@post()
static createUser = procedure({ body: /* ... */ }).handle(async (req) => {
  // throw new HttpException(HttpStatus.NOT_FOUND, 'User not found');
});
```

## Gotchas

- **A procedure without an operation decorator is left out**: no spec entry and no derived tool. `@operation({})` is enough to include it.
- **`@operation.error` message strings are identity-linked to `HttpException`**: decorator pins response's `message` to `enum` of declared values. If runtime throws different message string, docs will be accurate about status code but wrong about message. Keep them in sync, or parameterize via shared constant.
- **Regeneration required**: after changing `@operation*` decorators, run `PORT=<port> npx vovk dev --next-dev --exit` (it rewrites `.vovk-schema/` and the client, then exits); `vovk generate` alone reads the committed `.vovk-schema/` and doesn't see decorator changes. A `vovk.config.mjs` change such as `outputConfig.openAPIObject` needs only `vovk generate`.
- **`x-tool` doesn't affect OpenAPI semantics** — consumed only by `deriveTools`. Safe to include; `x-` fields are standard OpenAPI extension mechanism, though some strict linters still warn.
- **Segment key vs. path**: `outputConfig.segments.admin` matches `segmentName: 'admin'` — not folder path and not class name.
- **Content type is respected**: procedures with `contentType: 'multipart/form-data'` appear in spec with correct request-body encoding. Don't hand-patch generated spec.
- **No bundled docs UI**: Vovk exports JSON; wiring renderer is on you. **Scalar is the recommended pick** — renders per-language code samples Vovk emits, so endpoints show up with ready-to-copy TS / Python / Rust calls. Swagger UI / Redoc work fine but drop that part. Separation is deliberate; docs UIs evolve on their own release cadence.

© finom · MIT · finom/vovk

Похожие скиллы

Все скиллы по теме «Бэкенд и API» →

Сначала — openapi от других авторов.

openapi codewithmukesh Настраивает встроенную генерацию спецификаций OpenAPI в .NET 10 без Swashbuckle. Помогает агенту писать трансформеры, метаданные и схемы безопасности. CX Codex +1 2 800установок mcp-builder официальный anthropics Скилл проводит через создание MCP-сервера на TypeScript или Python: от изучения протокола до тестов и оценки качества. 130 949установок claude-api официальный anthropics Справочник по Anthropic SDK и API Claude для генерации кода без устаревших параметров и выдуманных методов. 74 198установок lark-im larksuite Скилл подключает Lark IM к агенту: он отправляет сообщения, ищет историю, управляет чатами и файлами. Подойдёт для задач вокруг командных коммуникаций. CX Codex +1 472 900установок lark-event larksuite Слушает события Lark/Feishu и отдаёт их потоком NDJSON. Подходит для ботов, обработчиков сообщений и долгих подписчиков. CX Codex +1 466 800установок caveman-discover JuliusBrussee Находит точки вызова LLM в репозитории и размечает их воркфлоу для Caveman Cloud, чтобы расходы на токены не сливались в одну общую кучу. CX Codex +1 104 400установок migration JuliusBrussee Скилл задаёт правила безопасных миграций схем, данных и API с обязательным откатом. Пригодится при изменениях, где нельзя терять данные или ломать совместимость. CX Codex +1 103 900установок

Из того же набора10

tools Скилл помогает превращать процедуры Vovk.ts и отдельные функции в инструменты для языковых моделей. Пригодится, когда нужно подключить вызовы инструментов или собрать MCP-сервер. CX Codex +1 ★ 57звёзд репо segment Делит бэкенд на сегменты Vovk.ts: отдельные префиксы API, свои настройки runtime и статические эндпоинты. Пригодится, когда бэкенд разрастается. CX Codex +1 ★ 57звёзд репо rpc Скилл помогает генерировать и настраивать типизированные RPC-клиенты Vovk.ts. Пригодится, когда нужно вызывать API из клиентского кода или разобраться, почему клиент устарел. CX Codex +1 ★ 57звёзд репо python Скилл генерирует типизированный Python-пакет из схем Vovk.ts: RPC-функции, TypedDict, стриминг через JSON Lines. Нужен, когда API уже есть, а Python-клиент писать руками не хочется. CX Codex +1 ★ 57звёзд репо multitenant Скилл для Vovk.ts: разводит несколько тенантов по поддоменам в одном Next.js-приложении — прокси, сегменты, страницы. CX Codex +1 ★ 57звёзд репо jsonlines Скилл помогает настроить потоковую передачу JSON Lines в Vovk.ts: сервер отправляет данные по мере готовности, а клиент читает их как асинхронный поток. CX Codex +1 ★ 57звёзд репо init Разворачивает бэкенд в Next.js через Vovk.ts: проверяет версии, запускает vovk-cli и доводит проект до рабочего состояния. CX Codex +1 ★ 57звёзд репо config Скилл объясняет настройки Vovk.ts и tsconfig.json. Пригодится, когда нужно изменить путь генерации, параметры клиента или настройки TypeScript. CX Codex +1 ★ 57звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой