Chapter 4: Runtime, Dependencies, and uv Packaging
April 13, 2026 · View on GitHub
This chapter covers the uv-based dependency and packaging model for generated MCP servers: how dependencies are declared, how lockfiles maintain reproducibility, and how to build and publish a server as a standalone Python package.
Learning Goals
- Manage dependencies with
uvconventions in generated projects - Run generated servers in development and published modes
- Keep lockfiles and build artifacts reproducible across environments
- Avoid environment drift across contributors and CI systems
The uv Packaging Model
Generated MCP servers use uv as the complete Python toolchain — environment manager, package manager, and build tool. This eliminates the pip + virtualenv + poetry + twine fragmentation common in Python projects.
graph LR
UV[uv toolchain]
UV --> SYNC[uv sync\nCreate .venv, install deps from lock]
UV --> RUN[uv run server-name\nRun in managed .venv]
UV --> BUILD[uv build\nCreate wheel + sdist in dist/]
UV --> PUBLISH[uv publish\nUpload to PyPI or private registry]
UV --> ADD[uv add package\nAdd dep + update lock]
pyproject.toml Structure
The generated pyproject.toml uses the hatchling build backend (the default for uv init):
[project]
name = "my-notes-server"
version = "0.1.0"
description = "A simple MCP server for managing notes"
readme = "README.md"
requires-python = ">=3.10"
dependencies = ["mcp>=1.0.0"]
[project.scripts]
my-notes-server = "my_notes_server:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Key constraints:
requires-python = ">=3.10"— themcpSDK requires Python 3.10+ for union type syntax (X | Y)mcp>=1.0.0— unpinned upper bound; updates get new SDK versions onuv sync[project.scripts]entry enablesuvx my-notes-serverwithout explicit path management
Development Workflow
flowchart TD
EDIT[Edit server.py]
EDIT --> SYNC[uv sync\ninstall or refresh deps]
SYNC --> TEST[Test with Inspector:\nnpx @modelcontextprotocol/inspector\nuv --directory . run my-notes-server]
TEST --> VERIFY[Verify tool calls\nresource list\nprompt rendering]
VERIFY --> EDIT
# Full development cycle
# 1. Install / refresh dependencies
uv sync --dev --all-extras
# 2. Run server directly (stdio — useful for pipes)
uv run my-notes-server
# 3. Run in Inspector for interactive testing
npx @modelcontextprotocol/inspector uv --directory /path/to/my-notes-server run my-notes-server
# 4. Add a new dependency
uv add requests
uv add --dev pytest
Lockfile Discipline
uv.lock pins every transitive dependency to an exact version and hash. This is committed to source control to ensure every environment gets identical packages:
# After anyone adds/removes a dependency, commit the updated lock file
git add uv.lock pyproject.toml
git commit -m "deps: add requests for HTTP resource fetching"
If a contributor runs uv sync without updating the lock, they get the locked versions — not the latest. To upgrade dependencies:
# Upgrade all dependencies within pyproject.toml constraints
uv lock --upgrade
# Upgrade a specific package
uv lock --upgrade-package mcp
Building for Distribution
# Build wheel and source distribution
uv build
# Output: dist/my_notes_server-0.1.0-py3-none-any.whl
# dist/my_notes_server-0.1.0.tar.gz
The generated wheel includes only the src/ package — no test files, no development artifacts. hatchling reads the [build-system] config to determine what to include.
flowchart LR
SRC[src/my_notes_server/\n__init__.py · server.py]
TOML[pyproject.toml]
SRC --> BUILD[uv build]
TOML --> BUILD
BUILD --> WHEEL[dist/my_notes_server-0.1.0.whl]
BUILD --> SDIST[dist/my_notes_server-0.1.0.tar.gz]
WHEEL --> INSTALL[pip install / uvx install]
Publishing to PyPI
# Configure credentials (one-time)
export UV_PUBLISH_TOKEN=pypi-...
# Publish
uv publish
# Or publish with explicit credential flags
uv publish --token $PYPI_TOKEN
After publishing, any user can run your server with:
uvx my-notes-server
No Python installation steps required — uvx handles environment creation transparently.
Version Management
Update the version in pyproject.toml before each release:
[project]
version = "0.2.0"
Semantic versioning conventions for MCP servers:
- Patch (0.1.x): bug fixes, no new tools/resources
- Minor (0.x.0): new tools, resources, or prompts added
- Major (x.0.0): breaking changes to tool signatures or removed primitives
Python Version Compatibility
| Python Version | Status | Notes |
|---|---|---|
| 3.9 | Not supported | mcp requires union syntax (X | Y) which needs 3.10+ |
| 3.10 | Minimum | Full support |
| 3.11 | Recommended | Better performance for async |
| 3.12+ | Supported | No known issues |
# Pin Python version for the project (creates .python-version file)
uv python pin 3.12
Source References
Summary
Generated MCP servers are standard Python packages managed entirely by uv. The uv sync → uv run → uv build → uv publish pipeline handles the full lifecycle without touching pip or virtualenv directly. Commit uv.lock to ensure reproducibility. Use semantic versioning to signal breaking changes in tool signatures to downstream clients.
Next: Chapter 5: Local Integration: Claude Desktop and Inspector