Expose any CLI as MCP tools via YAML configuration
Project description
CLImax
Expose any CLI as MCP tools via a YAML configuration file.
Instead of writing a custom MCP server for every CLI tool, write a YAML file that describes the CLI's interface and CLImax does the rest.
Contents
- How It Works
- Installation
- Quick Start
- Connecting to MCP Clients
- Discovery Modes
- Creating Configs
- CLI Reference
- Bundled Configs
- Config Reference
- Meta-Tools Reference
- Policies
- Security
- License
How It Works
┌──────────────┐ ┌──────────────────┐ ┌─────────────┐
│ MCP Client │────▶│ CLImax │────▶│ Target CLI │
│ (Claude, │ MCP │ (reads YAML, │ sub │ (git, │
│ Cursor, │◀────│ runs commands) │◀────│ docker..) │
│ etc.) │ └──────────────────┘proc │ │
└──────────────┘ ▲ ▲ └─────────────┘
│ │
┌────────┴───┐ ┌───┴──────────┐
│ config.yaml│ │ policy.yaml │
│ (tool defs)│ │ (optional) │
└────────────┘ └──────────────┘
You need three things:
- CLImax — this program
- A YAML config — describes which commands to expose and how
- The CLI — the actual tool you want to call (must be on PATH)
Optionally, a policy file controls which tools are enabled, constrains argument values, overrides descriptions, and can route execution through Docker containers.
Installation
Requirements: Python 3.11+
With uv (recommended)
# Install as a CLI tool (adds `climax` to PATH)
uv tool install climax-mcp
# Or run directly without installing
uvx --from climax-mcp climax list
With pip
pip install climax-mcp
From source
git clone https://github.com/get2know-io/climax.git
cd climax
uv sync # or: pip install -e .
Quick Start
# Run with a bundled config by name — starts an MCP server with progressive discovery (default)
climax git
# Combine multiple CLIs into one server
climax git docker
# Use classic mode — register all tools directly instead of meta-tools
climax --classic git
# Apply a policy to restrict tools and arguments
climax --policy my-project.policy.yaml git
# Enable logging to see commands being executed
climax git --log-level INFO
# Use a custom config file (path or .yaml extension)
climax my-config.yaml
CLImax ships with ready-to-use configs for git, docker, claude, and obsidian — see configs/README.md for details on each config, including available tools and environment variables.
Connecting to MCP Clients
Add a CLImax server to your MCP client's config file. The JSON structure is the same everywhere — only the file location differs:
| Client | Config file |
|---|---|
| Claude Desktop | claude_desktop_config.json |
| Claude Code | .mcp.json (project root) |
| Cursor | .cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Basic setup
{
"mcpServers": {
"git": {
"command": "climax",
"args": ["git"]
}
}
}
Run climax list to see available bundled config names. Custom config files work too — pass a path instead of a name.
By default, MCP clients will see two meta-tools (climax_search and climax_call) for on-demand tool discovery. See Discovery Modes for details.
Variations
Change the "args" array to customize behavior:
| Variation | "args" value |
|---|---|
| Classic mode (all tools directly) | ["--classic", "git"] |
| Multiple CLIs in one server | ["git", "docker"] |
| With a policy file | ["--policy", "/path/to/policy.yaml", "git"] |
| Custom config file | ["/path/to/my-config.yaml"] |
To run without installing, use uvx as the command:
"command": "uvx",
"args": ["climax-mcp", "git"]
Discovery Modes
Progressive Discovery (Default)
By default, CLImax registers only two meta-tools in the MCP tools/list response:
climax_search— Find tools by keyword, category, CLI name, or browse all available CLIsclimax_call— Execute a discovered tool by name
Instead of seeing hundreds of tools at once, the agent discovers what's available on-demand:
Agent: "Search for git commit tools"
↓
climax_search(query="commit")
→ Returns tools with names, descriptions, arguments, and types
↓
Agent: "Call git_commit with message='fix bug'"
↓
climax_call(tool_name="git_commit", args={message: "fix bug"})
→ Executes the command and returns stdout/stderr/exit code
This is ideal for:
- Large tool sets (hundreds of subcommands across multiple CLIs)
- LLMs with tight context windows
- Dynamic tool discovery workflows
See Meta-Tools Reference for full parameter details.
Classic Mode
If you prefer all tools registered directly in the MCP response, use the --classic flag:
climax --classic git docker
In classic mode, tools/list returns all individual tools directly. Meta-tools (climax_search and climax_call) are not registered. This matches the behavior of versions prior to 002-mcp-meta-tools.
Creating Configs
The easiest way to create a config is to let an LLM generate it. CLImax ships with a skill (skill/SKILL.md) that teaches agents how to read --help output and produce valid YAML configs automatically.
With Claude Code (recommended)
Install the CLImax skill into your project, then ask Claude to generate configs:
climax skill --install # copies skill to .claude/commands/climax-config.md
Now just ask:
> Create a CLImax config for kubectl
The agent will run kubectl --help, inspect relevant subcommands, and generate a ready-to-use YAML config. No manual YAML writing required.
With other coding agents
For Cursor, Windsurf, Copilot, or any agent that supports custom instructions, run climax skill to print the skill text and paste it into your agent's system prompt or rules file.
With any LLM
Capture your CLI's help output and paste it into any LLM along with the contents of skill/SKILL.md:
# Capture help output
my-cli --help > /tmp/help.txt
my-cli subcommand --help >> /tmp/help.txt
# Paste both into ChatGPT, Claude, etc.
The skill covers the full YAML schema, naming conventions, argument mapping patterns, and a validation checklist — everything the LLM needs to produce a correct config.
Validate the result
After generating a config, verify it:
climax validate my-config.yaml
climax list my-config.yaml
Writing configs by hand
If you prefer to write configs manually, see the Config Reference section below.
CLI Reference
CLImax provides four subcommands for working with configs:
climax run — Start the MCP server
Starts the MCP stdio server. This is what MCP clients connect to. Configs can be referenced by bare name (resolves to bundled configs) or by file path.
By default, the server uses progressive discovery mode — climax_search and climax_call meta-tools are registered instead of individual CLI tools.
climax run git
climax run git docker --log-level INFO
climax run --policy readonly.policy.yaml git
climax run --classic git # use classic mode instead
For backward compatibility, you can omit run:
climax git # equivalent to: climax run git
climax --policy policy.yaml git
climax --classic git # use classic mode
Options:
| Flag | Values | Default | Description |
|---|---|---|---|
--classic |
(flag) | disabled | Register all individual tools directly instead of using meta-tools (progressive discovery is default) |
--policy |
path to YAML | (none) | Policy file to restrict tools and constrain arguments |
--log-level |
DEBUG, INFO, WARNING, ERROR |
WARNING |
Log verbosity (logs go to stderr) |
--transport |
stdio |
stdio |
MCP transport protocol |
Environment variables:
| Variable | Description |
|---|---|
CLIMAX_LOG_FILE |
Path to a log file for persistent logging (in addition to stderr). Useful for debugging MCP servers where stderr may not be visible. Always logs at DEBUG level. |
climax validate — Check config files
Validates YAML configs against the schema and checks that the referenced CLI binary exists on PATH. If --policy is provided, the policy file is also validated.
climax validate git
# ✓ git-tools — 6 tool(s)
# All 1 config(s) valid
climax validate --policy policy.yaml git
# ✓ git-tools — 6 tool(s)
# ✓ policy — 3 tool rule(s)
# All 1 config(s) valid
If a config or policy has errors, validate prints them and exits with code 1:
climax validate bad-config.yaml
# ✗ bad-config.yaml
# command: Field required
# 0 valid, 1 invalid
climax list — Show available tools
Displays a table of all tools defined across the given configs. If --policy is provided, the list is filtered to show only enabled tools, with any policy constraints and description overrides applied.
With no arguments, lists the available bundled config names:
climax list
# Bundled configs:
# claude
# docker
# git
# obsidian
With a config name or path, shows the tools table:
climax list git
# git-tools — 6 tool(s)
#
# ┌────────────┬──────────────────────────────┬─────────────┬──────────────┐
# │ Tool │ Description │ Command │ Arguments │
# ├────────────┼──────────────────────────────┼─────────────┼──────────────┤
# │ git_status │ Show the working tree status │ git status │ short ... │
# │ git_log │ Show recent commit history │ git log │ max_count .. │
# │ ... │ │ │ │
# └────────────┴──────────────────────────────┴─────────────┴──────────────┘
climax list --policy readonly.policy.yaml git
# git-tools — 2 tool(s)
# ...only the tools enabled by the policy are shown...
This is useful for reviewing what a config exposes before connecting it to an LLM.
climax skill — Config generation skill
Outputs or installs the CLImax config-generation skill for use with coding agents.
climax skill # print skill text to stdout
climax skill --path # print path to SKILL.md
climax skill --install # install to .claude/commands/climax-config.md
See Creating Configs for usage details.
Bundled Configs
CLImax ships with bundled configs in configs/ — usable by bare name (climax git, climax list docker):
| Config | CLI | Tools | Description |
|---|---|---|---|
git |
git |
6 | Common Git operations (status, log, diff, branch, add, commit) |
docker |
docker |
5 | Container/image inspection (ps, images, logs, inspect, compose ps) |
obsidian |
Obsidian CLI | 53 | Vault management — read, write, search, tags, links, tasks, daily notes, properties. Uses inline flags for key=value argument style. |
claude |
claude |
4 | Claude Code integration — ask, ask with model, JSON output, custom system prompt |
The examples/ directory contains test-only configs:
coreutils.yaml— Simple echo-based tools (used by e2e tests)
Config Reference
A YAML config describes one CLI and the tools (subcommands) to expose.
Minimal example
name: my-tools
description: "My CLI tools"
command: my-cli
tools:
- name: my_cli_status
description: "Show status"
command: status
This exposes a single MCP tool my_cli_status that runs my-cli status.
Full config schema
name: my-tools # server name (used in logs and client UI)
description: "What these tools do"
command: my-cli # base command (on PATH or absolute path)
env: # optional extra env vars for subprocess
MY_VAR: "value"
working_dir: /some/path # optional working directory
tools:
- name: my_cli_action # tool name (snake_case)
description: "What this does" # shown to the LLM
command: "sub command" # appended to base → `my-cli sub command`
timeout: 120 # optional per-tool timeout in seconds (default: 30)
args:
- name: target
type: string # string | integer | number | boolean
description: "The target"
required: true
positional: true # no flag, value placed directly
- name: format
type: string
flag: "--format" # becomes `--format <value>`
enum: ["json", "table"] # restrict values
- name: verbose
type: boolean
flag: "--verbose" # boolean: flag present if true, absent if false
- name: count
type: integer
flag: "-n"
default: 10 # used when the argument is not provided
Argument types
| Type | JSON Schema | CLI Behavior |
|---|---|---|
string |
"string" |
--flag value |
integer |
"integer" |
--flag 42 |
number |
"number" |
--flag 3.14 |
boolean |
"boolean" |
--flag (present) or omitted |
Argument modes
- Flag args: Have
flag: "--something". The value follows the flag as a separate token. - Inline flags: Have
flag: "key="(ending with=). The flag and value are joined as a single token (key=value). Useful for CLIs that usekey=valuesyntax instead of--key value(e.g., Obsidian CLI). - Positional args: Have
positional: true. The value is placed directly in the command, in definition order. - Auto-flag: If neither
flagnorpositionalis set, a flag is auto-generated from the arg name (my_arg→--my-arg).
Argument fields
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | (required) | Argument name (used as the JSON property name) |
description |
string | "" |
Shown to the LLM to explain the argument |
type |
string | "string" |
One of string, integer, number, boolean |
required |
bool | false |
Whether the LLM must provide this argument |
default |
any | null |
Default value used when argument is not provided |
flag |
string | null |
CLI flag (e.g. "--format", "-n") |
positional |
bool | false |
Place value directly in the command (no flag) |
enum |
list | null |
Restrict values to this set |
Meta-Tools Reference
CLImax exposes two meta-tools by default that enable progressive discovery:
climax_search — Discover tools
Search for tools by keyword, category, or CLI name. Returns matching tools with their full argument schemas.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
query |
string | (optional) | Search by tool name or description (substring match) |
category |
string | (optional) | Filter by tool category (e.g., "vcs", "container") |
cli |
string | (optional) | Filter by CLI name (e.g., "git-tools", "docker-tools") |
limit |
integer | 10 | Maximum number of results to return |
Behavior:
- If
query,category, orcliare provided, returns matching tools with full details (name, description, CLI, category, tags, argument schema) - If none of these are provided (only
limitor no parameters), returns a summary of all loaded CLIs with tool counts, categories, and tags
Example request:
{
"tool": "climax_search",
"input": {
"query": "commit",
"limit": 5
}
}
Example response:
{
"mode": "search",
"results": [
{
"tool_name": "git_commit",
"description": "Create a new commit",
"cli_name": "git-tools",
"category": "vcs",
"tags": ["git"],
"args": [
{
"name": "message",
"type": "string",
"description": "Commit message",
"required": true,
"flag": "-m"
},
{
"name": "all",
"type": "boolean",
"description": "Stage all changes",
"flag": "-a"
}
]
}
]
}
climax_call — Execute a tool
Execute a discovered tool by name with optional arguments.
Parameters:
| Parameter | Type | Description |
|---|---|---|
tool_name |
string | The name of the tool to execute (e.g., "git_commit") |
args |
object | Arguments to pass to the tool (keys match tool's argument names) |
Validation:
- Required arguments are checked before execution
- Argument types are coerced where compatible (e.g., string "42" → integer 42)
- Enum values are validated against allowed values
- Extra arguments are ignored
Example request:
{
"tool": "climax_call",
"input": {
"tool_name": "git_commit",
"args": {
"message": "Add user authentication",
"all": true
}
}
}
Example response:
Creating commit with message: Add user authentication
[exit code: 0]
Example error response (missing required argument):
Error: Argument 'message' is required
Policies
A policy file restricts which tools are enabled and constrains argument values — separating what tools exist (the config) from what's allowed (the policy).
No policy = all tools enabled, no constraints.
Quick example
Read-only Git — only expose status, log, and diff, with a cap on log entries:
default: disabled
tools:
git_status: {}
git_log:
args:
max_count:
max: 100
git_diff: {}
climax --policy readonly.policy.yaml git
When to use policies
| Goal | How |
|---|---|
| Restrict to read-only tools | default: disabled, list only safe tools |
| Constrain argument values | Add pattern, min, max under args |
| Override tool descriptions | Add description per tool |
| Sandbox execution in Docker | Add executor with type: docker |
| Share configs, vary access | Same config YAML + different policy per environment |
Full schema
executor: # optional — execution environment
type: docker # "local" (default) or "docker"
image: "alpine/git:latest" # required when type is docker
volumes: # bind mounts (env vars expanded)
- "${PROJECT_DIR}:/workspace"
working_dir: /workspace # -w flag for docker run
network: none # --network flag for docker run
default: disabled # "disabled" (default) or "enabled"
# disabled = only listed tools are exposed
# enabled = all tools exposed, listed ones get constraints
tools:
git_status: {} # enabled with no constraints
git_log:
description: "Show recent log (max 100)" # overrides the config's description
args:
max_count:
max: 100 # inclusive maximum for numeric args
git_add:
args:
path:
pattern: "^src/.*" # regex (fullmatch) for string args
git_diff:
args:
commits:
min: 0 # inclusive minimum for numeric args
max: 5
Field reference
Top-level
| Field | Type | Default | Description |
|---|---|---|---|
executor |
object | {type: "local"} |
Execution environment configuration |
default |
string | "disabled" |
Whether unmentioned tools are enabled or disabled |
tools |
map | {} |
Per-tool policies keyed by tool name |
executor
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | "local" |
"local" or "docker" |
image |
string | (required for docker) | Docker image to use |
volumes |
list | [] |
Bind mounts (-v flags). Environment variables are expanded. |
working_dir |
string | null |
Working directory inside the container (-w flag) |
network |
string | null |
Docker network mode (--network flag) |
tools.<name>
| Field | Type | Default | Description |
|---|---|---|---|
description |
string | null |
Override the tool's description (shown to the LLM) |
args |
map | {} |
Per-argument constraints keyed by argument name |
tools.<name>.args.<arg>
| Field | Type | Default | Description |
|---|---|---|---|
pattern |
string | null |
Regex pattern — value must fullmatch (for string args) |
min |
number | null |
Inclusive minimum (for numeric args) |
max |
number | null |
Inclusive maximum (for numeric args) |
More examples
Docker-sandboxed execution:
executor:
type: docker
image: alpine/git:latest
volumes:
- "${PROJECT_DIR}:/workspace"
working_dir: /workspace
network: none
default: disabled
tools:
git_status: {}
git_log: {}
Allow all tools but constrain one:
default: enabled
tools:
git_add:
args:
path:
pattern: "^src/.*"
Behavior details
- Unknown tool names in the policy are warned about and skipped (not an error)
- Unknown argument names in a tool's policy are warned about and skipped
- When
default: disabled, only tools explicitly listed intoolsare exposed - When
default: enabled, all tools are exposed; listed tools get constraints/overrides applied - Argument validation happens before command execution — rejected calls never run the subprocess
- Docker executor prepends
docker run --rmwith the configured flags to every command
Security
- Commands are executed via
asyncio.create_subprocess_exec(no shell injection) - Commands time out after 30 seconds by default — override per-tool with
timeout:in the config - The YAML author controls what commands are exposed — review configs before use
- Use a policy file to restrict which tools are enabled and constrain argument values
- Use the Docker executor to sandbox command execution in a container
- Policy argument constraints use
re.fullmatch— patterns must match the entire value
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file climax_mcp-0.4.0rc2.tar.gz.
File metadata
- Download URL: climax_mcp-0.4.0rc2.tar.gz
- Upload date:
- Size: 224.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcfb42d717ce114fe8bcef5377a90d3eabd9a438453c6f77c56461f66c88b702
|
|
| MD5 |
b164ecd558a9f9ea902eb82108080600
|
|
| BLAKE2b-256 |
5782de510f86bf39c9fb6d86e6d081ff1e4077baa18a371097a8d82f03115e06
|
Provenance
The following attestation bundles were made for climax_mcp-0.4.0rc2.tar.gz:
Publisher:
release.yml on get2knowio/climax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
climax_mcp-0.4.0rc2.tar.gz -
Subject digest:
dcfb42d717ce114fe8bcef5377a90d3eabd9a438453c6f77c56461f66c88b702 - Sigstore transparency entry: 1096528158
- Sigstore integration time:
-
Permalink:
get2knowio/climax@3ec1de6792d7559d0dc55a41ea1ac2d05d4d37aa -
Branch / Tag:
refs/tags/v0.4.0-rc2 - Owner: https://github.com/get2knowio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3ec1de6792d7559d0dc55a41ea1ac2d05d4d37aa -
Trigger Event:
push
-
Statement type:
File details
Details for the file climax_mcp-0.4.0rc2-py3-none-any.whl.
File metadata
- Download URL: climax_mcp-0.4.0rc2-py3-none-any.whl
- Upload date:
- Size: 37.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49d60772f275b97fadc52b64b666cc7e782b8b9b68467451d342a7ffbdf539c9
|
|
| MD5 |
9a096ee95c5fe051cb1938fb61bacbb7
|
|
| BLAKE2b-256 |
ac1832a9f56ec3d9a9078d356c35439ff5364db3b2a333f41ac338f83b6b9ed0
|
Provenance
The following attestation bundles were made for climax_mcp-0.4.0rc2-py3-none-any.whl:
Publisher:
release.yml on get2knowio/climax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
climax_mcp-0.4.0rc2-py3-none-any.whl -
Subject digest:
49d60772f275b97fadc52b64b666cc7e782b8b9b68467451d342a7ffbdf539c9 - Sigstore transparency entry: 1096528225
- Sigstore integration time:
-
Permalink:
get2knowio/climax@3ec1de6792d7559d0dc55a41ea1ac2d05d4d37aa -
Branch / Tag:
refs/tags/v0.4.0-rc2 - Owner: https://github.com/get2knowio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3ec1de6792d7559d0dc55a41ea1ac2d05d4d37aa -
Trigger Event:
push
-
Statement type: