[project] name = "plant-genomics-mcp" version = "1.21.0" description = "Plant genomics MCP — 50 tools across 23 live backends (Ensembl Plants, Phytozome, UniProt, Europe PMC, QuickGO, Planteome, PlantCyc/PMN, g:Profiler, AlphaFold, PDBe, InterPro, JASPAR, ThaleMine, PANTHER, OrthoDB, AraGWAS, 1001 Genomes, BLAST, Gramene, KEGG, STRING, ATTED-II, BAR) for Claude Code" readme = "README.md" license = { text = "MIT" } authors = [{ name = "Jaret Arnold", email = "mjarnold1998@gmail.com" }] requires-python = ">=3.11" classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering :: Bio-Informatics", ] dependencies = [ # Floor is the version the suite actually runs against. It MOVED to 2.x # (rather than widening) when server.py migrated off the removed # @server.list_tools() decorator family onto Server(on_list_tools=...): # that code cannot run on 1.x at all, because ServerRequestContext does # not exist there and the import fails outright. Leaving the old >=1.28.1 # floor in place would let a resolver pick a version this package can no # longer import. The upper bound stays deliberate — a major SDK bump gets # opted into, not inherited silently by everyone who installs us. "mcp>=2,<3", "httpx>=0.27", "pydantic>=2.6", "starlette>=0.36", "uvicorn>=0.27", "defusedxml>=0.7", ] [project.urls] Homepage = "https://github.com/musharna/plant-genomics-mcp" Changelog = "https://github.com/musharna/plant-genomics-mcp/blob/main/CHANGELOG.md" Issues = "https://github.com/musharna/plant-genomics-mcp/issues" [project.optional-dependencies] dev = [ "pytest>=8.0", "pytest-asyncio>=0.23", "pytest-httpx>=0.30", "pytest-cov>=5.0", "ruff>=0.3", "mypy>=1.8", ] [project.scripts] plant-genomics-mcp = "plant_genomics_mcp.server:main" plant-genomics-mcp-http = "plant_genomics_mcp.server_http:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/plant_genomics_mcp"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" [tool.coverage.report] # Coverage floor (audit P8). Measured TOTAL was 94% after the v1.6.0 dispatch/ # batch/progress/consensus test backfill; 92 leaves ~2pts of slack while # failing the build if an untested dispatch arm or a deleted test regresses it. # pytest-cov honors fail_under from here, so `pytest --cov` fails below the # floor. The __main__/entrypoint block (server._serve/main) is the bulk of the # remaining gap and is intentionally not exercised in CI. fail_under = 92 show_missing = true [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] # E/F (default) plus import-sorting, bugbear, pyupgrade, and simplify. # E501 (line-too-long) is intentionally left to the formatter — `ruff format` # owns line-length, and a lint error on top of it is noise. select = ["E", "F", "I", "B", "UP", "SIM"] ignore = ["E501"] [tool.ruff.lint.per-file-ignores] # Nested `async with` (stdio_client + ClientSession) reads clearer than a # combined context manager in the MCP stdio integration tests. "tests/**" = ["SIM117"] [tool.mypy] python_version = "3.11" ignore_missing_imports = true warn_unused_ignores = true [tool.mutmut] source_paths = ["src"] also_copy = [".dockerignore", ".editorconfig", ".github", ".gitignore", ".mcp.json", ".pre-commit-config.yaml", ".zenodo.json", "CHANGELOG.md", "CITATION.cff", "CLAUDE.md", "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "Dockerfile", "Dockerfile.http", "LICENSE", "README.md", "REGISTRIES.md", "SECURITY.md", "assets", "docs", "examples", "glama.json", "scripts", "server.json", "smithery.yaml"] # a module-level asyncio.Semaphore binds to the first event loop, which under the in-process mutmut runner is not the test loop pytest_add_cli_args = ["--deselect", "tests/test_blast.py::test_blast_semaphore_caps_concurrent_at_two"]