Agent skill

API Endpoints

by latitude-dev in latitude-dev/latitude-llm

Adding or changing API operations in @repo/operations. An agent skill from latitude-dev/latitude-llm.

MITAuto-check passedBackend & APIs

Install API Endpoints

skills CLI
$ npx skills add latitude-dev/latitude-llm --skill api-endpoints -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install latitude-dev/latitude-llm api-endpoints --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/latitude-dev/latitude-llm.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/api-endpoints .claude/skills/api-endpoints && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
api-endpoints
GitHub stars
4.7k
Token cost
~6.5k tokens
SKILL.md length
2,606 words
Files
1
Skills in repo
28
Repo updated
First seen
Licence
MIT

At a glance

Adding or changing API operations in @repo/operations. An agent skill from latitude-dev/latitude-llm.

  • Works in 4 steps: Create… → Register the module in… → Regenerate manifests → …
  • Tasks that involve REST APIs
  • SKILL.md covers Before you start — reuse the…, What you're really doing, Recipe: add a new operation… and Schema descriptions — the rule…, plus 8 more sections
  • Calls pnpm, git and uv

What it does

API Endpoints is an agent skill from latitude-dev/latitude-llm. Adding or changing API operations in @repo/operations. One source of truth (defineOperation + a Zod schema) becomes an HTTP endpoint, an OpenAPI operation, an MCP tool, TS + Python SDK methods, a latitude CLI command, and an in-process agent tool — descriptions and contracts must be written with all of these readers in mind.

Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Backend & APIs, covering REST APIs, OpenAPI specifications and MCP servers. It works with OpenAPI, Python and Zod. The repository describes itself as: Open-source observability for AI agents. Find where your agents fail, dispatch your coding agent to fix it, and verify the fix against real traces. The licence is MIT.

When your agent uses it

  • Tasks that involve REST APIs
  • Tasks that involve OpenAPI specifications
  • Tasks that involve MCP servers

Example prompts

  • “/api-endpoints”

Requirements

  • Python 3
  • Docker

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Create packages/operations/src/operations/.ts
  2. Register the module in packages/operations/src/operations/index.ts
  3. Regenerate manifests
  4. Tests

What it can do on your machine

Read from SKILL.md and the folder at commit 87e8aa0. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • pnpm
    • git
    • uv
    • npm
    • cargo

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • modelcontextprotocol.io

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

API Endpoints loads about 6.5k tokens when it runs. Until then it costs about 87 tokens; SKILL.md has 2,606 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~87
When it runs · the whole SKILL.md, loaded when a task matches
~6.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from latitude-dev/latitude-llm at commit 87e8aa0, republished under its MIT licence (© latitude-dev). 2,606 words, ~6,452 tokens.

Download SKILL.mdSave it as .claude/skills/api-endpoints/SKILL.md (or your agent's skills folder).
name
api-endpoints
description
Adding or changing API operations in `@repo/operations`. One source of truth (`defineOperation` + a Zod schema) becomes an HTTP endpoint, an OpenAPI operation, an MCP tool, TS + Python SDK methods, a `latitude` CLI command, and an in-process agent tool — descriptions and contracts must be written with all of these readers in mind.

Adding API operations

When to use: Adding a new operation to the public API, changing an existing one, or wondering why mcp.json / openapi.json / the SDK aren't in sync.

Before you start — reuse the UI's logic via the domain layer

When you add a new operation, check whether the same action or read is already available in the web UI. The goal isn't full surface parity, it's not duplicating logic that the web already implements.

For each new operation, open apps/web/src/domains/<entity>/<entity>.functions.ts. Three cases:

  • The web's server fn already calls a domain use-case (imports *UseCase from @domain/*): reuse that use-case in the operation. Don't reimplement the logic.
  • The web's server fn has the logic inline (raw repository calls, validation, side effects in the server fn body itself): extract it into a new domain use-case first, then have both the web server fn AND your operation call it. The domain use-case becomes the shared seam.
  • The web's server fn delegates to a third-party API like getBetterAuth().api.*: the API process can't reach the same in-process instance. Write a domain use-case that replicates that behavior (carefully — read the third-party source so your use-case matches its rules), then point both the web and the operation at the use-case. Adds parity tests so the migration doesn't silently drift.

The domain use-case is the shared seam between web and API. Duplicating logic in both surfaces creates drift — one gets a bug fix the other doesn't.

If the entity doesn't have a .functions.ts because the UI doesn't expose this action yet, you're designing fresh. That's fine; just don't lose the option to share later — put the business logic in a @domain/* use-case from the start rather than inline in the operation.

What you're really doing

Every operation in packages/operations is one declaration that fans out into every generated surface:

SurfaceGenerated fromConsumed by
HTTP route (Hono)route.method + route.path + execute/handlercurl, internal services — mounted by apps/api
OpenAPI operationroute.name (→ operationId), route.description, request/response schemasapps/api/openapi.json — the source Fern reads for the SDKs + CLI
MCP toolroute.name, route.description, flattened input + 2xx-JSON output schemaapps/api/mcp.json, runtime /v1/mcp transport
TS + Python SDK methodsFern reads openapi.json; group/sdkMethod name the methodend-user TypeScript (@latitude-data/sdk) and Python (latitude-sdk) code
latitude CLI commandFern reads openapi.jsonshell users + AI agents (latitude <resource> <verb>, --help, --schema)
In-process agent tooldefineToolset({ groups }) selection over execute-form operationsinternal AI agents (e.g. the signal-creation agent) — no HTTP, no tokens

You don't write these configs separately. You write one. The machinery in packages/operations/src/core/* derives the MCP tool and agent tools, and Fern derives the SDKs + CLI from openapi.json.

This means: the descriptions you put on routes and on schema fields are read by SDK users, by AI agents calling the MCP or an internal toolset, AND by CLI users (as --help/--schema text). Treat every description as user-facing copy. Vague or absent descriptions are bugs.

Recipe: add a new operation module

1. Create packages/operations/src/operations/<resource>.ts

Prefer execute-form (execute + typedResponses) for new operations — it's transport-neutral, so the operation is eligible for in-process agent toolsets and gets a typed status/body union. handler-form (a raw Hono handler + openApiResponses) still exists on unconverted modules; convert opportunistically when touching one.

ts
import { createRoute, z } from "@hono/zod-openapi"
import { Effect } from "effect"
import { defineOperation } from "../core/define-operation.ts"
import type { OperationModule } from "../core/mount.ts"
import { jsonBody, PROTECTED_SECURITY, typedResponses } from "../openapi/schemas.ts"
import type { OrganizationScopedEnv } from "../types.ts"

// Step 1: declare the mount path once; the module carries it.
const widgetsPath = "/widgets"

// Step 2: bind the operation factory to the Env type AND the path.
const widgetOperation = defineOperation<OrganizationScopedEnv>(widgetsPath)

// Step 3: define the boundary schemas. EVERY field gets `.describe(...)` if its
// purpose isn't obvious from the name. Descriptions land in `openapi.json`
// (SDK docstrings), `mcp.json` (MCP tools), and agent toolsets.
const WidgetSchema = z
  .object({
    id: z.string().describe("Stable identifier; safe to use as a primary key in client storage."),
    name: z.string().describe("Human-readable label, unique within an organization."),
    createdAt: z.string().describe("ISO-8601 timestamp of creation."),
  })
  .openapi("Widget") // ← registers a named OpenAPI component (needed by Fern). Different from `.openapi({ description })`.

const CreateWidgetBody = z
  .object({
    name: z.string().min(1).describe("Display name for the new widget. Must be non-empty."),
  })
  .openapi("CreateWidgetBody")

// Step 4: declare each operation.
const createWidget = widgetOperation({
  route: createRoute({
    method: "post",
    path: "/",
    name: "createWidget", // ← camelCase. Becomes OpenAPI `operationId` AND MCP tool name.
    tags: ["Widgets"],
    group: "widgets", // ← SDK group (x-fern-sdk-group-name) AND agent-toolset selector. Declare right after `tags`.
    sdkMethod: "create", // ← SDK method name inside the group (x-fern-sdk-method-name).
    summary: "Create widget", // ← short label; falls through to MCP tool `title`.
    description: "Creates a widget in the caller's organization. Returns the persisted record.",
    security: PROTECTED_SECURITY,
    request: { body: jsonBody(CreateWidgetBody) },
    responses: typedResponses({ status: 201, schema: WidgetSchema, description: "Widget created" }),
  }),
  access: "write", // ← required: "read-only" | "write" | "destructive". See "Access" below.
  rateLimitTier: "low", // ← declarative; apps/api maps it to middleware at mount time.
  execute: (input, ctx) =>
    Effect.gen(function* () {
      // input.body / input.params / input.query are pre-validated; ctx carries
      // organization, auth, and clients. Pipe your layers exactly as before:
      // .pipe(withPostgres(..., ctx.postgresClient, ctx.organization.id), withTracing)
      const widget = yield* createWidgetUseCase({ name: input.body.name })
      return { status: 201, body: toResponse(widget) } as const
    }).pipe(/* withPostgres(...), withTracing */),
})

// Step 5: export the module — mount order within `operations` is meaningful
// (Hono matches static-before-param routes in registration order).
export const widgetsModule: OperationModule = {
  path: widgetsPath,
  operations: [createWidget],
}

Execute-form notes:

  • typedResponses is the literal-keyed twin of openApiResponses (identical runtime output). It makes the { status, body } union checkable: declared non-2xx variants (400/401/404) are returnable values; domain failures left in the Effect error channel re-throw and reach honoErrorHandler exactly as in handler-form. Keep its response keys literal — deriving them from a generic collapses the inference under tsgo (see the comment on typedResponses).
  • ctx (OperationContext) carries organization, auth, and the platform clients — everything the old c.var provided. Tenancy is baked in: the organization is resolved before execute runs and is never part of the input.
  • input contains only the sections the route declares (params / query / body), already validated.
2. Register the module in packages/operations/src/operations/index.ts

Add the import and one entry to operationModules. Position = mount position: openapi.json path order and mcp.json tool order derive from it, so append new modules at the end unless there's a routing reason not to.

3. Regenerate manifests
bash
pnpm openapi:emit   # rewrites apps/api/openapi.json
pnpm mcp:emit       # rewrites apps/api/mcp.json

Both files are checked in. CI guards against drift, so commit them alongside the module.

The TS + Python SDKs and the latitude CLI all regenerate from openapi.json via Fern — pnpm generate:sdk (both SDKs), pnpm generate:cli (CLI), or pnpm generate:all (all three; needs Docker + a Rust toolchain for the CLI). CI (api-manifests.yml) regenerates all of them and fails on drift, so run pnpm generate:all and commit the results when your PR changes the surface — otherwise the drift check goes red.

Always use these pnpm scripts — never run fern generate directly. The generate:sdk:* scripts pin --version (from the SDK's package.json / pyproject.toml) so the version Fern bakes into the generated Python client_wrapper.py User-Agent is deterministic; a bare fern generate omits it and re-stamps a registry-derived version ("last published + a patch"), which flaps the drift check on every SDK publish. See fern/README.md.

Publishing the regenerated surface is version-gated per package — and the CLI is the easy one to miss. Regeneration only rewrites the generated source; each package publishes on push to development only when its version advances, and the three track it differently. The SDKs read a manifest: bump version in packages/sdk/typescript/package.json (TS → npm; publishes when it differs from npm view) and in packages/sdk/python/pyproject.toml (Python → PyPI; publishes when the version isn't on PyPI). After bumping the Python version, run uv lock in packages/sdk/python and commit uv.lock — the publish action runs uv lock --check and a manifest bumped without a re-lock fails the publish. The CLI has no manifest to bump — its version is the top ## [X.Y.Z] entry in packages/cli/CHANGELOG.md (its Cargo.toml ships 0.0.0, patched at build via cargo set-version), and publish-cli.yml no-ops unless that top version has no cli-<version> release yet. Since new operations add SDK methods and CLI commands, whenever you bump the SDK versions for new surface, add a matching new ## [X.Y.Z] entry to packages/cli/CHANGELOG.md in the same change — otherwise the CLI regenerates with the new commands but never ships.

4. Tests
  • HTTP-level integration tests live in apps/api/src/routes/<resource>.test.ts — they stay in apps/api on purpose, testing through registerRoutes + app.fetch() so the full middleware chain (auth, org context, rate limiting, error mapping) runs end-to-end.
  • MCP-level integration tests for new tools live in apps/api/src/mcp/server.test.ts. Add a case there if the operation exposes behavior worth pinning at the MCP layer too.
  • The operation machinery itself (factory, execute wrapper, mount, toolsets) is unit-tested in packages/operations/src/core/*.test.ts.
  • If the operation joins an agent toolset's group, the toolset's manifest snapshot (packages/operations/src/toolsets/__snapshots__/*.manifest.json) will change — review and commit the diff deliberately; that diff IS the "this operation now flows to an agent" review surface.

Schema descriptions — the rule that matters most

Every field in every request/response schema needs a description unless the field name is self-explanatory. Descriptions reach four distinct audiences:

  • SDK users read them as TypeScript JSDoc / Python docstrings on the generated SDK methods (Fern emits them as @param / property comments).
  • AI agents read them via the MCP tool's inputSchema / outputSchema — and internal agents read the same schemas through toolsets — to decide what to put in a tool call.
  • CLI users read them as --help text and machine-readable --schema output on the generated latitude commands.

Write each description as one short sentence in present tense, like a microcopy label. Examples:

ts
// Good — tells the agent what the value is FOR
name: z.string().describe("Human-readable label, unique within an organization."),
nextCursor: z
  .string()
  .nullable()
  .describe("Opaque cursor for the next page. `null` when there are no more pages."),

// Not great — restates the field name
name: z.string().describe("The name."),

// Bad — no description at all on a non-obvious field
filters: filterSetSchema, // ← what shape? what semantics? agent has to guess.
.describe() vs .meta() vs .openapi()
APIWhen to use
.describe("…")Default for field-level descriptions. Sugar for .meta({ description }). Visible to OpenAPI AND MCP.
.meta({ description, examples, default, ... })Equivalent to .describe() plus JSON-Schema-standard fields (examples, default, title). Visible to both surfaces.
.openapi("Name")Schema-component registration only — gives the schema a name under components.schemas in OpenAPI. Required for Fern to emit reusable types. Has nothing to do with descriptions.
.openapi({ description, format, example, ... })OpenAPI-only metadata — format, example, param: { in, name }, etc. Lives in the openapi-extension WeakMap and does not propagate to MCP. Avoid for descriptions; use only for things that have no Zod-native equivalent.

TL;DR: prefer .describe() / .meta(). Use .openapi("Name") to register named schema components. Reach for .openapi({...}) for fields ONLY when you need an OpenAPI-only knob like format: "uri".

If you find yourself writing .openapi({ description }), replace it with .describe() — descriptions hidden in the openapi WeakMap are invisible to MCP clients, which silently degrades agent UX.

Don't leak internal implementation into descriptions

User-facing descriptions (route description, schema .describe(), response description) are read by SDK users and AI agents. They aren't release notes for our backend. Keep them about the contract, not how we implement it.

Concretely, avoid:

  • Storage mechanics: "soft-deletes", "hard-deletes", "marks as deleted", "removes from cache", "writes to outbox", "RLS-scoped", "via the admin connection". Just say "deletes" / "revokes" / "creates".
  • Side-effect details on related data: "Traces remain in storage but the project no longer appears in lists.", "The associated rows are kept for auditing." If the caller can't observe it through the API, don't mention it.
  • Internal table or column names, queue names, worker names, event-bus topics.
  • Comments about why the code is structured a certain way — those belong in code comments, not in description:.

Examples:

ts
// Bad — leaks soft-delete + retention behavior of an unrelated entity
description: "Soft-deletes a project by slug. Traces remain in storage but the project no longer appears in lists."
// Good
description: "Deletes a project by slug."

// Bad — describes the mechanism
description: "Revokes an API key by setting deletedAt and busting the Redis cache."
// Good
description: "Revokes an API key."

Same rule for the verbs used in route/operation summary: "Delete project" beats "Soft-delete project".

Rate limiting — every operation declares its own tier

rateLimitTier is a required-in-practice field on the operation args: mountOperationModules throws at mount time on a missing tier, and the emit scripts boot the same assembly, so an undeclared tier can't ship. apps/api maps the tier to createTierRateLimiter(tier) middleware attached to this exact (method, path) pair only, keyed on the authenticated org id (not IP), so one tenant's traffic doesn't eat another's quota and a stricter tier on DELETE /:id doesn't fire on GET /:id.

Show full SKILL.md (1,023 more words)Show less
Picking a tier

Default to low. Most CRUD operations don't need more — low is 1,000 req/min/org, which comfortably covers SDK polling, MCP tool calls, and human-driven dashboards. Step up only when the operation genuinely warrants tighter limits.

TierQuota (per org / min)Pick this when…
low1,000The default: id-keyed CRUD, list of bounded size, simple lookups, account/settings reads. Most operations land here.
medium600Mutations with non-trivial side effects, and moderate analytics reads.
high150Bulk reads with filter / search / semantic / vector load that scan large data sets per request.
ultra30Workflow-kicking ops: imports, exports, monitor-signal, anything that sends email or enqueues a heavy job.
max10Unauthenticated or abuse-prone surfaces (used with extra global limiting; see routes/bootstrap.ts).

Don't be harsh. A tighter tier doesn't make the API safer in any meaningful way for cheap operations — it just frustrates legitimate callers. When in doubt, pick low and bump it later if a specific operation shows up in incident traffic.

Choosing route names and shapes

  • name is camelCase, verb-first, and reads like an SDK method: createApiKey, listProjects, assignSavedSearch. Avoid resource-prefixed names that read awkwardly as SDK calls (apiKeysList → use listApiKeys).
  • group / sdkMethod name the Fern SDK surface (client.<group>.<sdkMethod>()) and are renamed in place to the x-fern-* extensions — declare them right after tags (emitted key order follows declaration order, and the checked-in openapi.json is diffed byte-for-byte in CI). group doubles as the agent-toolset selector.
  • description on the route is the single-line tool/method blurb. Treat it as the first sentence an SDK user or AI agent sees when discovering the operation.
  • summary is optional, shorter, and becomes the MCP tool title. Falls back to name when omitted.

Access — declare what the operation does to data

Every operation must declare an access field on its defineOperation args (a sibling of rateLimitTier, not inside createRoute). It's a single value — "read-only" | "write" | "destructive" — and TypeScript won't let you define an operation without it. access is the authoring surface for two things: the MCP tool annotations (translated to the spec's ToolAnnotations readOnlyHint/destructiveHint by accessToAnnotations, the single translation point) and the agent-toolset access ceiling (below).

ts
const deleteWidget = widgetOperation({
  route: createRoute({ /* method, path, name, group, sdkMethod, … */ }),
  access: "destructive",
  rateLimitTier: "medium",
  execute: (input, ctx) => /* … */,
})

Write vs destructive. Mirrors the MCP spec's additive/destructive split: a write is destructive if it can delete or overwrite an existing value (the prior value is lost), and just "write" if it only adds without touching what's there. An in-place update* is "destructive" even though it isn't a delete — overwriting a stored name/settings/cell replaces the previous value. When unsure about a non-additive write, prefer "destructive".

ValueWire annotationsUse for
"read-only"{ readOnlyHint: true, destructiveHint: false }Pure reads: GET lists, gets, analytics, histograms — even when the request uses POST to carry a complex query body.
"write"{ readOnlyHint: false, destructiveHint: false }Additive or reversible: create*/insert*/add*/invite*/import*, and state toggles (mute*/unmute*, monitor*/unmonitor*, resolve*, restore*, reorder*). Exports that enqueue a job or send email are "write" (they're not read-only).
"destructive"{ readOnlyHint: false, destructiveHint: true }delete*/revoke*/remove*, and in-place update* that overwrites stored values.

Set access to match what the implementation actually does — a wrong value misstates risk to MCP clients and is load-bearing for agent toolsets (defineToolset refuses operations above its access ceiling, so an over-broad access can leak a mutation to an agent and a too-narrow one blocks a legitimate read). access is stripped before the route reaches the OpenAPI generator, so it surfaces only in mcp.json and the live MCP transport, never in openapi.json.

Agent toolsets — exposing operations to internal AI agents

defineToolset (in packages/operations/src/core/toolset.ts) selects operations and shapes them as in-process tools (invoke(rawFlatInput, ctx) — validate, split, execute; no HTTP, no tokens). Selection is a filter: an operation is included when it's tool-eligible, execute-form (invocable in-process), within the access ceiling, and in spec.groups if given. Everything else is silently dropped — so an op joins a toolset automatically once it's converted to execute-form, and writes never reach a read-only toolset by construction. Only genuine misconfiguration throws: a groups entry matching no operation (typo), or a stale exclude. Concrete toolsets live in packages/operations/src/toolsets/ (they can't live in @domain/* — that would create a package cycle, since @repo/operations imports domain packages).

Access ceiling. ToolsetSpec.access (default "read-only") is the highest access the toolset admits, and it's cumulative: a "write" ceiling admits read-only and write operations; "destructive" admits everything. Operations above the ceiling are filtered out, so an agent gets no mutations unless the toolset explicitly opts up; the default keeps research agents read-only. groups is optional — omit it to span the whole registry, e.g. the shipped readOnlyToolset:

ts
export const readOnlyToolset = defineToolset({ name: "read-only", access: "read-only" }, operationModules)
// or scope + raise the ceiling:
defineToolset({ name: "signal-writer", groups: ["signals"], access: "write" }, operationModules)

Tenancy note for toolset consumers: build OperationContext from an already-resolved organization; the model-visible input never carries org identity.

Opting out of MCP per-route

Some operations shouldn't be tools — they make sense for HTTP/SDK clients but not for AI agents (e.g. internal lifecycle endpoints, web-only callbacks). Pass tool: false:

ts
const internalReindex = widgetOperation({
  route: createRoute({ ... }),
  handler: async (c) => { ... },
  tool: false, // ← HTTP route is mounted, MCP tool is skipped
})

Verification checklist

Run before opening the PR:

bash
pnpm --filter @repo/operations typecheck && pnpm --filter @repo/operations test
pnpm --filter @app/api typecheck
pnpm --filter @app/api test
pnpm openapi:emit && git diff --exit-code apps/api/openapi.json   # no drift
pnpm mcp:emit && git diff --exit-code apps/api/mcp.json           # no drift

Spot-check both manifests by hand: open apps/api/mcp.json and apps/api/openapi.json, find your operation, confirm every field has a description. If something is missing, it'll silently degrade SDK docs and agent UX — fix it at the Zod schema, not in the JSON output. In mcp.json, also confirm your tool's annotations (translated from access) match what the implementation actually does (see "Access" above).

Where the machinery lives

If you need to debug the auto-generation pipeline:

  • packages/operations/src/core/define-operation.ts — defineOperation factory; baked-in prefix, dual execute/handler form, in-place group/sdkMethod → x-fern rename, mountHttp registers with the MCP registry on tool-eligible mounts.
  • packages/operations/src/core/execute.ts — OperationInput/OperationOutput inference and the generated Hono handler around execute.
  • packages/operations/src/core/mount.ts — mountOperationModules; applies rateLimitTier middleware, throws on missing tiers.
  • packages/operations/src/core/registry.ts — module-global operation registry; collectToolDescriptors() emits the snapshot used by both the runtime MCP transport and mcp:emit.
  • packages/operations/src/core/toolset.ts / core/invoke.ts — agent toolset selection + in-process invocation.
  • packages/operations/src/operations/index.ts — the ordered module manifest (mount order lives here).
  • apps/api/src/mcp/server.ts — per-request MCP server, dispatches each tool call back through rootApp.fetch() so the full middleware chain (auth, rate-limit, org-context, validation) re-runs on every inner call.
  • apps/api/scripts/emit-openapi.ts / apps/api/scripts/emit-mcp.ts — boot the route registry with stub clients and serialize the manifests.
  • packages/operations/src/openapi/schemas.ts and openapi/pagination.ts — shared boundary primitives (security scheme, typedResponses, Paginated(...), common param schemas).
  • code-style — Zod-first contracts, naming conventions, literal-union enums.
  • architecture-boundaries — web vs API split, machine-facing surface invariants.
  • authentication — how c.var.auth / c.var.organization get populated on protected routes.
  • testing — Vitest harness layout, setupTestApi for HTTP-level integration tests.

© latitude-dev, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/api-endpoints of latitude-dev/latitude-llm.

Open the folder on GitHubat commit 87e8aa0

Compare with similar skills

API Endpoints next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

API Endpoints compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
API Endpoints this skilllatitude-dev/latitude-llm4.7k—~6.5kAutomated safety check: PassMIT
OpenAPI to MCP Servermcp-use/mcp-use11k—~5.2kAutomated safety check: PassApache-2.0
API Endpoint Contracttrycompai/comp2k—~2.7kAutomated safety check: PassAGPL-3.0
Kingdee MCP DevWaHaiLong/KingdeeMCP103—~853Automated safety check: PassMIT
Ns APINethServer/nethsecurity191—~2.8kAutomated safety check: PassCustom licence
FastAPI ExpertJeffallan/claude-skills12k—~1.8kAutomated safety check: PassMIT

Similar skills

  • OpenAPI to MCP Server

    mcp-use/mcp-use

    Turns an OpenAPI or Swagger spec into an MCP server with the mcp-use TypeScript SDK, mapping each operation to a tool, wiring auth, testing and deploying.

    11k GitHub stars~5.2k tokensUpdated today
    Backend & APIsAuto-check passed
  • API Endpoint Contract

    trycompai/comp

    The contract every new or modified API endpoint must follow so it is correct for the public OpenAPI spec, the MCP server (npm @trycompai/mcp-server), the ValidationPipe, and the docs.

    2k GitHub stars~2.7k tokensUpdated 5 days ago
    Backend & APIsAuto-check passed
  • Kingdee MCP Dev

    WaHaiLong/KingdeeMCP

    Knowledge base for the Kingdee MCP Dev Squad. An agent skill from WaHaiLong/KingdeeMCP.

    103 GitHub stars~853 tokensUpdated 1 mo ago
    Backend & APIsAuto-check passed
  • Ns API

    NethServer/nethsecurity

    Write or modify a NethSecurity Python RPCD API script or hook.

    191 GitHub stars~2.8k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • FastAPI Expert

    Jeffallan/claude-skills

    Builds async Python APIs with FastAPI and Pydantic V2, covering endpoints, JWT authentication, async SQLAlchemy, WebSockets and pytest checks against the OpenAPI docs.

    12k GitHub stars~1.8k tokensUpdated 4 days ago
    Backend & APIsAuto-check passed
  • Python Fastapi Patterns

    aiskillstore/marketplace

    FastAPI web framework patterns. An agent skill from aiskillstore/marketplace.

    430 GitHub starsUsed in 1 repo~1.3k tokens
    Backend & APIsAuto-check: notes

More from latitude-dev/latitude-llm

All 28 skills in this repo
  • Better Auth Best Practices

    latitude-dev/latitude-llm

    Configure Better Auth server and client, set up database adapters, manage sessions, add plugins, and handle environment variables.

    4.7k GitHub starsUsed in 7 repos~1.6k tokens
    Auto-check passed
  • Artifact Designer

    latitude-dev/latitude-llm

    Create, validate, preview, and publish self-contained HTML artifacts.

    4.7k GitHub stars~1.1k tokensUpdated yesterday
    Auto-check passed
  • CI Watchdog

    latitude-dev/latitude-llm

    Continuously monitor GitHub PR CI checks and automatically fix failures until all checks pass.

    4.7k GitHub stars~1.6k tokensUpdated yesterday
    Auto-check passed
  • Temporal Developer

    latitude-dev/latitude-llm

    This skill should be used when the user asks to "create a Temporal workflow", "write a Temporal activity", "debug stuck workflow", "fix non-determinism error", "Temporal Python", "Temporal…

    4.7k GitHub stars~1.5k tokensUpdated yesterday
    Auto-check passed
  • Docs

    latitude-dev/latitude-llm

    Review the current conversation context and git changes, then persist durable repository knowledge into dev-docs/.md by domain and into AGENTS.md for cross-cutting repo rules.

    4.7k GitHub stars~2.5k tokensUpdated yesterday
    Auto-check passed
  • Managing Maintenance Windows

    latitude-dev/latitude-llm

    Enables or disables Latitude production maintenance mode by redirecting all publicly exposed production services to the Better Stack status page.

    4.7k GitHub stars~802 tokensUpdated yesterday
    Auto-check passed

Categories

Questions about API Endpoints

What does API Endpoints do?

Adding or changing API operations in @repo/operations. An agent skill from latitude-dev/latitude-llm. API Endpoints is an agent skill from latitude-dev/latitude-llm. Adding or changing API operations in @repo/operations.

When should I use API Endpoints?

API Endpoints fits situations like: tasks that involve REST APIs; tasks that involve OpenAPI specifications; tasks that involve MCP servers.

How do I install API Endpoints in Claude Code?

Run `npx skills add latitude-dev/latitude-llm --skill api-endpoints -a claude-code`. Or copy the skill folder (.agents/skills/api-endpoints in latitude-dev/latitude-llm) into .claude/skills/api-endpoints in your project. Claude Code loads it when a task matches its description.

How do I install API Endpoints in Codex?

Run `npx skills add latitude-dev/latitude-llm --skill api-endpoints -a codex`. Or copy the skill folder (.agents/skills/api-endpoints in latitude-dev/latitude-llm) into .agents/skills/api-endpoints in your project. Codex loads it when a task matches its description.

Can I use API Endpoints in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add latitude-dev/latitude-llm --skill api-endpoints -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/api-endpoints, .gemini/skills/api-endpoints, .github/skills/api-endpoints and .opencode/skills/api-endpoints in your project.

What does API Endpoints need to run?

Going by SKILL.md and its folder, API Endpoints needs the command-line tools its instructions call (pnpm, git, uv, npm and cargo). Our summary lists: Python 3; Docker.

Does API Endpoints access the network?

SKILL.md names 1 domain. As links in the text: modelcontextprotocol.io. This is read from the text; nothing was executed.

Is API Endpoints safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does API Endpoints use?

API Endpoints is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does API Endpoints use?

About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to API Endpoints?

Skills that share tags, products or a category with API Endpoints: OpenAPI to MCP Server (mcp-use/mcp-use, 11k stars), API Endpoint Contract (trycompai/comp, 2k stars), Kingdee MCP Dev (WaHaiLong/KingdeeMCP, 103 stars) and Ns API (NethServer/nethsecurity, 191 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains API Endpoints?

latitude-dev (a GitHub organization) maintains it in latitude-dev/latitude-llm, which has 4,712 GitHub stars. The repository holds 28 skills in this directory. The repository was last updated on October 6, 2026.

Source: latitude-dev/latitude-llm on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.