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,450 @@
1
+ """
2
+ {{ agent_name }} - A2A Server Main Entry Point
3
+
4
+ This module initializes and starts the A2A server for {{ agent_name }}.
5
+ Auto-generated for the {{ agent_name }} utility agent.
6
+ Supports HTTP/2 multiplexing and SSE streaming.
7
+ """
8
+
9
+ import logging
10
+ import os
11
+ import json
12
+ import ssl
13
+ from pathlib import Path
14
+ from typing import AsyncIterator, Dict, Any, Optional
15
+ import asyncio
16
+ import uuid
17
+ from datetime import datetime
18
+ import time
19
+ import tempfile
20
+ import subprocess
21
+
22
+ from a2a.server.apps import A2AStarletteApplication
23
+ from a2a.types import AgentCard, AgentSkill, AgentCapabilities
24
+ from a2a.server.tasks import InMemoryTaskStore
25
+ from a2a.server.request_handlers import DefaultRequestHandler
26
+ from a2a.server.events.event_queue import EventQueue
27
+ from hypercorn.asyncio import serve
28
+ from hypercorn.config import Config
29
+ from starlette.responses import StreamingResponse
30
+ from starlette.requests import Request
31
+ from starlette.routing import Route
32
+ from starlette.middleware.base import BaseHTTPMiddleware
33
+ from starlette.responses import Response
34
+
35
+
36
+ import sys
37
+ from pathlib import Path
38
+ # Add parent directory to path to import mcp_agent_template
39
+ sys.path.insert(0, str(Path(__file__).parent.parent))
40
+ from traia_iatp.mcp import MCPServerConfig
41
+ from .agent_executor import {{ class_name }}AgentExecutor
42
+
43
+ # Configure logging
44
+ logging.basicConfig(
45
+ level=logging.INFO,
46
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
47
+ )
48
+ logger = logging.getLogger(__name__)
49
+
50
+ # Enable debug logging for HTTP/2 and connection events if requested
51
+ if os.environ.get("DEBUG_PROTOCOL", "false").lower() == "true":
52
+ logging.getLogger("hypercorn.access").setLevel(logging.DEBUG)
53
+ logging.getLogger("hypercorn.error").setLevel(logging.DEBUG)
54
+ logging.getLogger("httpcore").setLevel(logging.DEBUG)
55
+ logging.getLogger("httpx").setLevel(logging.DEBUG)
56
+ logging.getLogger("h2").setLevel(logging.DEBUG)
57
+ logging.getLogger("a2a").setLevel(logging.DEBUG)
58
+ logger.setLevel(logging.DEBUG)
59
+ logger.info("Protocol-level debug logging enabled")
60
+
61
+
62
+ class ProtocolLoggingMiddleware(BaseHTTPMiddleware):
63
+ """Middleware to log HTTP protocol details for debugging."""
64
+
65
+ def __init__(self, app):
66
+ super().__init__(app)
67
+ self.request_counter = 0
68
+
69
+ async def dispatch(self, request: Request, call_next):
70
+ self.request_counter += 1
71
+ request_id = self.request_counter
72
+
73
+ # Log request details
74
+ logger.info(f"[Request {request_id}] {request.method} {request.url.path}")
75
+ logger.info(f"[Request {request_id}] Client: {request.client}")
76
+ logger.info(f"[Request {request_id}] Headers: {dict(request.headers)}")
77
+
78
+ # Check HTTP version
79
+ http_version = request.scope.get("http_version", "unknown")
80
+ logger.info(f"[Request {request_id}] HTTP Version: {http_version}")
81
+
82
+ # Check if it's HTTP/2
83
+ if http_version == "2.0":
84
+ logger.info(f"[Request {request_id}] ✅ HTTP/2 connection detected")
85
+ stream_id = request.scope.get("stream_id", "unknown")
86
+ logger.info(f"[Request {request_id}] HTTP/2 Stream ID: {stream_id}")
87
+ else:
88
+ logger.info(f"[Request {request_id}] ⚠️ HTTP/1.1 connection (not HTTP/2)")
89
+
90
+ # Time the request
91
+ start_time = time.time()
92
+
93
+ # Process the request
94
+ response = await call_next(request)
95
+
96
+ # Log response details
97
+ process_time = time.time() - start_time
98
+ logger.info(f"[Request {request_id}] Response Status: {response.status_code}")
99
+ logger.info(f"[Request {request_id}] Process Time: {process_time:.3f}s")
100
+
101
+ return response
102
+
103
+
104
+ class StreamingRequestHandler(DefaultRequestHandler):
105
+ """Extended request handler with SSE streaming support."""
106
+
107
+ def __init__(self, agent_executor, task_store):
108
+ super().__init__(agent_executor, task_store)
109
+ self._active_streams: Dict[str, EventQueue] = {}
110
+
111
+ async def handle_subscribe(self, request: Dict[str, Any]) -> StreamingResponse:
112
+ """Handle tasks/sendSubscribe requests for SSE streaming."""
113
+ params = request.get("params", {})
114
+ task_id = params.get("id")
115
+ history_length = params.get("historyLength", 0)
116
+
117
+ if not task_id:
118
+ return {"error": {"code": -32602, "message": "Missing task ID"}}
119
+
120
+ # Get or create event queue for this task
121
+ event_queue = self._active_streams.get(task_id)
122
+ if not event_queue:
123
+ event_queue = EventQueue()
124
+ self._active_streams[task_id] = event_queue
125
+
126
+ async def event_generator():
127
+ """Generate SSE events."""
128
+ try:
129
+ # Send initial connection event
130
+ yield f"data: {json.dumps({'type': 'connection', 'status': 'connected', 'task_id': task_id})}\n\n"
131
+
132
+ # Send history if requested
133
+ if history_length > 0:
134
+ # TODO: Implement history retrieval from task store
135
+ pass
136
+
137
+ # Stream events
138
+ event_count = 0
139
+ while True:
140
+ try:
141
+ # Wait for events with timeout
142
+ event = await asyncio.wait_for(
143
+ event_queue.dequeue_event(),
144
+ timeout=30.0 # 30 second timeout
145
+ )
146
+
147
+ if event:
148
+ event_count += 1
149
+ # Handle different event types
150
+ if hasattr(event, 'type') and hasattr(event, 'data'):
151
+ event_data = {
152
+ "type": event.type,
153
+ "sequence": event_count,
154
+ "timestamp": datetime.utcnow().isoformat(),
155
+ "data": event.data
156
+ }
157
+ else:
158
+ # Fallback for simple message events
159
+ event_data = {
160
+ "type": "message",
161
+ "sequence": event_count,
162
+ "timestamp": datetime.utcnow().isoformat(),
163
+ "data": str(event)
164
+ }
165
+ yield f"data: {json.dumps(event_data)}\n\n"
166
+
167
+ # Check for completion
168
+ if hasattr(event, 'type') and event.type == "status" and hasattr(event, 'data') and event.data.get("state") in ["COMPLETED", "FAILED"]:
169
+ yield "data: [DONE]\n\n"
170
+ break
171
+ except asyncio.TimeoutError:
172
+ # Send keepalive
173
+ yield f"data: {json.dumps({'type': 'keepalive', 'timestamp': datetime.utcnow().isoformat()})}\n\n"
174
+
175
+ except Exception as e:
176
+ logger.error(f"Error in SSE stream: {e}")
177
+ yield f"data: {json.dumps({'type': 'error', 'message': str(e)})}\n\n"
178
+ finally:
179
+ # Cleanup
180
+ if task_id in self._active_streams:
181
+ del self._active_streams[task_id]
182
+
183
+ return StreamingResponse(
184
+ event_generator(),
185
+ media_type="text/event-stream",
186
+ headers={
187
+ "Cache-Control": "no-cache",
188
+ "Connection": "keep-alive",
189
+ "X-Accel-Buffering": "no" # Disable nginx buffering
190
+ }
191
+ )
192
+
193
+ async def handle_resubscribe(self, request: Dict[str, Any]) -> StreamingResponse:
194
+ """Handle tasks/resubscribe requests to resume SSE streaming."""
195
+ # Resubscribe uses the same logic as subscribe
196
+ # but may start from a different history point
197
+ return await self.handle_subscribe(request)
198
+
199
+
200
+ def create_app():
201
+ """Create and configure the A2A application with SSE support."""
202
+
203
+ # Load agent configuration if it exists
204
+ config_path = "agent_config.json"
205
+ if os.path.exists(config_path):
206
+ with open(config_path, "r") as f:
207
+ config_data = json.load(f)
208
+ mcp_data = config_data.get("mcp_server", {})
209
+ else:
210
+ # Use template variables directly
211
+ mcp_data = {
212
+ "name": "{{ mcp_server_name }}",
213
+ "url": "{{ mcp_server_url }}",
214
+ "description": "{{ mcp_server_description }}",
215
+ "server_type": "{{ mcp_server_type }}",
216
+ "capabilities": {{ mcp_server_capabilities | tojson }},
217
+ "metadata": {{ mcp_server_metadata | tojson }}
218
+ }
219
+
220
+ # Create MCP server configuration
221
+ mcp_config = MCPServerConfig(
222
+ name=mcp_data["name"],
223
+ url=mcp_data["url"],
224
+ description=mcp_data["description"],
225
+ server_type=mcp_data.get("server_type", "streamable-http"),
226
+ capabilities=mcp_data.get("capabilities", []),
227
+ metadata=mcp_data.get("metadata", {})
228
+ )
229
+
230
+ # Check if MCP server supports streaming
231
+ supports_streaming = mcp_data.get("server_type") == "streamable-http" or \
232
+ "stream" in mcp_data.get("capabilities", [])
233
+
234
+ # Create agent skills based on MCP capabilities
235
+ skills = []
236
+
237
+ # Add main processing skill
238
+ main_skill = AgentSkill(
239
+ id="process_request",
240
+ name=f"Process request using {{ agent_name }}",
241
+ description="{{ agent_description }}",
242
+ examples=[
243
+ {% for example in skill_examples %}
244
+ "{{ example }}",
245
+ {% endfor %}
246
+ ],
247
+ inputModes=["text", "text/plain"],
248
+ outputModes=["text", "text/plain", "text/event-stream"] if supports_streaming else ["text", "text/plain"],
249
+ tags=[
250
+ "mcp", "{{ mcp_server_name }}", "utility",
251
+ {% if mcp_server_metadata.tags %}
252
+ {% for tag in mcp_server_metadata.tags %}
253
+ "{{ tag }}",
254
+ {% endfor %}
255
+ {% endif %}
256
+ {% for capability in mcp_server_capabilities[:5] %}
257
+ "{{ capability }}",
258
+ {% endfor %}
259
+ ]
260
+ )
261
+ skills.append(main_skill)
262
+
263
+ {% if expose_individual_tools %}
264
+ # Add individual MCP tool skills
265
+ {% for capability in mcp_server_capabilities %}
266
+ skill_{{ loop.index }} = AgentSkill(
267
+ id="mcp_{{ capability }}",
268
+ name="Execute {{ capability }}",
269
+ description="Execute {{ capability }} tool on MCP server",
270
+ examples=[f"Run {{ capability }} with these parameters"],
271
+ inputModes=["text", "text/plain"],
272
+ outputModes=["text", "text/plain", "text/event-stream"] if supports_streaming else ["text", "text/plain"],
273
+ tags=["mcp", "{{ mcp_server_name }}", "{{ capability }}"]
274
+ )
275
+ skills.append(skill_{{ loop.index }})
276
+ {% endfor %}
277
+ {% endif %}
278
+
279
+ # Create capabilities with streaming support if available
280
+ capabilities = AgentCapabilities(
281
+ streaming=supports_streaming, # Enable streaming if MCP server supports it
282
+ pushNotifications=False, # Can be extended to support push notifications
283
+ stateTransitionHistory=True # Enable for SSE history support
284
+ )
285
+
286
+ # Authentication can be added here if needed in the future
287
+ # Currently the A2A protocol handles authentication at a different layer
288
+
289
+ # Create agent card
290
+ agent_card = AgentCard(
291
+ name="{{ agent_id }}",
292
+ description="{{ agent_description }}",
293
+ url=f"http://0.0.0.0:{os.environ.get('PORT', 8000)}",
294
+ version="{{ agent_version }}",
295
+ capabilities=capabilities,
296
+ skills=skills,
297
+ defaultInputModes=["text", "text/plain"],
298
+ defaultOutputModes=["text", "text/plain", "text/event-stream"] if supports_streaming else ["text", "text/plain"]
299
+ )
300
+
301
+ # Create executor with MCP config
302
+ executor = {{ class_name }}AgentExecutor(mcp_config, supports_streaming=supports_streaming)
303
+
304
+ # Create task store and request handler with streaming support
305
+ task_store = InMemoryTaskStore()
306
+ request_handler = StreamingRequestHandler(
307
+ agent_executor=executor,
308
+ task_store=task_store
309
+ )
310
+
311
+ # Create the A2A application
312
+ app = A2AStarletteApplication(
313
+ agent_card=agent_card,
314
+ http_handler=request_handler
315
+ )
316
+
317
+ # Build the Starlette app - this should add all necessary routes including JSON-RPC endpoint
318
+ starlette_app = app.build()
319
+
320
+ # Add protocol logging middleware if debug mode is enabled
321
+ if os.environ.get("DEBUG_PROTOCOL", "false").lower() == "true":
322
+ starlette_app.add_middleware(ProtocolLoggingMiddleware)
323
+ logger.info("Added protocol logging middleware")
324
+
325
+ # Log all routes that were created
326
+ logger.info("Routes created by A2A application:")
327
+ for route in starlette_app.routes:
328
+ if hasattr(route, 'path') and hasattr(route, 'methods'):
329
+ logger.info(f" {route.methods} {route.path}")
330
+ else:
331
+ logger.info(f" {route}")
332
+
333
+ # The A2AStarletteApplication.build() should have already added the JSON-RPC endpoint
334
+ # We only need to add custom SSE endpoints if they're not already included
335
+
336
+ # Add SSE endpoints using Starlette's routing
337
+ async def handle_subscribe_endpoint(request: Request):
338
+ """Handle SSE subscription requests."""
339
+ data = await request.json()
340
+ return await request_handler.handle_subscribe(data)
341
+
342
+ async def handle_resubscribe_endpoint(request: Request):
343
+ """Handle SSE resubscription requests."""
344
+ data = await request.json()
345
+ return await request_handler.handle_resubscribe(data)
346
+
347
+ # Add custom SSE routes (these might not be included by default)
348
+ starlette_app.routes.append(
349
+ Route("/a2a/tasks/subscribe", handle_subscribe_endpoint, methods=["POST"])
350
+ )
351
+ starlette_app.routes.append(
352
+ Route("/a2a/tasks/resubscribe", handle_resubscribe_endpoint, methods=["POST"])
353
+ )
354
+
355
+ return starlette_app
356
+
357
+
358
+ def generate_self_signed_cert(cert_path: str = "cert.pem", key_path: str = "key.pem") -> None:
359
+ """Generate self-signed certificate for local development."""
360
+ if Path(cert_path).exists() and Path(key_path).exists():
361
+ logger.info("TLS certificates already exist")
362
+ return
363
+
364
+ logger.info("Generating self-signed certificates for local development...")
365
+
366
+ try:
367
+ # Generate self-signed certificate using openssl
368
+ subprocess.run([
369
+ "openssl", "req", "-x509", "-newkey", "rsa:4096",
370
+ "-keyout", key_path, "-out", cert_path,
371
+ "-days", "365", "-nodes",
372
+ "-subj", "/CN=localhost/O=A2A Development/C=US"
373
+ ], check=True, capture_output=True)
374
+
375
+ logger.info(f"Generated self-signed certificates: {cert_path}, {key_path}")
376
+ except subprocess.CalledProcessError as e:
377
+ logger.error(f"Failed to generate certificates: {e}")
378
+ logger.error(f"Stdout: {e.stdout}")
379
+ logger.error(f"Stderr: {e.stderr}")
380
+ raise
381
+ except FileNotFoundError:
382
+ logger.error("OpenSSL not found. Please install OpenSSL to generate certificates.")
383
+ raise
384
+
385
+
386
+ async def main():
387
+ """Main function to start the A2A server with HTTP/2 support."""
388
+ # Get configuration from environment
389
+ host = os.environ.get("HOST", "0.0.0.0")
390
+ port = int(os.environ.get("PORT", 8000))
391
+
392
+ # Create the application
393
+ app = create_app()
394
+
395
+ # Configure Hypercorn for HTTP/2 support
396
+ config = Config()
397
+ config.bind = [f"{host}:{port}"]
398
+ config.alpn_protocols = ["h2", "http/1.1"] # Support both HTTP/2 and HTTP/1.1
399
+
400
+ # Enable access logging if debug mode is on
401
+ if os.environ.get("DEBUG_PROTOCOL", "false").lower() == "true":
402
+ config.accesslog = "-" # Log to stdout
403
+ config.errorlog = "-" # Log errors to stdout
404
+ config.loglevel = "DEBUG"
405
+ logger.info("Hypercorn access logging enabled")
406
+
407
+ # Enable HTTP/2
408
+ config.h2_max_concurrent_streams = 100
409
+ config.h2_max_header_list_size = 8192
410
+ config.h2_max_inbound_frame_size = 16384
411
+ config.h2_initial_connection_window_size = 65536
412
+
413
+ # Connection settings for high performance
414
+ config.keep_alive_timeout = 300 # 5 minutes
415
+ config.max_requests = 10000 # Max requests per connection
416
+ config.max_requests_jitter = 1000 # Add jitter to prevent thundering herd
417
+
418
+ # SSL/TLS configuration for production (optional)
419
+ if os.environ.get("USE_TLS", "false").lower() == "true":
420
+ cert_path = os.environ.get("TLS_CERT_PATH", "cert.pem")
421
+ key_path = os.environ.get("TLS_KEY_PATH", "key.pem")
422
+
423
+ # Generate self-signed certificates for local development if needed
424
+ if os.environ.get("GENERATE_CERTS", "true").lower() == "true":
425
+ generate_self_signed_cert(cert_path, key_path)
426
+
427
+ if Path(cert_path).exists() and Path(key_path).exists():
428
+ config.certfile = cert_path
429
+ config.keyfile = key_path
430
+ config.alpn_protocols = ["h2", "http/1.1"]
431
+ logger.info("TLS enabled with HTTP/2 support")
432
+ logger.info("Using HTTPS - connect to https://localhost:8000")
433
+ else:
434
+ logger.warning("TLS requested but certificates not found")
435
+
436
+ # Log startup information
437
+ logger.info(f"Starting {{ agent_name }} A2A Server with HTTP/2 support")
438
+ logger.info(f"MCP Server: {{ mcp_server_name }}")
439
+ logger.info(f"Listening on {host}:{port}")
440
+ logger.info(f"Agent Card available at: http://{host}:{port}/.well-known/agent.json")
441
+ logger.info(f"SSE endpoints available at: /a2a/tasks/subscribe and /a2a/tasks/resubscribe")
442
+ logger.info(f"HTTP/2 multiplexing enabled with max {config.h2_max_concurrent_streams} concurrent streams")
443
+
444
+ # Run the server with Hypercorn
445
+ await serve(app, config)
446
+
447
+
448
+ if __name__ == "__main__":
449
+ import asyncio
450
+ asyncio.run(main())
@@ -0,0 +1,80 @@
1
+ """
2
+ {{ agent_name }} - CrewAI Agent Implementation
3
+
4
+ This module defines the CrewAI agent that wraps the {{ mcp_server_name }} MCP server.
5
+ Auto-generated for the {{ agent_name }} utility agent.
6
+ """
7
+
8
+ import logging
9
+ from typing import List, Dict, Any, Optional
10
+ from crewai import Agent, Task, Crew, LLM
11
+ import os
12
+
13
+ # Import MCP integration from traia_iatp.mcp
14
+ from traia_iatp.mcp import MCPServerConfig, MCPAgentBuilder, run_with_mcp_tools, MCPServerInfo
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class {{ class_name }}Agent:
20
+ """{{ agent_name }} agent that processes requests using {{ mcp_server_name }}."""
21
+
22
+ def __init__(self, mcp_config: MCPServerConfig):
23
+ self.mcp_config = mcp_config
24
+ self.mcp_server_info = self._create_server_info()
25
+
26
+ def _create_server_info(self) -> MCPServerInfo:
27
+ """Create MCPServerInfo from config."""
28
+ return MCPServerInfo(
29
+ id="", # Not needed for direct usage
30
+ name=self.mcp_config.name,
31
+ url=self.mcp_config.url,
32
+ description=self.mcp_config.description,
33
+ server_type=self.mcp_config.server_type,
34
+ capabilities=self.mcp_config.capabilities,
35
+ metadata=self.mcp_config.metadata,
36
+ tags=self.mcp_config.metadata.get("tags", [])
37
+ )
38
+
39
+ def create_agent(self, tools_subset: Optional[List[str]] = None) -> Agent:
40
+ """Create a CrewAI agent for this MCP server."""
41
+ # Use MCPAgentBuilder to create the agent
42
+ return MCPAgentBuilder.create_agent(
43
+ role="{{ agent_name }} Specialist",
44
+ goal="Process requests using {{ mcp_server_name }} capabilities to provide accurate and helpful responses",
45
+ backstory=(
46
+ "You are an expert at using {{ mcp_server_name }}. "
47
+ "{{ mcp_server_description }} "
48
+ "You excel at understanding user requests and utilizing the available tools to provide comprehensive solutions."
49
+ ),
50
+ verbose=True,
51
+ allow_delegation=False,
52
+ tools_subset=tools_subset
53
+ )
54
+
55
+ def process_request(self, request: str, context: Dict[str, Any] = None) -> str:
56
+ """Process a request using the MCP server capabilities."""
57
+ try:
58
+ # Create an agent for this request
59
+ agent = self.create_agent()
60
+
61
+ # Create a task
62
+ task = Task(
63
+ description=request,
64
+ expected_output="A comprehensive response based on {{ mcp_server_name }} capabilities",
65
+ agent=agent
66
+ )
67
+
68
+ # Run with MCP tools
69
+ result = run_with_mcp_tools(
70
+ tasks=[task],
71
+ mcp_server=self.mcp_server_info,
72
+ inputs=context or {},
73
+ skip_health_check=True # Skip for production usage
74
+ )
75
+
76
+ return str(result)
77
+
78
+ except Exception as e:
79
+ logger.error(f"Error processing request: {e}")
80
+ raise
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "{{ agent_name }}",
3
+ "description": "{{ agent_description }}",
4
+ "version": "{{ agent_version }}",
5
+ "agent_id": "{{ agent_id }}",
6
+ "mcp_server": {
7
+ "name": "{{ mcp_server_name }}",
8
+ "url": "{{ mcp_server_url }}",
9
+ "description": "{{ mcp_server_description }}",
10
+ "server_type": "{{ mcp_server_type }}",
11
+ "capabilities": {{ mcp_server_capabilities | tojson }},
12
+ "metadata": {{ mcp_server_metadata | tojson }}
13
+ },
14
+ "a2a_config": {
15
+ "expose_individual_tools": {{ expose_individual_tools | lower }},
16
+ "auth_required": {{ auth_required | lower }},
17
+ {% if auth_required %}
18
+ "auth_schemes": {{ auth_schemes | tojson }},
19
+ {% endif %}
20
+ "skill_examples": {{ skill_examples | tojson }}
21
+ }
22
+ }