Skip to content

Intentface/just-mcp-to-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@intentface/just-mcp-to-cli

Turn any MCP server into native bash commands for just-bash.

Published on npm: @intentface/just-mcp-to-cli

An agent types slack send-message --channel "#dev" --text "deployed" and it just works. No SDK. No MCP knowledge. Just bash.

Install

npm install @intentface/just-mcp-to-cli just-bash

just-bash is a peer dependency, so install both packages together.

# npm
npm install @intentface/just-mcp-to-cli just-bash

# pnpm
pnpm add @intentface/just-mcp-to-cli just-bash

# yarn
yarn add @intentface/just-mcp-to-cli just-bash

# bun
bun add @intentface/just-mcp-to-cli just-bash

Quick Start

import { Bash } from "just-bash";
import { mcpCommand } from "@intentface/just-mcp-to-cli";

const bash = new Bash({
  customCommands: [
    await mcpCommand("slack", "https://mcp.slack.com/mcp"),
  ],
});

await bash.exec('slack send-message --channel "#general" --text "hello world"');

How It Works

mcpCommand() connects to an MCP server, discovers its tools, and registers them as a just-bash custom command. Each MCP tool becomes a subcommand with auto-generated CLI flags from the tool's JSON Schema.

mcpCommand("slack", url)
  → connects to MCP server
  → discovers tools via tools/list
  → returns a just-bash Command

bash.exec("slack send-message --channel '#dev' --text 'hi'")
  → parses subcommand + flags
  → calls tools/call on the MCP server
  → formats result to stdout

Discovery & Help

Every registered server and tool gets auto-generated --help:

# List all tools on a server
slack --help
# slack — MCP tools
#
# Available commands:
#   send_message       Send a message to a channel
#   list_channels      List available channels
#
# Run `slack <command> --help` for details on each command.

# Tool-specific help with full type info
slack send_message --help
# Usage: slack send_message [options]
#
# Send a message to a channel
#
# Required:
#   --channel <string>        Channel name or ID
#   --text <string>           Message content [maxLength: 4000]
#
# Optional:
#   --thread-ts <string>      Reply to a specific thread
#   --unfurl-links            Unfurl links in the message
#
# Flags:
#   --help              Show this help message
#   --json              Read arguments from stdin as JSON
#   --raw               Output raw MCP result
#   --verbose           Show request/response details

Help output includes type hints (<string>, <number>, <string[]>), enum values, defaults, format hints (<email>, <date-time>), and constraints (min, max, pattern, etc.).

Nested Parameters

Object parameters can be passed two ways:

# Dot-notation flags
linear create-issue --title "Bug" --assignee.id "user-123" --assignee.role "owner"

# Or as a JSON string
linear create-issue --title "Bug" --assignee '{"id":"user-123","role":"owner"}'

Help output shows nested properties inline:

Optional:
  --assignee <json>         Assignee details
    Pass as JSON string, or use dot-notation flags:
      --assignee.id <uuid>          User ID (required)
      --assignee.role <string>      Team role [one of: owner, reviewer, member]

Unix Pipes

Output goes to stdout, so piping works naturally:

# Pipe between MCP servers
github list-issues --repo "acme/app" --state open \
  | jq '.[] | .title' \
  | xargs -I{} slack send-message --channel "#bugs" --text "Open: {}"

# Read args from stdin as JSON
echo '{"channel":"#general","text":"hi"}' | slack send-message --json

# Output to file
linear list-issues --team ENG > /tmp/issues.json

Multiple Servers

import { Bash } from "just-bash";
import { mcpCommands } from "@intentface/just-mcp-to-cli";

const commands = await mcpCommands({
  slack: "https://mcp.slack.com/mcp",
  gmail: {
    url: "https://gmail.mcp.claude.com/mcp",
    headers: { Authorization: "Bearer ..." },
  },
  linear: "https://mcp.linear.app/mcp",
});

const bash = new Bash({ customCommands: commands });

Configuration

await mcpCommand("slack", "https://mcp.slack.com/mcp", {
  // Auth
  headers: { Authorization: "Bearer xoxb-..." },

  // Rename tools for shorter commands
  aliases: {
    send_message: "send",      // slack send --channel ...
    list_channels: "channels", // slack channels
  },

  // Filter which tools are exposed
  include: ["send_message", "list_channels"],
  // or: exclude: ["admin_*"],

  // Output format
  defaultOutput: "json", // "json" | "text" | "raw"

  // Connection
  timeout: 30000,
  retries: 2,
});

API

mcpCommand(name, url, options?)

Connect to an MCP server and return a just-bash Command.

Param Type Description
name string Command name (e.g. "slack")
url string MCP server endpoint URL
options McpCommandOptions Optional configuration (see above)

Returns Promise<Command> — pass to Bash({ customCommands: [...] }).

mcpCommands(servers)

Register multiple servers at once.

Param Type Description
servers Record<string, string | ServerConfig> Map of name → URL or config

Returns Promise<Command[]>.

MCPTransport

Low-level MCP client if you need direct access:

import { MCPTransport } from "@intentface/just-mcp-to-cli";

const transport = new MCPTransport("https://mcp.slack.com/mcp", {
  headers: { Authorization: "Bearer ..." },
  timeout: 30000,
  retries: 2,
});

await transport.initialize();
const tools = await transport.listTools();
const result = await transport.callTool("send_message", { channel: "#dev", text: "hi" });
await transport.close();

Utility Exports

For advanced use cases, the internals are also exported:

import {
  schemaToArgs,    // JSON Schema → CliArg[]
  parseArgs,       // argv string[] → parsed object
  generateServerHelp, // tool list → help text
  generateToolHelp,   // single tool → help text
  formatResult,       // MCP result → formatted string
} from "@intentface/just-mcp-to-cli";

Reserved Flags

These flags are available on every tool:

Flag Description
--help, -h Show help for the server or tool
--json Read arguments from stdin as JSON instead of flags
--raw Output the raw MCP result envelope
--verbose Show request/response details

Error Handling

Follows standard Unix conventions:

Exit Code Meaning
0 Success
1 MCP error or connection error
2 Invalid arguments (missing required, bad type, unknown flag)

Errors go to stderr, output goes to stdout.


Developing

Prerequisites

Setup

git clone https://github.com/intentface/just-mcp-to-cli.git
cd just-mcp-to-cli
bun install

Commands

# Run tests
bun test

# Run tests in watch mode
bun test --watch

# Type-check
bunx tsc --noEmit

# Build (ESM + CJS + types)
bun run build

# Inspect the package before publishing
npm pack --dry-run

Project Structure

src/
  index.ts              Public API — mcpCommand(), mcpCommands()
  transport.ts          MCP HTTP/SSE client (thin JSON-RPC, no SDK)
  schema-to-args.ts     JSON Schema ↔ CLI flags + arg parser
  help-generator.ts     --help text formatting with rich type info
  output.ts             MCP result → stdout formatting (text/json/raw)
  command-factory.ts    Glue — wires everything into defineCommand()
  types.ts              Shared TypeScript types

tests/
  schema-to-args.test.ts
  help-generator.test.ts
  output.test.ts
  transport.test.ts
  command-factory.test.ts

examples/
  basic.ts              Connect to one MCP server
  multi-server.ts       Connect to multiple servers
  custom-commands.ts    Mix MCP tools with your own commands (runs standalone)
  interactive.ts        Interactive REPL

Architecture

The library has a simple pipeline:

  1. Transport — thin JSON-RPC client using fetch(). Supports Streamable HTTP (primary) and SSE (fallback). No @modelcontextprotocol/sdk dependency.
  2. Schema-to-args — converts each tool's inputSchema (JSON Schema) into CLI flag definitions. Flattens nested objects into dot-notation (--filter.status). Maps between kebab-case, snake_case, and camelCase.
  3. Help generator — produces GNU-style --help output from the arg definitions, showing types, constraints, enums, defaults, and nested object structure.
  4. Output formatter — transforms MCP tools/call results into text, JSON, or raw format for stdout.
  5. Command factory — ties it all together into a defineCommand() handler that routes subcommands, parses args, calls the MCP server, and formats output.

Running Examples

# Run without MCP server (uses only custom commands)
bun run examples/custom-commands.ts

# Connect to an MCP server
MCP_URL=https://your-server.com/mcp MCP_NAME=mytools bun run examples/basic.ts

# Interactive REPL
MCP_URL=https://your-server.com/mcp MCP_NAME=mytools bun run examples/interactive.ts

Publishing

npm login --scope=@intentface
npm publish --access public

License

MIT

About

Use MCP servers as a cli tool with just-bash

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors