[project]
name = "onetool-mcp"
version = "1.0.0b1"
description = "One MCP, unlimited tools."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "GPL-3.0" }
authors = [{ name = "Gavin Las", email = "beycom99@gmail.com" }]
keywords = ["mcp", "mcp-server", "llm", "code-execution", "tools", "model-context-protocol", "token-efficiency", "context-rot", "agents"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
    "Typing :: Typed",
]
dependencies = [
    # === Core ===
    "fastmcp>=2.14.4",
    "httpx>=0.28.1",
    "loguru>=0.7.3",
    "mcp>=1.26.0",
    "pydantic>=2.12.5",
    "pydantic-settings>=2.12.0",
    "pyyaml>=6.0.3",
    "rich>=14.3.1",
    "typer>=0.21.1",
    "jinja2>=3.1.6",              # Snippets template engine
    "docstring-parser>=0.17",     # ot.help() and ot.tools() docstring parsing
    "aiofiles>=25.1.0",           # Message tool async file I/O
    "questionary>=2.1.1",         # bench interactive prompts

    # === LLM & Search ===
    "openai>=2.15.0",             # code_search embeddings, LLM tools
    "google-genai>=1.60.0",       # grounding_search (Gemini)

    # === Database ===
    "duckdb>=1.4.3",              # code_search, db tool
    "sqlalchemy>=2.0.46",         # db tool

    # === Web ===
    "trafilatura>=2.0.0",         # web_fetch content extraction
    "firecrawl>=4.13.4",          # firecrawl web scraping

    # === Documents ===
    "openpyxl>=3.1.5",            # excel, convert (Excel files)
    "pillow>=12.1.0",             # convert (PDF image handling)
    "pymupdf>=1.26.7",            # convert (PDF)
    "python-docx>=1.2.0",         # convert (Word)
    "python-pptx>=1.0.2",         # convert (PowerPoint)

]

[project.urls]
Homepage = "https://github.com/beycom/onetool"
Repository = "https://github.com/beycom/onetool"
Documentation = "https://onetool.beycom.online"
Issues = "https://github.com/beycom/onetool/issues"

[project.optional-dependencies]
# File tool safe deletion (moves to trash instead of permanent delete)
file = ["send2trash>=2.1.0"]

[project.scripts]
onetool = "onetool.cli:cli"
bench = "bench.cli:cli"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/ot", "src/ot_tools", "src/onetool", "src/bench"]

[dependency-groups]
docs = [
    "mkdocs-material>=9.7.1",
    "mkdocs-section-index>=0.3.10",
    "mkdocs-redirects>=1.2.2",
]
dev = [
    "mypy>=1.19.1",
    "pytest>=9.0.2",
    "pytest-asyncio>=1.3.0",
    "pytest-cov>=7.0.0",
    "pytest-sugar>=1.1.1",
    "ruff>=0.14.14",
    "types-aiofiles>=25.1.0.20251011",
    "types-pyyaml>=6.0.12.20250915",
]

[tool.uv]
default-groups = ["dev", "docs"]

[tool.ruff]
target-version = "py311"
line-length = 88
src = ["src", "tests"]
cache-dir = "tmp/.ruff_cache"
# Exclude Jinja2 templates (not valid Python)
extend-exclude = ["src/ot/config/defaults/tool_templates/*.py"]

[tool.ruff.lint]
select = [
    "E",      # pycodestyle errors
    "W",      # pycodestyle warnings
    "F",      # Pyflakes
    "I",      # isort
    "B",      # flake8-bugbear
    "C4",     # flake8-comprehensions
    "UP",     # pyupgrade
    "ARG",    # flake8-unused-arguments
    "SIM",    # flake8-simplify
    "TCH",    # flake8-type-checking
    "PTH",    # flake8-use-pathlib
    "RUF",    # Ruff-specific rules
]
ignore = [
    "E501",   # line too long (handled by formatter)
    "B008",   # function call in default argument (required by typer)
]

[tool.ruff.lint.isort]
known-first-party = ["ot", "ot_tools", "onetool", "bench"]

[tool.ruff.lint.per-file-ignores]
# CLI files need Path and CaseResult at runtime for typer type annotations and callbacks
# E402 in cli.py is intentional - atexit.register must run before importing pymupdf
"src/onetool/*.py" = ["TC001", "TCH003", "E402"]
"src/bench/*.py" = ["TC001", "TCH003"]
# Tools use namespace declarations before imports for tool discovery
"src/ot_tools/*.py" = ["E402"]
# Executor modules use Path/Callable at runtime for tool loading and worker management
"src/ot/executor/*.py" = ["TC003"]

[tool.mypy]
python_version = "3.11"
cache_dir = "tmp/.mypy_cache"
# Exclude Jinja2 templates (not valid Python)
exclude = ["src/ot/config/defaults/tool_templates/"]
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_configs = true
show_error_codes = true
files = ["src/ot"]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
addopts = "-v"
asyncio_mode = "auto"
cache_dir = "tmp/.pytest_cache"
markers = [
    # Speed tiers (required - pick one)
    "smoke: Quick sanity checks (<1s each)",
    "unit: Fast isolated tests (<1s each, no I/O)",
    "integration: End-to-end tests (may be slow)",
    "slow: Long-running tests (>10s)",

    # Component tags (required - pick one)
    "serve: MCP server tests",
    "bench: Benchmark harness tests",
    "core: Core library tests",
    "tools: Tool implementation tests",
    "pkg: Package tests",
    "spec: Specification tests",

    # Dependency tags (add as needed)
    "network: Requires network access",
    "api: Requires API keys",
    "docker: Requires Docker daemon",
]

[tool.deptry]
# Ignore internal packages (DEP001) - these are first-party, not external deps
per_rule_ignores = { DEP001 = ["ot", "ot_tools", "bench", "onetool", "anyio"] }
# Map package names where import name differs
[tool.deptry.package_module_name_map]
pyyaml = "yaml"
pillow = "PIL"
pydantic-settings = "pydantic_settings"
google-genai = "google"
pymupdf = "fitz"
python-docx = "docx"
python-pptx = "pptx"

[tool.coverage.html]
directory = "tmp/htmlcov"
