traia-iatp 0.1.1__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.

Potentially problematic release.


This version of traia-iatp might be problematic. Click here for more details.

Files changed (72) hide show
  1. traia_iatp/README.md +368 -0
  2. traia_iatp/__init__.py +30 -0
  3. traia_iatp/cli/__init__.py +5 -0
  4. traia_iatp/cli/main.py +483 -0
  5. traia_iatp/client/__init__.py +10 -0
  6. traia_iatp/client/a2a_client.py +274 -0
  7. traia_iatp/client/crewai_a2a_tools.py +335 -0
  8. traia_iatp/client/grpc_a2a_tools.py +349 -0
  9. traia_iatp/client/root_path_a2a_client.py +1 -0
  10. traia_iatp/core/__init__.py +43 -0
  11. traia_iatp/core/models.py +161 -0
  12. traia_iatp/mcp/__init__.py +15 -0
  13. traia_iatp/mcp/client.py +201 -0
  14. traia_iatp/mcp/mcp_agent_template.py +422 -0
  15. traia_iatp/mcp/templates/Dockerfile.j2 +56 -0
  16. traia_iatp/mcp/templates/README.md.j2 +212 -0
  17. traia_iatp/mcp/templates/cursor-rules.md.j2 +326 -0
  18. traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
  19. traia_iatp/mcp/templates/docker-compose.yml.j2 +23 -0
  20. traia_iatp/mcp/templates/dockerignore.j2 +47 -0
  21. traia_iatp/mcp/templates/gitignore.j2 +77 -0
  22. traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
  23. traia_iatp/mcp/templates/pyproject.toml.j2 +26 -0
  24. traia_iatp/mcp/templates/run_local_docker.sh.j2 +94 -0
  25. traia_iatp/mcp/templates/server.py.j2 +240 -0
  26. traia_iatp/mcp/traia_mcp_adapter.py +381 -0
  27. traia_iatp/preview_diagrams.html +181 -0
  28. traia_iatp/registry/__init__.py +26 -0
  29. traia_iatp/registry/atlas_search_indexes.json +280 -0
  30. traia_iatp/registry/embeddings.py +298 -0
  31. traia_iatp/registry/iatp_search_api.py +839 -0
  32. traia_iatp/registry/mongodb_registry.py +771 -0
  33. traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
  34. traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
  35. traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
  36. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
  37. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
  38. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
  39. traia_iatp/registry/readmes/README.md +251 -0
  40. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
  41. traia_iatp/server/__init__.py +15 -0
  42. traia_iatp/server/a2a_server.py +215 -0
  43. traia_iatp/server/example_template_usage.py +72 -0
  44. traia_iatp/server/iatp_server_agent_generator.py +237 -0
  45. traia_iatp/server/iatp_server_template_generator.py +235 -0
  46. traia_iatp/server/templates/Dockerfile.j2 +49 -0
  47. traia_iatp/server/templates/README.md +137 -0
  48. traia_iatp/server/templates/README.md.j2 +425 -0
  49. traia_iatp/server/templates/__init__.py +1 -0
  50. traia_iatp/server/templates/__main__.py.j2 +450 -0
  51. traia_iatp/server/templates/agent.py.j2 +80 -0
  52. traia_iatp/server/templates/agent_config.json.j2 +22 -0
  53. traia_iatp/server/templates/agent_executor.py.j2 +264 -0
  54. traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
  55. traia_iatp/server/templates/env.example.j2 +67 -0
  56. traia_iatp/server/templates/gitignore.j2 +78 -0
  57. traia_iatp/server/templates/grpc_server.py.j2 +218 -0
  58. traia_iatp/server/templates/pyproject.toml.j2 +76 -0
  59. traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
  60. traia_iatp/server/templates/server.py.j2 +190 -0
  61. traia_iatp/special_agencies/__init__.py +4 -0
  62. traia_iatp/special_agencies/registry_search_agency.py +392 -0
  63. traia_iatp/utils/__init__.py +10 -0
  64. traia_iatp/utils/docker_utils.py +251 -0
  65. traia_iatp/utils/general.py +64 -0
  66. traia_iatp/utils/iatp_utils.py +126 -0
  67. traia_iatp-0.1.1.dist-info/METADATA +414 -0
  68. traia_iatp-0.1.1.dist-info/RECORD +72 -0
  69. traia_iatp-0.1.1.dist-info/WHEEL +5 -0
  70. traia_iatp-0.1.1.dist-info/entry_points.txt +2 -0
  71. traia_iatp-0.1.1.dist-info/licenses/LICENSE +21 -0
  72. traia_iatp-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,349 @@
1
+ """gRPC-based A2A client for high-performance agent communication."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from typing import Dict, Any, Optional, List, Union, AsyncIterator
6
+ from datetime import datetime
7
+ import json
8
+ import uuid
9
+ from contextlib import asynccontextmanager
10
+
11
+ import grpc
12
+ from grpc import aio
13
+ from crewai.tools import BaseTool
14
+ from pydantic import Field, BaseModel
15
+
16
+ # Note: These imports assume the A2A proto files have been compiled
17
+ # In practice, you'd need to generate these from the A2A .proto definitions
18
+ # from a2a.proto import a2a_pb2, a2a_pb2_grpc
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class GrpcA2AConfig(BaseModel):
24
+ """Configuration for gRPC A2A tools."""
25
+ endpoint: str = Field(description="The gRPC endpoint (host:port)")
26
+ agency_name: str = Field(description="Name of the utility agency")
27
+ agency_description: str = Field(description="Description of what the agency does")
28
+ timeout: int = Field(default=300, description="Timeout in seconds for gRPC calls")
29
+ retry_attempts: int = Field(default=3, description="Number of retry attempts")
30
+
31
+ # gRPC specific settings
32
+ max_message_length: int = Field(default=4 * 1024 * 1024, description="Max message size (4MB default)")
33
+ keepalive_time_ms: int = Field(default=10000, description="Keepalive ping interval")
34
+ keepalive_timeout_ms: int = Field(default=5000, description="Keepalive timeout")
35
+ max_concurrent_streams: int = Field(default=100, description="Max concurrent gRPC streams")
36
+
37
+ # Connection pool settings
38
+ pool_size: int = Field(default=10, description="Number of gRPC channels in pool")
39
+ use_tls: bool = Field(default=False, description="Whether to use TLS")
40
+ tls_cert_path: Optional[str] = Field(default=None, description="Path to TLS certificate")
41
+
42
+
43
+ class GrpcChannelPool:
44
+ """Manages a pool of gRPC channels for load distribution."""
45
+
46
+ def __init__(self, config: GrpcA2AConfig):
47
+ self.config = config
48
+ self.channels: List[aio.Channel] = []
49
+ self.current_index = 0
50
+ self._lock = asyncio.Lock()
51
+
52
+ async def initialize(self):
53
+ """Initialize the channel pool."""
54
+ options = [
55
+ ('grpc.max_send_message_length', self.config.max_message_length),
56
+ ('grpc.max_receive_message_length', self.config.max_message_length),
57
+ ('grpc.keepalive_time_ms', self.config.keepalive_time_ms),
58
+ ('grpc.keepalive_timeout_ms', self.config.keepalive_timeout_ms),
59
+ ('grpc.http2.max_concurrent_streams', self.config.max_concurrent_streams),
60
+ ('grpc.enable_http_proxy', 0), # Disable proxy for direct connection
61
+ ]
62
+
63
+ for _ in range(self.config.pool_size):
64
+ if self.config.use_tls:
65
+ # Load TLS credentials
66
+ if self.config.tls_cert_path:
67
+ with open(self.config.tls_cert_path, 'rb') as f:
68
+ credentials = grpc.ssl_channel_credentials(f.read())
69
+ else:
70
+ credentials = grpc.ssl_channel_credentials()
71
+
72
+ channel = aio.secure_channel(
73
+ self.config.endpoint,
74
+ credentials,
75
+ options=options
76
+ )
77
+ else:
78
+ channel = aio.insecure_channel(
79
+ self.config.endpoint,
80
+ options=options
81
+ )
82
+
83
+ self.channels.append(channel)
84
+
85
+ logger.info(f"Initialized gRPC channel pool with {self.config.pool_size} channels")
86
+
87
+ async def get_channel(self) -> aio.Channel:
88
+ """Get the next available channel (round-robin)."""
89
+ async with self._lock:
90
+ channel = self.channels[self.current_index]
91
+ self.current_index = (self.current_index + 1) % len(self.channels)
92
+ return channel
93
+
94
+ async def close(self):
95
+ """Close all channels in the pool."""
96
+ for channel in self.channels:
97
+ await channel.close()
98
+ self.channels.clear()
99
+
100
+
101
+ class GrpcA2ATool(BaseTool):
102
+ """gRPC-based A2A tool for high-performance agent communication."""
103
+
104
+ name: str
105
+ description: str
106
+ config: GrpcA2AConfig
107
+ _channel_pool: Optional[GrpcChannelPool] = None
108
+ _initialized: bool = False
109
+
110
+ def __init__(self, config: GrpcA2AConfig):
111
+ """Initialize the gRPC A2A tool."""
112
+ tool_name = f"grpc_a2a_{config.agency_name.replace(' ', '_').replace('-', '_').lower()}"
113
+
114
+ super().__init__(
115
+ name=tool_name,
116
+ description=f"Use {config.agency_name} via gRPC A2A: {config.agency_description}",
117
+ config=config
118
+ )
119
+ self._channel_pool = None
120
+ self._initialized = False
121
+
122
+ async def _ensure_initialized(self):
123
+ """Ensure the gRPC channel pool is initialized."""
124
+ if not self._initialized:
125
+ self._channel_pool = GrpcChannelPool(self.config)
126
+ await self._channel_pool.initialize()
127
+ self._initialized = True
128
+
129
+ async def _execute_unary(self, request: str, context: Optional[Dict[str, Any]] = None) -> str:
130
+ """Execute a unary (request-response) gRPC call."""
131
+ await self._ensure_initialized()
132
+
133
+ # Get a channel from the pool
134
+ channel = await self._channel_pool.get_channel()
135
+
136
+ # Create stub (lightweight, can be created per request)
137
+ # stub = a2a_pb2_grpc.A2AServiceStub(channel)
138
+
139
+ # For now, return a placeholder since we don't have the actual proto files
140
+ # In a real implementation, you would:
141
+ # 1. Create the request message
142
+ # 2. Call the appropriate gRPC method
143
+ # 3. Handle the response
144
+
145
+ return f"gRPC call to {self.config.agency_name} with request: {request}"
146
+
147
+ async def _execute_streaming(self, request: str, context: Optional[Dict[str, Any]] = None) -> AsyncIterator[str]:
148
+ """Execute a server-streaming gRPC call."""
149
+ await self._ensure_initialized()
150
+
151
+ # Get a channel from the pool
152
+ channel = await self._channel_pool.get_channel()
153
+
154
+ # Create stub
155
+ # stub = a2a_pb2_grpc.A2AServiceStub(channel)
156
+
157
+ # For demonstration, yield simulated chunks
158
+ for i in range(5):
159
+ yield f"Chunk {i} from {self.config.agency_name}"
160
+ await asyncio.sleep(0.1)
161
+
162
+ async def _execute_with_retry(self, request: str, context: Optional[Dict[str, Any]] = None) -> str:
163
+ """Execute request with retry logic."""
164
+ last_error = None
165
+
166
+ for attempt in range(self.config.retry_attempts):
167
+ try:
168
+ return await self._execute_unary(request, context)
169
+
170
+ except grpc.RpcError as e:
171
+ last_error = f"gRPC error: {e.code()}: {e.details()}"
172
+ logger.warning(f"Attempt {attempt + 1} failed: {last_error}")
173
+
174
+ # Check if retryable
175
+ if e.code() in [
176
+ grpc.StatusCode.UNAVAILABLE,
177
+ grpc.StatusCode.DEADLINE_EXCEEDED,
178
+ grpc.StatusCode.INTERNAL
179
+ ]:
180
+ # Wait before retry with exponential backoff
181
+ if attempt < self.config.retry_attempts - 1:
182
+ await asyncio.sleep(2 ** attempt)
183
+ else:
184
+ # Non-retryable error
185
+ break
186
+
187
+ except Exception as e:
188
+ last_error = str(e)
189
+ logger.warning(f"Attempt {attempt + 1} failed: {e}")
190
+
191
+ if attempt < self.config.retry_attempts - 1:
192
+ await asyncio.sleep(2 ** attempt)
193
+
194
+ return f"Failed after {self.config.retry_attempts} attempts. Last error: {last_error}"
195
+
196
+ async def _arun(self, request: str, **kwargs) -> str:
197
+ """Async execution of the tool."""
198
+ try:
199
+ context = kwargs.get('context', {})
200
+ return await self._execute_with_retry(request, context)
201
+ except Exception as e:
202
+ logger.error(f"Error in gRPC A2A tool {self.name}: {e}")
203
+ return f"Error: {str(e)}"
204
+
205
+ def _run(self, request: str, **kwargs) -> str:
206
+ """Sync execution of the tool."""
207
+ try:
208
+ loop = asyncio.get_running_loop()
209
+ return asyncio.run_coroutine_threadsafe(
210
+ self._arun(request, **kwargs),
211
+ loop
212
+ ).result()
213
+ except RuntimeError:
214
+ return asyncio.run(self._arun(request, **kwargs))
215
+
216
+ async def stream(self, request: str, **kwargs) -> AsyncIterator[str]:
217
+ """Stream responses using server-streaming gRPC."""
218
+ try:
219
+ context = kwargs.get('context', {})
220
+ async for chunk in self._execute_streaming(request, context):
221
+ yield chunk
222
+ except Exception as e:
223
+ logger.error(f"Error in gRPC streaming: {e}")
224
+ yield f"Streaming error: {str(e)}"
225
+
226
+ async def close(self):
227
+ """Close the gRPC channel pool."""
228
+ if self._channel_pool:
229
+ await self._channel_pool.close()
230
+ self._channel_pool = None
231
+ self._initialized = False
232
+ logger.info(f"Closed gRPC connections for {self.name}")
233
+
234
+ def __del__(self):
235
+ """Cleanup gRPC channels when tool is destroyed."""
236
+ if self._channel_pool:
237
+ try:
238
+ loop = asyncio.get_running_loop()
239
+ asyncio.create_task(self.close())
240
+ except RuntimeError:
241
+ loop = asyncio.new_event_loop()
242
+ loop.run_until_complete(self.close())
243
+ loop.close()
244
+ except Exception:
245
+ pass
246
+
247
+
248
+ class GrpcA2AToolkit:
249
+ """Toolkit for creating gRPC-based A2A tools."""
250
+
251
+ @staticmethod
252
+ def create_tool_from_endpoint(
253
+ endpoint: str,
254
+ name: str,
255
+ description: str,
256
+ timeout: int = 300,
257
+ retry_attempts: int = 3,
258
+ pool_size: int = 10,
259
+ use_tls: bool = False,
260
+ tls_cert_path: Optional[str] = None
261
+ ) -> GrpcA2ATool:
262
+ """Create a gRPC A2A tool from an endpoint."""
263
+ config = GrpcA2AConfig(
264
+ endpoint=endpoint,
265
+ agency_name=name,
266
+ agency_description=description,
267
+ timeout=timeout,
268
+ retry_attempts=retry_attempts,
269
+ pool_size=pool_size,
270
+ use_tls=use_tls,
271
+ tls_cert_path=tls_cert_path
272
+ )
273
+
274
+ return GrpcA2ATool(config)
275
+
276
+ @staticmethod
277
+ def create_tools_from_endpoints(
278
+ endpoints: List[Dict[str, Any]],
279
+ default_timeout: int = 300,
280
+ default_retry_attempts: int = 3,
281
+ default_pool_size: int = 10
282
+ ) -> List[GrpcA2ATool]:
283
+ """Create multiple gRPC A2A tools."""
284
+ tools = []
285
+
286
+ for ep_config in endpoints:
287
+ tool = GrpcA2AToolkit.create_tool_from_endpoint(
288
+ endpoint=ep_config["endpoint"],
289
+ name=ep_config["name"],
290
+ description=ep_config["description"],
291
+ timeout=ep_config.get("timeout", default_timeout),
292
+ retry_attempts=ep_config.get("retry_attempts", default_retry_attempts),
293
+ pool_size=ep_config.get("pool_size", default_pool_size),
294
+ use_tls=ep_config.get("use_tls", False),
295
+ tls_cert_path=ep_config.get("tls_cert_path")
296
+ )
297
+ tools.append(tool)
298
+ logger.info(f"Created gRPC A2A tool: {tool.name}")
299
+
300
+ return tools
301
+
302
+
303
+ # Example usage with both HTTP/2 and gRPC
304
+ async def compare_protocols():
305
+ """Compare HTTP/2 and gRPC performance."""
306
+ from .crewai_a2a_tools import A2AToolkit as HttpA2AToolkit
307
+
308
+ # Create HTTP/2 tool
309
+ http_tool = HttpA2AToolkit.create_tool_from_endpoint(
310
+ endpoint="http://localhost:8000",
311
+ name="Trading Agency HTTP",
312
+ description="Trading via HTTP/2"
313
+ )
314
+
315
+ # Create gRPC tool
316
+ grpc_tool = GrpcA2AToolkit.create_tool_from_endpoint(
317
+ endpoint="localhost:50051",
318
+ name="Trading Agency gRPC",
319
+ description="Trading via gRPC",
320
+ pool_size=20 # More channels for high load
321
+ )
322
+
323
+ # Run parallel requests
324
+ import time
325
+
326
+ # HTTP/2 test
327
+ start = time.time()
328
+ http_tasks = [
329
+ http_tool._arun(f"Get price for BTC {i}")
330
+ for i in range(100)
331
+ ]
332
+ await asyncio.gather(*http_tasks)
333
+ http_time = time.time() - start
334
+
335
+ # gRPC test
336
+ start = time.time()
337
+ grpc_tasks = [
338
+ grpc_tool._arun(f"Get price for BTC {i}")
339
+ for i in range(100)
340
+ ]
341
+ await asyncio.gather(*grpc_tasks)
342
+ grpc_time = time.time() - start
343
+
344
+ print(f"HTTP/2 time: {http_time:.2f}s")
345
+ print(f"gRPC time: {grpc_time:.2f}s")
346
+
347
+ # Cleanup
348
+ await http_tool.close()
349
+ await grpc_tool.close()
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,43 @@
1
+ """Core data models for IATP."""
2
+
3
+ from .models import (
4
+ # Core server models
5
+ MCPServer,
6
+ MCPServerType,
7
+
8
+ # Agent models
9
+ AgentSkill,
10
+ AgentCapabilities,
11
+ AgentCard,
12
+
13
+ # IATP protocol models
14
+ IATPEndpoints,
15
+ IATPRequest,
16
+ IATPResponse,
17
+
18
+ # Utility agent models
19
+ UtilityAgent,
20
+ UtilityAgentStatus,
21
+ UtilityAgentRegistryEntry,
22
+ )
23
+
24
+ __all__ = [
25
+ # Core server models
26
+ "MCPServer",
27
+ "MCPServerType",
28
+
29
+ # Agent models
30
+ "AgentSkill",
31
+ "AgentCapabilities",
32
+ "AgentCard",
33
+
34
+ # IATP protocol models
35
+ "IATPEndpoints",
36
+ "IATPRequest",
37
+ "IATPResponse",
38
+
39
+ # Utility agent models
40
+ "UtilityAgent",
41
+ "UtilityAgentStatus",
42
+ "UtilityAgentRegistryEntry",
43
+ ]
@@ -0,0 +1,161 @@
1
+ """Core data models for IATP."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional, Dict, Any, List
5
+ from pydantic import BaseModel, Field, HttpUrl
6
+ from enum import Enum
7
+
8
+
9
+ class MCPServerType(str, Enum):
10
+ """Types of MCP servers."""
11
+ STREAMABLE_HTTP = "streamable-http"
12
+
13
+
14
+ class MCPServer(BaseModel):
15
+ """MCP Server specification."""
16
+ id: Optional[str] = Field(default=None, description="Unique identifier")
17
+ name: str = Field(..., description="Name of the MCP server")
18
+ url: str = Field(..., description="URL or path to connect to the MCP server")
19
+ server_type: MCPServerType = Field(default=MCPServerType.STREAMABLE_HTTP, description="Type of MCP server connection")
20
+ description: str = Field(..., description="Description of what the server enables")
21
+ capabilities: List[str] = Field(default_factory=list, description="List of capabilities/APIs")
22
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
23
+
24
+ class Config:
25
+ json_encoders = {HttpUrl: str}
26
+
27
+
28
+ class AgentSkill(BaseModel):
29
+ """IATP Agent skill definition."""
30
+ id: str = Field(..., description="Unique skill identifier")
31
+ name: str = Field(..., description="Human-readable skill name")
32
+ description: str = Field(..., description="Detailed skill description")
33
+ examples: List[str] = Field(default_factory=list, description="Example usage patterns")
34
+ input_modes: List[str] = Field(default_factory=list, description="Supported input modes")
35
+ output_modes: List[str] = Field(default_factory=list, description="Supported output modes")
36
+ tags: List[str] = Field(default_factory=list, description="Tags for categorization")
37
+
38
+
39
+ class AgentCapabilities(BaseModel):
40
+ """IATP Agent capabilities."""
41
+ streaming: bool = Field(default=False, description="Supports SSE streaming")
42
+ push_notifications: bool = Field(default=False, description="Supports push notifications")
43
+ state_transition_history: bool = Field(default=False, description="Maintains state history")
44
+ custom_features: Dict[str, Any] = Field(default_factory=dict, description="Custom capabilities")
45
+
46
+
47
+ class AgentCard(BaseModel):
48
+ """IATP Agent card for discovery and initialization."""
49
+ name: str = Field(..., description="Agent name")
50
+ description: str = Field(..., description="Agent description")
51
+ version: str = Field(..., description="Agent version")
52
+ skills: List[AgentSkill] = Field(default_factory=list, description="Available skills")
53
+ capabilities: AgentCapabilities = Field(default_factory=AgentCapabilities, description="Agent capabilities")
54
+ default_input_modes: List[str] = Field(default_factory=list, description="Default input modes")
55
+ default_output_modes: List[str] = Field(default_factory=list, description="Default output modes")
56
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
57
+
58
+
59
+ class IATPEndpoints(BaseModel):
60
+ """IATP server endpoints configuration.
61
+
62
+ Note: The A2A protocol only defines a minimal set of endpoints:
63
+ - Root path (/) for JSON-RPC
64
+ - /.well-known/agent.json for agent card
65
+ - /a2a/tasks/* for SSE subscriptions (if streaming is supported)
66
+
67
+ Health and info endpoints are NOT part of the A2A protocol standard.
68
+ """
69
+ base_url: str = Field(..., description="Base URL of the IATP server")
70
+ iatp_endpoint: str = Field(..., description="Main IATP JSON-RPC endpoint (usually at root path)")
71
+ streaming_endpoint: Optional[str] = Field(None, description="SSE streaming endpoint (same as iatp_endpoint when supported)")
72
+ health_endpoint: Optional[str] = Field(None, description="Health check endpoint (not part of A2A protocol)")
73
+ info_endpoint: Optional[str] = Field(None, description="Agent info endpoint (not part of A2A protocol)")
74
+ agent_card_endpoint: str = Field(..., description="Agent card endpoint (.well-known/agent.json)")
75
+ subscribe_endpoint: Optional[str] = Field(None, description="SSE subscription endpoint (/a2a/tasks/subscribe)")
76
+ resubscribe_endpoint: Optional[str] = Field(None, description="SSE resubscription endpoint (/a2a/tasks/resubscribe)")
77
+
78
+
79
+ class UtilityAgentStatus(str, Enum):
80
+ """Status of a utility agent."""
81
+ GENERATED = "generated"
82
+ DEPLOYING = "deploying"
83
+ DEPLOYED = "deployed"
84
+ RUNNING = "running"
85
+ STOPPED = "stopped"
86
+ ERROR = "error"
87
+
88
+
89
+ #this is the model for internal management of utility agents by their creator and traia protocol (should be persisted into db)
90
+ class UtilityAgent(BaseModel):
91
+ """Utility Agent configuration and metadata."""
92
+ id: str = Field(..., description="Unique identifier for the agent")
93
+ name: str = Field(..., description="Name of the utility agent")
94
+ description: str = Field(..., description="Description of the agent's purpose")
95
+ mcp_server_id: str = Field(..., description="ID of the associated MCP server")
96
+
97
+ # IATP specific fields
98
+ agent_card: Optional[AgentCard] = Field(None, description="IATP agent card for discovery")
99
+ endpoints: Optional[IATPEndpoints] = Field(None, description="IATP endpoints configuration")
100
+
101
+ capabilities: List[str] = Field(default_factory=list, description="List of exposed capabilities")
102
+ status: UtilityAgentStatus = Field(default=UtilityAgentStatus.GENERATED)
103
+ code_path: Optional[str] = Field(None, description="Path to generated code")
104
+ docker_image: Optional[str] = Field(None, description="Docker image name when built")
105
+ github_repo: Optional[str] = Field(None, description="GitHub repository URL")
106
+ cloud_run_url: Optional[str] = Field(None, description="Cloud Run deployment URL")
107
+
108
+ # Search and discovery
109
+ search_text: Optional[str] = Field(None, description="Concatenated searchable text")
110
+ tags: List[str] = Field(default_factory=list, description="Tags for search and categorization")
111
+
112
+ created_at: datetime = Field(default_factory=datetime.utcnow)
113
+ updated_at: datetime = Field(default_factory=datetime.utcnow)
114
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
115
+
116
+ class Config:
117
+ json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
118
+
119
+
120
+ #this is a model for registry discovery that will be used by the mongodb indexes
121
+ class UtilityAgentRegistryEntry(BaseModel):
122
+ """Registry entry for a deployed utility agent."""
123
+ agent_id: str = Field(..., description="ID of the utility agent")
124
+ name: str = Field(..., description="Name for discovery")
125
+ description: str = Field(..., description="Description for search")
126
+
127
+ # Base URL for the agent - all endpoints are derived from this
128
+ base_url: Optional[str] = Field(None, description="Base URL of the deployed agent")
129
+
130
+ # Enhanced IATP discovery fields
131
+ agent_card: Optional[AgentCard] = Field(None, description="IATP agent card")
132
+ endpoints: Optional[IATPEndpoints] = Field(None, description="IATP endpoints")
133
+
134
+ capabilities: List[str] = Field(..., description="List of capabilities")
135
+ skills: List[AgentSkill] = Field(default_factory=list, description="Detailed skills from agent card")
136
+ tags: List[str] = Field(default_factory=list, description="Tags for search")
137
+
138
+ # Search optimization
139
+ search_text: Optional[str] = Field(None, description="Full text for search")
140
+
141
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
142
+ registered_at: datetime = Field(default_factory=datetime.utcnow)
143
+ last_health_check: Optional[datetime] = Field(None, description="Last successful health check")
144
+ is_active: bool = Field(default=True, description="Whether the agent is active")
145
+
146
+ class Config:
147
+ json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
148
+
149
+
150
+ class IATPRequest(BaseModel):
151
+ """IATP protocol request."""
152
+ action: str = Field(..., description="Action to perform")
153
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Action parameters")
154
+ context: Dict[str, Any] = Field(default_factory=dict, description="Request context")
155
+
156
+
157
+ class IATPResponse(BaseModel):
158
+ """IATP protocol response."""
159
+ result: Any = Field(..., description="Result of the action")
160
+ status: str = Field(..., description="Status of the response")
161
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Response metadata")
@@ -0,0 +1,15 @@
1
+ """MCP (Model Context Protocol) integration module."""
2
+
3
+ from .client import MCPClient
4
+ from .mcp_agent_template import MCPServerConfig, MCPAgentBuilder, run_with_mcp_tools, MCPServerInfo
5
+ from .traia_mcp_adapter import TraiaMCPAdapter, create_mcp_adapter
6
+
7
+ __all__ = [
8
+ "MCPClient",
9
+ "MCPServerConfig",
10
+ "MCPAgentBuilder",
11
+ "run_with_mcp_tools",
12
+ "MCPServerInfo",
13
+ "TraiaMCPAdapter",
14
+ "create_mcp_adapter",
15
+ ]