Prerequisites
- Pick a package name that isn't already taken on https://pypi.org/project/
- Register an account on https://pypi.org/
- Install uv and Python on your system
Steps
1. Initialize the project
Run this in the directory where you want the project:
uv init --package --name project_name --build-backend uv
This creates a src/project_name package and a pyproject.toml.
2. Write your code
Start coding inside ./src/project_name — that's where the package source lives. All your modules go here.
3. Update the project metadata
Edit pyproject.toml:
- Update description
- Add a license
- Update the author name and email
- Add your dependencies
Also update README.md and LICENSE.
4. (Optional) Add entry points for a CLI app or MCP server
If your app is a CLI or an MCP server, you can expose a command that gets installed on the user's system. Add an entry.py under the relevant subpackage.
CLI entry point — src/package_name/cli/entry.py:
# src/package_name/cli/entry.py
import sys
def run():
try:
from package_name.cli.main import cli
except ModuleNotFoundError as e:
if e.name in {"typer", "keyring"}:
sys.exit(
"Missing CLI deps. Install: uv tool install 'package_name[cli]'"
)
raise
cli()
MCP server entry point — src/package_name/mcp_server/entry.py:
# src/package_name/mcp_server/entry.py
import sys
def run():
try:
from package_name.mcp_server.server import main
except ModuleNotFoundError as e:
if e.name == "fastmcp":
sys.exit(
"Missing MCP deps. Install: uv tool install 'package_name[mcp]'"
)
raise
main()
Then register the commands in pyproject.toml:
[project.scripts]
command_name = "package_name.cli.entry:run"
mcp_server_name = "package_name.mcp_server.entry:run"
Note: the
[cli]and[mcp]extras used above must be defined under[project.optional-dependencies]inpyproject.toml.
5. Update .gitignore and test locally
Add any files that should not be committed (virtual environments, dist/, secrets, etc.) to .gitignore, then test the application locally.
6. Build the package
uv build
This creates a .tar.gz (source distribution) and a .whl (wheel) in ./dist. Extract the .tar.gz and check its contents to make sure no unnecessary files are included.
7. Test the build locally
Before publishing, install the wheel to confirm it works.
As a library — install it into a virtual environment:
uv pip install ./dist/package_name-x.x.x-py3-none-any.whl
As a CLI or MCP server — uv tool install is meant for packages that expose a command (a CLI entry point or an MCP server entry point). Install the wheel with the relevant extra:
uv tool install "package_name[cli] @ ./dist/package_name-x.x.x-py3-none-any.whl"
# or
uv tool install "package_name[mcp] @ ./dist/package_name-x.x.x-py3-none-any.whl"
8. Publish to PyPI
Log in at https://pypi.org/manage/account/ and create an API token.
Use the token to publish:
export UV_PUBLISH_TOKEN=pypi-...
uv publish
The package is now live under your account, and anyone can install it with:
pip install package_name
Note: a new token can be scoped to your whole account only until the project exists. After the first upload, create a token scoped to that project and use it for future releases.
9. Publish updates
After making code changes:
Bump the version in pyproject.toml:
uv version --bump patch # or minor / major
Clean old builds:
rm -rf dist
Build:
uv build
Publish (with UV_PUBLISH_TOKEN set):
uv publish
You can also pass the token directly with uv publish --token pypi-....
Note: PyPI does not allow re-uploading a version that already exists, so always bump the version before publishing.
Top comments (0)