[build-system]
requires = ["poetry-core>=1.5.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "mcpstat"
version = "0.2.2"
description = "Usage tracking, analytics, and file logging for MCP (Model Context Protocol) servers"
authors = ["Vadim Bakhrenkov <hello@vadim.dev>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/tekkidev/mcpstat"
repository = "https://github.com/tekkidev/mcpstat"
keywords = [
"mcp",
"model-context-protocol",
"statistics",
"analytics",
"usage-tracking",
"logging",
"ai",
"llm",
"agent",
"mcp-server",
"tool-tracking",
"observability",
"token-tracking",
"latency-tracking",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: System :: Logging",
"Topic :: System :: Monitoring",
"Typing :: Typed",
"Framework :: AsyncIO",
]
packages = [{include = "mcpstat"}]
include = [
"CHANGELOG.md",
"CODE_OF_CONDUCT.md",
"LICENSE",
"README.md",
"llms.txt",
"docs/**/*.md",
"examples/**/*.py",
"tests/**/*",
"mcpstat/py.typed",
]
exclude = [
"**/__pycache__",
"**/*.pyc",
"**/*.egg-info",
"**/*.sqlite",
"**/*.sqlite-wal",
"**/*.sqlite-shm",
"**/*.log",
]
[tool.poetry.urls]
Changelog = "https://github.com/tekkidev/mcpstat/blob/main/CHANGELOG.md"
Issues = "https://github.com/tekkidev/mcpstat/issues"
Author = "https://vadim.dev"
[tool.poetry.dependencies]
python = ">=3.10,<4.0"
# Optional dependencies for extras
mcp = {version = ">=1.0.0", optional = true}
pytest = {version = ">=7.0", optional = true}
pytest-asyncio = {version = ">=0.21", optional = true}
pytest-cov = {version = ">=4.0", optional = true}
mypy = {version = ">=1.0", optional = true}
ruff = {version = ">=0.1", optional = true}
[tool.poetry.extras]
mcp = ["mcp"]
dev = ["pytest", "pytest-asyncio", "pytest-cov", "mypy", "ruff"]
all = ["mcp", "pytest", "pytest-asyncio", "pytest-cov", "mypy", "ruff"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
asyncio_default_fixture_loop_scope = "function"
addopts = "-v --cov=mcpstat --cov-branch --cov-report=term-missing"
filterwarnings = [
"error",
"ignore::DeprecationWarning",
]
[tool.coverage.run]
source = ["mcpstat"]
branch = true
relative_files = true
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/.venv/*",
"*/venv/*",
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"if __name__ == .__main__.:",
"@abstractmethod",
]
show_missing = true
[tool.mypy]
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
strict = true
exclude = ["examples/", "tests/"]
[tool.ruff]
target-version = "py312"
line-length = 100
[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
]
ignore = [
"E501", # line too long (handled by formatter)
"B008", # function-call-in-default-argument
"UP017", # datetime.UTC - requires Python 3.11+, we support 3.10
]
[tool.ruff.lint.isort]
known-first-party = ["mcpstat"]
[tool.bandit]
exclude_dirs = ["tests", "examples"]
skips = ["B101"] # assert_used - acceptable in tests