[project]
name = "mcp-server-ns-bridge"
version = "0.1.1"
description = "MCP server for Netherlands NS trains - route planning and pricing"
readme = "README.md"
requires-python = ">=3.11"
authors = [
{ name = "Eze Godoy", email = "contact@ezegodoy.com" }
]
license = { text = "MIT" }
dependencies = [
"mcp[cli]>=1.1.2",
"httpx>=0.27.0",
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"python-dotenv>=1.0.0",
]
[project.optional-dependencies]
dev = [
# Testing
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"pytest-cov>=4.1.0",
"pytest-httpx>=0.30.0",
# Code quality
"ruff>=0.8.0",
"mypy>=1.8.0",
"pre-commit>=4.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/ns_bridge"]
# Ruff configuration - modern, fast Python linter and formatter
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
# Enable recommended rules + additional helpful ones
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"B", # flake8-bugbear
"A", # flake8-builtins
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T10", # flake8-debugger
"EM", # flake8-errmsg
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"PIE", # flake8-pie
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"ARG", # flake8-unused-arguments
"PTH", # flake8-use-pathlib
"PL", # pylint
"TRY", # tryceratops
"RUF", # ruff-specific rules
]
ignore = [
"E501", # line too long (handled by formatter)
"ISC001", # implicit string concatenation (conflicts with formatter)
"PLR0913", # too many arguments
"TRY003", # avoid specifying long messages outside exception class
"TRY300", # consider moving statement to else block (overly pedantic)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["PLR2004", "S101", "ARG001"]
"src/ns_bridge/server.py" = ["PLR0912", "PLR0915"] # Complex search_trips function
# MyPy configuration - static type checker
[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
disallow_untyped_decorators = false
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
extra_checks = true
[[tool.mypy.overrides]]
module = "mcp.*"
ignore_missing_imports = true
# Pytest configuration
[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"--showlocals",
"--cov=src",
"--cov-report=term-missing",
"--cov-report=html",
]
testpaths = ["tests"]
pythonpath = ["src"]
asyncio_mode = "auto"
# Coverage configuration
[tool.coverage.run]
source = ["src"]
branch = true
omit = [
"*/tests/*",
"*/__pycache__/*",
]
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
# Bandit configuration - security linting
[tool.bandit]
exclude_dirs = ["tests", ".venv"]
skips = ["B101"] # Skip assert_used in tests
# Python Semantic Release configuration
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_variables = []
branch = "main"
changelog_file = "CHANGELOG.md"
build_command = "pip install build && python -m build"
dist_path = "dist/"
upload_to_release = true
upload_to_pypi = true
commit_parser = "conventional"
commit_author = "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
major_on_zero = true # Allow 0.x.x -> 1.0.0 bump
[tool.semantic_release.commit_parser_options]
allowed_tags = ["feat", "fix", "perf", "refactor", "docs", "chore", "test", "ci", "build"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf", "refactor", "docs", "chore", "test", "ci", "build"]
[tool.semantic_release.changelog]
exclude_commit_patterns = []
[tool.semantic_release.publish]
dist_glob_patterns = ["dist/*"]
upload_to_vcs_release = true