click2mcp
click2mcp turns a Click CLI into a typed
MCP server. At startup it walks the command tree and creates one MCP tool per leaf
command, using the command's parameter names, types, defaults, and allowed values.
The JSON Schema comes from a function signature. MCPServer.add_tool and
Tool.from_function build it with pydantic and do not accept a hand-written
inputSchema, so click2mcp gives each command a function whose __signature__
and __annotations__ are the Click parameters themselves.
Install
pip install click2mcp
Quickstart
Add an adapter module to your CLI package:
# src/mycli/mcp/server.py
from click2mcp import ClickMCP
from mycli.main import main as cli
bridge = ClickMCP(app="mycli", cli=cli, module="mycli.main")
mcp = bridge.server
def main() -> None:
bridge.run()
Expose it as a console script and an optional dependency:
[project.scripts]
mycli-mcp = "mycli.mcp.server:main"
[project.optional-dependencies]
mcp = ["click2mcp>=0.1.0"]
Install the extra, register the server with an MCP client, and run it over stdio. For Claude Code:
claude mcp add mycli -- mycli-mcp
cli can also be a zero-argument callable that returns the root group or None.
It is resolved once. If it raises, the server still starts with only the escape
hatches. module="mycli.main" is the fallback launcher used when no installed
mycli executable can be found.
Configuration
| Parameter | Default | Meaning |
|---|---|---|
app |
required | CLI executable name, tool-name prefix ({app}_{path}), MCP server name, and {app}-mcp program name. |
cli |
required | Root click.Group, zero-arg callable returning one, or None. |
module |
None |
Module passed to python -m when no executable is found. Required if app is not installed and no launcher is given. |
instructions |
generic template | MCP server instructions; used verbatim when supplied. |
exclude |
frozenset() |
Command paths omitted from the typed tools and refused by {app}_run. |
include |
None |
Command path prefixes to expose; None exposes every leaf outside exclude. |
escape_hatches |
True |
Register {app}_help and {app}_run. |
help_description |
generic | Description override for {app}_help. |
run_description |
generic | Description override for {app}_run. |
inherit_group_options |
True |
Expose options declared on ancestor groups on every leaf tool below them. |
launcher |
None |
Explicit argv prefix used to run the CLI. |
env |
None |
Extra child environment variables, merged over the defaults. |
invoke |
None |
Custom (tokens) -> CompletedProcess[str] runner; shared by typed tools and escape hatches. |
The launcher is resolved once, in this order: launcher verbatim, the console
script next to the running interpreter, shutil.which(app), then
[sys.executable, "-m", module]. If none of them exist, construction raises
RuntimeError. The child process gets NO_COLOR=1, TERM=dumb, and
PYTHONIOENCODING=utf-8, merged over os.environ and then over env.
Behavior notes
Tool generation. Every leaf command becomes one tool. Spaces and hyphens in the
command path turn into _, so mycli browse following users is
mycli_browse_following_users, and the description ends with the exact CLI
spelling. --groups restricts the typed tools to the named subtrees, written with
dots or spaces. An empty entry is rejected.
Escape hatches and inherited options. {app}_help(path) runs --help, and
{app}_run(args) runs an arbitrary command line. Set escape_hatches=False to omit
both. Only ToolError text reaches the client. Typed tools also expose the options
of their ancestor groups, and the generated argv puts each option before the
subcommand it belongs to: {app} --verbose sub --profile=x run -- n. A leaf
parameter shadows a group option with the same name, the innermost group wins over
outer ones, and eager options such as --help and --version are skipped. Set
inherit_group_options=False to disable the inheritance.
Prompts and failures. An option declared with prompt= is exposed as required,
because the child's stdin is /dev/null and a prompt can never be answered over
MCP, even when Click does not mark the option required. A leaf whose parameters
cannot be turned into a signature, or whose tool name collides with another
command's, is logged and skipped. The rest of the server still starts.
Runtime and exclusions. A call returns when the child exits. There is no
server-side timeout, and the child's stdin is /dev/null, so it cannot read the
JSON-RPC stream; deciding when to give up is the MCP client's job. Options are
passed as one token, --long=value, never --long value, and repeated options are
repeated. Positionals follow an end-of-options -- in declaration order, so values
that begin with - survive. Full-screen TUIs and endless streams cannot work over
MCP. List them in exclude, because nothing here kills a subprocess and
{app}_run refuses excluded paths before spawning.
License
MIT
Metadata
Release files for click2mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| click2mcp-0.1.0.tar.gz | 28.3 kB | With provenance | Details for click2mcp-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| click2mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for click2mcp-0.1.0-py3-none-any.whl |
Total release size: 49.3 kB
Release files / click2mcp-0.1.0.tar.gz
| Download URL | click2mcp-0.1.0.tar.gz |
|---|---|
| Size | 28.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60f4982d0d9d7dc9e89bfa6f2738ba7504a8b6f89103e20da53973111d72d994
|
|
BLAKE2b-256 checksum How to use checksums |
982a82d718c3650024cac26fe65905c31bdd9af96a35d3e1623548502e210e81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logRelease files / click2mcp-0.1.0-py3-none-any.whl
| Download URL | click2mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
57c4ad2e0a5806d95ef842687964f0473b4aa88012540e619f3e16745cc36fe0
|
|
BLAKE2b-256 checksum How to use checksums |
afd6f082842a8dab21bed70c8a33dd8aa67d94206d1a7d094fc976d9d84606da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logSecurity for click2mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for click2mcp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.