mcp-hangar 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. mcp_hangar/__init__.py +139 -0
  2. mcp_hangar/application/__init__.py +1 -0
  3. mcp_hangar/application/commands/__init__.py +67 -0
  4. mcp_hangar/application/commands/auth_commands.py +118 -0
  5. mcp_hangar/application/commands/auth_handlers.py +296 -0
  6. mcp_hangar/application/commands/commands.py +59 -0
  7. mcp_hangar/application/commands/handlers.py +189 -0
  8. mcp_hangar/application/discovery/__init__.py +21 -0
  9. mcp_hangar/application/discovery/discovery_metrics.py +283 -0
  10. mcp_hangar/application/discovery/discovery_orchestrator.py +497 -0
  11. mcp_hangar/application/discovery/lifecycle_manager.py +315 -0
  12. mcp_hangar/application/discovery/security_validator.py +414 -0
  13. mcp_hangar/application/event_handlers/__init__.py +50 -0
  14. mcp_hangar/application/event_handlers/alert_handler.py +191 -0
  15. mcp_hangar/application/event_handlers/audit_handler.py +203 -0
  16. mcp_hangar/application/event_handlers/knowledge_base_handler.py +120 -0
  17. mcp_hangar/application/event_handlers/logging_handler.py +69 -0
  18. mcp_hangar/application/event_handlers/metrics_handler.py +152 -0
  19. mcp_hangar/application/event_handlers/persistent_audit_store.py +217 -0
  20. mcp_hangar/application/event_handlers/security_handler.py +604 -0
  21. mcp_hangar/application/mcp/tooling.py +158 -0
  22. mcp_hangar/application/ports/__init__.py +9 -0
  23. mcp_hangar/application/ports/observability.py +237 -0
  24. mcp_hangar/application/queries/__init__.py +52 -0
  25. mcp_hangar/application/queries/auth_handlers.py +237 -0
  26. mcp_hangar/application/queries/auth_queries.py +118 -0
  27. mcp_hangar/application/queries/handlers.py +227 -0
  28. mcp_hangar/application/read_models/__init__.py +11 -0
  29. mcp_hangar/application/read_models/provider_views.py +139 -0
  30. mcp_hangar/application/sagas/__init__.py +11 -0
  31. mcp_hangar/application/sagas/group_rebalance_saga.py +137 -0
  32. mcp_hangar/application/sagas/provider_failover_saga.py +266 -0
  33. mcp_hangar/application/sagas/provider_recovery_saga.py +172 -0
  34. mcp_hangar/application/services/__init__.py +9 -0
  35. mcp_hangar/application/services/provider_service.py +208 -0
  36. mcp_hangar/application/services/traced_provider_service.py +211 -0
  37. mcp_hangar/bootstrap/runtime.py +328 -0
  38. mcp_hangar/context.py +178 -0
  39. mcp_hangar/domain/__init__.py +117 -0
  40. mcp_hangar/domain/contracts/__init__.py +57 -0
  41. mcp_hangar/domain/contracts/authentication.py +225 -0
  42. mcp_hangar/domain/contracts/authorization.py +229 -0
  43. mcp_hangar/domain/contracts/event_store.py +178 -0
  44. mcp_hangar/domain/contracts/metrics_publisher.py +59 -0
  45. mcp_hangar/domain/contracts/persistence.py +383 -0
  46. mcp_hangar/domain/contracts/provider_runtime.py +146 -0
  47. mcp_hangar/domain/discovery/__init__.py +20 -0
  48. mcp_hangar/domain/discovery/conflict_resolver.py +267 -0
  49. mcp_hangar/domain/discovery/discovered_provider.py +185 -0
  50. mcp_hangar/domain/discovery/discovery_service.py +412 -0
  51. mcp_hangar/domain/discovery/discovery_source.py +192 -0
  52. mcp_hangar/domain/events.py +433 -0
  53. mcp_hangar/domain/exceptions.py +525 -0
  54. mcp_hangar/domain/model/__init__.py +70 -0
  55. mcp_hangar/domain/model/aggregate.py +58 -0
  56. mcp_hangar/domain/model/circuit_breaker.py +152 -0
  57. mcp_hangar/domain/model/event_sourced_api_key.py +413 -0
  58. mcp_hangar/domain/model/event_sourced_provider.py +423 -0
  59. mcp_hangar/domain/model/event_sourced_role_assignment.py +268 -0
  60. mcp_hangar/domain/model/health_tracker.py +183 -0
  61. mcp_hangar/domain/model/load_balancer.py +185 -0
  62. mcp_hangar/domain/model/provider.py +810 -0
  63. mcp_hangar/domain/model/provider_group.py +656 -0
  64. mcp_hangar/domain/model/tool_catalog.py +105 -0
  65. mcp_hangar/domain/policies/__init__.py +19 -0
  66. mcp_hangar/domain/policies/provider_health.py +187 -0
  67. mcp_hangar/domain/repository.py +249 -0
  68. mcp_hangar/domain/security/__init__.py +85 -0
  69. mcp_hangar/domain/security/input_validator.py +710 -0
  70. mcp_hangar/domain/security/rate_limiter.py +387 -0
  71. mcp_hangar/domain/security/roles.py +237 -0
  72. mcp_hangar/domain/security/sanitizer.py +387 -0
  73. mcp_hangar/domain/security/secrets.py +501 -0
  74. mcp_hangar/domain/services/__init__.py +20 -0
  75. mcp_hangar/domain/services/audit_service.py +376 -0
  76. mcp_hangar/domain/services/image_builder.py +328 -0
  77. mcp_hangar/domain/services/provider_launcher.py +1046 -0
  78. mcp_hangar/domain/value_objects.py +1138 -0
  79. mcp_hangar/errors.py +818 -0
  80. mcp_hangar/fastmcp_server.py +1105 -0
  81. mcp_hangar/gc.py +134 -0
  82. mcp_hangar/infrastructure/__init__.py +79 -0
  83. mcp_hangar/infrastructure/async_executor.py +133 -0
  84. mcp_hangar/infrastructure/auth/__init__.py +37 -0
  85. mcp_hangar/infrastructure/auth/api_key_authenticator.py +388 -0
  86. mcp_hangar/infrastructure/auth/event_sourced_store.py +567 -0
  87. mcp_hangar/infrastructure/auth/jwt_authenticator.py +360 -0
  88. mcp_hangar/infrastructure/auth/middleware.py +340 -0
  89. mcp_hangar/infrastructure/auth/opa_authorizer.py +243 -0
  90. mcp_hangar/infrastructure/auth/postgres_store.py +659 -0
  91. mcp_hangar/infrastructure/auth/projections.py +366 -0
  92. mcp_hangar/infrastructure/auth/rate_limiter.py +311 -0
  93. mcp_hangar/infrastructure/auth/rbac_authorizer.py +323 -0
  94. mcp_hangar/infrastructure/auth/sqlite_store.py +624 -0
  95. mcp_hangar/infrastructure/command_bus.py +112 -0
  96. mcp_hangar/infrastructure/discovery/__init__.py +110 -0
  97. mcp_hangar/infrastructure/discovery/docker_source.py +289 -0
  98. mcp_hangar/infrastructure/discovery/entrypoint_source.py +249 -0
  99. mcp_hangar/infrastructure/discovery/filesystem_source.py +383 -0
  100. mcp_hangar/infrastructure/discovery/kubernetes_source.py +247 -0
  101. mcp_hangar/infrastructure/event_bus.py +260 -0
  102. mcp_hangar/infrastructure/event_sourced_repository.py +443 -0
  103. mcp_hangar/infrastructure/event_store.py +396 -0
  104. mcp_hangar/infrastructure/knowledge_base/__init__.py +259 -0
  105. mcp_hangar/infrastructure/knowledge_base/contracts.py +202 -0
  106. mcp_hangar/infrastructure/knowledge_base/memory.py +177 -0
  107. mcp_hangar/infrastructure/knowledge_base/postgres.py +545 -0
  108. mcp_hangar/infrastructure/knowledge_base/sqlite.py +513 -0
  109. mcp_hangar/infrastructure/metrics_publisher.py +36 -0
  110. mcp_hangar/infrastructure/observability/__init__.py +10 -0
  111. mcp_hangar/infrastructure/observability/langfuse_adapter.py +534 -0
  112. mcp_hangar/infrastructure/persistence/__init__.py +33 -0
  113. mcp_hangar/infrastructure/persistence/audit_repository.py +371 -0
  114. mcp_hangar/infrastructure/persistence/config_repository.py +398 -0
  115. mcp_hangar/infrastructure/persistence/database.py +333 -0
  116. mcp_hangar/infrastructure/persistence/database_common.py +330 -0
  117. mcp_hangar/infrastructure/persistence/event_serializer.py +280 -0
  118. mcp_hangar/infrastructure/persistence/event_upcaster.py +166 -0
  119. mcp_hangar/infrastructure/persistence/in_memory_event_store.py +150 -0
  120. mcp_hangar/infrastructure/persistence/recovery_service.py +312 -0
  121. mcp_hangar/infrastructure/persistence/sqlite_event_store.py +386 -0
  122. mcp_hangar/infrastructure/persistence/unit_of_work.py +409 -0
  123. mcp_hangar/infrastructure/persistence/upcasters/README.md +13 -0
  124. mcp_hangar/infrastructure/persistence/upcasters/__init__.py +7 -0
  125. mcp_hangar/infrastructure/query_bus.py +153 -0
  126. mcp_hangar/infrastructure/saga_manager.py +401 -0
  127. mcp_hangar/logging_config.py +209 -0
  128. mcp_hangar/metrics.py +1007 -0
  129. mcp_hangar/models.py +31 -0
  130. mcp_hangar/observability/__init__.py +54 -0
  131. mcp_hangar/observability/health.py +487 -0
  132. mcp_hangar/observability/metrics.py +319 -0
  133. mcp_hangar/observability/tracing.py +433 -0
  134. mcp_hangar/progress.py +542 -0
  135. mcp_hangar/retry.py +613 -0
  136. mcp_hangar/server/__init__.py +120 -0
  137. mcp_hangar/server/__main__.py +6 -0
  138. mcp_hangar/server/auth_bootstrap.py +340 -0
  139. mcp_hangar/server/auth_cli.py +335 -0
  140. mcp_hangar/server/auth_config.py +305 -0
  141. mcp_hangar/server/bootstrap.py +735 -0
  142. mcp_hangar/server/cli.py +161 -0
  143. mcp_hangar/server/config.py +224 -0
  144. mcp_hangar/server/context.py +215 -0
  145. mcp_hangar/server/http_auth_middleware.py +165 -0
  146. mcp_hangar/server/lifecycle.py +467 -0
  147. mcp_hangar/server/state.py +117 -0
  148. mcp_hangar/server/tools/__init__.py +16 -0
  149. mcp_hangar/server/tools/discovery.py +186 -0
  150. mcp_hangar/server/tools/groups.py +75 -0
  151. mcp_hangar/server/tools/health.py +301 -0
  152. mcp_hangar/server/tools/provider.py +939 -0
  153. mcp_hangar/server/tools/registry.py +320 -0
  154. mcp_hangar/server/validation.py +113 -0
  155. mcp_hangar/stdio_client.py +229 -0
  156. mcp_hangar-0.2.0.dist-info/METADATA +347 -0
  157. mcp_hangar-0.2.0.dist-info/RECORD +160 -0
  158. mcp_hangar-0.2.0.dist-info/WHEEL +4 -0
  159. mcp_hangar-0.2.0.dist-info/entry_points.txt +2 -0
  160. mcp_hangar-0.2.0.dist-info/licenses/LICENSE +21 -0
mcp_hangar/gc.py ADDED
@@ -0,0 +1,134 @@
1
+ """Background workers for garbage collection and health checks."""
2
+
3
+ import threading
4
+ import time
5
+ from typing import Any, Literal, Optional
6
+
7
+ from .domain.contracts.provider_runtime import normalize_state_to_str, ProviderMapping, ProviderRuntime
8
+ from .infrastructure.event_bus import get_event_bus
9
+ from .logging_config import get_logger
10
+ from .metrics import observe_health_check, record_error, record_gc_cycle, record_provider_stop
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ class BackgroundWorker:
16
+ """Generic background worker for GC and health checks.
17
+
18
+ Expects provider storage that supports `.items()` (dict-like) returning
19
+ `(provider_id, provider)` pairs where `provider` satisfies the `ProviderRuntime`
20
+ contract.
21
+
22
+ Works with:
23
+ - Provider aggregates
24
+ - backward-compatibility wrappers (as long as they implement the contract)
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ providers: ProviderMapping,
30
+ interval_s: int = 10,
31
+ task: Literal["gc", "health_check"] = "gc",
32
+ event_bus: Optional[Any] = None,
33
+ ):
34
+ """
35
+ Initialize background worker.
36
+
37
+ Args:
38
+ providers: Dict-like mapping (provider_id -> ProviderRuntime).
39
+ interval_s: Interval between runs in seconds.
40
+ task: Task type - either "gc" (garbage collection) or "health_check".
41
+ event_bus: Optional event bus for publishing events (uses global if not provided).
42
+ """
43
+ self.providers: ProviderMapping = providers
44
+ self.interval_s = interval_s
45
+ self.task = task
46
+ self._event_bus = event_bus or get_event_bus()
47
+ self.thread = threading.Thread(target=self._loop, daemon=True, name=f"worker-{task}")
48
+ self.running = False
49
+
50
+ def start(self):
51
+ """Start the background worker thread."""
52
+ if self.running:
53
+ logger.warning("background_worker_already_running", task=self.task)
54
+ return
55
+
56
+ self.running = True
57
+ self.thread.start()
58
+ logger.info("background_worker_started", task=self.task, interval_s=self.interval_s)
59
+
60
+ def stop(self):
61
+ """Stop the background worker thread."""
62
+ self.running = False
63
+ logger.info("background_worker_stopped", task=self.task)
64
+
65
+ def _publish_events(self, provider: ProviderRuntime) -> None:
66
+ """Publish all collected events from a provider.
67
+
68
+ ProviderRuntime is expected to support event collection.
69
+ """
70
+ for event in provider.collect_events():
71
+ try:
72
+ self._event_bus.publish(event)
73
+ except Exception:
74
+ logger.exception("event_publish_failed")
75
+
76
+ def _loop(self):
77
+ """Main worker loop."""
78
+ while self.running:
79
+ time.sleep(self.interval_s)
80
+
81
+ start_time = time.perf_counter()
82
+ gc_collected = {"idle": 0, "dead": 0}
83
+
84
+ # Get snapshot of providers to avoid holding mapping lock (if any)
85
+ providers_snapshot = list(self.providers.items())
86
+
87
+ for provider_id, provider in providers_snapshot:
88
+ try:
89
+ if self.task == "gc":
90
+ # Garbage collection - shutdown idle providers
91
+ if provider.maybe_shutdown_idle():
92
+ logger.info("gc_shutdown", provider_id=provider_id)
93
+ gc_collected["idle"] += 1
94
+ record_provider_stop(provider_id, "idle")
95
+
96
+ elif self.task == "health_check":
97
+ # Determine whether provider is cold (not started yet)
98
+ state_str = normalize_state_to_str(provider.state)
99
+ is_cold = state_str == "cold"
100
+
101
+ # Active health check
102
+ hc_start = time.perf_counter()
103
+ is_healthy = provider.health_check()
104
+ hc_duration = time.perf_counter() - hc_start
105
+
106
+ consecutive = int(getattr(provider.health, "consecutive_failures", 0))
107
+
108
+ observe_health_check(
109
+ provider=provider_id,
110
+ duration=hc_duration,
111
+ healthy=is_healthy,
112
+ is_cold=is_cold,
113
+ consecutive_failures=consecutive,
114
+ )
115
+
116
+ if not is_healthy and not is_cold:
117
+ logger.warning("health_check_unhealthy", provider_id=provider_id)
118
+
119
+ # Publish any collected events
120
+ self._publish_events(provider)
121
+
122
+ except Exception as e:
123
+ record_error("gc", type(e).__name__)
124
+ logger.exception(
125
+ "background_task_failed",
126
+ provider_id=provider_id,
127
+ task=self.task,
128
+ error=str(e),
129
+ )
130
+
131
+ # Record GC cycle metrics
132
+ if self.task == "gc":
133
+ duration = time.perf_counter() - start_time
134
+ record_gc_cycle(duration, gc_collected)
@@ -0,0 +1,79 @@
1
+ """Infrastructure layer - Technical implementations.
2
+
3
+ This package provides infrastructure components for:
4
+ - Command Bus: CQRS command dispatching
5
+ - Query Bus: CQRS query dispatching
6
+ - Event Bus: Publish/subscribe for domain events
7
+ - Event Store: Append-only event persistence
8
+ - Event Sourced Repository: Provider persistence via event sourcing
9
+ - Saga Manager: Long-running business process orchestration
10
+
11
+ Note: Command classes (StartProviderCommand, etc.) have been moved to
12
+ application.commands to maintain proper layer separation.
13
+ """
14
+
15
+ from .command_bus import CommandBus, CommandHandler, get_command_bus, reset_command_bus
16
+ from .event_bus import EventBus, EventHandler, get_event_bus, reset_event_bus
17
+ from .event_sourced_repository import EventSourcedProviderRepository, ProviderConfigStore
18
+ from .event_store import (
19
+ ConcurrencyError,
20
+ EventStore,
21
+ EventStoreSnapshot,
22
+ get_event_store,
23
+ InMemoryEventStore,
24
+ StoredEvent,
25
+ )
26
+ from .query_bus import (
27
+ get_query_bus,
28
+ GetProviderHealthQuery,
29
+ GetProviderQuery,
30
+ GetProviderToolsQuery,
31
+ GetSystemMetricsQuery,
32
+ ListProvidersQuery,
33
+ Query,
34
+ QueryBus,
35
+ QueryHandler,
36
+ reset_query_bus,
37
+ )
38
+ from .saga_manager import get_saga_manager, Saga, SagaContext, SagaManager, SagaState, SagaStep
39
+
40
+ __all__ = [
41
+ # Command Bus
42
+ "CommandBus",
43
+ "CommandHandler",
44
+ "get_command_bus",
45
+ "reset_command_bus",
46
+ # Event Bus
47
+ "EventBus",
48
+ "EventHandler",
49
+ "get_event_bus",
50
+ "reset_event_bus",
51
+ # Query Bus
52
+ "Query",
53
+ "QueryBus",
54
+ "QueryHandler",
55
+ "ListProvidersQuery",
56
+ "GetProviderQuery",
57
+ "GetProviderToolsQuery",
58
+ "GetProviderHealthQuery",
59
+ "GetSystemMetricsQuery",
60
+ "get_query_bus",
61
+ "reset_query_bus",
62
+ # Event Store
63
+ "EventStore",
64
+ "InMemoryEventStore",
65
+ "StoredEvent",
66
+ "EventStoreSnapshot",
67
+ "ConcurrencyError",
68
+ "get_event_store",
69
+ # Event Sourced Repository
70
+ "EventSourcedProviderRepository",
71
+ "ProviderConfigStore",
72
+ # Saga
73
+ "Saga",
74
+ "SagaManager",
75
+ "SagaState",
76
+ "SagaStep",
77
+ "SagaContext",
78
+ "get_saga_manager",
79
+ ]
@@ -0,0 +1,133 @@
1
+ """Async executor for running async operations from sync context.
2
+
3
+ This module provides a clean way to execute async coroutines from
4
+ synchronous code without blocking, using a background thread pool.
5
+
6
+ This solves the common problem of "Event loop is closed" errors
7
+ when trying to use asyncio from sync handlers.
8
+
9
+ Usage:
10
+ from mcp_hangar.infrastructure.async_executor import async_executor
11
+
12
+ # Fire-and-forget
13
+ async_executor.submit(some_coroutine())
14
+
15
+ # With callback
16
+ async_executor.submit(some_coroutine(), on_error=lambda e: logger.error(e))
17
+ """
18
+
19
+ import asyncio
20
+ import atexit
21
+ from concurrent.futures import ThreadPoolExecutor
22
+ from typing import Any, Callable, Coroutine, Optional
23
+
24
+ from ..logging_config import get_logger
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ class AsyncExecutor:
30
+ """Executes async coroutines from sync context using a thread pool.
31
+
32
+ This class provides a singleton executor that runs async operations
33
+ in background threads, each with their own event loop. This avoids
34
+ the "Event loop is closed" error that occurs when trying to use
35
+ asyncio from sync code.
36
+
37
+ Attributes:
38
+ _executor: Thread pool for running async operations
39
+ _max_workers: Maximum number of concurrent background operations
40
+ """
41
+
42
+ _instance: Optional["AsyncExecutor"] = None
43
+
44
+ def __init__(self, max_workers: int = 4):
45
+ """Initialize the async executor.
46
+
47
+ Args:
48
+ max_workers: Maximum number of concurrent background threads
49
+ """
50
+ self._max_workers = max_workers
51
+ self._executor: Optional[ThreadPoolExecutor] = None
52
+ self._started = False
53
+
54
+ @classmethod
55
+ def get_instance(cls) -> "AsyncExecutor":
56
+ """Get or create the singleton instance."""
57
+ if cls._instance is None:
58
+ cls._instance = cls()
59
+ return cls._instance
60
+
61
+ def _ensure_started(self) -> None:
62
+ """Ensure the thread pool is started."""
63
+ if not self._started:
64
+ self._executor = ThreadPoolExecutor(max_workers=self._max_workers, thread_name_prefix="async-executor-")
65
+ self._started = True
66
+ # Register cleanup on exit
67
+ atexit.register(self.shutdown)
68
+
69
+ def submit(
70
+ self,
71
+ coro: Coroutine[Any, Any, Any],
72
+ on_success: Optional[Callable[[Any], None]] = None,
73
+ on_error: Optional[Callable[[Exception], None]] = None,
74
+ ) -> None:
75
+ """Submit an async coroutine for background execution.
76
+
77
+ The coroutine will be executed in a background thread with its
78
+ own event loop. This is fire-and-forget by default.
79
+
80
+ Args:
81
+ coro: The coroutine to execute
82
+ on_success: Optional callback on successful completion
83
+ on_error: Optional callback on error (default: log debug)
84
+ """
85
+ self._ensure_started()
86
+
87
+ def run_coro():
88
+ try:
89
+ result = asyncio.run(coro)
90
+ if on_success:
91
+ try:
92
+ on_success(result)
93
+ except (TypeError, ValueError, RuntimeError) as e:
94
+ logger.debug("async_executor_callback_error", error=str(e))
95
+ except (asyncio.CancelledError, asyncio.TimeoutError, ValueError, RuntimeError, OSError) as e:
96
+ # Handle expected async/runtime errors
97
+ if on_error:
98
+ try:
99
+ on_error(e)
100
+ except (TypeError, ValueError, RuntimeError) as callback_err:
101
+ logger.debug("async_executor_error_callback_failed", error=str(callback_err))
102
+ else:
103
+ logger.debug("async_executor_error", error_type=type(e).__name__, error=str(e))
104
+
105
+ self._executor.submit(run_coro)
106
+
107
+ def shutdown(self, wait: bool = False) -> None:
108
+ """Shutdown the executor.
109
+
110
+ Args:
111
+ wait: Whether to wait for pending operations to complete
112
+ """
113
+ if self._executor:
114
+ self._executor.shutdown(wait=wait)
115
+ self._executor = None
116
+ self._started = False
117
+
118
+
119
+ # Singleton instance for easy access
120
+ async_executor = AsyncExecutor.get_instance()
121
+
122
+
123
+ def submit_async(
124
+ coro: Coroutine[Any, Any, Any],
125
+ on_error: Optional[Callable[[Exception], None]] = None,
126
+ ) -> None:
127
+ """Convenience function to submit an async operation.
128
+
129
+ Args:
130
+ coro: The coroutine to execute
131
+ on_error: Optional error callback
132
+ """
133
+ async_executor.submit(coro, on_error=on_error)
@@ -0,0 +1,37 @@
1
+ """Authentication infrastructure implementations.
2
+
3
+ This module provides concrete implementations of authentication contracts
4
+ defined in the domain layer.
5
+ """
6
+
7
+ from .api_key_authenticator import ApiKeyAuthenticator, InMemoryApiKeyStore
8
+ from .jwt_authenticator import JWKSTokenValidator, JWTAuthenticator, OIDCConfig
9
+ from .middleware import AuthContext, AuthenticationMiddleware, AuthorizationMiddleware
10
+ from .opa_authorizer import OPAAuthorizer
11
+ from .rate_limiter import AuthRateLimitConfig, AuthRateLimiter
12
+ from .rbac_authorizer import InMemoryRoleStore, RBACAuthorizer
13
+
14
+ # Persistent stores - imported lazily to avoid dependency issues
15
+ # Use: from mcp_hangar.infrastructure.auth.sqlite_store import SQLiteApiKeyStore
16
+ # Use: from mcp_hangar.infrastructure.auth.postgres_store import PostgresApiKeyStore
17
+
18
+ __all__ = [
19
+ # API Key authentication
20
+ "ApiKeyAuthenticator",
21
+ "InMemoryApiKeyStore",
22
+ # JWT/OIDC authentication
23
+ "JWTAuthenticator",
24
+ "JWKSTokenValidator",
25
+ "OIDCConfig",
26
+ # Middleware
27
+ "AuthenticationMiddleware",
28
+ "AuthorizationMiddleware",
29
+ "AuthContext",
30
+ # Rate limiting
31
+ "AuthRateLimiter",
32
+ "AuthRateLimitConfig",
33
+ # Authorization
34
+ "RBACAuthorizer",
35
+ "InMemoryRoleStore",
36
+ "OPAAuthorizer",
37
+ ]