One Declaration Generates a CLI, Agent Skills, and Docs from Company APIs
Helping Companies Wrap Interfaces into CLI + Skills for Agent Invocation
Recently, I helped a company convert interfaces into CLI + Skill format for Agent invocation. The process encountered many problems, and in the end, I crystallized these experiences into an SDK: @renxqoo/agent-cli-sdk. You just need to tell the AI Agent "which interface to call and how to map the fields," and it can simultaneously produce Skill files for both CLI and Agent use. Authentication, unified output, typed errors, and progressive disclosure are all built-in.
Project address: https://github.com/renxqoo/agent-cli-sdk
What Problems Did I Encounter?
Initially, we had the Agent use the curl tool to directly request the company's business interfaces, or write a script to call the interfaces directly, but we quickly found this unworkable:
- It was impossible to provide accurate parameter validation for every interface; the Agent could only rely on error messages to retry repeatedly;
- There was no unified error code or output format;
- The format returned by each execution could be different, creating significant uncertainty for the Agent;
- Authentication was the most troublesome: all company interfaces required a login state, credentials couldn't be hardcoded into scripts, and we couldn't have the Agent ask for a token every time. When the token expired, the Agent had no idea what to do.
So I wrote @renxqoo/agent-cli-sdk to solve these problems. The authentication handling approach is worth expanding on separately:
- Keep the Agent away from credentials: Login uses OAuth 2.1 (default device flow, providing a link and code in the terminal completes it). Credentials are persisted by the CLI to a local state directory (e.g.,
~/.orders/credentials/orders.json). The Agent only runs commands and never touches the token from start to finish; - Automatic token refresh on expiry: The CLI detects expiration and refreshes automatically, transparent to the Agent;
- Deterministic failure signal: When refresh fails, it uniformly throws an
authenticationtype error with exit code 3. The Agent uses this to guide re-execution oforders auth logininstead of blindly retrying.
I encapsulated this logic into the defineAuth plugin; the "Usage" section below has a complete example.
Why CLI Instead of MCP?
You might notice a phenomenon: Major companies like Lark and MiniMax, when providing external AI capabilities, almost all release official CLI tools, wrapping all internal interfaces into commands for developers and Agents to use. Why not use MCP (Model Context Protocol) directly?
Limitations of MCP
MCP has indeed gained considerable attention in the Agent tool ecosystem, but it's not perfect:
- Heavier architecture: MCP requires a persistent Server process, increasing deployment and operational costs;
- Persistent context: Every startup injects the Tools returned by MCP into the context, even if you don't use them;
Natural Advantages of CLI
In contrast, CLI is a universal standard validated over decades, with unique advantages in Agent scenarios:
- Natural Agent fit: CLI tools typically have clear parameter descriptions, help information, unified
--jsonoutput, and explicit exit codes—all foundations for reliable Agent invocation; - Piping and composition: CLI can easily combine with other commands via Unix pipes (e.g.,
lark messages list | jq), while MCP cannot directly participate in this ecosystem; - Direct invocation in code: Any language can call CLI commands via
child_processorsubprocessand process the returned structured data in code, whereas MCP requires additional client libraries and protocol stacks; - Standardized output and error handling: CLI's stdout/stderr separation and exit code mechanism are naturally suited for automation workflows and error branching.
Core Idea: One Declaration, Three Synchronized Artifacts
The core idea of agent-cli-sdk is: Use a single defineCommand declaration to simultaneously generate three synchronized artifacts:
defineCommand(name / description / zod / run)
│
├── CLI Usable by humans and Unix pipes (acme orders list | jq)
├── SKILL.md AI Agent progressively loaded skill description (auto-generated by skills gen)
└── agent dirs Synced to ~/.claude, ~/.codex, etc. directories (skills sync)
The command, documentation, and what the Agent reads are always the same thing.
Additionally, the project includes a built-in skill that teaches the Agent how to use @renxqoo/agent-cli-sdk to generate an entire standardized CLI based on an API description, without needing to write code yourself.
Core Highlights
- 🧩 Skill Factory: Install a skill to let the AI Agent turn any company API into a CLI + Agent Skill
- 🔁 One declaration, multi-sync: CLI commands, SKILL.md, and Agent directories automatically stay consistent
- 🔐 OAuth 2.1 one-line integration:
defineAuthplugin auto-injectslogin / status / logout / registercommands - 📦 Unified output contract: Both success and failure have fixed JSON structures, reliably parseable by both humans and Agents
- 🚦 9 typed error categories + exit codes: Agents can automatically branch based on exit codes
- 📚 Progressive disclosure: Agents load Skill details on demand; unused APIs consume fewer tokens
- 🧱 Plugin system: Authentication, logging, auditing, etc., can all be injected via plugins
- ⚡ TypeScript-first, ESM-only, Node.js >= 20
Usage
Method 1: Install the Skill, Let the Agent Auto-Generate the CLI
This is the fastest path; you don't even need to write code yourself.
1. Install the Skill
Send the following directly to your Agent:
Please install @user_4998424d/agent-cli-builder according to https://skillhub.cn/install/skillhub.md.
2. Assign Tasks to the Agent
After installation, you can directly assign tasks to the Agent, for example:
Find all interfaces in the current project, then use the agent-cli-builder skill to generate a CLI application.
The Agent will automatically generate src/commands/*.ts and src/index.ts according to the SDK contract, including Zod parameter validation, unified error handling, etc.
Method 2: Manually Install the Library and Write the CLI
If you prefer to control the code yourself, you can manually install the SDK:
npm install @renxqoo/agent-cli-sdk
# or
pnpm add @renxqoo/agent-cli-sdk
Requires Node.js >= 20, ESM only.
Below is a complete single-command CLI example (under 30 lines, no auth, public data):
#!/usr/bin/env node
import { defineCli, defineCommand } from "@renxqoo/agent-cli-sdk";
import * as z from "zod";
import { realpathSync } from "node:fs";
import { fileURLToPath } from "node:url";
const app = defineCli({
name: "myapp",
description: "My data CLI",
baseUrl: "https://api.example.com",
commands: {
list: defineCommand({
name: "list",
description: "Query list",
args: {
schema: z.object({
limit: z.coerce.number().min(1).max(100).default(20),
}),
},
async run(ctx, args) {
const res = await ctx.get<{ items: Array<{ id: string; title: string }> }>("/items", {
limit: args.limit,
});
return { data: res.data.items, meta: { count: res.data.items.length } };
},
}),
},
});
function isMainEntry(): boolean {
try {
return realpathSync(process.argv[1] ?? "") === fileURLToPath(import.meta.url);
} catch {
return false;
}
}
if (isMainEntry()) app.run(process.argv.slice(2));
export default app;
Runtime:
myapp list --limit 5
If OAuth authentication is needed, just one line integrates it:
import { defineCliApp, defineAuth } from "@renxqoo/agent-cli-sdk";
import { homedir } from "node:os";
import { join } from "node:path";
export default await defineCliApp({
name: "orders",
dir: join(homedir(), ".orders"), // App's own state directory
plugins: [
defineAuth({
credentialNamespace: "orders", // → config/orders.json + credentials/orders.json
baseUrl: "https://auth.example.com",
scope: "orders.read offline_access",
}),
],
commands: {},
});
// Auto-injects: orders auth login / status / logout / register
Supports three OAuth 2.1 flows: device (default), authorization_code + PKCE, and client_credentials.
Core API Analysis
defineCli(options) — Assemble CLI
defineCli({
name: 'orders', // Required: namespace
description: '...', // Required
plugins: [authPlugin], // Optional: auth/logging/audit plugins
commands: { list, get }, // Required: top-level commands → orders list
namespaces: { orders: {...} }, // Optional: sub-namespaces → orders orders list
baseUrl: 'https://api.x.com', // Optional: backend address for ctx.get/post/...
errorOnStatus: { 404: 'not_found', '5xx': 'server_error' }, // Optional
defaultFormat: 'auto', // Optional: 'auto' (default) | 'json' | 'human'
skillsDir: './skills', // Optional: enable built-in skills commands
skillsTargets: [...], // Optional: sync targets (default detects common Agent directories)
})
defineCommand(spec) — Declare Command
import * as z from "zod";
defineCommand({
name: "get",
description: "Query a single order",
args: {
schema: z.object({
id: z.string().min(1).describe("Order ID"),
verbose: z.boolean().describe("Verbose output").default(false),
}),
pos: ["id"], // id is a positional argument, not a flag of the same name
},
humanFormat: (data) => `Order: ${data.id}`, // Optional: custom human-readable output
async run(ctx, args) {
const res = await ctx.get(`/orders/${args.id}`); // ctx.get/post/put/patch/delete
return { data: res.data };
},
});
The Zod schema is the sole source of parameter validation and types. args.type defaults to argv, but can also be set to json to receive complete structured input via --input / --input-file / stdin.
defineAuth(opts) — OAuth 2.1 Factory
Returns a Plugin, directly placed into defineCliApp({ plugins: [auth] }), and authentication-related commands are automatically mounted.
Plugin (Hooks + Providers)
const myPlugin = {
name: "audit",
enforce: "pre", // 'pre' | 'post' (default normal)
provides: {
commands: { telemetry: telemetryCmd }, // Contribute commands
namespaces: { admin: { users: userCmd } },
},
async beforeRequest(ctx, req) {
return { ...req, headers: { ...req.headers, "x-client": "my-cli" } };
},
async transformOutput(ctx, data) {
return data;
},
async handleUnauthorized(ctx, event) {
return { action: "decline" };
},
};
Commands provided by a plugin are automatically exempt from that plugin's own beforeCommand, but not from other plugins.
Unified Output Contract
This is one of the key designs making the SDK Agent-friendly: Both success and failure have fixed JSON structures.
Success output (stdout):
{"ok":true,"source":"orders","data":{"orders":[...]},"meta":{"count":2,"pagination":{"complete":true}}}
Error output (stderr):
{
"ok": false,
"error": {
"type": "api",
"subtype": "not_found",
"message": "Order not found",
"hint": "Check the ID"
}
}
Exit Code Table
| Exit Code | Category | Meaning |
|---|---|---|
| 0 | — | Success |
| 1 | api | Server-side business error (404/500/429…) |
| 2 | validation | Parameter validation failure |
| 3 | authentication / authorization / config | Not logged in / No permission / Missing config |
| 4 | network | DNS / Timeout / Connection refused |
| 5 | internal | SDK internal error |
| 6 | policy | Risk control interception |
| 10 | confirmation | High-risk write operation requires --yes |
Comes with 9 typed error classes: ValidationError / AuthenticationError / PermissionError / ConfigError / NetworkError / APIError (including NotFoundError) / PolicyError / InternalError / ConfirmationRequiredError. Always throw using errs.*; bare Error will be downgraded to internal/unknown.
The output mode defaults to auto: TTY environments output human-readable text, pipe/script environments automatically switch to JSON. Agents and scripts should always pass --json.
Skills and Progressive Disclosure
This is the project's biggest differentiator from ordinary CLI frameworks.
Built-in commands:
mycli skills gen mycli --init— Generate aSKILL.mdskeleton with an auto-generated command tablemycli skills gen mycli— Only refresh the auto-generated blocks, preserving hand-written semanticsmycli skills sync— Copy skills to installed Agent directories (~/.agentsalways synced;~/.claude/~/.codex/~/.cursoretc. auto-detected if present)mycli skills list/mycli skills read <name>— List/read built-in skills
The Agent will lazy-load skills: first only read name + description, and only expand the full SKILL.md when a task matches, reading references/ on demand. Unused APIs consume no tokens.
Who Is It For?
- Teams needing to quickly provide a CLI for internal company APIs
- Developers building AI Agent toolchains who want CLI and Agent capabilities to stay in sync
- CLI framework enthusiasts wanting unified output formats, error handling, and authentication flows
- Explorers wanting to use the "skill factory" pattern to let Agents auto-generate tools
Conclusion and Thoughts
@renxqoo/agent-cli-sdk quickly builds company data interfaces into standardized, uniformly outputting CLI + Skill tools. It merges the three tasks of "writing commands," "writing docs," and "adapting for Agents" into a single declaration, and through a unified output contract and typed errors, allows Agents to reliably use tools to fetch interface data.
However, CLI + Skill has a prerequisite: it must run in an environment capable of executing shell commands. Locally used Claude Code, Codex, and Workbuddy all satisfy this; even if a cloud Agent provides a sandbox, authentication becomes a new problem.
When used in the cloud, OAuth login requires human interaction, but a cloud Agent can only interact with you through a dialog box; Agent platforms generally don't provide credential injection, so you can only pre-set a short-term token in environment variables—once it expires, you must log in again, and storing a long-term refresh token carries leakage risks.
If the Agent is deployed by your own company, you can solve this by adding a gateway layer between the CLI and the backend interface for request forwarding—the CLI sends the request, and the gateway automatically attaches the token when forwarding to access the real interface. Credentials always stay within your controlled internal network, and the Agent in the sandbox never touches them. The prerequisite is that the gateway itself must implement proper access control—restrict to the internal network, bind the caller's identity, issue short-term tokens, and retain audit logs to reduce authentication risks.
If you are also seeking a CLI + Skill integration solution, why not give it a Star ⭐ and try it out.
Project address: https://github.com/renxqoo/agent-cli-sdk