# How to Create and Distribute a Python CLI Tool


[pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) lets you attach any Python function to an executable command name via `[project.scripts]`. When someone installs the package, their package manager creates a wrapper script that calls that function. With [uvx](https://pydevtools.com/handbook/reference/uvx.md), users can run the tool without even installing it first.

> [!TIP]
> New to Python packaging? Start with the [Create your first Python project with uv](https://pydevtools.com/handbook/tutorial/create-your-first-python-project.md) tutorial before following this guide.

## Define the entry point

Add a `[project.scripts]` table to your `pyproject.toml`. If you don't have a package project yet, `uv init --package my-tool` scaffolds one with a [build backend](https://pydevtools.com/handbook/explanation/what-is-a-build-backend.md) pre-configured:

```toml {filename="pyproject.toml"}
[project.scripts]
my-tool = "my_tool:main"
```

The value `my_tool:main` means "call the `main` function from the `my_tool` package." The key on the left (`my-tool`) becomes the executable command name on the user's `PATH`. This is the [PEP 621](https://pydevtools.com/handbook/explanation/what-is-pep-621-compatibility.md) equivalent of the `console_scripts` entry point in older [setuptools](https://pydevtools.com/handbook/reference/setuptools.md)-based projects.

The function can use [argparse](https://docs.python.org/3/library/argparse.html), [click](https://click.palletsprojects.com/), or plain `sys.argv`. The packaging mechanism is the same regardless of what the function does internally.

## Test locally

```bash
uv run my-tool --help
```

`uv run` installs the project into the local [virtual environment](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md) and invokes the entry point. No manual `pip install -e .` step is needed.

## Build the package

```console
$ uv build
Successfully built dist/my_tool-0.1.0.tar.gz
Successfully built dist/my_tool-0.1.0-py3-none-any.whl
```

Both a source distribution and a [wheel](https://pydevtools.com/handbook/reference/wheel.md) land in the `dist/` directory.

## Publish to PyPI

The recommended publishing workflow is [trusted publishing from GitHub Actions](https://pydevtools.com/handbook/how-to/how-to-publish-to-pypi-with-trusted-publishing.md), which requires no stored secrets. For quick one-off uploads, generate an API token at [pypi.org/manage/account/](https://pypi.org/manage/account/) and pass it with `--token`.

Before publishing to the live index, verify the package looks correct on [TestPyPI](https://test.pypi.org):

```bash
uv publish --publish-url https://test.pypi.org/legacy/ --token YOUR_TEST_TOKEN
```

Then publish to PyPI:

```bash
uv publish
```

## Run the published tool with uvx

After publishing, anyone with uv installed can run the tool without a prior install step:

```console
$ uvx my-tool Alice
Hello, Alice!
```

[uvx](https://pydevtools.com/handbook/reference/uvx.md) resolves and caches the tool environment on first run and reuses the cache on subsequent calls. The tool never touches the user's project dependencies.

For a permanent installation that puts the command on `PATH`:

```bash
uv tool install my-tool
my-tool Alice
```

The executable lands in `~/.local/bin/`.
```powershell
uv tool install my-tool
my-tool Alice
```

The executable lands in `%APPDATA%\uv\tools\my-tool\Scripts\`.
Run `uv tool upgrade my-tool` to pull a newer version later.

## Learn More

- [uvx reference](https://pydevtools.com/handbook/reference/uvx.md)
- [pyproject.toml reference](https://pydevtools.com/handbook/reference/pyproject.toml.md)
- [What is a build backend?](https://pydevtools.com/handbook/explanation/what-is-a-build-backend.md)
- [How to publish to PyPI with trusted publishing](https://pydevtools.com/handbook/how-to/how-to-publish-to-pypi-with-trusted-publishing.md)
- [uv tools documentation](https://docs.astral.sh/uv/concepts/tools/)
- [pipx reference](https://pydevtools.com/handbook/reference/pipx.md) (alternative tool installer if uv isn't available)
