mcp-mesh 0.7.21__py3-none-any.whl → 0.8.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 (124) hide show
  1. _mcp_mesh/__init__.py +1 -1
  2. _mcp_mesh/engine/dependency_injector.py +13 -15
  3. _mcp_mesh/engine/http_wrapper.py +69 -10
  4. _mcp_mesh/engine/mesh_llm_agent.py +29 -10
  5. _mcp_mesh/engine/mesh_llm_agent_injector.py +77 -41
  6. _mcp_mesh/engine/provider_handlers/__init__.py +14 -1
  7. _mcp_mesh/engine/provider_handlers/base_provider_handler.py +114 -8
  8. _mcp_mesh/engine/provider_handlers/claude_handler.py +15 -57
  9. _mcp_mesh/engine/provider_handlers/gemini_handler.py +181 -0
  10. _mcp_mesh/engine/provider_handlers/openai_handler.py +8 -63
  11. _mcp_mesh/engine/provider_handlers/provider_handler_registry.py +16 -10
  12. _mcp_mesh/engine/response_parser.py +61 -15
  13. _mcp_mesh/engine/signature_analyzer.py +58 -68
  14. _mcp_mesh/engine/unified_mcp_proxy.py +19 -35
  15. _mcp_mesh/pipeline/__init__.py +9 -20
  16. _mcp_mesh/pipeline/api_heartbeat/__init__.py +12 -7
  17. _mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +23 -49
  18. _mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +429 -0
  19. _mcp_mesh/pipeline/api_startup/api_pipeline.py +7 -9
  20. _mcp_mesh/pipeline/api_startup/api_server_setup.py +91 -70
  21. _mcp_mesh/pipeline/api_startup/fastapi_discovery.py +22 -23
  22. _mcp_mesh/pipeline/api_startup/middleware_integration.py +32 -24
  23. _mcp_mesh/pipeline/api_startup/route_collection.py +2 -4
  24. _mcp_mesh/pipeline/mcp_heartbeat/__init__.py +5 -17
  25. _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +710 -0
  26. _mcp_mesh/pipeline/mcp_startup/__init__.py +2 -5
  27. _mcp_mesh/pipeline/mcp_startup/configuration.py +1 -1
  28. _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +31 -8
  29. _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +6 -7
  30. _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +23 -11
  31. _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +3 -8
  32. _mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -2
  33. _mcp_mesh/reload.py +1 -3
  34. _mcp_mesh/shared/__init__.py +2 -8
  35. _mcp_mesh/shared/config_resolver.py +124 -80
  36. _mcp_mesh/shared/defaults.py +89 -14
  37. _mcp_mesh/shared/fastapi_middleware_manager.py +149 -91
  38. _mcp_mesh/shared/host_resolver.py +8 -46
  39. _mcp_mesh/shared/server_discovery.py +115 -86
  40. _mcp_mesh/shared/simple_shutdown.py +44 -86
  41. _mcp_mesh/tracing/execution_tracer.py +2 -6
  42. _mcp_mesh/tracing/redis_metadata_publisher.py +24 -79
  43. _mcp_mesh/tracing/trace_context_helper.py +3 -13
  44. _mcp_mesh/tracing/utils.py +29 -15
  45. _mcp_mesh/utils/fastmcp_schema_extractor.py +5 -4
  46. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/METADATA +7 -5
  47. mcp_mesh-0.8.0.dist-info/RECORD +85 -0
  48. mesh/__init__.py +12 -1
  49. mesh/decorators.py +248 -33
  50. mesh/helpers.py +52 -0
  51. mesh/types.py +40 -13
  52. _mcp_mesh/generated/.openapi-generator/FILES +0 -50
  53. _mcp_mesh/generated/.openapi-generator/VERSION +0 -1
  54. _mcp_mesh/generated/.openapi-generator-ignore +0 -15
  55. _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +0 -90
  56. _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +0 -6
  57. _mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -1088
  58. _mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -764
  59. _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +0 -303
  60. _mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -798
  61. _mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -21
  62. _mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -577
  63. _mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -217
  64. _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +0 -55
  65. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +0 -158
  66. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -126
  67. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -139
  68. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -92
  69. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -103
  70. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -136
  71. _mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -100
  72. _mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -107
  73. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -112
  74. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -103
  75. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -105
  76. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -103
  77. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_resolution_info.py +0 -106
  78. _mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -91
  79. _mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -103
  80. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -101
  81. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -111
  82. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -117
  83. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider.py +0 -93
  84. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider_resolution_info.py +0 -106
  85. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter.py +0 -109
  86. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py +0 -139
  87. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py +0 -91
  88. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_info.py +0 -101
  89. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_resolution_info.py +0 -120
  90. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -112
  91. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +0 -129
  92. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -153
  93. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -101
  94. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -93
  95. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -107
  96. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -117
  97. _mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -119
  98. _mcp_mesh/generated/mcp_mesh_registry_client/models/resolved_llm_provider.py +0 -110
  99. _mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -93
  100. _mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -92
  101. _mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -93
  102. _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +0 -106
  103. _mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
  104. _mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -259
  105. _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +0 -418
  106. _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +0 -117
  107. _mcp_mesh/pipeline/api_heartbeat/api_health_check.py +0 -140
  108. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +0 -243
  109. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +0 -311
  110. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +0 -386
  111. _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +0 -104
  112. _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +0 -396
  113. _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py +0 -116
  114. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py +0 -311
  115. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py +0 -282
  116. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py +0 -98
  117. _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py +0 -84
  118. _mcp_mesh/pipeline/mcp_heartbeat/llm_tools_resolution.py +0 -264
  119. _mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py +0 -79
  120. _mcp_mesh/pipeline/shared/registry_connection.py +0 -80
  121. _mcp_mesh/shared/registry_client_wrapper.py +0 -515
  122. mcp_mesh-0.7.21.dist-info/RECORD +0 -152
  123. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/WHEEL +0 -0
  124. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,386 +0,0 @@
1
- """
2
- API heartbeat send step for API heartbeat pipeline.
3
-
4
- Sends service health status and registration data to the registry
5
- for FastAPI applications using @mesh.route decorators.
6
- """
7
-
8
- import logging
9
- from typing import Any
10
-
11
- from ...engine.decorator_registry import DecoratorRegistry
12
- from ..shared.base_step import PipelineStep
13
- from ..shared.pipeline_types import PipelineResult
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class APIHeartbeatSendStep(PipelineStep):
19
- """
20
- Send API service heartbeat to registry.
21
-
22
- Communicates service health status and registration information
23
- to the registry for monitoring and discovery purposes.
24
- """
25
-
26
- def __init__(self, required: bool = True):
27
- super().__init__(
28
- name="api-heartbeat-send",
29
- required=required,
30
- )
31
-
32
- async def execute(self, context: dict[str, Any]) -> PipelineResult:
33
- """
34
- Send API service heartbeat to registry.
35
-
36
- Args:
37
- context: Pipeline context containing registry_wrapper, health_status, service_id
38
-
39
- Returns:
40
- PipelineResult with heartbeat_response in context
41
- """
42
- self.logger.debug("Sending API service heartbeat to registry")
43
-
44
- try:
45
- # Get required components from context
46
- registry_wrapper = context.get("registry_wrapper")
47
- health_status = context.get("health_status")
48
- service_id = context.get("service_id") or context.get("agent_id", "unknown")
49
-
50
- if not registry_wrapper:
51
- error_msg = "No registry wrapper available for heartbeat"
52
- self.logger.error(f"❌ {error_msg}")
53
-
54
- from ..shared.pipeline_types import PipelineStatus
55
-
56
- result = PipelineResult(
57
- status=PipelineStatus.FAILED, message=error_msg, context=context
58
- )
59
- result.add_error(error_msg)
60
- return result
61
-
62
- if not health_status:
63
- error_msg = "No health status available for heartbeat"
64
- self.logger.error(f"❌ {error_msg}")
65
-
66
- from ..shared.pipeline_types import PipelineStatus
67
-
68
- result = PipelineResult(
69
- status=PipelineStatus.FAILED, message=error_msg, context=context
70
- )
71
- result.add_error(error_msg)
72
- return result
73
-
74
- # Prepare heartbeat data for API service
75
- heartbeat_data = self._prepare_api_heartbeat_data(
76
- health_status, service_id, context
77
- )
78
-
79
- self.logger.debug(f"📡 Sending heartbeat for API service '{service_id}'")
80
-
81
- # Send heartbeat to registry using the same format as test_api_service.json
82
- # Import json at the beginning
83
- import json
84
-
85
- import aiohttp
86
-
87
- try:
88
- # For API services, send directly to registry using the format that works
89
- # Get registry URL
90
- registry_url = context.get("registry_url", "http://localhost:8000")
91
-
92
- # Build the API service payload using actual dependencies from @mesh.route decorators
93
- display_config = context.get("display_config", {})
94
-
95
- # Build per-route tool entries using METHOD:path as unique identifiers
96
- # This allows dependency resolution to map back to specific route wrappers
97
- route_wrappers = DecoratorRegistry.get_all_route_wrappers()
98
- tools_list = []
99
-
100
- for route_id, route_info in route_wrappers.items():
101
- dependencies = route_info.get("dependencies", [])
102
- if dependencies: # Only include routes with dependencies
103
- tools_list.append(
104
- {
105
- "function_name": route_id, # e.g., "GET:/api/v1/benchmark-services"
106
- "dependencies": [
107
- {"capability": dep, "tags": []}
108
- for dep in dependencies
109
- ],
110
- }
111
- )
112
-
113
- # Fallback to old behavior if no route wrappers registered yet
114
- if not tools_list:
115
- all_route_dependencies = self._extract_all_route_dependencies(
116
- context
117
- )
118
- if all_route_dependencies:
119
- tools_list.append(
120
- {
121
- "function_name": "api_endpoint_handler",
122
- "dependencies": all_route_dependencies,
123
- }
124
- )
125
-
126
- self.logger.debug(
127
- f"Route wrappers registered: {list(route_wrappers.keys())}"
128
- )
129
-
130
- api_service_payload = {
131
- "agent_id": service_id,
132
- "agent_type": "api",
133
- "tools": tools_list,
134
- "http_host": display_config.get("display_host", "127.0.0.1"),
135
- "http_port": display_config.get("display_port", 8080),
136
- }
137
-
138
- self.logger.debug(
139
- f"Sending API service payload to {registry_url}/heartbeat"
140
- )
141
-
142
- try:
143
- async with aiohttp.ClientSession() as session:
144
- async with session.post(
145
- f"{registry_url}/heartbeat",
146
- headers={"Content-Type": "application/json"},
147
- data=json.dumps(api_service_payload),
148
- ) as response:
149
- self.logger.debug(
150
- f'{registry_url} "POST /heartbeat HTTP/1.1" {response.status}'
151
- )
152
- if response.status == 200:
153
- heartbeat_response = await response.json()
154
- else:
155
- response_text = await response.text()
156
- self.logger.error(
157
- f"❌ Registry error {response.status}: {response_text}"
158
- )
159
- raise Exception(
160
- f"Registry returned {response.status}: {response_text}"
161
- )
162
-
163
- except Exception as http_error:
164
- self.logger.error(f"❌ HTTP request failed: {http_error}")
165
- raise http_error
166
-
167
- if heartbeat_response:
168
- self.logger.info(
169
- f"💚 API heartbeat successful for service '{service_id}'"
170
- )
171
-
172
- return PipelineResult(
173
- message=f"API heartbeat sent for service {service_id}",
174
- context={
175
- "heartbeat_response": heartbeat_response,
176
- "heartbeat_success": True,
177
- "heartbeat_data": heartbeat_data,
178
- },
179
- )
180
- else:
181
- error_msg = f"Registry heartbeat failed for service {service_id}"
182
- self.logger.warning(f"⚠️ {error_msg}")
183
-
184
- from ..shared.pipeline_types import PipelineStatus
185
-
186
- result = PipelineResult(
187
- status=PipelineStatus.FAILED, message=error_msg, context=context
188
- )
189
- result.add_error(error_msg)
190
- return result
191
-
192
- except Exception as e:
193
- error_msg = f"Registry communication failed: {e}"
194
- self.logger.error(f"❌ {error_msg}")
195
-
196
- from ..shared.pipeline_types import PipelineStatus
197
-
198
- result = PipelineResult(
199
- status=PipelineStatus.FAILED, message=error_msg, context=context
200
- )
201
- result.add_error(str(e))
202
- return result
203
-
204
- except Exception as e:
205
- error_msg = f"API heartbeat send failed: {e}"
206
- self.logger.error(f"❌ {error_msg}")
207
-
208
- from ..shared.pipeline_types import PipelineStatus
209
-
210
- result = PipelineResult(
211
- status=PipelineStatus.FAILED, message=error_msg, context=context
212
- )
213
- result.add_error(str(e))
214
- return result
215
-
216
- def _prepare_api_heartbeat_data(
217
- self, health_status: Any, service_id: str, context: dict[str, Any]
218
- ) -> dict[str, Any]:
219
- """Prepare heartbeat data specific to API services."""
220
- try:
221
- # Extract FastAPI-specific information
222
- app_title = context.get("app_title", "Unknown API")
223
- app_version = context.get("app_version", "1.0.0")
224
- routes_total = context.get("routes_total", 0)
225
- routes_with_mesh = context.get("routes_with_mesh", 0)
226
-
227
- # Get display configuration
228
- display_config = context.get("display_config", {})
229
- host = display_config.get("host", "0.0.0.0")
230
- port = display_config.get("port", 8080)
231
-
232
- heartbeat_data = {
233
- "service_id": service_id,
234
- "service_type": "api",
235
- "app_title": app_title,
236
- "app_version": app_version,
237
- "host": host,
238
- "port": port,
239
- "routes": {
240
- "total": routes_total,
241
- "with_mesh": routes_with_mesh,
242
- },
243
- "health_status": (
244
- health_status
245
- if isinstance(health_status, dict)
246
- else {
247
- "status": (
248
- health_status.status.value
249
- if hasattr(health_status, "status")
250
- and hasattr(health_status.status, "value")
251
- else str(getattr(health_status, "status", "healthy"))
252
- ),
253
- "timestamp": (
254
- health_status.timestamp.isoformat()
255
- if hasattr(health_status, "timestamp")
256
- and hasattr(health_status.timestamp, "isoformat")
257
- else str(getattr(health_status, "timestamp", ""))
258
- ),
259
- "version": getattr(health_status, "version", "1.0.0"),
260
- "metadata": getattr(health_status, "metadata", {}),
261
- }
262
- ),
263
- }
264
-
265
- return heartbeat_data
266
-
267
- except Exception as e:
268
- self.logger.warning(f"⚠️ Could not prepare heartbeat data: {e}")
269
- return {
270
- "service_id": service_id,
271
- "service_type": "api",
272
- "error": f"Failed to prepare heartbeat data: {e}",
273
- }
274
-
275
- def _extract_all_route_dependencies(
276
- self, context: dict[str, Any]
277
- ) -> list[dict[str, Any]]:
278
- """
279
- Extract all unique dependencies from @mesh.route decorators in the FastAPI app.
280
-
281
- This method looks at the actual route dependencies that were discovered during
282
- the API startup pipeline and extracts them for registry registration.
283
-
284
- Args:
285
- context: Pipeline context containing FastAPI app and route information
286
-
287
- Returns:
288
- List of unique dependency objects in the format expected by registry
289
- """
290
- try:
291
- # Try to get dependencies from startup context (preferred method)
292
- api_service_metadata = context.get("api_service_metadata", {})
293
- route_capabilities = api_service_metadata.get("capabilities", [])
294
-
295
- self.logger.debug(
296
- f"api_service_metadata keys: {list(api_service_metadata.keys())}"
297
- )
298
-
299
- # Extract dependencies from route capabilities
300
- all_dependencies = []
301
- seen_capabilities = set()
302
-
303
- for route_capability in route_capabilities:
304
- route_deps = route_capability.get("dependencies", [])
305
- for dep in route_deps:
306
- # dep should already be a string (capability name)
307
- if isinstance(dep, str) and dep not in seen_capabilities:
308
- seen_capabilities.add(dep)
309
- # Convert to object format for registry
310
- all_dependencies.append(
311
- {
312
- "capability": dep,
313
- "tags": [], # No tags info available at this level
314
- }
315
- )
316
-
317
- # If we found dependencies from startup context, use them
318
- if all_dependencies:
319
- self.logger.debug(
320
- f"Extracted {len(all_dependencies)} unique dependencies from API startup: "
321
- f"{[dep['capability'] for dep in all_dependencies]}"
322
- )
323
- return all_dependencies
324
-
325
- # Fallback: try to extract directly from FastAPI app routes
326
- fastapi_app = context.get("fastapi_app")
327
- if fastapi_app:
328
- return self._extract_dependencies_from_routes(fastapi_app)
329
-
330
- # Final fallback: empty dependencies
331
- self.logger.warning(
332
- "⚠️ No route dependencies found in context or FastAPI app"
333
- )
334
- return []
335
-
336
- except Exception as e:
337
- self.logger.error(f"❌ Failed to extract route dependencies: {e}")
338
- return []
339
-
340
- def _extract_dependencies_from_routes(
341
- self, fastapi_app: Any
342
- ) -> list[dict[str, Any]]:
343
- """
344
- Fallback method to extract dependencies directly from FastAPI route metadata.
345
-
346
- Args:
347
- fastapi_app: FastAPI application instance
348
-
349
- Returns:
350
- List of unique dependency objects
351
- """
352
- try:
353
- all_dependencies = []
354
- seen_capabilities = set()
355
-
356
- routes = getattr(fastapi_app, "routes", [])
357
- for route in routes:
358
- endpoint = getattr(route, "endpoint", None)
359
- if endpoint and hasattr(endpoint, "_mesh_route_metadata"):
360
- metadata = endpoint._mesh_route_metadata
361
- route_deps = metadata.get("dependencies", [])
362
-
363
- for dep in route_deps:
364
- if isinstance(dep, dict):
365
- capability = dep.get("capability")
366
- if capability and capability not in seen_capabilities:
367
- seen_capabilities.add(capability)
368
- all_dependencies.append(
369
- {
370
- "capability": capability,
371
- "tags": dep.get("tags", []),
372
- }
373
- )
374
- elif isinstance(dep, str) and dep not in seen_capabilities:
375
- seen_capabilities.add(dep)
376
- all_dependencies.append({"capability": dep, "tags": []})
377
-
378
- self.logger.debug(
379
- f"Extracted {len(all_dependencies)} unique dependencies from FastAPI routes: "
380
- f"{[dep['capability'] for dep in all_dependencies]}"
381
- )
382
- return all_dependencies
383
-
384
- except Exception as e:
385
- self.logger.error(f"❌ Failed to extract dependencies from routes: {e}")
386
- return []
@@ -1,104 +0,0 @@
1
- """
2
- API registry connection step for API heartbeat pipeline.
3
-
4
- Prepares registry communication for FastAPI service heartbeat operations.
5
- Simpler than MCP registry connection since API services don't require
6
- complex dependency resolution.
7
- """
8
-
9
- import logging
10
- from typing import Any
11
-
12
- from ..shared.base_step import PipelineStep
13
- from ..shared.pipeline_types import PipelineResult
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class APIRegistryConnectionStep(PipelineStep):
19
- """
20
- Prepare registry connection for API service heartbeat.
21
-
22
- Ensures registry client is available and properly configured for
23
- FastAPI service registration and health monitoring.
24
- """
25
-
26
- def __init__(self, required: bool = True):
27
- super().__init__(
28
- name="api-registry-connection",
29
- required=required,
30
- )
31
-
32
- async def execute(self, context: dict[str, Any]) -> PipelineResult:
33
- """
34
- Prepare registry connection for API heartbeat operations.
35
-
36
- Args:
37
- context: Pipeline context containing agent_config and registration_data
38
-
39
- Returns:
40
- PipelineResult with registry_wrapper in context
41
- """
42
- self.logger.trace("🔗 Preparing API registry connection for heartbeat")
43
-
44
- try:
45
- # Check if registry_wrapper already exists in context
46
- registry_wrapper = context.get("registry_wrapper")
47
-
48
- if registry_wrapper is not None:
49
- self.logger.trace("✅ Registry wrapper already available in context")
50
- return PipelineResult(
51
- message="Registry connection already established",
52
- context={"registry_wrapper": registry_wrapper}
53
- )
54
-
55
- # Get registry configuration from context and environment
56
- agent_config = context.get("agent_config", {})
57
- registration_data = context.get("registration_data", {})
58
-
59
- # Use proper config resolver to respect environment variables first
60
- from ...shared.config_resolver import ValidationRule, get_config_value
61
-
62
- registry_url = get_config_value(
63
- "MCP_MESH_REGISTRY_URL",
64
- override=(
65
- agent_config.get("registry_url")
66
- or registration_data.get("registry_url")
67
- ),
68
- default="http://localhost:8000",
69
- rule=ValidationRule.URL_RULE,
70
- )
71
-
72
- self.logger.trace(f"🔍 Using registry URL: {registry_url}")
73
-
74
- # Create registry client wrapper
75
- from ...generated.mcp_mesh_registry_client.api_client import ApiClient
76
- from ...generated.mcp_mesh_registry_client.configuration import Configuration
77
- from ...shared.registry_client_wrapper import RegistryClientWrapper
78
-
79
- config = Configuration(host=registry_url)
80
- api_client = ApiClient(configuration=config)
81
- registry_wrapper = RegistryClientWrapper(api_client)
82
-
83
- self.logger.trace(f"🔗 API registry connection prepared: {registry_url}")
84
-
85
- return PipelineResult(
86
- message=f"Registry connection prepared for {registry_url}",
87
- context={
88
- "registry_wrapper": registry_wrapper,
89
- "registry_url": registry_url,
90
- }
91
- )
92
-
93
- except Exception as e:
94
- error_msg = f"Failed to prepare API registry connection: {e}"
95
- self.logger.error(f"❌ {error_msg}")
96
-
97
- from ..shared.pipeline_types import PipelineStatus
98
- result = PipelineResult(
99
- status=PipelineStatus.FAILED,
100
- message=error_msg,
101
- context=context
102
- )
103
- result.add_error(str(e))
104
- return result