[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "fastmcp-feedback" version = "2026.09.29.1" # YYYYMMDD.patch versioning - update for each release description = "Production-ready feedback collection system for FastMCP servers" readme = "README.md" license = "MIT" authors = [ {name = "Ryan Malloy", email = "ryan@supported.systems"} ] maintainers = [ {name = "Ryan Malloy", email = "ryan@supported.systems"} ] keywords = [ "fastmcp", "feedback", "mcp", "model-context-protocol", "analytics", "database", "server", "privacy-compliant", "enterprise" ] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Database :: Database Engines/Servers", "Topic :: System :: Logging", "Topic :: System :: Monitoring", "Typing :: Typed" ] requires-python = ">=3.11" dependencies = [ "fastmcp>=2.12.2,<5", # CI tests 2.12, 2.14 and 4.0; raise the ceiling once 5.x passes "sqlalchemy>=2.0.43", "pydantic>=2.11.7" ] [project.optional-dependencies] postgresql = ["psycopg2-binary>=2.9.0"] # Async database sink for per-call instrumentation. Postgres hosts add asyncpg. instrumentation = ["sqlalchemy[asyncio]>=2.0.43", "aiosqlite>=0.22.1"] # Vector column for EmbeddingSink on Postgres. Postgres hosts add asyncpg. embeddings = ["pgvector>=0.5.0"] mysql = ["pymysql>=1.1.0"] dev = [ "sqlalchemy[asyncio]>=2.0.43", "aiosqlite>=0.22.1", "asyncpg>=0.31.0", "pgvector>=0.5.0", "pytest>=8.2.0", "pytest-asyncio>=0.23.0", "pytest-cov>=5.0.0", "pytest-mock>=3.12.0", "pytest-html>=4.2.0", "ruff>=0.1.0", "mypy>=1.7.0", ] test = [ "sqlalchemy[asyncio]>=2.0.43", "aiosqlite>=0.22.1", "asyncpg>=0.31.0", "pgvector>=0.5.0", "pytest>=8.2.0", "pytest-asyncio>=0.23.0", "pytest-cov>=5.0.0", "pytest-mock>=3.12.0", "pytest-html>=4.2.0", ] all = ["fastmcp-feedback[postgresql,mysql,dev]"] [project.urls] Homepage = "https://git.supported.systems/fastmcp-feedback/fastmcp-feedback" Documentation = "https://fastmcp-feedback.l.supported.systems" Repository = "https://git.supported.systems/fastmcp-feedback/fastmcp-feedback.git" Issues = "https://git.supported.systems/fastmcp-feedback/fastmcp-feedback/issues" Changelog = "https://git.supported.systems/fastmcp-feedback/fastmcp-feedback/blob/main/CHANGELOG.md" [tool.hatch.build.targets.wheel] packages = ["src/fastmcp_feedback"] [tool.hatch.build.targets.sdist] include = [ "/src", "/tests", "/examples", "README.md", "LICENSE", "CHANGELOG.md", "CONTRIBUTING.md", "pyproject.toml" ] # Testing [tool.pytest.ini_options] minversion = "8.0" addopts = [ "-ra", "--strict-markers", "--strict-config", "--tb=short", "--cov=src/fastmcp_feedback", "--cov-report=term-missing:skip-covered", ] testpaths = ["tests"] python_files = ["test_*.py"] python_classes = ["Test*"] python_functions = ["test_*"] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" markers = [ "unit: Unit tests for individual components", "integration: Integration tests for component interaction", "slow: Slow running tests", "database: Tests requiring database setup", "postgres: Runs against the Postgres named by FFB_TEST_DATABASE_URL; skipped when unset", ] filterwarnings = [ "ignore::DeprecationWarning", "ignore::PendingDeprecationWarning", ] # Coverage [tool.coverage.run] source = ["src/fastmcp_feedback"] omit = ["tests/*", "src/fastmcp_feedback/__init__.py"] branch = true [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", ] show_missing = true # Ruff [tool.ruff] target-version = "py311" line-length = 88 src = ["src", "tests"] [tool.ruff.lint] select = ["E", "W", "F", "UP", "B", "SIM", "I", "N", "C4"] ignore = [ "E501", "B008", # StrEnum changes str()/format() output of FeedbackType/FeedbackStatus, # which reach the database and API responses. Worth doing, not silently. "UP042", ] [tool.ruff.lint.per-file-ignores] "tests/**/*.py" = ["ARG", "PLR2004"] "src/fastmcp_feedback/__init__.py" = ["F401"] [tool.ruff.lint.isort] known-first-party = ["fastmcp_feedback"] # MyPy [tool.mypy] python_version = "3.11" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true check_untyped_defs = true no_implicit_optional = true [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs = false [[tool.mypy.overrides]] module = ["fastmcp.*", "sqlalchemy.*", "pytest.*"] ignore_missing_imports = true