[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "amtsblatt-mcp" version = "0.22.1" description = "MCP server for amtsblattportal.ch (SHAB + cantonal gazettes) — procurement and official notices, person-data rubrics excluded by design" readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.11" keywords = [ "mcp", "model-context-protocol", "swiss-open-data", "amtsblatt", "shab", "gazette", "procurement", "submissionen", "beschaffung", "data-protection", "revdsg", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] dependencies = [ # OBS-003: structured logging with contextvars-bound correlation ids. "structlog>=24.1,<27", # Bounded to the 2.x line. The upper bound stays on principle: `mcp` 2.0.0 # removed `mcp.server.fastmcp` outright with no deprecation shim, and an # unbounded requirement turned every CI run red the day it was published. # A major version is a decision to take on purpose, not one a resolver # should make on a random Tuesday. "mcp[cli]>=2.0.0,<3", "httpx>=0.27.0", "pydantic>=2.0.0", # SDK-004: _cors.py imports Starlette directly. It arrives transitively via # `mcp`, but an undeclared import is one upstream dependency change away # from an ImportError at startup. "starlette>=0.37,<2", # Windows ships no system tz database; Europe/Zurich is required for the # legally relevant submission-deadline arithmetic. "tzdata>=2024.1; sys_platform == 'win32'", ] [project.optional-dependencies] dev = [ "pytest>=8.0.0", "pytest-asyncio>=0.23.0", "pytest-cov>=5.0.0", "respx>=0.21.0", # The only ruff pin in the repository. CI installs this extra and nothing # else, so local gates and CI reach the same linter by construction. A # floating requirement here would let a fresh install resolve to whatever # ruff shipped most recently, and the local gate would report findings CI # never sees — or miss the ones it does. # test_ruff_pin_lives_only_in_the_dev_extra fails if a second pin appears. "ruff==0.16.4", # OBS-006: tests/test_otel.py uses importorskip, so without these installed # it would skip silently in CI — and a test that always skips is not a test. "opentelemetry-api>=1.27.0", "opentelemetry-sdk>=1.27.0", ] otel = [ "opentelemetry-api>=1.27.0", "opentelemetry-sdk>=1.27.0", "opentelemetry-exporter-otlp-proto-http>=1.27.0", "opentelemetry-instrumentation-httpx>=0.48b0", ] [project.scripts] amtsblatt-mcp = "amtsblatt_mcp.server:main" [project.urls] Homepage = "https://github.com/malkreide/amtsblatt-mcp" Repository = "https://github.com/malkreide/amtsblatt-mcp" Issues = "https://github.com/malkreide/amtsblatt-mcp/issues" [tool.hatch.build.targets.wheel] packages = ["src/amtsblatt_mcp"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] addopts = "-v --tb=short" markers = [ "live: live API tests (skipped in CI by default)", ] [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "W", "I", "UP"] ignore = ["E501"]