traia-iatp 0.1.2__py3-none-any.whl → 0.1.67__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 (95) hide show
  1. traia_iatp/__init__.py +105 -8
  2. traia_iatp/cli/main.py +85 -1
  3. traia_iatp/client/__init__.py +28 -3
  4. traia_iatp/client/crewai_a2a_tools.py +32 -12
  5. traia_iatp/client/d402_a2a_client.py +348 -0
  6. traia_iatp/contracts/__init__.py +11 -0
  7. traia_iatp/contracts/data/abis/contract-abis-localhost.json +4091 -0
  8. traia_iatp/contracts/data/abis/contract-abis-sepolia.json +4890 -0
  9. traia_iatp/contracts/data/addresses/contract-addresses.json +17 -0
  10. traia_iatp/contracts/data/addresses/contract-proxies.json +12 -0
  11. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  12. traia_iatp/contracts/wallet_creator.py +369 -0
  13. traia_iatp/core/models.py +17 -3
  14. traia_iatp/d402/MIDDLEWARE_ARCHITECTURE.md +205 -0
  15. traia_iatp/d402/PRICE_BUILDER_USAGE.md +249 -0
  16. traia_iatp/d402/README.md +489 -0
  17. traia_iatp/d402/__init__.py +54 -0
  18. traia_iatp/d402/asgi_wrapper.py +469 -0
  19. traia_iatp/d402/chains.py +102 -0
  20. traia_iatp/d402/client.py +150 -0
  21. traia_iatp/d402/clients/__init__.py +7 -0
  22. traia_iatp/d402/clients/base.py +218 -0
  23. traia_iatp/d402/clients/httpx.py +266 -0
  24. traia_iatp/d402/common.py +114 -0
  25. traia_iatp/d402/encoding.py +28 -0
  26. traia_iatp/d402/examples/client_example.py +197 -0
  27. traia_iatp/d402/examples/server_example.py +171 -0
  28. traia_iatp/d402/facilitator.py +481 -0
  29. traia_iatp/d402/mcp_middleware.py +296 -0
  30. traia_iatp/d402/models.py +116 -0
  31. traia_iatp/d402/networks.py +98 -0
  32. traia_iatp/d402/path.py +43 -0
  33. traia_iatp/d402/payment_introspection.py +126 -0
  34. traia_iatp/d402/payment_signing.py +183 -0
  35. traia_iatp/d402/price_builder.py +164 -0
  36. traia_iatp/d402/servers/__init__.py +61 -0
  37. traia_iatp/d402/servers/base.py +139 -0
  38. traia_iatp/d402/servers/example_general_server.py +140 -0
  39. traia_iatp/d402/servers/fastapi.py +253 -0
  40. traia_iatp/d402/servers/mcp.py +304 -0
  41. traia_iatp/d402/servers/starlette.py +878 -0
  42. traia_iatp/d402/starlette_middleware.py +529 -0
  43. traia_iatp/d402/types.py +300 -0
  44. traia_iatp/mcp/D402_MCP_ADAPTER_FLOW.md +357 -0
  45. traia_iatp/mcp/__init__.py +3 -0
  46. traia_iatp/mcp/d402_mcp_tool_adapter.py +526 -0
  47. traia_iatp/mcp/mcp_agent_template.py +78 -13
  48. traia_iatp/mcp/templates/Dockerfile.j2 +27 -4
  49. traia_iatp/mcp/templates/README.md.j2 +104 -8
  50. traia_iatp/mcp/templates/cursor-rules.md.j2 +194 -0
  51. traia_iatp/mcp/templates/deployment_params.json.j2 +1 -2
  52. traia_iatp/mcp/templates/docker-compose.yml.j2 +13 -3
  53. traia_iatp/mcp/templates/env.example.j2 +60 -0
  54. traia_iatp/mcp/templates/mcp_health_check.py.j2 +2 -2
  55. traia_iatp/mcp/templates/pyproject.toml.j2 +11 -5
  56. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  57. traia_iatp/mcp/templates/run_local_docker.sh.j2 +320 -10
  58. traia_iatp/mcp/templates/server.py.j2 +174 -197
  59. traia_iatp/mcp/traia_mcp_adapter.py +182 -20
  60. traia_iatp/registry/__init__.py +47 -12
  61. traia_iatp/registry/atlas_search_indexes.json +108 -54
  62. traia_iatp/registry/iatp_search_api.py +169 -39
  63. traia_iatp/registry/mongodb_registry.py +241 -69
  64. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +1 -1
  65. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +8 -8
  66. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +1 -1
  67. traia_iatp/registry/readmes/README.md +3 -3
  68. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +6 -6
  69. traia_iatp/scripts/__init__.py +2 -0
  70. traia_iatp/scripts/create_wallet.py +244 -0
  71. traia_iatp/server/a2a_server.py +22 -7
  72. traia_iatp/server/iatp_server_template_generator.py +23 -0
  73. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  74. traia_iatp/server/templates/Dockerfile.j2 +23 -1
  75. traia_iatp/server/templates/README.md +2 -2
  76. traia_iatp/server/templates/README.md.j2 +5 -5
  77. traia_iatp/server/templates/__main__.py.j2 +374 -66
  78. traia_iatp/server/templates/agent.py.j2 +12 -11
  79. traia_iatp/server/templates/agent_config.json.j2 +3 -3
  80. traia_iatp/server/templates/agent_executor.py.j2 +45 -27
  81. traia_iatp/server/templates/env.example.j2 +32 -4
  82. traia_iatp/server/templates/gitignore.j2 +7 -0
  83. traia_iatp/server/templates/pyproject.toml.j2 +13 -12
  84. traia_iatp/server/templates/run_local_docker.sh.j2 +143 -11
  85. traia_iatp/server/templates/server.py.j2 +197 -10
  86. traia_iatp/special_agencies/registry_search_agency.py +1 -1
  87. traia_iatp/utils/iatp_utils.py +6 -6
  88. traia_iatp-0.1.67.dist-info/METADATA +320 -0
  89. traia_iatp-0.1.67.dist-info/RECORD +117 -0
  90. traia_iatp-0.1.2.dist-info/METADATA +0 -414
  91. traia_iatp-0.1.2.dist-info/RECORD +0 -72
  92. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/WHEEL +0 -0
  93. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/entry_points.txt +0 -0
  94. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/licenses/LICENSE +0 -0
  95. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/top_level.txt +0 -0
@@ -26,13 +26,11 @@ if __name__ == "__main__":
26
26
  # When running as a script, import directly
27
27
  import sys
28
28
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
29
- from iatp.core.models import UtilityAgentRegistryEntry, UtilityAgent
30
- from iatp.registry.embeddings import get_embedding_service
29
+ from src.traia_iatp.core.models import UtilityAgentRegistryEntry, UtilityAgent
31
30
  from ..utils import get_now_in_utc
32
31
  else:
33
32
  # When imported as a module
34
33
  from ..core.models import UtilityAgentRegistryEntry, UtilityAgent
35
- from .embeddings import get_embedding_service
36
34
  from ..utils import get_now_in_utc
37
35
 
38
36
  logger = logging.getLogger(__name__)
@@ -127,34 +125,51 @@ class UtilityAgentRegistry:
127
125
  if connection_string:
128
126
  self.connection_string = connection_string
129
127
  else:
130
- # Try X.509 certificate authentication first
131
- cert_file = os.getenv("MONGODB_X509_CERT_FILE")
132
- if cert_file and os.path.exists(cert_file):
133
- # For X.509 authentication, we need to extract the subject from the certificate
134
- # to use as the username. MongoDB Atlas typically uses the full DN as username.
135
- # The connection string format for X.509 is:
136
- # mongodb+srv://cluster.mongodb.net/?authSource=$external&authMechanism=MONGODB-X509
137
- # Extract just the cluster hostname without query parameters
138
- cluster_host = CLUSTER_URI.split('?')[0]
139
- self.connection_string = f"mongodb+srv://{cluster_host}?authSource=$external&authMechanism=MONGODB-X509&tls=true&tlsCertificateKeyFile={cert_file}"
140
- logger.info(f"Using X.509 certificate authentication from {cert_file}")
128
+ # Check if running locally or in cloud environment
129
+ is_local = os.getenv("LOCAL_EXECUTION", "false").lower() == "true"
130
+
131
+ if is_local:
132
+ # Local execution - require explicit credentials
133
+ # Try X.509 certificate authentication first
134
+ cert_file = os.getenv("MONGODB_X509_CERT_FILE")
135
+ if cert_file and os.path.exists(cert_file):
136
+ # For X.509 authentication, we need to extract the subject from the certificate
137
+ # to use as the username. MongoDB Atlas typically uses the full DN as username.
138
+ # The connection string format for X.509 is:
139
+ # mongodb+srv://cluster.mongodb.net/?authSource=$external&authMechanism=MONGODB-X509
140
+ # Extract just the cluster hostname without query parameters
141
+ cluster_host = CLUSTER_URI.split('?')[0]
142
+ self.connection_string = f"mongodb+srv://{cluster_host}?authSource=$external&authMechanism=MONGODB-X509&tls=true&tlsCertificateKeyFile={cert_file}"
143
+ logger.info(f"Using X.509 certificate authentication from {cert_file}")
144
+ else:
145
+ # Fallback to username/password authentication
146
+ user = os.getenv("MONGODB_USER")
147
+ password = os.getenv("MONGODB_PASSWORD")
148
+ if user and password:
149
+ self.connection_string = f"mongodb+srv://{user}:{password}@{CLUSTER_URI}"
150
+ logger.info("Using username/password authentication")
151
+ else:
152
+ # Try connection string as last resort
153
+ self.connection_string = os.getenv("MONGODB_CONNECTION_STRING")
154
+ if not self.connection_string:
155
+ raise ValueError(
156
+ "MongoDB authentication required for local execution. Please provide either:\n"
157
+ "1. MONGODB_X509_CERT_FILE - Path to X.509 certificate file\n"
158
+ "2. MONGODB_USER and MONGODB_PASSWORD - Username and password\n"
159
+ "3. MONGODB_CONNECTION_STRING - Full connection string"
160
+ )
141
161
  else:
142
- # Fallback to username/password authentication
143
- user = os.getenv("MONGODB_USER")
144
- password = os.getenv("MONGODB_PASSWORD")
145
- if user and password:
146
- self.connection_string = f"mongodb+srv://{user}:{password}@{CLUSTER_URI}"
147
- logger.info("Using username/password authentication")
162
+ # Cloud execution (Lambda) - use IAM role authentication
163
+ cluster_host = CLUSTER_URI.split('?')[0].rstrip('/')
164
+ # Preserve useful query parameters from original CLUSTER_URI
165
+ original_params = CLUSTER_URI.split('?')[1] if '?' in CLUSTER_URI else ""
166
+ # Combine AWS auth params with original params
167
+ auth_params = "authSource=$external&authMechanism=MONGODB-AWS"
168
+ if original_params:
169
+ self.connection_string = f"mongodb+srv://{cluster_host}?{auth_params}&{original_params}"
148
170
  else:
149
- # Try connection string as last resort
150
- self.connection_string = os.getenv("MONGODB_CONNECTION_STRING")
151
- if not self.connection_string:
152
- raise ValueError(
153
- "MongoDB authentication required. Please provide either:\n"
154
- "1. MONGODB_X509_CERT_FILE - Path to X.509 certificate file\n"
155
- "2. MONGODB_USER and MONGODB_PASSWORD - Username and password\n"
156
- "3. MONGODB_CONNECTION_STRING - Full connection string"
157
- )
171
+ self.connection_string = f"mongodb+srv://{cluster_host}?{auth_params}"
172
+ logger.info("Using IAM role authentication for cloud execution")
158
173
 
159
174
  self.database_name = database_name
160
175
  self.client = _create_mongodb_client_with_retry(self.connection_string)
@@ -196,7 +211,7 @@ class UtilityAgentRegistry:
196
211
  except Exception as e:
197
212
  logger.warning(f"Could not create index {index_name}: {e}")
198
213
 
199
- async def add_utility_agent(self, agent: UtilityAgent, tags: List[str] = None) -> UtilityAgentRegistryEntry:
214
+ async def add_utility_agent(self, agent: UtilityAgent, tags: List[str] = None, endpoints: List[Dict[str, Any]] = None) -> UtilityAgentRegistryEntry:
200
215
  """Add a utility agent to the cloud registry.
201
216
 
202
217
  Args:
@@ -225,6 +240,8 @@ class UtilityAgentRegistry:
225
240
 
226
241
  if os.getenv("ENABLE_EMBEDDINGS", "true").lower() == "true":
227
242
  try:
243
+ # Lazy import to avoid loading OpenAI/Cohere unless embeddings are enabled
244
+ from .embeddings import get_embedding_service
228
245
  embedding_service = get_embedding_service()
229
246
 
230
247
  # Generate embedding for description
@@ -266,14 +283,19 @@ class UtilityAgentRegistry:
266
283
  except Exception as e:
267
284
  logger.warning(f"Failed to generate embeddings: {e}. Proceeding without embeddings.")
268
285
 
269
- # Create entry with enhanced fields
286
+ # Create entry with enhanced fields including d402 payment info
270
287
  entry = UtilityAgentRegistryEntry(
271
288
  agent_id=agent_id,
272
289
  name=agent.name,
273
290
  description=agent.description,
291
+ mcp_server_id=agent.mcp_server_id, # MCP server reference
274
292
  capabilities=agent.capabilities,
275
293
  tags=tags or agent.tags,
276
- metadata=agent.metadata
294
+ metadata=agent.metadata,
295
+ # D402 payment fields (must be at root level)
296
+ contract_address=agent.contract_address,
297
+ d402_enabled=agent.d402_enabled,
298
+ d402_payment_info=agent.d402_config # d402_config becomes d402_payment_info in registry
277
299
  )
278
300
 
279
301
  # Add agent card if available
@@ -281,9 +303,8 @@ class UtilityAgentRegistry:
281
303
  entry.agent_card = agent.agent_card
282
304
  entry.skills = agent.agent_card.skills
283
305
 
284
- # Add endpoints if available
306
+ # Add base_url if available
285
307
  if agent.endpoints:
286
- entry.endpoints = agent.endpoints
287
308
  # Store base_url for indexing since all endpoints are derived from it
288
309
  entry.base_url = agent.endpoints.base_url
289
310
 
@@ -308,6 +329,8 @@ class UtilityAgentRegistry:
308
329
  # Generate embedding for the search text we just created
309
330
  if os.getenv("ENABLE_EMBEDDINGS", "true").lower() == "true" and entry.search_text:
310
331
  try:
332
+ # Lazy import to avoid loading OpenAI/Cohere unless embeddings are enabled
333
+ from .embeddings import get_embedding_service
311
334
  embedding_service = get_embedding_service()
312
335
  search_text_embedding = await embedding_service.generate_embedding(entry.search_text)
313
336
  except Exception as e:
@@ -317,6 +340,43 @@ class UtilityAgentRegistry:
317
340
  # Convert to dict and ensure all values are JSON-serializable
318
341
  entry_dict = entry.model_dump(mode='json')
319
342
 
343
+ # Add iatp_endpoints (protocol structure) and endpoints array (payment info) separately
344
+ if agent.endpoints:
345
+ # Store IATPEndpoints as iatp_endpoints (protocol info)
346
+ entry_dict["iatp_endpoints"] = agent.endpoints.model_dump(mode='json')
347
+
348
+ # Store endpoints as array matching MCP server format (payment info)
349
+ # Use endpoints parameter if provided (includes actual payment configuration)
350
+ if endpoints and len(endpoints) > 0:
351
+ # Use endpoints array (includes actual payment configuration)
352
+ entry_dict["endpoints"] = endpoints
353
+ logger.info(f"Using {len(endpoints)} endpoints with payment configuration")
354
+ else:
355
+ # Fallback: Create endpoint from d402_config (for backward compatibility)
356
+ logger.info("Creating endpoint from d402_config (PostgreSQL endpoints not provided)")
357
+ endpoints_array = [
358
+ {
359
+ "endpoint_path": "/a2a",
360
+ "endpoint_name": "a2a",
361
+ "endpoint_method": "POST",
362
+ "endpoint_description": "A2A protocol JSON-RPC endpoint",
363
+ "icon_url": None,
364
+ "endpoint_input_schema": None, # A2A protocol defines this
365
+ "endpoint_output_schema": None, # A2A protocol defines this
366
+ # Payment info from d402_config
367
+ "payment_price_float": float(agent.d402_config.get("price_usd", 0.01)) if agent.d402_config else 0.01,
368
+ "payment_price_wei": agent.d402_config.get("price_wei") if agent.d402_config else None,
369
+ "settlement_token_address": agent.d402_config.get("token_address") if agent.d402_config else None,
370
+ "settlement_token_symbol": agent.d402_config.get("token_symbol") if agent.d402_config else None,
371
+ "settlement_token_network": agent.d402_config.get("network", "sepolia") if agent.d402_config else "sepolia",
372
+ "settlement_token_chain_id": agent.d402_config.get("chain_id") if agent.d402_config else None
373
+ }
374
+ ]
375
+ entry_dict["endpoints"] = endpoints_array
376
+ else:
377
+ entry_dict["iatp_endpoints"] = None
378
+ entry_dict["endpoints"] = []
379
+
320
380
  # Add embeddings if available
321
381
  embeddings = {}
322
382
  if description_embedding:
@@ -333,9 +393,13 @@ class UtilityAgentRegistry:
333
393
  if embeddings:
334
394
  entry_dict["embeddings"] = embeddings
335
395
 
336
- # Store the full UtilityAgent data as well
337
- agent_dict = agent.model_dump(mode='json')
338
- entry_dict["utility_agent_data"] = agent_dict
396
+ # Do NOT store utility_agent_data as a nested structure - all fields are already at root level
397
+ # The entry_dict from UtilityAgentRegistryEntry already contains all necessary fields:
398
+ # - agent_id, name, description, capabilities, tags, metadata (at root)
399
+ # - agent_card, skills, endpoints (at root)
400
+ # - contract_address, d402_enabled, d402_payment_info (at root)
401
+ # - base_url, search_text, is_active, registered_at (at root)
402
+ # No utility_agent_data nesting is created
339
403
 
340
404
  result = self.registry.replace_one(
341
405
  {"agent_id": agent_id}, # Use agent_id as the key for upsert
@@ -401,7 +465,7 @@ class UtilityAgentRegistry:
401
465
  return False
402
466
 
403
467
  # Create new endpoints based on the new base URL
404
- from iatp.utils.iatp_utils import create_iatp_endpoints
468
+ from ..utils.iatp_utils import create_iatp_endpoints
405
469
  supports_streaming = doc.get("endpoints", {}).get("streaming_endpoint") is not None
406
470
  new_endpoints = create_iatp_endpoints(new_base_url, supports_streaming)
407
471
 
@@ -456,6 +520,58 @@ class UtilityAgentRegistry:
456
520
  logger.warning(f"No utility agent found with ID {agent_id} or no changes made")
457
521
  return False
458
522
 
523
+ async def update_utility_agent_status(
524
+ self,
525
+ agent_id: str,
526
+ is_active: Optional[bool] = None,
527
+ core_tests_passed: Optional[bool] = None,
528
+ crewai_tests_passed: Optional[bool] = None
529
+ ) -> Dict[str, Any]:
530
+ """
531
+ Update utility agent status and test results in MongoDB.
532
+
533
+ Similar to MCP server status updates, allows updating:
534
+ - is_active: Whether the agent is active and available
535
+ - core_tests_passed: Whether core (direct A2A) tests passed
536
+ - crewai_tests_passed: Whether CrewAI agency tests passed
537
+
538
+ Args:
539
+ agent_id: ID of the utility agent
540
+ is_active: Optional new active status
541
+ core_tests_passed: Optional core test status
542
+ crewai_tests_passed: Optional CrewAI test status
543
+
544
+ Returns:
545
+ Dict with success status and updated fields
546
+ """
547
+ update_data = {}
548
+
549
+ if is_active is not None:
550
+ update_data["metadata.is_active"] = is_active
551
+
552
+ if core_tests_passed is not None:
553
+ update_data["metadata.core_tests_passed"] = core_tests_passed
554
+
555
+ if crewai_tests_passed is not None:
556
+ update_data["metadata.crewai_tests_passed"] = crewai_tests_passed
557
+
558
+ if not update_data:
559
+ return {"success": False, "error": "No fields to update"}
560
+
561
+ update_data["updated_at"] = get_now_in_utc()
562
+
563
+ result = self.registry.update_one(
564
+ {"agent_id": agent_id},
565
+ {"$set": update_data}
566
+ )
567
+
568
+ if result.modified_count > 0:
569
+ logger.info(f"Updated utility agent {agent_id} - {list(update_data.keys())}")
570
+ return {"success": True, "modified_count": result.modified_count, "fields": list(update_data.keys())}
571
+ else:
572
+ logger.warning(f"No documents modified for agent_id: {agent_id}")
573
+ return {"success": False, "error": "No documents modified"}
574
+
459
575
  async def get_statistics(self) -> Dict[str, Any]:
460
576
  """Get registry statistics."""
461
577
  total_agents = self.registry.count_documents({})
@@ -499,34 +615,51 @@ class MCPServerRegistry:
499
615
  if connection_string:
500
616
  self.connection_string = connection_string
501
617
  else:
502
- # Try X.509 certificate authentication first
503
- cert_file = os.getenv("MONGODB_X509_CERT_FILE")
504
- if cert_file and os.path.exists(cert_file):
505
- # For X.509 authentication, we need to extract the subject from the certificate
506
- # to use as the username. MongoDB Atlas typically uses the full DN as username.
507
- # The connection string format for X.509 is:
508
- # mongodb+srv://cluster.mongodb.net/?authSource=$external&authMechanism=MONGODB-X509
509
- # Extract just the cluster hostname without query parameters
510
- cluster_host = CLUSTER_URI.split('?')[0]
511
- self.connection_string = f"mongodb+srv://{cluster_host}?authSource=$external&authMechanism=MONGODB-X509&tls=true&tlsCertificateKeyFile={cert_file}"
512
- logger.info(f"Using X.509 certificate authentication from {cert_file}")
618
+ # Check if running locally or in cloud environment
619
+ is_local = os.getenv("LOCAL_EXECUTION", "false").lower() == "true"
620
+
621
+ if is_local:
622
+ # Local execution - require explicit credentials
623
+ # Try X.509 certificate authentication first
624
+ cert_file = os.getenv("MONGODB_X509_CERT_FILE")
625
+ if cert_file and os.path.exists(cert_file):
626
+ # For X.509 authentication, we need to extract the subject from the certificate
627
+ # to use as the username. MongoDB Atlas typically uses the full DN as username.
628
+ # The connection string format for X.509 is:
629
+ # mongodb+srv://cluster.mongodb.net/?authSource=$external&authMechanism=MONGODB-X509
630
+ # Extract just the cluster hostname without query parameters
631
+ cluster_host = CLUSTER_URI.split('?')[0]
632
+ self.connection_string = f"mongodb+srv://{cluster_host}?authSource=$external&authMechanism=MONGODB-X509&tls=true&tlsCertificateKeyFile={cert_file}"
633
+ logger.info(f"Using X.509 certificate authentication from {cert_file}")
634
+ else:
635
+ # Fallback to username/password authentication
636
+ user = os.getenv("MONGODB_USER")
637
+ password = os.getenv("MONGODB_PASSWORD")
638
+ if user and password:
639
+ self.connection_string = f"mongodb+srv://{user}:{password}@{CLUSTER_URI}"
640
+ logger.info("Using username/password authentication")
641
+ else:
642
+ # Try connection string as last resort
643
+ self.connection_string = os.getenv("MONGODB_CONNECTION_STRING")
644
+ if not self.connection_string:
645
+ raise ValueError(
646
+ "MongoDB authentication required for local execution. Please provide either:\n"
647
+ "1. MONGODB_X509_CERT_FILE - Path to X.509 certificate file\n"
648
+ "2. MONGODB_USER and MONGODB_PASSWORD - Username and password\n"
649
+ "3. MONGODB_CONNECTION_STRING - Full connection string"
650
+ )
513
651
  else:
514
- # Fallback to username/password authentication
515
- user = os.getenv("MONGODB_USER")
516
- password = os.getenv("MONGODB_PASSWORD")
517
- if user and password:
518
- self.connection_string = f"mongodb+srv://{user}:{password}@{CLUSTER_URI}"
519
- logger.info("Using username/password authentication")
652
+ # Cloud execution (Lambda) - use IAM role authentication
653
+ cluster_host = CLUSTER_URI.split('?')[0].rstrip('/')
654
+ # Preserve useful query parameters from original CLUSTER_URI
655
+ original_params = CLUSTER_URI.split('?')[1] if '?' in CLUSTER_URI else ""
656
+ # Combine AWS auth params with original params
657
+ auth_params = "authSource=$external&authMechanism=MONGODB-AWS"
658
+ if original_params:
659
+ self.connection_string = f"mongodb+srv://{cluster_host}?{auth_params}&{original_params}"
520
660
  else:
521
- # Try connection string as last resort
522
- self.connection_string = os.getenv("MONGODB_CONNECTION_STRING")
523
- if not self.connection_string:
524
- raise ValueError(
525
- "MongoDB authentication required. Please provide either:\n"
526
- "1. MONGODB_X509_CERT_FILE - Path to X.509 certificate file\n"
527
- "2. MONGODB_USER and MONGODB_PASSWORD - Username and password\n"
528
- "3. MONGODB_CONNECTION_STRING - Full connection string"
529
- )
661
+ self.connection_string = f"mongodb+srv://{cluster_host}?{auth_params}"
662
+ logger.info("Using IAM role authentication for cloud execution")
530
663
 
531
664
  self.client = _create_mongodb_client_with_retry(self.connection_string)
532
665
  self.db: Database = self.client[database_name]
@@ -557,7 +690,16 @@ class MCPServerRegistry:
557
690
  description: str,
558
691
  server_type: str = "streamable-http",
559
692
  capabilities: List[str] = None,
560
- metadata: Dict[str, Any] = None
693
+ metadata: Dict[str, Any] = None,
694
+ core_tests_passed: bool = False,
695
+ crewai_tests_passed: bool = False,
696
+ endpoints: Optional[List[Dict[str, Any]]] = None,
697
+ user_uuid: Optional[str] = None,
698
+ operator_address: Optional[str] = None,
699
+ server_address: Optional[str] = None,
700
+ server_network: Optional[str] = None,
701
+ server_chain_id: Optional[int] = None,
702
+ accepts: Optional[Dict[str, Any]] = None
561
703
  ) -> str:
562
704
  """Add an MCP server to the registry with retry logic."""
563
705
  # Generate embeddings if enabled
@@ -566,6 +708,8 @@ class MCPServerRegistry:
566
708
 
567
709
  if os.getenv("ENABLE_EMBEDDINGS", "true").lower() == "true":
568
710
  try:
711
+ # Lazy import to avoid loading OpenAI/Cohere unless embeddings are enabled
712
+ from .embeddings import get_embedding_service
569
713
  embedding_service = get_embedding_service()
570
714
 
571
715
  # Generate embedding for description
@@ -587,11 +731,29 @@ class MCPServerRegistry:
587
731
  "description": description,
588
732
  "server_type": server_type,
589
733
  "capabilities": capabilities or [],
734
+ "endpoints": endpoints or [],
590
735
  "metadata": metadata or {},
591
736
  "registered_at": get_now_in_utc(),
592
- "is_active": True
737
+ "core_tests_passed": core_tests_passed,
738
+ "crewai_tests_passed": crewai_tests_passed,
739
+ "is_active": False,
740
+ "user_uuid": user_uuid
593
741
  }
594
742
 
743
+ # Add blockchain fields if provided (from PostgreSQL database)
744
+ if operator_address:
745
+ doc["operator_address"] = operator_address
746
+ if server_address:
747
+ doc["server_address"] = server_address
748
+ if server_network:
749
+ doc["server_network"] = server_network
750
+ if server_chain_id:
751
+ doc["server_chain_id"] = server_chain_id
752
+
753
+ # Add token acceptance information if provided
754
+ if accepts:
755
+ doc["accepts"] = accepts
756
+
595
757
  # Add embeddings if available
596
758
  if description_embedding:
597
759
  doc["description_embedding"] = description_embedding
@@ -638,8 +800,18 @@ class MCPServerRegistry:
638
800
 
639
801
 
640
802
  async def get_mcp_server(self, name: str) -> Optional[Dict[str, Any]]:
641
- """Get an MCP server by name."""
642
- doc = self.collection.find_one({"name": name})
803
+ """Get an MCP server by name.
804
+
805
+ Note: Excludes embedding fields to reduce response size and improve performance.
806
+ Embeddings are only used internally for vector search.
807
+ """
808
+ # Exclude embedding fields from response (large arrays not needed by clients)
809
+ projection = {
810
+ "description_embedding": 0,
811
+ "capabilities_embedding": 0
812
+ }
813
+
814
+ doc = self.collection.find_one({"name": name}, projection)
643
815
  if doc:
644
816
  doc["_id"] = str(doc["_id"])
645
817
  return doc
@@ -700,7 +872,7 @@ if __name__ == "__main__":
700
872
  )
701
873
 
702
874
  # Create test agent with endpoints
703
- from iatp.utils.iatp_utils import create_iatp_endpoints
875
+ from ..utils.iatp_utils import create_iatp_endpoints
704
876
  test_agent.endpoints = create_iatp_endpoints("http://weather-agent:8100", supports_streaming=True)
705
877
 
706
878
  # Add to registry
@@ -116,7 +116,7 @@ import os
116
116
  os.environ["ENABLE_EMBEDDINGS"] = "true"
117
117
  os.environ["OPENAI_API_KEY"] = "your-key"
118
118
 
119
- from iatp.registry.mongodb_registry import UtilityAgencyRegistry
119
+ from traia_iatp.registry.mongodb_registry import UtilityAgencyRegistry
120
120
 
121
121
  # Create registry (embeddings will be auto-generated)
122
122
  registry = UtilityAgencyRegistry()
@@ -22,7 +22,7 @@ The API automatically detects and uses the appropriate authentication method:
22
22
  Find a single utility agent by specific criteria.
23
23
 
24
24
  ```python
25
- from iatp.registry.iatp_search_api import find_utility_agent
25
+ from traia_iatp.registry.iatp_search_api import find_utility_agent
26
26
 
27
27
  # Find by exact name
28
28
  agent = find_utility_agent(name="hyperliquid-mcp-traia-utility-agency")
@@ -41,7 +41,7 @@ agent = find_utility_agent(query="trading bot")
41
41
  List utility agents with optional filters.
42
42
 
43
43
  ```python
44
- from iatp.registry.iatp_search_api import list_utility_agents
44
+ from traia_iatp.registry.iatp_search_api import list_utility_agents
45
45
 
46
46
  # List all active agents
47
47
  agents = list_utility_agents(limit=20)
@@ -57,7 +57,7 @@ agents = list_utility_agents(capabilities=["market_info", "trading_orders"])
57
57
  Search utility agents using vector search.
58
58
 
59
59
  ```python
60
- from iatp.registry.iatp_search_api import search_utility_agents
60
+ from traia_iatp.registry.iatp_search_api import search_utility_agents
61
61
 
62
62
  # Search using default search_text embedding (recommended for best performance)
63
63
  agents = await search_utility_agents("trading hyperliquid", limit=5)
@@ -83,7 +83,7 @@ agents = await search_utility_agents(
83
83
  Find a single MCP server by specific criteria.
84
84
 
85
85
  ```python
86
- from iatp.registry.iatp_search_api import find_mcp_server
86
+ from traia_iatp.registry.iatp_search_api import find_mcp_server
87
87
 
88
88
  # Find by exact name
89
89
  server = find_mcp_server(name="hyperliquid-mcp")
@@ -96,7 +96,7 @@ server = find_mcp_server(capability="trading_orders")
96
96
  List MCP servers with optional filters.
97
97
 
98
98
  ```python
99
- from iatp.registry.iatp_search_api import list_mcp_servers
99
+ from traia_iatp.registry.iatp_search_api import list_mcp_servers
100
100
 
101
101
  # List all servers
102
102
  servers = list_mcp_servers(limit=10)
@@ -109,7 +109,7 @@ servers = list_mcp_servers(capabilities=["market_info"])
109
109
  Search MCP servers using vector search.
110
110
 
111
111
  ```python
112
- from iatp.registry.iatp_search_api import search_mcp_servers
112
+ from traia_iatp.registry.iatp_search_api import search_mcp_servers
113
113
 
114
114
  # Search using default embedding fields (description and capabilities)
115
115
  servers = await search_mcp_servers("trading", limit=5)
@@ -126,7 +126,7 @@ servers = await search_mcp_servers(
126
126
  Get detailed MCP server information by name (returns raw MongoDB document).
127
127
 
128
128
  ```python
129
- from iatp.registry.iatp_search_api import get_mcp_server
129
+ from traia_iatp.registry.iatp_search_api import get_mcp_server
130
130
 
131
131
  # Get full server details
132
132
  server_doc = get_mcp_server("hyperliquid-mcp")
@@ -172,7 +172,7 @@ class MCPServerInfo:
172
172
  #!/usr/bin/env python
173
173
  import asyncio
174
174
  import os
175
- from iatp.registry.iatp_search_api import (
175
+ from traia_iatp.registry.iatp_search_api import (
176
176
  find_utility_agent,
177
177
  list_utility_agents,
178
178
  search_utility_agents,
@@ -132,7 +132,7 @@ openssl s_client -connect your-cluster.mongodb.net:27017 -CAfile cert.pem
132
132
  The registry automatically detects and uses X.509 authentication when configured:
133
133
 
134
134
  ```python
135
- from iatp.registry.mongodb_registry import UtilityAgentRegistry
135
+ from traia_iatp.registry.mongodb_registry import UtilityAgentRegistry
136
136
 
137
137
  # No need to pass credentials - uses MONGODB_X509_CERT_FILE automatically
138
138
  registry = UtilityAgentRegistry()
@@ -65,7 +65,7 @@ export MONGODB_CONNECTION_STRING=mongodb+srv://user:pass@cluster.mongodb.net/...
65
65
  ### Using the High-Level API (No MongoDB Required)
66
66
 
67
67
  ```python
68
- from iatp.registry.iatp_search_api import find_utility_agent, list_mcp_servers
68
+ from traia_iatp.registry.iatp_search_api import find_utility_agent, list_mcp_servers
69
69
 
70
70
  # Find a specific utility agent
71
71
  agent = await find_utility_agent(name="trading-agent")
@@ -77,7 +77,7 @@ servers = list_mcp_servers(limit=10)
77
77
  ### Direct MongoDB Access
78
78
 
79
79
  ```python
80
- from iatp.registry.mongodb_registry import UtilityAgentRegistry
80
+ from traia_iatp.registry.mongodb_registry import UtilityAgentRegistry
81
81
 
82
82
  # Registry will automatically use configured authentication
83
83
  registry = UtilityAgentRegistry()
@@ -168,7 +168,7 @@ See [atlas_search_indexes.json](./atlas_search_indexes.json) for index definitio
168
168
  ### Register a New Utility Agent
169
169
 
170
170
  ```python
171
- from iatp.core.models import UtilityAgent, AgentEndpoints
171
+ from traia_iatp.core.models import UtilityAgent, AgentEndpoints
172
172
 
173
173
  agent = UtilityAgent(
174
174
  name="My Trading Bot",
@@ -95,7 +95,7 @@ This refactoring separates the concerns between registry management (write opera
95
95
 
96
96
  ### Registry Operations (Write)
97
97
  ```python
98
- from iatp.registry.mongodb_registry import UtilityAgentRegistry
98
+ from traia_iatp.registry.mongodb_registry import UtilityAgentRegistry
99
99
 
100
100
  # Add new agent
101
101
  registry = UtilityAgentRegistry()
@@ -108,7 +108,7 @@ await registry.update_agent_base_url(agent_id, new_url)
108
108
 
109
109
  ### Search Operations (Read)
110
110
  ```python
111
- from iatp.registry.iatp_search_api import search_utility_agents, find_utility_agent
111
+ from traia_iatp.registry.iatp_search_api import search_utility_agents, find_utility_agent
112
112
 
113
113
  # Search for agents
114
114
  agents = await search_utility_agents("hyperliquid trading", limit=5)
@@ -120,7 +120,7 @@ agent = find_utility_agent(capability="market_data")
120
120
  ### Discovery Test
121
121
  ```python
122
122
  # The discovery test now uses the search API
123
- from iatp.registry.iatp_search_api import search_utility_agents
123
+ from traia_iatp.registry.iatp_search_api import search_utility_agents
124
124
 
125
125
  # Search for Hyperliquid agent
126
126
  search_results = await search_utility_agents(
@@ -137,19 +137,19 @@ search_results = await search_utility_agents(
137
137
  1. **If using registry for search/query:**
138
138
  ```python
139
139
  # OLD
140
- from iatp.registry.mongodb_registry import UtilityAgentRegistry
140
+ from traia_iatp.registry.mongodb_registry import UtilityAgentRegistry
141
141
  registry = UtilityAgentRegistry()
142
142
  agents = await registry.query_agents(query="trading")
143
143
 
144
144
  # NEW
145
- from iatp.registry.iatp_search_api import search_utility_agents
145
+ from traia_iatp.registry.iatp_search_api import search_utility_agents
146
146
  agents = await search_utility_agents("trading")
147
147
  ```
148
148
 
149
149
  2. **If using registry for write operations:**
150
150
  ```python
151
151
  # No changes needed - these methods are still available
152
- from iatp.registry.mongodb_registry import UtilityAgentRegistry
152
+ from traia_iatp.registry.mongodb_registry import UtilityAgentRegistry
153
153
  registry = UtilityAgentRegistry()
154
154
  await registry.add_utility_agent(agent)
155
155
  ```
@@ -0,0 +1,2 @@
1
+ """IATP utility scripts."""
2
+