Coverage for src / mcp_server_langgraph / __init__.py: 53%
130 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-03 00:43 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-03 00:43 +0000
1"""
2MCP Server with LangGraph.
4A production-ready MCP (Model Context Protocol) server built with LangGraph,
5featuring multi-LLM support, fine-grained authorization, and comprehensive observability.
6"""
8import sys
9import tomllib
10from pathlib import Path
11from typing import TYPE_CHECKING
13# TYPE_CHECKING imports satisfy static analysis (CodeQL, mypy) while keeping lazy loading
14# These imports only run during type checking, not at runtime
15if TYPE_CHECKING:
16 from mcp_server_langgraph.core.agent import agent_graph as agent_graph
17 from mcp_server_langgraph.observability.telemetry import tracer as tracer
19# Read version from pyproject.toml (single source of truth)
20try:
21 pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
22 with open(pyproject_path, "rb") as f:
23 pyproject_data = tomllib.load(f)
24 __version__ = pyproject_data["project"]["version"]
25except Exception:
26 # Fallback if reading fails
27 __version__ = "2.8.0"
29# Core exports (lightweight - eagerly imported)
30from mcp_server_langgraph.core.config import settings
32__all__ = [
33 "AgentState",
34 "AuthMiddleware",
35 "OpenFGAClient",
36 "__version__",
37 "agent_graph",
38 "create_llm_from_config",
39 "logger",
40 "metrics",
41 "settings",
42 "tracer",
43]
46def __getattr__(name: str): # type: ignore[no-untyped-def] # noqa: C901
47 """
48 Lazy import heavy dependencies on demand.
50 This prevents ModuleNotFoundError for users who don't have optional dependencies
51 installed. Heavy modules are only loaded when actually accessed.
53 Pattern:
54 import mcp_server_langgraph # ✓ Fast, no heavy deps loaded
55 from mcp_server_langgraph import settings # ✓ Fast, lightweight
56 from mcp_server_langgraph import AuthMiddleware # Loads FastAPI, OpenFGA only when needed
58 Raises:
59 AttributeError: If module attribute doesn't exist
60 """
61 # Heavy auth modules (require FastAPI, OpenFGA SDK)
62 if name == "AuthMiddleware":
63 from mcp_server_langgraph.auth.middleware import AuthMiddleware
65 return AuthMiddleware
66 elif name == "OpenFGAClient":
67 from mcp_server_langgraph.auth.openfga import OpenFGAClient
69 return OpenFGAClient
71 # Heavy agent modules (require LangGraph, LangChain)
72 elif name == "AgentState":
73 from mcp_server_langgraph.core.agent import AgentState
75 return AgentState
76 elif name == "agent_graph":
77 from mcp_server_langgraph.core.agent import agent_graph
79 return agent_graph
81 # Heavy LLM modules (require langchain_core, sentence_transformers, etc.)
82 elif name == "create_llm_from_config":
83 from mcp_server_langgraph.llm.factory import create_llm_from_config
85 return create_llm_from_config
87 # Observability modules (potentially heavy with OpenTelemetry)
88 elif name == "logger":
89 from mcp_server_langgraph.observability.telemetry import logger
91 return logger
92 elif name == "tracer":
93 from mcp_server_langgraph.observability.telemetry import tracer
95 return tracer
96 elif name == "metrics":
97 from mcp_server_langgraph.observability.telemetry import metrics
99 return metrics
100 elif name == "auth":
101 # Allow access to auth submodule for importlib.reload() scenarios
102 # Used in tests/regression/test_bearer_scheme_isolation.py
103 # Prevent recursion: if auth module is already being imported, return it from sys.modules
104 module_name = f"{__name__}.auth"
105 if module_name in sys.modules: 105 ↛ 108line 105 didn't jump to line 108 because the condition on line 105 was always true
106 return sys.modules[module_name]
108 import mcp_server_langgraph.auth as auth_module
110 return auth_module
111 elif name == "api":
112 # Allow access to api submodule for importlib.reload() scenarios
113 # Used in tests/regression/test_bearer_scheme_isolation.py
114 # Prevent recursion: if api module is already being imported, return it from sys.modules
115 module_name = f"{__name__}.api"
116 if module_name in sys.modules: 116 ↛ 119line 116 didn't jump to line 119 because the condition on line 116 was always true
117 return sys.modules[module_name]
119 import mcp_server_langgraph.api as api_module
121 return api_module
122 elif name == "health": 122 ↛ 126line 122 didn't jump to line 126 because the condition on line 122 was never true
123 # Allow access to health submodule for health check endpoints
124 # Used in tests/integration/test_health_check.py
125 # Prevent recursion: if health module is already being imported, return it from sys.modules
126 module_name = f"{__name__}.health"
127 if module_name in sys.modules:
128 return sys.modules[module_name]
130 import mcp_server_langgraph.health as health_module
132 return health_module
133 elif name == "tools": 133 ↛ 136line 133 didn't jump to line 136 because the condition on line 133 was never true
134 # Allow access to tools submodule for patch/mock scenarios in tests
135 # Prevent recursion: if tools module is already being imported, return it from sys.modules
136 module_name = f"{__name__}.tools"
137 if module_name in sys.modules:
138 return sys.modules[module_name]
140 import mcp_server_langgraph.tools as tools_module
142 return tools_module
143 elif name == "llm": 143 ↛ 145line 143 didn't jump to line 145 because the condition on line 143 was never true
144 # Allow access to llm submodule for tests (needed for Python 3.10 mock compatibility)
145 module_name = f"{__name__}.llm"
146 if module_name in sys.modules:
147 return sys.modules[module_name]
149 import mcp_server_langgraph.llm as llm_module
151 return llm_module
152 elif name == "core":
153 # Allow access to core submodule for tests (needed for Python 3.10 mock compatibility)
154 module_name = f"{__name__}.core"
155 if module_name in sys.modules: 155 ↛ 158line 155 didn't jump to line 158 because the condition on line 155 was always true
156 return sys.modules[module_name]
158 import mcp_server_langgraph.core as core_module
160 return core_module
161 elif name == "monitoring":
162 # Allow access to monitoring submodule for tests (needed for Python 3.10 mock compatibility)
163 module_name = f"{__name__}.monitoring"
164 if module_name in sys.modules: 164 ↛ 167line 164 didn't jump to line 167 because the condition on line 164 was always true
165 return sys.modules[module_name]
167 import mcp_server_langgraph.monitoring as monitoring_module
169 return monitoring_module
170 elif name == "app": 170 ↛ 172line 170 didn't jump to line 172 because the condition on line 170 was never true
171 # Allow access to app submodule for tests (needed for Python 3.10 mock compatibility)
172 module_name = f"{__name__}.app"
173 if module_name in sys.modules:
174 return sys.modules[module_name]
176 import mcp_server_langgraph.app as app_module
178 return app_module
179 elif name == "middleware": 179 ↛ 181line 179 didn't jump to line 181 because the condition on line 179 was never true
180 # Allow access to middleware submodule for tests (needed for Python 3.10 mock compatibility)
181 module_name = f"{__name__}.middleware"
182 if module_name in sys.modules:
183 return sys.modules[module_name]
185 import mcp_server_langgraph.middleware as middleware_module
187 return middleware_module
188 elif name == "execution": 188 ↛ 190line 188 didn't jump to line 190 because the condition on line 188 was never true
189 # Allow access to execution submodule for tests (needed for Python 3.10 mock compatibility)
190 module_name = f"{__name__}.execution"
191 if module_name in sys.modules:
192 return sys.modules[module_name]
194 import mcp_server_langgraph.execution as execution_module
196 return execution_module
197 elif name == "compliance":
198 # Allow access to compliance submodule for tests (needed for Python 3.10 mock compatibility)
199 module_name = f"{__name__}.compliance"
200 if module_name in sys.modules: 200 ↛ 203line 200 didn't jump to line 203 because the condition on line 200 was always true
201 return sys.modules[module_name]
203 import mcp_server_langgraph.compliance as compliance_module
205 return compliance_module
206 elif name == "schedulers": 206 ↛ 208line 206 didn't jump to line 208 because the condition on line 206 was never true
207 # Allow access to schedulers submodule for tests (needed for Python 3.10 mock compatibility)
208 module_name = f"{__name__}.schedulers"
209 if module_name in sys.modules:
210 return sys.modules[module_name]
212 import mcp_server_langgraph.schedulers as schedulers_module
214 return schedulers_module
215 elif name == "resilience": 215 ↛ 217line 215 didn't jump to line 217 because the condition on line 215 was never true
216 # Allow access to resilience submodule for resilience pattern tests
217 module_name = f"{__name__}.resilience"
218 if module_name in sys.modules:
219 return sys.modules[module_name]
221 import mcp_server_langgraph.resilience as resilience_module
223 return resilience_module
224 elif name == "mcp": 224 ↛ 226line 224 didn't jump to line 226 because the condition on line 224 was never true
225 # Allow access to mcp submodule for MCP server tests
226 module_name = f"{__name__}.mcp"
227 if module_name in sys.modules:
228 return sys.modules[module_name]
230 import mcp_server_langgraph.mcp as mcp_module
232 return mcp_module
233 elif name == "observability": 233 ↛ 235line 233 didn't jump to line 235 because the condition on line 233 was never true
234 # Allow access to observability submodule for telemetry tests
235 module_name = f"{__name__}.observability"
236 if module_name in sys.modules:
237 return sys.modules[module_name]
239 import mcp_server_langgraph.observability as observability_module
241 return observability_module
243 # Not found
244 msg = f"module {__name__!r} has no attribute {name!r}"
245 raise AttributeError(msg)