[project] name = "ms-graph-mcp" version = "0.3.0" description = "Microsoft Graph MCP server — 85 tools across mail, calendar, meetings, Teams chat, files, search, people, directory, tasks and OneNote, over stdio or Streamable HTTP." requires-python = ">=3.12" readme = "README.md" license = "MIT" license-files = ["LICENSE"] authors = [{ name = "Nitin Singh", email = "nitin27may@gmail.com" }] keywords = ["mcp", "model-context-protocol", "microsoft-graph", "microsoft-365", "entra", "azure"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries", "Typing :: Typed", ] dependencies = [ # Floors are set by mcp 2.0's own requirements, not by what this package uses. "pydantic>=2.12.0,<3.0", "pydantic-settings>=2.15.0", # The Graph client's HTTP stack, and the same distribution mcp 2.0 runs on — # one HTTP stack in the process, not two. The floor is mcp's own (`httpx2>=2.5.0`). "httpx2>=2.5.0,<3.0", # Entra JWT validation (JWKS, RS256) for the HTTP transport's auth middleware. "pyjwt[crypto]>=2.13.0,<3.0", # OTEL tracing spans in the Graph client. API only — these are no-ops unless the # host process configures an SDK, so a standalone run needs no exporter. "opentelemetry-api>=1.28.0", # MCP server + transports, speaking the 2026-07-28 protocol revision. "mcp>=2.0.0,<3.0", # mcp 2.0 requires starlette 1.x. BaseHTTPMiddleware, which the auth layer # subclasses, is unchanged across that major. "starlette>=1.6.0,<2.0", "uvicorn[standard]>=0.52.3", "anyio>=4.0", # Confidential-client on-behalf-of / client-credentials exchange. Only used when # the server performs its own OBO (GRAPH_MCP_DOES_OBO=true); small and pure-Python. "msal>=1.28.0", # NB: the Graph client is raw httpx2 by design — msgraph-sdk and azure-identity are # intentionally NOT dependencies. ] [project.urls] Homepage = "https://github.com/nitin27may/ms-graph-mcp" Repository = "https://github.com/nitin27may/ms-graph-mcp" Issues = "https://github.com/nitin27may/ms-graph-mcp/issues" [project.scripts] ms-graph-mcp = "ms_graph_mcp.stdio:main" # stdio transport (Claude Desktop, Claude Code, …) ms-graph-mcp-http = "ms_graph_mcp.app:run" # Streamable-HTTP transport [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/ms_graph_mcp"] [dependency-groups] dev = [ "packaging>=26.3", "pytest>=9.0.2", "pytest-asyncio>=0.23", "ruff>=0.16.3", ] [tool.ruff] line-length = 100 target-version = "py312" fix = true [tool.ruff.lint] select = ["E", "F", "I", "UP"] # E501: long lines are intentional in places (URLs, prompts, log messages). `ruff format` # already handles anything genuinely reformattable. ignore = ["E501"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] # importlib mode keeps same-named test modules in different directories distinct # (tests/test_config.py vs tests/entra/test_config.py) without needing __init__.py. addopts = "--import-mode=importlib"