Rename packages for PyPI: winegard-birdcage + mcbirdcage

Core library published as winegard-birdcage (import stays `birdcage`).
MCP server renamed birdcage-mcp → mcbirdcage, matching mcserial pattern.
Both packages live on PyPI — `uvx mcbirdcage` works out of the box.
This commit is contained in:
Ryan Malloy 2026-02-17 18:18:31 -07:00
parent b98a5482fa
commit 5a26dce075
20 changed files with 122 additions and 92 deletions

View file

@ -3,22 +3,33 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "birdcage-mcp"
name = "mcbirdcage"
version = "2026.02.17"
description = "FastMCP server for Winegard satellite dish control"
description = "MCP server for Winegard satellite dish control via serial"
license = "MIT"
requires-python = ">=3.11"
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
keywords = ["mcp", "satellite", "antenna", "winegard", "amateur-radio"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
"Topic :: Communications :: Ham Radio",
]
dependencies = [
"birdcage",
"winegard-birdcage",
"fastmcp>=2.0",
]
[project.urls]
Repository = "https://git.supported.systems/warehack.ing/birdcage"
Documentation = "https://birdcage.warehack.ing"
[project.scripts]
birdcage-mcp = "birdcage_mcp.server:main"
mcbirdcage = "mcbirdcage.server:main"
[tool.uv.sources]
birdcage = { path = "..", editable = true }
winegard-birdcage = { path = "..", editable = true }
[tool.ruff]
target-version = "py311"
@ -28,7 +39,7 @@ src = ["src"]
select = ["E", "F", "I", "UP", "B", "SIM"]
[tool.hatch.build.targets.wheel]
packages = ["src/birdcage_mcp"]
packages = ["src/mcbirdcage"]
[dependency-groups]
dev = ["pytest>=9.0.2", "pytest-asyncio>=1.3.0"]

View file

@ -1 +0,0 @@
"""birdcage-mcp: FastMCP server for Winegard satellite dish control."""

View file

@ -0,0 +1 @@
"""mcbirdcage: MCP server for Winegard satellite dish control via serial."""

View file

@ -7,7 +7,7 @@ from contextlib import asynccontextmanager
from fastmcp import FastMCP
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
logger = logging.getLogger(__name__)
@ -67,8 +67,8 @@ mcp = FastMCP(
)
# Register tool modules
from birdcage_mcp import prompts, resources # noqa: E402, F401
from birdcage_mcp.tools import ( # noqa: E402
from mcbirdcage import prompts, resources # noqa: E402, F401
from mcbirdcage.tools import ( # noqa: E402
connection,
console,
movement,
@ -88,5 +88,5 @@ prompts.register(mcp)
def main():
print("birdcage-mcp v2026.02.17", file=sys.stderr)
print("mcbirdcage v2026.02.17", file=sys.stderr)
mcp.run()

View file

@ -2,7 +2,7 @@
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
def register(mcp):

View file

@ -2,7 +2,7 @@
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
# Commands that are dangerous at the root menu level.
_BLOCKED_ROOT_COMMANDS = {"q"}

View file

@ -2,7 +2,7 @@
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
def _require_device(state: BirdcageState):

View file

@ -4,7 +4,7 @@ from dataclasses import asdict
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
def _require_craft(state: BirdcageState):

View file

@ -2,7 +2,7 @@
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
def _require_device(state: BirdcageState):

View file

@ -2,7 +2,7 @@
from fastmcp import Context
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
def _require_device(state: BirdcageState):

View file

@ -1,4 +1,4 @@
"""Shared fixtures for birdcage-mcp tests.
"""Shared fixtures for mcbirdcage tests.
Uses FastMCP's run_server_async to spin up a real MCP server backed by
DemoDevice + DemoCraftClient. No serial hardware, no subprocesses.
@ -13,7 +13,7 @@ from fastmcp import Client, FastMCP
from fastmcp.client.transports import StreamableHttpTransport
from fastmcp.utilities.tests import run_server_async
from birdcage_mcp.state import BirdcageState
from mcbirdcage.state import BirdcageState
@asynccontextmanager
@ -36,8 +36,8 @@ async def _test_lifespan(server: FastMCP):
def _build_server() -> FastMCP:
"""Build a FastMCP server with all tools registered."""
from birdcage_mcp import prompts, resources
from birdcage_mcp.tools import (
from mcbirdcage import prompts, resources
from mcbirdcage.tools import (
connection,
console,
movement,

84
mcp/uv.lock generated
View file

@ -81,48 +81,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" },
]
[[package]]
name = "birdcage"
version = "2026.2.12.1"
source = { editable = "../" }
dependencies = [
{ name = "click" },
{ name = "pyserial" },
]
[package.metadata]
requires-dist = [
{ name = "click", specifier = ">=8.0" },
{ name = "pyserial", specifier = ">=3.5" },
]
[[package]]
name = "birdcage-mcp"
version = "2026.2.17"
source = { editable = "." }
dependencies = [
{ name = "birdcage" },
{ name = "fastmcp" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
]
[package.metadata]
requires-dist = [
{ name = "birdcage", editable = "../" },
{ name = "fastmcp", specifier = ">=2.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pytest", specifier = ">=9.0.2" },
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
]
[[package]]
name = "cachetools"
version = "7.0.1"
@ -774,6 +732,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
]
[[package]]
name = "mcbirdcage"
version = "2026.2.17"
source = { editable = "." }
dependencies = [
{ name = "fastmcp" },
{ name = "winegard-birdcage" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
]
[package.metadata]
requires-dist = [
{ name = "fastmcp", specifier = ">=2.0" },
{ name = "winegard-birdcage", editable = "../" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pytest", specifier = ">=9.0.2" },
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
]
[[package]]
name = "mcp"
version = "1.26.0"
@ -1668,6 +1653,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
]
[[package]]
name = "winegard-birdcage"
version = "2026.2.17"
source = { editable = "../" }
dependencies = [
{ name = "click" },
{ name = "pyserial" },
]
[package.metadata]
requires-dist = [
{ name = "click", specifier = ">=8.0" },
{ name = "pyserial", specifier = ">=3.5" },
]
[[package]]
name = "zipp"
version = "3.23.0"