@tronsfey/ucli 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +349 -0
- package/README.zh.md +340 -0
- package/assets/logo.svg +138 -0
- package/dist/index.js +550 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
- package/skill.md +223 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/lib/server-client.ts","../src/commands/configure.ts","../src/lib/cache.ts","../src/lib/oas-runner.ts","../src/commands/services.ts","../src/commands/run.ts","../src/commands/refresh.ts","../src/commands/help-cmd.ts","../src/lib/mcp-runner.ts","../src/commands/mcp.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { createRequire } from 'node:module'\nimport { registerConfigure } from './commands/configure.js'\nimport { registerServices } from './commands/services.js'\nimport { registerRun } from './commands/run.js'\nimport { registerRefresh } from './commands/refresh.js'\nimport { registerHelp } from './commands/help-cmd.js'\nimport { registerMcp } from './commands/mcp.js'\n\nconst require = createRequire(import.meta.url)\nconst pkg = require('../package.json') as { version: string; description: string }\n\nconst program = new Command()\n\nprogram\n .name('ucli')\n .description(pkg.description)\n .version(pkg.version, '-v, --version')\n .addHelpCommand(false) // we provide our own help command\n\nregisterConfigure(program)\nregisterServices(program)\nregisterRun(program)\nregisterRefresh(program)\nregisterMcp(program)\nregisterHelp(program)\n\nprogram.parse(process.argv)\n","import Conf from 'conf'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface CLIConfig {\n serverUrl: string\n token: string\n}\n\nconst conf = new Conf<CLIConfig>({\n projectName: 'ucli',\n schema: {\n serverUrl: { type: 'string' },\n token: { type: 'string' },\n },\n})\n\nexport const cacheDir = join(homedir(), '.cache', 'ucli')\n\nexport function getConfig(): CLIConfig {\n const serverUrl = conf.get('serverUrl')\n const token = conf.get('token')\n\n if (!serverUrl || !token) {\n console.error('ucli is not configured. Run: ucli configure --server <url> --token <jwt>')\n process.exit(1)\n }\n\n return { serverUrl, token }\n}\n\nexport function saveConfig(cfg: CLIConfig): void {\n conf.set('serverUrl', cfg.serverUrl)\n conf.set('token', cfg.token)\n}\n\nexport function isConfigured(): boolean {\n return Boolean(conf.get('serverUrl') && conf.get('token'))\n}\n","/**\n * HTTP client for the ucli-server API.\n * Attaches group JWT and handles common error cases.\n */\nimport axios, { type AxiosInstance } from 'axios'\nimport type { CLIConfig } from '../config.js'\n\nexport interface OASEntryPublic {\n id: string\n name: string\n description: string\n remoteUrl: string\n baseEndpoint: string | null\n authType: 'bearer' | 'api_key' | 'basic' | 'oauth2_cc' | 'none'\n authConfig: Record<string, unknown>\n cacheTtl: number\n}\n\nexport type McpAuthConfig =\n | { type: 'none' }\n | { type: 'http_headers'; headers: Record<string, string> }\n | { type: 'env'; env: Record<string, string> }\n\nexport interface McpEntryPublic {\n id: string\n groupId: string\n name: string\n description: string\n transport: 'http' | 'stdio'\n serverUrl: string | null\n command: string | null\n authConfig: McpAuthConfig\n enabled: boolean\n}\n\nexport class ServerClient {\n private http: AxiosInstance\n\n constructor(cfg: CLIConfig) {\n this.http = axios.create({\n baseURL: cfg.serverUrl.replace(/\\/$/, ''),\n headers: {\n Authorization: `Bearer ${cfg.token}`,\n 'Content-Type': 'application/json',\n },\n timeout: 15_000,\n })\n\n this.http.interceptors.response.use(\n (r) => r,\n (err: unknown) => {\n if (axios.isAxiosError(err)) {\n const status = err.response?.status\n const message = (err.response?.data as { message?: string })?.message ?? err.message\n\n if (status === 401) {\n console.error('Authentication failed. Run: ucli configure --server <url> --token <jwt>')\n process.exit(1)\n }\n\n throw new Error(`Server error ${status ?? 'unknown'}: ${message}`)\n }\n throw err\n },\n )\n }\n\n async listOAS(): Promise<OASEntryPublic[]> {\n const { data } = await this.http.get<OASEntryPublic[]>('/api/v1/oas')\n return data\n }\n\n async getOAS(name: string): Promise<OASEntryPublic> {\n const { data } = await this.http.get<OASEntryPublic>(`/api/v1/oas/${encodeURIComponent(name)}`)\n return data\n }\n\n async listMCP(): Promise<McpEntryPublic[]> {\n const { data } = await this.http.get<McpEntryPublic[]>('/api/v1/mcp')\n return data\n }\n\n async getMCP(name: string): Promise<McpEntryPublic> {\n const { data } = await this.http.get<McpEntryPublic>(`/api/v1/mcp/${encodeURIComponent(name)}`)\n return data\n }\n}\n","import type { Command } from 'commander'\nimport { saveConfig, isConfigured } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\n\nexport function registerConfigure(program: Command): void {\n program\n .command('configure')\n .description('Configure the OAS Gateway server URL and authentication token')\n .requiredOption('--server <url>', 'OAS Gateway server URL (e.g. https://oas.example.com)')\n .requiredOption('--token <jwt>', 'Group JWT token issued by the server admin')\n .action(async (opts: { server: string; token: string }) => {\n const serverUrl = opts.server.replace(/\\/$/, '')\n const token = opts.token\n\n // Validate connectivity before saving\n console.log(`Connecting to ${serverUrl}...`)\n const client = new ServerClient({ serverUrl, token })\n\n try {\n await client.listOAS()\n saveConfig({ serverUrl, token })\n console.log('✓ Configuration saved successfully.')\n console.log(` Server: ${serverUrl}`)\n console.log(` Token: ${token.slice(0, 20)}...`)\n } catch (err) {\n console.error('Connection failed:', (err as Error).message)\n console.error('Please check the server URL and token.')\n process.exit(1)\n }\n })\n}\n","/**\n * Local file cache for OAS entries.\n * Stored at ~/.cache/oas-cli/<name>.json with TTL metadata.\n */\nimport { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { cacheDir } from '../config.js'\nimport type { OASEntryPublic } from './server-client.js'\n\ninterface CacheFile {\n entries: OASEntryPublic[]\n fetchedAt: number // ms timestamp\n ttlSec: number // cache duration\n}\n\nconst LIST_CACHE_FILE = join(cacheDir, 'oas-list.json')\n\nasync function ensureCacheDir(): Promise<void> {\n await mkdir(cacheDir, { recursive: true })\n}\n\nexport async function readOASListCache(): Promise<OASEntryPublic[] | null> {\n try {\n const raw = await readFile(LIST_CACHE_FILE, 'utf8')\n const cached: CacheFile = JSON.parse(raw)\n const age = (Date.now() - cached.fetchedAt) / 1000\n if (cached.ttlSec === 0 || age > cached.ttlSec) return null // expired\n return cached.entries\n } catch {\n return null // not found or parse error\n }\n}\n\nexport async function writeOASListCache(entries: OASEntryPublic[], ttlSec: number): Promise<void> {\n await ensureCacheDir()\n const cached: CacheFile = { entries, fetchedAt: Date.now(), ttlSec }\n await writeFile(LIST_CACHE_FILE, JSON.stringify(cached, null, 2), 'utf8')\n}\n\nexport async function clearOASListCache(): Promise<void> {\n try {\n await writeFile(LIST_CACHE_FILE, '{}', 'utf8')\n } catch {\n // ignore if not found\n }\n}\n","/**\n * Bridge between oas-cli and @tronsfey/openapi2cli run mode.\n *\n * Spawns openapi2cli as a child process, injecting auth config\n * as environment variables (never exposed to the agent's shell).\n */\nimport { spawn } from 'node:child_process'\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport { join, dirname } from 'node:path'\nimport type { OASEntryPublic } from './server-client.js'\n\nconst require = createRequire(import.meta.url)\n\nfunction resolveOpenapi2CliBin(): string {\n try {\n const pkgPath = require.resolve('@tronsfey/openapi2cli/package.json')\n const pkgDir = dirname(pkgPath)\n // @tronsfey/openapi2cli exposes its binary; find it\n const pkg = require('@tronsfey/openapi2cli/package.json') as { bin?: Record<string, string> }\n const binEntry = pkg.bin?.['openapi2cli'] ?? 'bin/openapi2cli.js'\n return join(pkgDir, binEntry)\n } catch {\n throw new Error(\n '@tronsfey/openapi2cli is not installed. Run: pnpm add @tronsfey/openapi2cli in packages/cli',\n )\n }\n}\n\nexport interface RunOptions {\n entry: OASEntryPublic\n /** Operation command args (e.g. ['listPets', '--limit', '10']) */\n operationArgs: string[]\n format?: 'json' | 'table' | 'yaml'\n query?: string\n}\n\n/**\n * Build auth environment variables for injection into child process.\n * The calling shell (and thus the AI agent) never sees these values.\n */\nfunction buildAuthEnv(entry: OASEntryPublic): Record<string, string> {\n const cfg = entry.authConfig\n const prefix = entry.name.toUpperCase().replace(/[^A-Z0-9]/g, '_')\n\n switch (cfg['type']) {\n case 'bearer':\n return { [`${prefix}_TOKEN`]: cfg['token'] as string }\n\n case 'api_key':\n return { [`${prefix}_API_KEY`]: cfg['key'] as string }\n\n case 'basic':\n return { [`${prefix}_CREDENTIALS`]: `${cfg['username']}:${cfg['password']}` }\n\n case 'oauth2_cc':\n return {\n [`${prefix}_CLIENT_ID`]: cfg['clientId'] as string,\n [`${prefix}_CLIENT_SECRET`]: cfg['clientSecret'] as string,\n [`${prefix}_SCOPES`]: (cfg['scopes'] as string[]).join(' '),\n }\n\n default:\n return {}\n }\n}\n\nexport async function runOperation(opts: RunOptions): Promise<void> {\n const bin = resolveOpenapi2CliBin()\n const { entry, operationArgs, format, query } = opts\n\n const args = [\n 'run',\n '--oas', entry.remoteUrl,\n '--cache-ttl', String(entry.cacheTtl),\n ...(entry.baseEndpoint ? ['--endpoint', entry.baseEndpoint] : []),\n ...(format ? ['--format', format] : []),\n ...(query ? ['--query', query] : []),\n ...operationArgs,\n ]\n\n const authEnv = buildAuthEnv(entry)\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, [bin, ...args], {\n stdio: 'inherit',\n env: {\n ...process.env,\n ...authEnv, // inject auth — not visible to parent shell\n },\n })\n\n child.on('close', (code) => {\n if (code === 0) resolve()\n else reject(new Error(`openapi2cli exited with code ${code}`))\n })\n child.on('error', reject)\n })\n}\n\n/**\n * Get list of available operations for a service by running `--help`.\n * Returns the raw help text from openapi2cli.\n */\nexport async function getServiceHelp(entry: OASEntryPublic): Promise<string> {\n const bin = resolveOpenapi2CliBin()\n const args = [\n 'run',\n '--oas', entry.remoteUrl,\n '--cache-ttl', String(entry.cacheTtl),\n '--help',\n ]\n\n return new Promise<string>((resolve, reject) => {\n let output = ''\n const child = spawn(process.execPath, [bin, ...args], {\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n\n child.stdout?.on('data', (d: Buffer) => { output += d.toString() })\n child.stderr?.on('data', (d: Buffer) => { output += d.toString() })\n child.on('close', () => resolve(output))\n child.on('error', reject)\n })\n}\n","import type { Command } from 'commander'\nimport { getConfig } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\nimport { readOASListCache, writeOASListCache } from '../lib/cache.js'\nimport { getServiceHelp } from '../lib/oas-runner.js'\n\nexport function registerServices(program: Command): void {\n const services = program\n .command('services')\n .description('Manage and inspect available OAS services')\n\n // services list\n services\n .command('list')\n .description('List all OAS services available in the current group')\n .option('--no-cache', 'Bypass local cache and fetch fresh from server')\n .action(async (opts: { cache: boolean }) => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entries = opts.cache ? await readOASListCache() : null\n\n if (!entries) {\n entries = await client.listOAS()\n if (entries.length > 0) {\n const maxTtl = Math.min(...entries.map((e) => e.cacheTtl))\n await writeOASListCache(entries, maxTtl)\n }\n }\n\n if (entries.length === 0) {\n console.log('No services registered in this group.')\n return\n }\n\n const nameWidth = Math.max(10, ...entries.map((e) => e.name.length))\n console.log(`\\n${'SERVICE'.padEnd(nameWidth)} AUTH DESCRIPTION`)\n console.log(`${'-'.repeat(nameWidth)} -------- ${'-'.repeat(40)}`)\n for (const e of entries) {\n const auth = e.authType.padEnd(8)\n const desc = e.description.length > 60 ? e.description.slice(0, 57) + '...' : e.description\n console.log(`${e.name.padEnd(nameWidth)} ${auth} ${desc}`)\n }\n console.log()\n })\n\n // services info <name>\n services\n .command('info <name>')\n .description('Show detailed information and available operations for a service')\n .action(async (name: string) => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entry\n try {\n entry = await client.getOAS(name)\n } catch (err) {\n console.error(`Service not found: ${name}`)\n console.error('Run `ucli services list` to see available services.')\n process.exit(1)\n }\n\n console.log(`\\nService: ${entry.name}`)\n console.log(`Description: ${entry.description || '(none)'}`)\n console.log(`OAS URL: ${entry.remoteUrl}`)\n if (entry.baseEndpoint) console.log(`Base endpoint: ${entry.baseEndpoint}`)\n console.log(`Auth type: ${entry.authType}`)\n console.log(`Cache TTL: ${entry.cacheTtl}s`)\n\n console.log('\\nAvailable operations:')\n console.log('─'.repeat(60))\n\n const help = await getServiceHelp(entry)\n console.log(help)\n })\n}\n","import type { Command } from 'commander'\nimport { getConfig } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\nimport { runOperation } from '../lib/oas-runner.js'\n\nexport function registerRun(program: Command): void {\n program\n .command('run <service> [args...]')\n .description('Execute an operation on a service')\n .option('--format <fmt>', 'Output format: json | table | yaml', 'json')\n .option('--query <jmespath>', 'Filter response with JMESPath expression')\n .option('--data <json>', 'Request body (JSON string or @filename)')\n .allowUnknownOption(true)\n .action(async (\n service: string,\n args: string[],\n opts: { format?: string; query?: string; data?: string; args?: string[] },\n ) => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entry\n try {\n entry = await client.getOAS(service)\n } catch {\n console.error(`Unknown service: ${service}`)\n console.error('Run `ucli services list` to see available services.')\n process.exit(1)\n }\n\n // Collect extra args (pass-through to openapi2cli)\n const extraArgs = opts.args ?? []\n const operationArgs = [\n ...args,\n ...extraArgs,\n ...(opts.data ? ['--data', opts.data] : []),\n ]\n\n const format = opts.format as 'json' | 'table' | 'yaml' | undefined\n const query = opts.query as string | undefined\n\n try {\n await runOperation({\n entry,\n operationArgs,\n ...(format !== undefined ? { format } : {}),\n ...(query !== undefined ? { query } : {}),\n })\n } catch (err) {\n console.error('Operation failed:', (err as Error).message)\n process.exit(1)\n }\n })\n}\n","import type { Command } from 'commander'\nimport { getConfig } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\nimport { writeOASListCache, clearOASListCache } from '../lib/cache.js'\n\nexport function registerRefresh(program: Command): void {\n program\n .command('refresh')\n .description('Force-refresh the local OAS cache from the server')\n .action(async () => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n console.log('Refreshing OAS list from server...')\n await clearOASListCache()\n\n const entries = await client.listOAS()\n if (entries.length > 0) {\n const maxTtl = Math.min(...entries.map((e) => e.cacheTtl))\n await writeOASListCache(entries, maxTtl)\n }\n\n console.log(`✓ Refreshed ${entries.length} service(s).`)\n })\n}\n","import type { Command } from 'commander'\nimport { getConfig, isConfigured } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\nimport { readOASListCache, writeOASListCache } from '../lib/cache.js'\nimport { getServiceHelp } from '../lib/oas-runner.js'\n\nexport function registerHelp(program: Command): void {\n program\n .command('help [service]')\n .description('Show usage guide. Pass a service name for service-specific operations.')\n .action(async (service?: string) => {\n if (!service) {\n printGeneralHelp()\n if (isConfigured()) {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n let entries = await readOASListCache()\n if (!entries) {\n entries = await client.listOAS()\n if (entries.length > 0) {\n const maxTtl = Math.min(...entries.map((e) => e.cacheTtl))\n await writeOASListCache(entries, maxTtl)\n }\n }\n if (entries.length > 0) {\n console.log('\\nAvailable services:')\n for (const e of entries) {\n console.log(` ${e.name.padEnd(20)} ${e.description}`)\n }\n console.log('\\nTip: Run `ucli help <service>` for service-specific operations.')\n }\n } else {\n console.log('\\nRun `ucli configure --server <url> --token <jwt>` to get started.')\n }\n return\n }\n\n // Service-specific help\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entry\n try {\n entry = await client.getOAS(service)\n } catch {\n console.error(`Unknown service: ${service}`)\n console.error('Run `ucli services list` to see available services.')\n process.exit(1)\n }\n\n console.log(`\\n=== ${entry.name} ===`)\n console.log(`${entry.description}`)\n console.log(`\\nOAS spec: ${entry.remoteUrl}`)\n console.log('\\nOperations:')\n console.log('─'.repeat(60))\n\n const help = await getServiceHelp(entry)\n console.log(help)\n\n console.log('\\nExamples:')\n console.log(` ucli run ${entry.name} <operation>`)\n console.log(` ucli run ${entry.name} <operation> --format table`)\n console.log(` ucli run ${entry.name} <operation> --query \"results[*].id\"`)\n console.log(` ucli run ${entry.name} <operation> --data '{\"key\":\"value\"}'`)\n })\n}\n\nfunction printGeneralHelp(): void {\n console.log(`\nucli — OpenAPI & MCP Gateway for AI Agents\n════════════════════════════════════════\n\nSETUP\n ucli configure --server <url> --token <jwt>\n Configure server connection and authentication.\n\nDISCOVERY\n ucli services list\n List all OAS services available in your group.\n\n ucli services info <service>\n Show detailed service info and all available operations.\n\n ucli help [service]\n Show this guide, or service-specific operations.\n\nEXECUTION\n ucli run <service> <operation> [options]\n Execute a service operation.\n\n Options:\n --format json|table|yaml Output format (default: json)\n --query <jmespath> Filter response with JMESPath\n --data <json|@file> Request body for POST/PUT/PATCH\n\nMAINTENANCE\n ucli refresh\n Force-refresh the local OAS cache from the server.\n\nERRORS\n 401 Unauthorized → Run: ucli configure --server <url> --token <jwt>\n 404 Not Found → Check service name: ucli services list\n 4xx Client Error → Check operation args: ucli services info <service>\n 5xx Server Error → Retry or run: ucli refresh\n`)\n}\n","/**\n * MCP tool runner using @tronsfey/mcp2cli programmatic API.\n *\n * Auth credentials are injected via McpServerConfig (headers or env) —\n * never passed as CLI arguments (which would be visible in `ps`).\n */\nimport type { McpEntryPublic } from './server-client.js'\n\n// Dynamic imports to avoid top-level await in ESM\nasync function getMcp2cli() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const clientMod = await import('@tronsfey/mcp2cli/dist/client/index.js') as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const runnerMod = await import('@tronsfey/mcp2cli/dist/runner/index.js') as any\n return { createMcpClient: clientMod.createMcpClient, getTools: runnerMod.getTools, runTool: runnerMod.runTool }\n}\n\nfunction buildMcpConfig(entry: McpEntryPublic): Record<string, unknown> {\n const base: Record<string, unknown> = { type: entry.transport }\n if (entry.transport === 'http') {\n base.url = entry.serverUrl\n } else {\n base.command = entry.command\n }\n const auth = entry.authConfig\n if (auth.type === 'http_headers') {\n base.headers = auth.headers\n } else if (auth.type === 'env') {\n base.env = auth.env\n }\n return base\n}\n\nexport async function listMcpTools(entry: McpEntryPublic): Promise<{ name: string; description?: string }[]> {\n const { createMcpClient, getTools } = await getMcp2cli()\n const config = buildMcpConfig(entry)\n const client = await createMcpClient(config)\n const tools = await getTools(client, config, { noCache: true })\n return tools\n}\n\nexport async function runMcpTool(entry: McpEntryPublic, toolName: string, rawArgs: string[]): Promise<void> {\n const { createMcpClient, getTools, runTool } = await getMcp2cli()\n const config = buildMcpConfig(entry)\n const client = await createMcpClient(config)\n const tools = await getTools(client, config, { noCache: false, cacheTtl: 3600 })\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tool = tools.find((t: any) => t.name === toolName)\n if (!tool) throw new Error(`Tool \"${toolName}\" not found on MCP server \"${entry.name}\"`)\n await runTool(client, tool, rawArgs, {})\n}\n","import type { Command } from 'commander'\nimport { getConfig } from '../config.js'\nimport { ServerClient } from '../lib/server-client.js'\nimport { listMcpTools, runMcpTool } from '../lib/mcp-runner.js'\n\nexport function registerMcp(program: Command): void {\n const mcp = program\n .command('mcp')\n .description('Interact with MCP servers registered in your group')\n\n // mcp list\n mcp\n .command('list')\n .description('List all MCP servers available in the current group')\n .action(async () => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n const entries = await client.listMCP()\n\n if (entries.length === 0) {\n console.log('No MCP servers registered in this group.')\n return\n }\n\n const nameWidth = Math.max(10, ...entries.map(e => e.name.length))\n console.log(`\\n${'SERVER'.padEnd(nameWidth)} TRANSPORT DESCRIPTION`)\n console.log(`${'-'.repeat(nameWidth)} --------- ${'-'.repeat(40)}`)\n for (const e of entries) {\n const desc = e.description.length > 60 ? e.description.slice(0, 57) + '...' : e.description\n console.log(`${e.name.padEnd(nameWidth)} ${e.transport.padEnd(9)} ${desc}`)\n }\n console.log()\n })\n\n // mcp tools <server>\n mcp\n .command('tools <server>')\n .description('List tools available on a MCP server')\n .action(async (serverName: string) => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entry\n try {\n entry = await client.getMCP(serverName)\n } catch {\n console.error(`Unknown MCP server: ${serverName}`)\n console.error('Run `ucli mcp list` to see available servers.')\n process.exit(1)\n }\n\n let tools\n try {\n tools = await listMcpTools(entry)\n } catch (err) {\n console.error('Failed to fetch tools:', (err as Error).message)\n process.exit(1)\n }\n\n if (tools.length === 0) {\n console.log(`No tools found on MCP server \"${serverName}\".`)\n return\n }\n\n console.log(`\\nTools on \"${serverName}\":`)\n console.log('─'.repeat(60))\n for (const t of tools) {\n console.log(` ${t.name}`)\n if (t.description) console.log(` ${t.description}`)\n }\n console.log()\n })\n\n // mcp run <server> <tool> [args...]\n mcp\n .command('run <server> <tool> [args...]')\n .description('Call a tool on a MCP server')\n .action(async (serverName: string, toolName: string, args: string[]) => {\n const cfg = getConfig()\n const client = new ServerClient(cfg)\n\n let entry\n try {\n entry = await client.getMCP(serverName)\n } catch {\n console.error(`Unknown MCP server: ${serverName}`)\n console.error('Run `ucli mcp list` to see available servers.')\n process.exit(1)\n }\n\n try {\n await runMcpTool(entry, toolName, args)\n } catch (err) {\n console.error('Tool execution failed:', (err as Error).message)\n process.exit(1)\n }\n })\n}\n"],"mappings":"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","names":["createRequire","program","join","join","join","require","pkg","program","program","program","program","cfg","client","program","require","createRequire"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tronsfey/ucli",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "ucli — proxy OpenAPI and MCP services for AI agents",
|
|
5
|
+
"keywords": ["openapi", "oas", "cli", "ai-agent", "api-client"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": { "node": ">=18" },
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/**",
|
|
13
|
+
"assets/**",
|
|
14
|
+
"skill.md",
|
|
15
|
+
"README.md",
|
|
16
|
+
"README.zh.md"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"ucli": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "tsx watch src/index.ts",
|
|
24
|
+
"build": "tsup && chmod +x dist/index.js",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"lint": "tsc --noEmit",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@tronsfey/mcp2cli": "^1.0.0",
|
|
32
|
+
"@tronsfey/openapi2cli": "^1.0.12",
|
|
33
|
+
"axios": "^1.8.4",
|
|
34
|
+
"commander": "^12.1.0",
|
|
35
|
+
"conf": "^13.1.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.14.0",
|
|
39
|
+
"tsx": "^4.19.3",
|
|
40
|
+
"tsup": "^8.4.0",
|
|
41
|
+
"vitest": "^3.1.1",
|
|
42
|
+
"typescript": "^5.7.3"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/skill.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# ucli Skill
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Use `ucli` whenever you need to call any external business API or MCP server tool. This tool:
|
|
6
|
+
- Proxies registered OpenAPI services on your behalf
|
|
7
|
+
- Handles authentication automatically (credentials are never exposed to you)
|
|
8
|
+
- Returns structured JSON output suitable for further processing
|
|
9
|
+
|
|
10
|
+
**When to use:** Any time you need to interact with external services (business APIs, data sources, microservices). Always check available services first with `ucli services list`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Step 1 — Discover Available Services
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
ucli services list
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Returns a table of service names, auth type, and descriptions. Run this first to see what's available.
|
|
21
|
+
|
|
22
|
+
**Example output:**
|
|
23
|
+
```
|
|
24
|
+
SERVICE AUTH DESCRIPTION
|
|
25
|
+
---------- -------- ------------------------------------------
|
|
26
|
+
payments bearer Payment processing service
|
|
27
|
+
inventory api_key Product inventory management
|
|
28
|
+
crm oauth2_cc Customer relationship management
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Step 2 — Inspect a Service's Operations
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ucli services info <service-name>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Shows all available operations, their parameters, and expected inputs.
|
|
40
|
+
|
|
41
|
+
**Example:**
|
|
42
|
+
```bash
|
|
43
|
+
ucli services info payments
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 3 — Execute an Operation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ucli run <service> <operation> [options]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Options
|
|
55
|
+
|
|
56
|
+
| Flag | Description | Example |
|
|
57
|
+
|------|-------------|---------|
|
|
58
|
+
| `--format json\|table\|yaml` | Output format (default: json) | `--format table` |
|
|
59
|
+
| `--query <jmespath>` | Filter response with JMESPath | `--query "items[*].id"` |
|
|
60
|
+
| `--data <json\|@file>` | Request body for POST/PUT/PATCH | `--data '{"amount":100}'` |
|
|
61
|
+
|
|
62
|
+
### Examples
|
|
63
|
+
|
|
64
|
+
**List resources (GET):**
|
|
65
|
+
```bash
|
|
66
|
+
ucli run payments listTransactions --format json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Filter response:**
|
|
70
|
+
```bash
|
|
71
|
+
ucli run inventory listProducts --query "items[?stock > \`0\`].name"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Create a resource (POST):**
|
|
75
|
+
```bash
|
|
76
|
+
ucli run payments createCharge --data '{"amount": 5000, "currency": "USD", "customerId": "cus_123"}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Update a resource (PUT/PATCH):**
|
|
80
|
+
```bash
|
|
81
|
+
ucli run crm updateContact --data '{"email": "new@example.com"}' --contactId abc123
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Get a specific resource:**
|
|
85
|
+
```bash
|
|
86
|
+
ucli run inventory getProduct --productId SKU-001
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Step 4 — Process the Output
|
|
92
|
+
|
|
93
|
+
By default, `ucli run` returns JSON. You can:
|
|
94
|
+
- Parse it directly as structured data
|
|
95
|
+
- Use `--query` to extract specific fields (JMESPath syntax)
|
|
96
|
+
- Use `--format table` for human-readable display
|
|
97
|
+
|
|
98
|
+
**JMESPath examples:**
|
|
99
|
+
```bash
|
|
100
|
+
# Extract all IDs
|
|
101
|
+
--query "results[*].id"
|
|
102
|
+
|
|
103
|
+
# Filter by field value
|
|
104
|
+
--query "items[?status == 'active']"
|
|
105
|
+
|
|
106
|
+
# Get nested field
|
|
107
|
+
--query "data.user.email"
|
|
108
|
+
|
|
109
|
+
# Count results
|
|
110
|
+
--query "length(items)"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Complete Workflow Example
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# 1. Discover services
|
|
119
|
+
ucli services list
|
|
120
|
+
|
|
121
|
+
# 2. Check what operations are available on "payments"
|
|
122
|
+
ucli services info payments
|
|
123
|
+
|
|
124
|
+
# 3. List recent transactions
|
|
125
|
+
ucli run payments listTransactions --query "transactions[*].{id:id,amount:amount,status:status}"
|
|
126
|
+
|
|
127
|
+
# 4. Get a specific transaction
|
|
128
|
+
ucli run payments getTransaction --transactionId txn_abc123
|
|
129
|
+
|
|
130
|
+
# 5. Create a new charge
|
|
131
|
+
ucli run payments createCharge --data '{
|
|
132
|
+
"amount": 9900,
|
|
133
|
+
"currency": "USD",
|
|
134
|
+
"customerId": "cus_xyz789",
|
|
135
|
+
"description": "Monthly subscription"
|
|
136
|
+
}'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Error Handling
|
|
142
|
+
|
|
143
|
+
| Error | Cause | Resolution |
|
|
144
|
+
|-------|-------|------------|
|
|
145
|
+
| `Authentication failed` | Token expired or invalid | Run `ucli configure --server <url> --token <jwt>` |
|
|
146
|
+
| `Unknown service: <name>` | Service not registered | Run `ucli services list` to see valid names |
|
|
147
|
+
| `400 Bad Request` | Invalid parameters | Check operation signature with `ucli services info <service>` |
|
|
148
|
+
| `404 Not Found` | Resource doesn't exist | Verify the resource ID |
|
|
149
|
+
| `429 Too Many Requests` | Rate limit exceeded | Wait and retry |
|
|
150
|
+
| `5xx Server Error` | Upstream service error | Retry once; if persistent, report to the service owner |
|
|
151
|
+
|
|
152
|
+
**On persistent errors:**
|
|
153
|
+
```bash
|
|
154
|
+
# Refresh the local OAS cache (may resolve stale spec issues)
|
|
155
|
+
ucli refresh
|
|
156
|
+
|
|
157
|
+
# Then retry the operation
|
|
158
|
+
ucli run <service> <operation>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## MCP Server Tools
|
|
164
|
+
|
|
165
|
+
In addition to OpenAPI services, ucli can interact with MCP (Model Context Protocol) servers. Auth credentials (`http_headers` or `env`) are injected programmatically — they are never passed as CLI arguments (which would be visible in `ps`).
|
|
166
|
+
|
|
167
|
+
### Workflow
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Step 1: Discover available MCP servers
|
|
171
|
+
ucli mcp list
|
|
172
|
+
|
|
173
|
+
# Step 2: Inspect a server's available tools
|
|
174
|
+
ucli mcp tools <server-name>
|
|
175
|
+
|
|
176
|
+
# Step 3: Run a tool (args as key=value pairs)
|
|
177
|
+
ucli mcp run <server-name> <tool-name> [key=value ...]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Commands
|
|
181
|
+
|
|
182
|
+
| Command | Description |
|
|
183
|
+
|---------|-------------|
|
|
184
|
+
| `ucli mcp list` | List all MCP servers available to your group |
|
|
185
|
+
| `ucli mcp tools <server>` | List tools available on the server |
|
|
186
|
+
| `ucli mcp run <server> <tool> [args...]` | Execute a tool on the server |
|
|
187
|
+
|
|
188
|
+
### Examples
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# List available MCP servers
|
|
192
|
+
ucli mcp list
|
|
193
|
+
|
|
194
|
+
# See what tools are available on "weather" server
|
|
195
|
+
ucli mcp tools weather
|
|
196
|
+
|
|
197
|
+
# Call the get_forecast tool with arguments
|
|
198
|
+
ucli mcp run weather get_forecast location="New York" units=metric
|
|
199
|
+
|
|
200
|
+
# Call a search tool
|
|
201
|
+
ucli mcp run search-server web_search query="ucli documentation" limit=5
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Tips for AI Agents
|
|
207
|
+
|
|
208
|
+
1. **Always discover first.** Don't guess service or operation names — run `ucli services list` then `ucli services info <name>`.
|
|
209
|
+
|
|
210
|
+
2. **Use JSON output.** Default `--format json` gives you machine-parseable data. Only switch to `table` when presenting to humans.
|
|
211
|
+
|
|
212
|
+
3. **Use `--query` to extract.** Instead of parsing the entire response, use JMESPath to extract exactly what you need.
|
|
213
|
+
|
|
214
|
+
4. **Chain operations.** Use the output of one operation as input to the next:
|
|
215
|
+
```bash
|
|
216
|
+
# Get customer ID, then create a charge
|
|
217
|
+
CUSTOMER_ID=$(ucli run crm findCustomer --email user@example.com --query "id" | tr -d '"')
|
|
218
|
+
ucli run payments createCharge --data "{\"customerId\": \"$CUSTOMER_ID\", \"amount\": 1000}"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
5. **Check pagination.** Large result sets may be paginated. Look for `nextPage`, `cursor`, or `Link` headers in the response.
|
|
222
|
+
|
|
223
|
+
6. **Validate before mutating.** For destructive operations (DELETE, large updates), confirm the resource exists and is correct before proceeding.
|