[project] name = "jdocmunch-mcp" version = "1.143.0" description = "Token-efficient MCP server for structured documentation retrieval via section-level indexing" readme = "README.md" requires-python = ">=3.10" authors = [ { name = "J. Gravelle", email = "j@gravelle.us" }, ] license = "LicenseRef-jDocMunch-Dual-Use" license-files = ["LICENSE"] dependencies = [ "mcp>=1.10.0,<2.0.0", "httpx>=0.27.0", "pathspec>=0.12.0", "pyyaml>=6.0", "watchfiles>=0.21.0", ] [project.urls] Homepage = "https://github.com/jgravelle/jdocmunch-mcp" Repository = "https://github.com/jgravelle/jdocmunch-mcp" "Bug Tracker" = "https://github.com/jgravelle/jdocmunch-mcp/issues" [project.optional-dependencies] anthropic = ["anthropic>=0.40.0"] gemini = ["google-generativeai>=0.8.0"] openai = ["openai>=1.0.0"] minimax = ["openai>=1.0.0"] zhipu = ["openai>=1.0.0"] semantic = ["google-generativeai>=0.8.0"] # jdoc#126: offline embeddings through onnxruntime instead of torch. An # OPTIONAL extra, never a runtime dependency -- a lexical install must not # acquire a native runtime it will never call. fastembed = ["fastembed>=0.8.0"] office = ["markitdown[pdf,docx,pptx]>=0.1.6"] [project.scripts] jdocmunch-mcp = "jdocmunch_mcp.server:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.sdist] # Build-time exclusion, NOT git exclusion. `.claude/settings.local.json` # is ignored via a GLOBAL gitignore, so `git status` never shows it and a # fresh clone never has it -- which is why the CI sdist guard grepping for # `.claude` cannot fire. It leaks only from a LOCAL build, and a local build # is how twine uploads happen. exclude = [".claude/"] [tool.hatch.build.targets.wheel] packages = ["src/jdocmunch_mcp"] [dependency-groups] dev = [ "pytest>=9.0.2", "pytest-asyncio>=1.3.0", "pytest-cov>=7.0.0", # Test-only. numpy stays an OPTIONAL runtime dependency, lazily imported by # the vectorized semantic-search path in storage/doc_store.py; without it # here, the tests asserting that path agrees with the pure-Python reference # skipped on every CI run, and a divergence between the two would have # shipped unnoticed. "numpy>=1.24.0", # Required by the `lint` job in .github/workflows/test.yml. Locally # `uv run ruff` fetches it on demand, which is exactly why its absence here # was invisible until CI ran: the job failed with "Failed to spawn: ruff" # on its very first run. "ruff>=0.6.0", ] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" [tool.ruff] target-version = "py310" [tool.ruff.lint] # ⚠⚠ EXPLICIT `select`. Ruff's DEFAULT rule set is not stable across versions, # and this repo has no `uv.lock` to pin ruff with, so `ignore`-only config means # the gate's strictness changes under you whenever ruff ships a release. # Measured 2026-08-07: config unchanged, ruff 0.16.2 resolved 446 findings # (YTT/ASYNC/S/BLE/B/UP/I) where the intended set gives 10. # # These four are pycodestyle E4/E7/E9 + pyflakes F: the high-signal correctness # rules (undefined names, unused imports, f-string bugs, duplicate keys, # redefinitions). Widening the set is a deliberate decision, not a ruff upgrade. select = ["E4", "E7", "E9", "F"] # Default rule set is pycodestyle E4/E7/E9 + pyflakes F. We keep the high-signal # correctness rules -- undefined names, unused imports, f-string bugs, duplicate # keys, redefinitions -- and grandfather the codes that reflect deliberate, # pervasive patterns here rather than defects. # # Added 2026-08-07 with v1.124.3. Adding the gate immediately surfaced an # undefined `logger` (F821) shipped in v1.124.0, sitting inside the `except` # block that exists to swallow errors: a real failure raised NameError out of # the handler. That is the whole argument for the gate. ignore = [ "E402", # module-import-not-at-top: 65 deliberate lazy/local imports (startup cost) "F841", # local assigned but never used: 2 pre-existing dead locals, low risk ] [tool.ruff.lint.per-file-ignores] # __init__.py re-exports are the package's public API surface, not dead imports. "__init__.py" = ["F401"]