1. Conduid
  2. Developer Tools
  3. Acli
MCP server · Developer Tools

Acli

acli - Agent CLI protocol for AI agents on MCP

Unclaimed MIT last commit 8 months ago devtools
56Fair

Scored 5 days ago · breakdown

About Acli

Acli is an MCP server published by lifeprompt-team in the Developer Tools category: acli - Agent CLI protocol for AI agents on MCP.

The repository has 3 stars and 0 forks, with the last commit 8 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues before depending on it in production.

Install

From source
git clone https://github.com/lifeprompt-team/acli

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

✦

Ask AI about Acli

Powered by Claude · Grounded in docs

I know everything about Acli. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

Static checks derived from the repository 5 days ago. Heuristics on the source, not an audit.

  • ✓README presentThe repository ships documentation.
  • ✓License declaredUsable under a stated license.
  • ✓Tests presentA test suite exists in the repository.
  • ✓Dependencies pinnedA lockfile pins dependency versions.
  • ✓No dynamic code executionSource scan found no eval, dynamic Function, or shell execution.
  • !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.

Releases

v0.7.3v0.7.3 · 10 Feb 2026What's Changed Feature/repl cli by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/13 Full Changelog: https://github.com/lifeprompt-team/acli/compare/v0.7.2...v0.7.3
v0.7.2v0.7.2 · 10 Feb 2026What's Changed update workflow by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/11 release: v0.7.2 by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/12 Full Changelog:…
v0.7.1v0.7.1 · 9 Feb 2026What's Changed release: v0.7.1 by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/10 Full Changelog: https://github.com/lifeprompt-team/acli/compare/v0.7.0...v0.7.1
v0.7.0v0.7.0 · 9 Feb 2026What's Changed release: v0.7.0 by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/8 ci: add auto-tag workflow for automated releases by @s-endo17 in https://github.com/lifeprompt-team/acli/pull/9 Full Changelog:…
v0.6.10v0.6.10 · 4 Feb 2026Full Changelog: https://github.com/lifeprompt-team/acli/compare/v0.6.9...v0.6.10

README

ACLI - Agent CLI

CI npm version

ACLI (Agent CLI) is a lightweight CLI protocol for AI agents built on top of MCP (Model Context Protocol).

Why ACLI?

Traditional MCP tool definitions require extensive schema for each tool, consuming valuable context window space. ACLI solves this by:

  • Single Tool per Domain: One MCP tool (e.g., math, calendar) handles related commands
  • Dynamic Discovery: Agents learn commands via help and schema
  • Shell-less Security: No shell execution, preventing injection attacks
  • Type-safe Arguments: Zod-based validation with full TypeScript inference
  • CLI & MCP Dual Support: Use as MCP tool or standalone CLI

Installation

npm install @lifeprompt/acli zod
# or
pnpm add @lifeprompt/acli zod

https://github.com/user-attachments/assets/c4b2a395-446c-4178-b552-9868ee40403c

Quick Start

MCP Server Integration

import { z } from "zod"
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { registerAcli, defineCommand, arg } from "@lifeprompt/acli"

// Use defineCommand() for full type inference in handlers
const add = defineCommand({
  description: "Add two numbers",
  args: {
    a: arg(z.coerce.number(), { positional: 0 }),
    b: arg(z.coerce.number(), { positional: 1 }),
  },
  handler: async ({ a, b }) => ({ result: a + b }),  // a, b are inferred as number
})

const multiply = defineCommand({
  description: "Multiply two numbers",
  args: {
    a: arg(z.coerce.number(), { positional: 0 }),
    b: arg(z.coerce.number(), { positional: 1 }),
  },
  handler: async ({ a, b }) => ({ result: a * b }),
})

const commands = { add, multiply }

const server = new McpServer({ name: "my-server", version: "1.0.0" })

// Register as "math" tool
registerAcli(server, "math", commands, "Mathematical operations.")
// → Tool description: "Mathematical operations. Commands: add, multiply. Run 'help' for details."

How AI Agents Call ACLI Tools

Once registered, AI agents (like Claude) call the tool with a command string:

// Tool call from AI agent
{
  "name": "math",
  "arguments": {
    "command": "add 10 20"
  }
}

// Response
{
  "content": [{ "type": "text", "text": "{\"result\":30}" }]
}
// Discovery - agents can explore available commands
{ "name": "math", "arguments": { "command": "help" } }
{ "name": "math", "arguments": { "command": "help add" } }
{ "name": "math", "arguments": { "command": "schema" } }

Standalone CLI

#!/usr/bin/env node
import { z } from "zod"
import { defineCommand, runCli, arg } from "@lifeprompt/acli"

const greet = defineCommand({
  description: "Say hello",
  args: {
    name: arg(z.string(), { positional: 0 }),
  },
  handler: async ({ name }) => ({ message: `Hello, ${name}!` }),  // name is inferred as string
})

runCli({ commands: { greet } })
node my-cli.mjs greet World
# → { "message": "Hello, World!" }

Interactive REPL

Export commands from a file and explore them interactively — like dropping into a Docker container:

// tools.ts
import { z } from "zod"
import { defineCommand, arg } from "@lifeprompt/acli"

export const add = defineCommand({
  description: "Add two numbers",
  args: {
    a: arg(z.number(), { positional: 0 }),
    b: arg(z.number(), { positional: 1 }),
  },
  handler: async ({ a, b }) => ({ result: a + b }),
})

export const greet = defineCommand({
  description: "Say hello",
  args: {
    name: arg(z.string(), { positional: 0 }),
    shout: arg(z.boolean().default(false)),
  },
  handler: async ({ name, shout = false }) => {
    const msg = `Hello, ${name}!`
    return { message: shout ? msg.toUpperCase() : msg }
  },
})
npx @lifeprompt/acli repl ./tools.ts

  acli v0.7.3 — Interactive REPL
  Loaded 2 command(s) from ./tools.ts
  Type 'help' for commands, '.exit' to quit

acli> add 10 20
{ "result": 30 }

acli> greet Alice --shout
{ "message": "HELLO, ALICE!" }

acli> help
{ "commands": [{ "name": "add", ... }, { "name": "greet", ... }] }

acli> exit
Bye!

Single command execution (useful for scripting):

npx @lifeprompt/acli exec ./tools.ts "add 1 2"
# → { "result": 3 }

TypeScript support: Works natively on Node.js 22.6+, Bun, and Deno. For older Node.js, install jiti: npm install -D jiti


Argument Definition

ACLI uses Zod for type-safe argument parsing with rich validation.

arg(schema, meta?)

Wraps a Zod schema with CLI metadata:

import { z } from "zod"
import { arg } from "@lifeprompt/acli"

// Basic types
arg(z.string())                           // Required string
arg(z.coerce.number())                    // Number (coerced from string)
arg(z.coerce.number().int())              // Integer
arg(z.boolean().default(false))           // Flag (presence = true)
arg(z.array(z.string()))                  // Array (--tag a --tag b → ["a", "b"])
arg(z.coerce.date())                      // Date (ISO8601 string → Date)

// Validation
arg(z.string().min(1).max(100))           // Length validation
arg(z.coerce.number().min(0).max(100))    // Range validation
arg(z.enum(["json", "csv", "table"]))     // Enum validation
arg(z.string().email())                   // Email validation
arg(z.string().regex(/^[a-z]+$/))         // Regex validation

// Optional & defaults
arg(z.string().optional())                // Optional
arg(z.string().default("hello"))          // With default

// Metadata
arg(z.string(), { positional: 0 })        // Positional argument
arg(z.string(), { short: 'n' })           // Short alias (-n)
arg(z.string(), { description: "Name" })  // Help text
arg(z.string(), { examples: ["foo"] })    // Example values

InferArgs<T>

Infers the parsed argument types from an args definition:

const myArgs = {
  name: arg(z.string()),
  count: arg(z.coerce.number().default(10)),
  active: arg(z.boolean().optional()),
}

type MyArgs = InferArgs<typeof myArgs>
// { name: string; count: number; active?: boolean }

Command Definition

Structure

import { z } from "zod"
import { defineCommand, arg, type InferArgs } from "@lifeprompt/acli"

interface CommandDefinition<TArgs extends ArgsDefinition> {
  description: string                        // Required
  args?: TArgs                               // Zod-based arguments
  handler?: (args: InferArgs<TArgs>) => Promise<unknown>
  subcommands?: CommandRegistry              // Nested commands
}

Example with Subcommands

Use cmd() (alias for defineCommand) inside subcommands to enable type inference:

import { z } from "zod"
import { defineCommand, cmd, arg } from "@lifeprompt/acli"

const calendar = defineCommand({
  description: "Calendar management",
  subcommands: {
    events: cmd({
      description: "Manage events",
      subcommands: {
        list: cmd({
          description: "List events",
          args: {
            from: arg(z.coerce.date().optional()),
            limit: arg(z.coerce.number().int().default(10)),
          },
          handler: async ({ from, limit }) => {
            // from: Date | undefined, limit: number (types inferred!)
            return { events: await fetchEvents({ from, limit }) }
          },
        }),
        create: cmd({
          description: "Create event",
          args: {
            title: arg(z.string().min(1)),
            date: arg(z.coerce.date()),
          },
          handler: async ({ title, date }) => {
            // title: string, date: Date (types inferred!)
            return { event: await createEvent({ title, date }) }
          },
        }),
      },
    }),
  },
})

// Use directly: registerAcli(server, "cli", { calendar })

Note: Without cmd(), inline subcommand handlers receive unknown types due to TypeScript's type inference limitations. Always wrap subcommands with cmd() for full type safety.

Usage:

calendar events list --from 2026-02-01 --limit 5
calendar events create --title "Meeting" --date 2026-02-02T10:00:00Z

Positional Arguments

Positional arguments allow cleaner syntax:

const add = defineCommand({
  description: "Add numbers",
  args: {
    a: arg(z.coerce.number(), { positional: 0 }),
    b: arg(z.coerce.number(), { positional: 1 }),
  },
  handler: async ({ a, b }) => ({ result: a + b }),
})

// Use: registerAcli(server, "math", { add })

All syntaxes work:

add 10 20          # Positional
add --a 10 --b 20  # Named

To use short options like -a, define them explicitly with the short metadata:

const add = defineCommand({
  description: "Add numbers",
  args: {
    a: arg(z.coerce.number(), { positional: 0, short: 'a' }),
    b: arg(z.coerce.number(), { positional: 1, short: 'b' }),
  },
  handler: async ({ a, b }) => ({ result: a + b }),
})

// Now supports: add -a 10 -b 20

Flag Negation (--no- prefix)

Boolean flags can be explicitly set to false using the --no- prefix:

command --no-verbose    # verbose = false
command --no-color      # color = false

Repeated Options (Arrays)

Arguments defined with z.array(...) accumulate values from repeated options:

const search = defineCommand({
  description: "Search files",
  args: {
    ext: arg(z.array(z.string()), { short: 'e', description: "File extensions" }),
  },
  handler: async ({ ext }) => ({ extensions: ext }),
})

// search --ext .ts --ext .tsx  → ext: [".ts", ".tsx"]
// search -e .ts -e .tsx        → ext: [".ts", ".tsx"]

Built-in Commands

These commands are automatically available:

Command Description
help List all commands
help <cmd> Show command details
schema JSON schema for all commands
schema <cmd> JSON schema for specific command
version Show ACLI version

Response Format

ACLI uses MCP-native response format for seamless integration.

Handler Return Values

Handlers can return values in two ways:

// 1. Simple object (auto-wrapped to MCP format)
handler: async () => ({ result: 123 })
// → { content: [{ type: "text", text: '{"result":123}' }] }

// 2. MCP native format (passed through as-is)
handler: async () => ({
  content: [
    { type: "text", text: "Hello" },
    { type: "image", data: "base64...", mimeType: "image/png" },
  ]
})
// → passed through unchanged

Error Codes

Code Description
COMMAND_NOT_FOUND Command does not exist
VALIDATION_ERROR Invalid arguments or missing required
EXECUTION_ERROR Handler threw an error
PARSE_ERROR Malformed command string
PERMISSION_DENIED Authorization failed

Security

ACLI is designed with security in mind:

  • No Shell Execution: Commands are parsed and executed directly in-process
  • Command Whitelist: Only registered commands can be executed
  • Argument Validation: Zod validation before handler execution
  • DoS Prevention: Length and count limits on commands and arguments

API Reference

registerAcli(server, name, commands, description?)

Register commands as an MCP tool.

registerAcli(server, "tool_name", commands)

// With description
registerAcli(server, "tool_name", commands, "Base description.")
// → "Base description. Commands: cmd1, cmd2. Run 'help' for details."

runCli({ commands, args? })

Run as standalone CLI.

runCli({ commands })                          // Uses process.argv
runCli({ commands, args: ["add", "1", "2"] }) // Custom args

createAcli(commands)

Create a tool definition for manual integration.

const tool = createAcli(commands)
const result = await tool.execute({ command: "add 1 2" })

CLI (npx @lifeprompt/acli)

npx @lifeprompt/acli repl <file>           # Interactive REPL
npx @lifeprompt/acli exec <file> <command>  # Single command execution
npx @lifeprompt/acli --help                 # Show help
npx @lifeprompt/acli --version              # Show version

The <file> should export ACLI commands via default export, named commands export, or individual named exports.


TypeScript Types

All types are exported:

import type {
  // Argument types
  ArgSchema,
  ArgMeta,
  ArgsDefinition,
  InferArgs,
  // Command types
  CommandDefinition,
  CommandRegistry,
  // MCP migration types
  McpToolLike,
  // MCP response types
  CallToolResult,
  TextContent,
  ImageContent,
  // Error types
  AcliError,
  AcliErrorCode,
  // Options
  AcliToolOptions,
  CliOptions,
} from "@lifeprompt/acli"

// Helper functions
import { arg, defineCommand, cmd, aclify } from "@lifeprompt/acli"
// cmd is an alias for defineCommand - use inside subcommands for type inference
// aclify converts MCP-style tool definitions to ACLI CommandRegistry

Documentation


Contributing

See CONTRIBUTING.md for development setup, release flow, and guidelines.


License

MIT

README fetched from the repository 5 days ago. The original is authoritative.

Questions

About Acli

How do I install Acli?

No package has been published for this server, so install it from source: clone the repository and follow its README.

Is Acli safe to use with an AI agent?

Its trust score is 56 out of 100 (fair). It passes 5 of the 6 static security checks run so far; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Acli still maintained?

The last commit was 8 months ago. That's long enough that you should check whether the maintainer is responding to issues before depending on it.