[project] name = "fastmcp" dynamic = ["version"] description = "The fast, Pythonic way to build MCP servers and clients." authors = [{ name = "Jeremiah Lowin" }] dependencies = [ "python-dotenv>=1.1.0", "exceptiongroup>=1.2.2", "httpx>=0.28.1,<1.0", "mcp>=1.24.0,<2.0", "openapi-pydantic>=0.5.1", "opentelemetry-api>=1.20.0", "packaging>=24.0", "platformdirs>=4.0.0", "rich>=13.9.4", "cyclopts>=4.0.0", "authlib>=1.6.11", "pydantic[email]>=2.11.7", "pyyaml>=6.0,<7.0", "pyperclip>=1.9.0", "py-key-value-aio[filetree,keyring,memory]>=0.4.4,<0.5.0", "python-multipart>=0.0.26", "uvicorn>=0.35", "websockets>=15.0.1", "jsonschema-path>=0.3.4", "jsonref>=1.1.0", "uncalled-for>=0.2.0", "watchfiles>=1.0.0", "griffelib>=2.0.0", ] requires-python = ">=3.10" readme = "README.md" license = "Apache-2.0" keywords = [ "mcp", "mcp server", "mcp client", "model context protocol", "fastmcp", "llm", "agent", ] classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Typing :: Typed", ] [project.optional-dependencies] anthropic = ["anthropic>=0.48.0"] apps = ["prefab-ui>=0.18.0"] # PyJWT floor: transitive via msal; CVE-2026-32597 affects <= 2.11.0 azure = ["azure-identity>=1.16.0", "PyJWT>=2.12.0"] code-mode = ["pydantic-monty==0.0.14"] gemini = ["google-genai>=1.18.0"] openai = ["openai>=1.102.0"] tasks = ["pydocket>=0.19.1"] [dependency-groups] dev = [ "dirty-equals>=0.9.0", "fastmcp[anthropic,apps,azure,code-mode,gemini,openai,tasks]", # add optional dependencies for fastmcp dev "fastapi>=0.115.12", "opentelemetry-sdk>=1.20.0", "inline-snapshot[dirty-equals]>=0.27.2", "ipython>=8.12.3", "pdbpp>=0.11.7", "psutil>=7.0.0", "pyinstrument>=5.0.2", "pyperclip>=1.9.0", "pytest>=8.3.3", "pytest-asyncio>=1.2.0", "pytest-cov>=6.1.1", "pytest-env>=1.1.5", "pytest-flakefinder>=1.1.0", "pytest-httpx>=0.35.0", "pytest-report>=0.2.1", "pytest-retry>=1.7.0", "pytest-timeout>=2.4.0", "pytest-xdist>=3.6.1", "ruff>=0.12.8", "ty>=0.0.29", "prek>=0.2.12", "loq>=0.1.0a3", "opentelemetry-exporter-otlp-proto-grpc>=1.39.0", "pytest-examples>=0.0.18", ] [project.scripts] fastmcp = "fastmcp.cli:app" [project.urls] Homepage = "https://gofastmcp.com" Repository = "https://github.com/PrefectHQ/fastmcp" Documentation = "https://gofastmcp.com" [build-system] requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"] build-backend = "hatchling.build" [tool.hatch.version] source = "uv-dynamic-versioning" [tool.hatch.metadata] allow-direct-references = true [tool.uv-dynamic-versioning] vcs = "git" style = "pep440" bump = true fallback-version = "0.0.0" [tool.pytest.ini_options] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" # filterwarnings = ["error::DeprecationWarning"] filterwarnings = [ # Suppress OAuth in-memory token storage warnings in tests # Tests intentionally use ephemeral storage; this warning is for end users "ignore:Using in-memory token storage:UserWarning", # Treat unawaited coroutine warnings as errors - these are almost always bugs "error:coroutine .* was never awaited:RuntimeWarning", "error:Exception ignored in.*coroutine:pytest.PytestUnraisableExceptionWarning", ] timeout = 5 env = [ "FASTMCP_TEST_MODE=1", 'D:FASTMCP_LOG_LEVEL=DEBUG', 'D:FASTMCP_ENABLE_RICH_TRACEBACKS=0', ] markers = [ "integration: marks tests as integration tests (deselect with '-m \"not integration\"')", "client_process: marks tests that spawn client processes via stdio transport. These can create issues when run in the same CI environment as other subprocess-based tests.", "conformance: marks MCP conformance tests (require Node.js/npx)", ] # Automatically mark all tests in integration_tests folder pythonpath = ["."] testpaths = ["tests"] python_files = ["test_*.py", "*_test.py"] python_classes = ["Test*"] python_functions = ["test_*"] addopts = ["--inline-snapshot=disable"] [tool.ty.src] include = ["src", "tests"] exclude = ["**/node_modules", "**/__pycache__", ".venv", ".git", "dist"] [tool.ty.environment] python-version = "3.10" [tool.ty.rules] # NOTE: ty currently doesn't support type narrowing with isinstance() on unions # See: https://github.com/astral-sh/ty/issues/122 and https://github.com/astral-sh/ty/issues/1113 # Some code uses `# ty: ignore[invalid-argument-type]` for this limitation. # TODO: Remove these ignores once ty supports union narrowing # Promote rules from default-ignore to warn (becomes error via error-on-warning) division-by-zero = "warn" possibly-missing-attribute = "warn" possibly-missing-import = "warn" possibly-unresolved-reference = "warn" unsupported-dynamic-base = "warn" unsupported-operator = "warn" unused-ignore-comment = "warn" [tool.ty.terminal] error-on-warning = true [tool.ruff.lint] fixable = ["ALL"] ignore = [ "COM812", "PERF203", # try-except in loop — all existing hits are intentional (retry loops, error skipping) "PLR0913", # Too many arguments, MCP Servers have a lot of arguments, OKAY?! "SIM102", # Dont require combining if statements ] extend-select = [ "B", # flake8-bugbear: Catches actual bugs like mutable default arguments "C4", # flake8-comprehensions: More efficient/readable comprehensions "DTZ", # flake8-datetimez: Enforce timezone-aware datetime usage "ERA", # eradicate: Detect commented-out code "FA", # flake8-future-annotations: Enforce from __future__ import annotations "FLY", # flynt: Static string joins that should be f-strings "I", # isort: Import sorting "INP", # flake8-no-pep420: Require __init__.py in namespace packages "ISC", # flake8-implicit-str-concat: Prevent accidental string concatenation "LOG", # flake8-logging: Catches logging module misuse "PERF", # perflint: Performance anti-patterns (unnecessary copies, allocations) "PIE", # flake8-pie: More idiomatic Python code "PLE", # pylint-error: Catches actual errors (invalid operations, syntax issues) "RSE", # flake8-raise: Unnecessary parentheses on raise "RUF", # Ruff-specific: Modern best practices unique to Ruff "SIM", # flake8-simplify: Simplifies verbose code patterns "SLOT", # flake8-slots: Enforce __slots__ where applicable "T20", # flake8-print: Catch accidental print() in library code "TID", # flake8-tidy-imports: Banned imports and relative import enforcement "UP", # pyupgrade: Modernize syntax for newer Python versions ] [tool.ruff.lint.isort] known-first-party = ["fastmcp"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401", "I001", "RUF013"] # allow imports not at the top of the file "src/fastmcp/__init__.py" = ["E402"] # CLI and example code legitimately uses print() for user-facing output "src/fastmcp/cli/**.py" = ["T20"] "src/fastmcp/client/oauth_callback.py" = ["T20"] "src/fastmcp/contrib/**/example.py" = ["T20"] "!src/**.py" = [ # Only enforce extended ruff rules for code in src/ "B", # flake8-bugbear "C4", # flake8-comprehensions "DTZ", # flake8-datetimez "ERA", # eradicate "FA", # flake8-future-annotations "FLY", # flynt "INP", # flake8-no-pep420 "ISC", # flake8-implicit-str-concat "LOG", # flake8-logging "PERF", # perflint "PIE", # flake8-pie "PLE", # pylint-error "RSE", # flake8-raise "RUF", # Ruff-specific "SIM", # flake8-simplify "SLOT", # flake8-slots "T20", # flake8-print "TID", # flake8-tidy-imports ] [tool.codespell] ignore-words-list = "asend,shttp,te"