api2cli
Generate a working Node.js CLI from any API, then wrap it in a Claude Code skill. Discovers endpoints, scaffolds a dual-mode Commander.js CLI with a full-featured API client, and creates a skill folder so Claude knows how to use it.
Workflow
-
Identify the API -- user provides a docs URL, a live API base URL, or a peek-api capture
-
Discover endpoints -- parse docs, probe the API, or read a peek-api catalog
-
Build endpoint catalog -- normalize all discovered endpoints into a standard format
-
Generate CLI -- scaffold Commander.js CLI from the catalog
-
User chooses destination -- scaffold into current project or create standalone project
-
Generate skill -- create a SKILL.md that teaches Claude how to use the generated CLI
Step 1: Identify the API
Ask the user:
- "What API do you want to wrap? Share a docs URL, a base URL, or point me at a peek-api capture."
Determine which discovery paths to use based on what they provide:
Input Discovery Path
Docs URL (e.g., https://docs.stripe.com/api ) Docs parsing + active probing
Base URL (e.g., https://api.example.com/v1 ) Active probing
peek-api capture dir (e.g., ./peek-api-linkedin/ ) Read existing catalog
Live website URL Suggest running peek-api first, then active probing
Also ask:
-
"What auth does this API use?" (API key, Bearer token, cookies, OAuth, none)
-
"Do you want this CLI in your current project or as a standalone project?"
Step 2: Discover Endpoints
Use all applicable discovery paths. Combine results into a single catalog.
Path A: Docs Parsing
-
Fetch the docs URL with WebFetch
-
Extract endpoint information: method, path, description, parameters, request/response examples
-
Look for pagination patterns, auth requirements, rate limit info
-
Follow links to sub-pages for individual endpoint docs if the main page is an index
Path B: Active Probing
-
Check well-known paths for API specs:
-
/.well-known/openapi.json , /.well-known/openapi.yaml
-
/openapi.json , /openapi.yaml , /swagger.json , /swagger.yaml
-
/api-docs , /docs , /api/docs
-
/graphql (with introspection query)
-
Try OPTIONS on the base URL and common resource paths
-
Probe common REST patterns: /api/v1/ , /api/v2/ , /v1/ , /v2/
-
For each discovered resource, try standard CRUD: GET /resources , GET /resources/:id , POST /resources , etc.
-
Parse response shapes to understand data models
-
Check response headers for rate limit info (X-RateLimit-* , Retry-After )
-
Check for pagination patterns in responses (next , cursor , page , offset )
See references/discovery-strategies.md for detailed probing patterns.
Path C: peek-api Capture
-
Read the capture directory: endpoints.json , auth.json , CAPTURE.md
-
Parse endpoints into the standard catalog format
-
Extract auth headers and cookies from auth.json
If peek-api is not installed or no capture exists, tell the user:
To capture endpoints from a live site, install peek-api: git clone https://github.com/alexknowshtml/peek-api cd peek-api && npm install node bin/cli.js https://example.com
Step 3: Build Endpoint Catalog
Normalize all discovered endpoints into this format:
interface EndpointCatalog { service: string; // e.g., "stripe", "nexudus" baseUrl: string; auth: { type: 'api-key' | 'bearer' | 'cookies' | 'oauth' | 'none'; headerName?: string; // e.g., "Authorization", "X-API-Key" envVar: string; // e.g., "STRIPE_API_KEY" }; pagination?: { style: 'cursor' | 'offset' | 'page' | 'link-header'; paramName: string; // e.g., "starting_after", "offset", "page" responseField: string; // e.g., "has_more", "next", "next_page_url" }; rateLimit?: { requests: number; window: string; // e.g., "1m", "1h" }; resources: ResourceGroup[]; }
interface ResourceGroup { name: string; // e.g., "customers", "invoices" description: string; endpoints: Endpoint[]; }
interface Endpoint { method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; path: string; // e.g., "/v1/customers/:id" description: string; parameters: Parameter[]; requestBody?: object; // JSON schema or example responseExample?: object; }
interface Parameter { name: string; in: 'path' | 'query' | 'header'; required: boolean; type: string; description: string; }
Present the catalog to the user for review before generating:
Found 24 endpoints across 5 resources: customers (6 endpoints): list, get, create, update, delete, search invoices (5 endpoints): list, get, create, send, void ... Ready to generate the CLI?
Step 4: Generate CLI
Generate a dual-mode CLI using Commander.js. The CLI auto-detects human vs agent output via process.stdout.isTTY .
File Structure
In-project scaffold:
scripts/ {service}.ts # Entry point with shebang {service}/ lib/ client.ts # API client (auth, pagination, retry, caching) envelope.ts # Agent JSON envelope helpers commands/ {resource}.ts # One file per resource group
Standalone project:
{service}-cli/ package.json tsconfig.json bin/ {service}.ts # Entry point with shebang src/ lib/ client.ts envelope.ts commands/ {resource}.ts
Code Generation Patterns
See these references for the patterns to apply during generation:
-
references/api-client-template.md -- API client class with pagination, retry, rate limiting, caching
-
references/agent-first-patterns.md -- JSON envelope, HATEOAS next_actions, context-safe output, error fix suggestions
-
references/commander-patterns.md -- Commander.js subcommands, global options, interactive prompts, colored output
Key Generation Rules
Entry point ({service}.ts ):
-
Shebang: #!/usr/bin/env npx tsx
-
Self-documenting root command (no args → prints full command tree as JSON)
-
Global options: --json (force JSON output), --verbose , --config <path>
API client (lib/client.ts ):
-
Constructor takes base URL + auth config
-
Auth from env var (name based on catalog.auth.envVar )
-
Built-in pagination matching the API's pattern
-
Retry with exponential backoff for 5xx and 429 errors
-
Rate limiting based on discovered limits
-
Optional response caching
Envelope helpers (lib/envelope.ts ):
const isAgent = !process.stdout.isTTY;
function respond(command: string, result: any, nextActions: Action[] = []) { if (isAgent) { console.log(JSON.stringify({ ok: true, command, result, next_actions: nextActions })); } else { return result; // caller handles human rendering } }
function respondError(command: string, message: string, code: string, fix: string, nextActions: Action[] = []) {
if (isAgent) {
console.log(JSON.stringify({ ok: false, command, error: { message, code }, fix, next_actions: nextActions }));
} else {
console.error(Error: ${message});
console.error(Fix: ${fix});
}
process.exit(1);
}
Command files (commands/{resource}.ts ):
-
One file per resource group
-
Each endpoint becomes a subcommand: mycli customers list , mycli customers get <id>
-
list commands: support --limit , --offset /--cursor , --status (if filterable)
-
get commands: take ID as argument
-
create /update commands: accept --data <json> or individual --field flags
-
Every command includes contextual next_actions for agent mode
-
Errors include fix suggestions
Standalone project extras:
-
package.json with commander , tsx as dependencies, bin field pointing to entry
-
tsconfig.json for TypeScript
-
.env.example with the required env var
Step 5: Verify
After generating the CLI:
-
Verify it runs: Execute with no args, confirm the self-documenting root works
-
Test one endpoint: Pick a simple GET endpoint, run it, verify output
-
Move on to Step 6 to wrap the CLI in a skill
Step 6: Generate Skill
Create a Claude Code skill folder that teaches Claude how to use the generated CLI. This is the final step -- it turns the CLI into something any Claude session can pick up and use without reading the code.
Skill Structure
.claude/skills/{service}/ SKILL.md # Skill instructions
SKILL.md Template
Generate a SKILL.md with this structure:
name: {service} description: Interact with the {Service} API via CLI. Use when user wants to {list of actions based on discovered resources, e.g., "list customers, create invoices, check order status"}. Commands: {service} {resource} {action}.
{Service} CLI
CLI wrapper for the {Service} API.
Setup
Set the {SERVICE_ENV_VAR} environment variable:
```bash
export {SERVICE_ENV_VAR}=your-api-key-here
```
Commands
{For each resource group, list commands with examples:}
{Resource}
```bash
List {resources}
npx tsx {path/to/cli}.ts {resource} list
Get a specific {resource}
npx tsx {path/to/cli}.ts {resource} get <id>
Create a {resource}
npx tsx {path/to/cli}.ts {resource} create --field value ```
Common Workflows
{Generate 2-3 practical workflows combining multiple commands:}
Example: {Workflow name}
```bash
Step 1: Find the customer
npx tsx {path/to/cli}.ts customers list --status=active
Step 2: Get their invoices
npx tsx {path/to/cli}.ts invoices list --customer-id=abc123 ```
Agent Usage
When piped, all commands return JSON with next_actions:
```bash
npx tsx {path/to/cli}.ts {resource} list | cat
```
Key Rules for Skill Generation
-
Description is critical -- include specific trigger phrases and list the actions the CLI supports. This is what Claude reads to decide when to use the skill.
-
Include real command examples -- use the actual CLI path and real subcommand names from the generated CLI.
-
Generate practical workflows -- combine multiple commands into realistic multi-step scenarios based on how the API's resources relate to each other.
-
Keep it lean -- the skill should be a quick reference, not a restatement of --help . Focus on what Claude needs to know that it can't infer.
Tell the User
After generating both the CLI and the skill:
CLI generated at {cli_path} Skill generated at .claude/skills/{service}/SKILL.md
To use the CLI directly: npx tsx {cli_path} # See all commands npx tsx {cli_path} customers list # List customers
Claude will now automatically use this skill when you ask about {service}.
Reference Files
-
references/discovery-strategies.md -- Detailed probing patterns, well-known paths, GraphQL introspection, response parsing
-
references/api-client-template.md -- Full API client class with pagination, retry, rate limiting, caching
-
references/agent-first-patterns.md -- Agent JSON envelope, HATEOAS, context-safe output, error handling
-
references/commander-patterns.md -- Commander.js subcommands, nested commands, interactive prompts, colored output, config files, testing