pyproject.toml•12.5 kB
[project]
name = "mcp-code-analysis-server"
version = "0.1.0"
description = "MCP server for intelligent code analysis and search capabilities"
readme = "README.md"
requires-python = ">=3.11"
authors = [
{name = "MCP Code Analysis Team", email = "team@example.com"}
]
license = {text = "MIT"}
keywords = ["mcp", "code-analysis", "treesitter", "embeddings", "search"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
# MCP Framework
"fastmcp>=0.1.0",
# LangChain/LangGraph
"langchain-core>=0.1.0",
"langgraph>=0.0.20",
"langchain-openai>=0.0.5",
# Database
"psycopg2-binary>=2.9.9",
"sqlalchemy>=2.0.0",
"alembic>=1.13.0",
"asyncpg>=0.29.0",
"pgvector>=0.2.4",
# Code Parsing
"tree-sitter>=0.20.4",
"tree-sitter-python>=0.20.4",
"tree-sitter-php>=0.23.0",
"tree-sitter-java>=0.23.0",
"tree-sitter-typescript>=0.23.0",
"tree-sitter-javascript>=0.23.0",
"GitPython>=3.1.40",
# Web Framework
"fastapi>=0.109.0",
"uvicorn>=0.25.0",
"httpx>=0.25.0",
"pydantic>=2.5.0",
"pydantic-settings>=2.1.0",
# Utilities
"pyyaml>=6.0.1",
"click>=8.1.7",
"python-dotenv>=1.0.0",
"rich>=13.7.0",
"tenacity>=8.2.3",
"aiofiles>=23.2.1",
# Configuration and Logging
"dynaconf>=3.2.0",
"structlog>=24.0.0",
# OpenAI
"openai>=1.10.0",
"tiktoken>=0.5.2",
# Data Processing
"numpy>=1.26.0",
"pandas>=2.1.0",
# Graph Analysis
"networkx>=3.2",
"igraph>=0.11.3", # Python 3.12 compatible python-igraph for Leiden algorithm
]
[project.urls]
Homepage = "https://github.com/johannhartmann/mcp-code-analysis-server"
Repository = "https://github.com/johannhartmann/mcp-code-analysis-server"
Issues = "https://github.com/johannhartmann/mcp-code-analysis-server/issues"
[project.optional-dependencies]
dev = [
# Testing
"pytest>=7.4.0",
"pytest-asyncio>=0.21.0",
"pytest-cov>=4.1.0",
"pytest-mock>=3.12.0",
"aiosqlite>=0.19.0", # For async SQLite in tests
"pytest-xdist>=3.5.0", # parallel test execution
"pytest-timeout>=2.2.0", # test timeouts
"pytest-benchmark>=4.0.0", # performance testing
"hypothesis>=6.92.0", # property-based testing
"faker>=22.0.0", # test data generation
# Code Quality
"black>=23.12.0",
"ruff>=0.1.0",
"mypy>=1.8.0",
"pre-commit>=3.6.0",
"isort>=5.13.0", # import sorting
"bandit>=1.7.5", # security linting
"safety>=3.0.0", # dependency security check
"pylint>=3.0.0", # additional linting
"pyupgrade>=3.15.0", # upgrade Python syntax
"vulture>=2.10", # dead code detection
# Type stubs
"types-pyyaml>=6.0.0",
"types-requests>=2.31.0",
"pandas-stubs>=2.1.0",
"types-click>=7.1.0",
# Documentation
"mkdocs>=1.5.0",
"mkdocs-material>=9.5.0",
"mkdocstrings[python]>=0.24.0",
# Development tools
"ipdb>=0.13.13", # debugger
"python-semantic-release>=8.7.0", # versioning
]
[project.scripts]
mcp-code-server = "src.mcp_server.main:main"
mcp-scanner = "src.scanner.main:main"
mcp-indexer = "src.indexer.main:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src"]
[tool.black]
line-length = 88
target-version = ['py311']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| build
| dist
)/
'''
[tool.ruff]
target-version = "py311"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
"UP", # pyupgrade
"N", # pep8-naming
"YTT", # flake8-2020
"ANN", # flake8-annotations
"ASYNC",# flake8-async
"S", # flake8-bandit
"BLE", # flake8-blind-except
"FBT", # flake8-boolean-trap
"A", # flake8-builtins
"COM", # flake8-commas
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T10", # flake8-debugger
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"G", # flake8-logging-format
"INP", # flake8-no-pep420
"PIE", # flake8-pie
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
"SLF", # flake8-self
"SLOT", # flake8-slots
"SIM", # flake8-simplify
"TID", # flake8-tidy-imports
"TCH", # flake8-type-checking
"ARG", # flake8-unused-arguments
"PTH", # flake8-use-pathlib
"TD", # flake8-todos
"FIX", # flake8-fixme
"ERA", # eradicate
"PGH", # pygrep-hooks
"PL", # Pylint
"TRY", # tryceratops
"FLY", # flynt
"NPY", # NumPy-specific rules
"PERF", # Perflint
"RUF", # Ruff-specific rules
]
ignore = [
"E501", # line too long (handled by black)
"B008", # do not perform function calls in argument defaults
"C901", # too complex
"W191", # indentation contains tabs
"ANN401",# dynamically typed expressions (Any)
"S101", # use of assert detected
"TD003", # missing issue link on TODO
"FIX002",# line contains TODO
"PLR0913",# too many arguments
"ANN001",# missing type annotation for function argument
"ANN002",# missing type annotation for *args
"ANN003",# missing type annotation for **kwargs
"ANN201",# missing return type annotation for public function
"ANN202",# missing return type annotation for private function
"ANN204",# missing return type annotation for special method
"ANN205",# missing return type annotation for staticmethod
"ANN206",# missing return type annotation for classmethod
"D100", # Missing docstring in public module
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
"D103", # Missing docstring in public function
"D104", # Missing docstring in public package
"D105", # Missing docstring in magic method
"D106", # Missing docstring in public nested class
"D107", # Missing docstring in __init__
"UP006", # Use `list` instead of `List` for type annotations
"UP007", # Use `X | Y` for type annotations
"UP037", # Remove quotes from type annotation (needed for SQLAlchemy forward refs)
"SLF001", # Private member access (sometimes necessary)
"COM812", # Trailing comma missing (style preference)
"PLC0415", # Import outside top level (needed for lazy loading)
"TRY300", # Try-consider-else (often not practical)
"INP001", # Implicit namespace package (not all dirs need __init__.py)
"S607", # Start process with partial path (handled by environment)
"S106", # Hardcoded password func arg (false positives on test fixtures)
"S108", # Hardcoded temp file (test fixtures)
"RUF006", # Asyncio dangling task (we handle task lifecycle properly)
"ASYNC221", # Run process in async function (necessary for git operations)
"ASYNC110", # Async busy wait (sometimes necessary for polling)
"S104", # Hardcoded bind all interfaces (config controlled)
"S105", # Hardcoded password string (test data)
"FBT001", # Boolean type hint positional (API design choice)
"FBT002", # Boolean default value positional (API design choice)
"FBT003", # Boolean positional value in function call (Pydantic Field)
"PLR2004", # Magic value used in comparison (tolerance values)
]
fixable = ["ALL"]
unfixable = []
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "D104"]
"tests/**/*.py" = ["S101", "ARG", "FBT", "PLR2004", "PLR0913", "TRY003", "SIM117"]
"src/mcp_server/tools/*.py" = ["ANN201", "PLR0911", "PLR0912", "PLR0915"] # MCP tools complexity
"src/query/ranking.py" = ["PLR0915", "PLR0912"] #TODO
"src/query/search_engine.py" = ["PLR0912"] #TODO
"src/mcp_server/server.py" = ["PLW0603"] # global statements for shared resources
"src/mcp_server/config.py" = ["PLW0603"] # global statements for singleton config
"src/mcp_server/tools/explain.py" = ["PERF401"] # false positive on list append
"src/parser/code_extractor.py" = ["PERF401"] # false positive on list append
"src/scanner/github_monitor.py" = ["PERF401"] # false positive on list append
"src/embeddings/embedding_generator.py" = ["PLR0912"] # complex chunking logic
[tool.ruff.lint.isort]
known-third-party = ["fastapi", "pydantic", "sqlalchemy", "langchain", "langgraph", "fastmcp"]
combine-as-imports = true
force-wrap-aliases = true
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
follow_imports = "normal"
ignore_missing_imports = true
plugins = ['pydantic.mypy', 'sqlalchemy.ext.mypy.plugin']
[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
[tool.pytest.ini_options]
minversion = "7.0"
addopts = [
"-ra",
"-q",
"--strict-markers",
"--strict-config",
"--cov=src",
"--cov-branch",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=0",
"--no-cov-on-fail",
"--timeout=300",
"--durations=10",
]
testpaths = ["tests"]
pythonpath = ["src"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks tests as integration tests",
"unit: marks tests as unit tests",
"requires_db: marks tests that require database",
"requires_openai: marks tests that require OpenAI API",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]
[tool.coverage.run]
source = ["src"]
omit = [
"*/tests/*",
"*/test_*.py",
"*/__init__.py",
"*/migrations/*",
"*/conftest.py",
]
parallel = true
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
[tool.coverage.html]
directory = "htmlcov"
[tool.isort]
profile = "black"
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 88
known_third_party = ["fastapi", "pydantic", "sqlalchemy", "langchain", "langgraph", "fastmcp"]
known_first_party = ["src"]
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
[tool.bandit]
targets = ["src"]
exclude_dirs = ["tests", "docs"]
severity = "medium"
confidence = "medium"
skips = ["B101", "B601"]
[tool.pylint.main]
py-version = "3.11"
jobs = 0
persistent = true
suggestion-mode = true
exit-zero = false
[tool.pylint.messages_control]
enable = ["all"]
disable = [
"missing-module-docstring",
"missing-class-docstring",
"missing-function-docstring",
"too-few-public-methods",
"too-many-arguments",
"too-many-instance-attributes",
"too-many-locals",
"too-many-branches",
"fixme",
"import-outside-toplevel",
]
[tool.pylint.format]
max-line-length = 88
[tool.pylint.basic]
good-names = ["i", "j", "k", "e", "x", "y", "z", "id", "db", "df"]
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_pattern = ["src/__init__.py:__version__"]
branch = "main"
upload_to_pypi = false
upload_to_release = true
build_command = "python -m build"
commit_subject = "chore(release): v{version}"
[tool.vulture]
min_confidence = 80
paths = ["src", "tests"]
exclude = ["vulture_whitelist.py"]
[dependency-groups]
dev = [
"httpx>=0.28.1",
]