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.
- traia_iatp/README.md +368 -0
- traia_iatp/__init__.py +30 -0
- traia_iatp/cli/__init__.py +5 -0
- traia_iatp/cli/main.py +483 -0
- traia_iatp/client/__init__.py +10 -0
- traia_iatp/client/a2a_client.py +274 -0
- traia_iatp/client/crewai_a2a_tools.py +335 -0
- traia_iatp/client/grpc_a2a_tools.py +349 -0
- traia_iatp/client/root_path_a2a_client.py +1 -0
- traia_iatp/core/__init__.py +43 -0
- traia_iatp/core/models.py +161 -0
- traia_iatp/mcp/__init__.py +15 -0
- traia_iatp/mcp/client.py +201 -0
- traia_iatp/mcp/mcp_agent_template.py +422 -0
- traia_iatp/mcp/templates/Dockerfile.j2 +56 -0
- traia_iatp/mcp/templates/README.md.j2 +212 -0
- traia_iatp/mcp/templates/cursor-rules.md.j2 +326 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
- traia_iatp/mcp/templates/docker-compose.yml.j2 +23 -0
- traia_iatp/mcp/templates/dockerignore.j2 +47 -0
- traia_iatp/mcp/templates/gitignore.j2 +77 -0
- traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
- traia_iatp/mcp/templates/pyproject.toml.j2 +26 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +94 -0
- traia_iatp/mcp/templates/server.py.j2 +240 -0
- traia_iatp/mcp/traia_mcp_adapter.py +381 -0
- traia_iatp/preview_diagrams.html +181 -0
- traia_iatp/registry/__init__.py +26 -0
- traia_iatp/registry/atlas_search_indexes.json +280 -0
- traia_iatp/registry/embeddings.py +298 -0
- traia_iatp/registry/iatp_search_api.py +839 -0
- traia_iatp/registry/mongodb_registry.py +771 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
- traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
- traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
- traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
- traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
- traia_iatp/registry/readmes/README.md +251 -0
- traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
- traia_iatp/server/__init__.py +15 -0
- traia_iatp/server/a2a_server.py +215 -0
- traia_iatp/server/example_template_usage.py +72 -0
- traia_iatp/server/iatp_server_agent_generator.py +237 -0
- traia_iatp/server/iatp_server_template_generator.py +235 -0
- traia_iatp/server/templates/Dockerfile.j2 +49 -0
- traia_iatp/server/templates/README.md +137 -0
- traia_iatp/server/templates/README.md.j2 +425 -0
- traia_iatp/server/templates/__init__.py +1 -0
- traia_iatp/server/templates/__main__.py.j2 +450 -0
- traia_iatp/server/templates/agent.py.j2 +80 -0
- traia_iatp/server/templates/agent_config.json.j2 +22 -0
- traia_iatp/server/templates/agent_executor.py.j2 +264 -0
- traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
- traia_iatp/server/templates/env.example.j2 +67 -0
- traia_iatp/server/templates/gitignore.j2 +78 -0
- traia_iatp/server/templates/grpc_server.py.j2 +218 -0
- traia_iatp/server/templates/pyproject.toml.j2 +76 -0
- traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
- traia_iatp/server/templates/server.py.j2 +190 -0
- traia_iatp/special_agencies/__init__.py +4 -0
- traia_iatp/special_agencies/registry_search_agency.py +392 -0
- traia_iatp/utils/__init__.py +10 -0
- traia_iatp/utils/docker_utils.py +251 -0
- traia_iatp/utils/general.py +64 -0
- traia_iatp/utils/iatp_utils.py +126 -0
- traia_iatp-0.1.1.dist-info/METADATA +414 -0
- traia_iatp-0.1.1.dist-info/RECORD +72 -0
- traia_iatp-0.1.1.dist-info/WHEEL +5 -0
- traia_iatp-0.1.1.dist-info/entry_points.txt +2 -0
- traia_iatp-0.1.1.dist-info/licenses/LICENSE +21 -0
- traia_iatp-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Utility agent generator for A2A servers.
|
|
2
|
+
|
|
3
|
+
This module generates utility agents that wrap MCP servers and expose them via A2A protocol.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import shutil
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from ..core.models import MCPServer, UtilityAgent, UtilityAgentStatus
|
|
14
|
+
from .iatp_server_template_generator import IATPServerTemplateGenerator
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IATPServerAgentGenerator:
|
|
20
|
+
"""Generate utility agents from MCP servers using templates."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, output_base_dir: Optional[Path] = None):
|
|
23
|
+
"""Initialize the generator.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
output_base_dir: Base directory for generated agents
|
|
27
|
+
"""
|
|
28
|
+
self.output_base_dir = output_base_dir or Path("generated_agents")
|
|
29
|
+
self.template_generator = IATPServerTemplateGenerator()
|
|
30
|
+
|
|
31
|
+
def _convert_datetime_to_string(self, obj: Any) -> Any:
|
|
32
|
+
"""Convert datetime objects to strings recursively.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
obj: Object that may contain datetime objects
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Object with datetime objects converted to strings
|
|
39
|
+
"""
|
|
40
|
+
if isinstance(obj, datetime):
|
|
41
|
+
return obj.isoformat()
|
|
42
|
+
elif isinstance(obj, dict):
|
|
43
|
+
return {key: self._convert_datetime_to_string(value) for key, value in obj.items()}
|
|
44
|
+
elif isinstance(obj, list):
|
|
45
|
+
return [self._convert_datetime_to_string(item) for item in obj]
|
|
46
|
+
elif isinstance(obj, tuple):
|
|
47
|
+
return tuple(self._convert_datetime_to_string(item) for item in obj)
|
|
48
|
+
else:
|
|
49
|
+
return obj
|
|
50
|
+
|
|
51
|
+
def generate_agent(
|
|
52
|
+
self,
|
|
53
|
+
mcp_server: MCPServer,
|
|
54
|
+
agent_name: Optional[str] = None,
|
|
55
|
+
agent_id: Optional[str] = None,
|
|
56
|
+
agent_description: Optional[str] = None,
|
|
57
|
+
expose_individual_tools: bool = False,
|
|
58
|
+
auth_required: bool = False,
|
|
59
|
+
auth_schemes: Optional[list] = None,
|
|
60
|
+
use_simple_server: bool = True,
|
|
61
|
+
**kwargs
|
|
62
|
+
) -> UtilityAgent:
|
|
63
|
+
"""Generate a utility agent from an MCP server.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
mcp_server: MCP server to wrap
|
|
67
|
+
agent_name: Name for the agent (defaults to MCP server name)
|
|
68
|
+
agent_id: Unique ID for the agent (defaults to generated)
|
|
69
|
+
agent_description: Description of the agent
|
|
70
|
+
expose_individual_tools: Whether to expose individual MCP tools
|
|
71
|
+
auth_required: Whether authentication is required
|
|
72
|
+
auth_schemes: Authentication schemes if required
|
|
73
|
+
use_simple_server: Whether to use simplified server template
|
|
74
|
+
**kwargs: Additional template variables
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Generated UtilityAgent object
|
|
78
|
+
"""
|
|
79
|
+
# Generate defaults
|
|
80
|
+
if agent_name is None:
|
|
81
|
+
agent_name = f"{mcp_server.name} Utility Agent"
|
|
82
|
+
|
|
83
|
+
if agent_id is None:
|
|
84
|
+
# Generate a clean ID based on MCP server name without timestamp
|
|
85
|
+
base_id = mcp_server.name.lower().replace(' ', '-').replace('_', '-')
|
|
86
|
+
agent_id = f"{base_id}-traia-utility-agent"
|
|
87
|
+
|
|
88
|
+
if agent_description is None:
|
|
89
|
+
agent_description = f"A2A utility agent that exposes {mcp_server.name} capabilities"
|
|
90
|
+
|
|
91
|
+
# Prepare capabilities list
|
|
92
|
+
capabilities = []
|
|
93
|
+
if hasattr(mcp_server, 'capabilities') and mcp_server.capabilities:
|
|
94
|
+
if isinstance(mcp_server.capabilities, dict):
|
|
95
|
+
capabilities = list(mcp_server.capabilities.keys())
|
|
96
|
+
elif isinstance(mcp_server.capabilities, list):
|
|
97
|
+
capabilities = mcp_server.capabilities
|
|
98
|
+
|
|
99
|
+
# Prepare skill examples based on MCP server type
|
|
100
|
+
skill_examples = kwargs.get('skill_examples', [])
|
|
101
|
+
if not skill_examples:
|
|
102
|
+
skill_examples = self._generate_skill_examples(mcp_server)
|
|
103
|
+
|
|
104
|
+
# Generate output directory path
|
|
105
|
+
output_dir = self.output_base_dir / agent_id
|
|
106
|
+
|
|
107
|
+
# Get MCP server metadata and convert datetime objects to strings
|
|
108
|
+
mcp_server_metadata = getattr(mcp_server, 'metadata', {})
|
|
109
|
+
mcp_server_metadata_serializable = self._convert_datetime_to_string(mcp_server_metadata)
|
|
110
|
+
|
|
111
|
+
# Use template generator to create the agent
|
|
112
|
+
generated_path = self.template_generator.generate_agent(
|
|
113
|
+
output_dir=output_dir,
|
|
114
|
+
agent_name=agent_name,
|
|
115
|
+
agent_id=agent_id,
|
|
116
|
+
agent_description=agent_description,
|
|
117
|
+
agent_version="0.1.0",
|
|
118
|
+
mcp_server_name=mcp_server.name,
|
|
119
|
+
mcp_server_url=mcp_server.url,
|
|
120
|
+
mcp_server_description=mcp_server.description,
|
|
121
|
+
mcp_server_type=getattr(mcp_server, 'server_type', 'streamable-http'),
|
|
122
|
+
mcp_server_capabilities=capabilities,
|
|
123
|
+
mcp_server_metadata=mcp_server_metadata_serializable,
|
|
124
|
+
expose_individual_tools=expose_individual_tools,
|
|
125
|
+
auth_required=auth_required,
|
|
126
|
+
auth_schemes=auth_schemes or [],
|
|
127
|
+
skill_examples=skill_examples,
|
|
128
|
+
use_simple_server=use_simple_server,
|
|
129
|
+
**kwargs
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Create UtilityAgent object
|
|
133
|
+
utility_agent = UtilityAgent(
|
|
134
|
+
id=agent_id,
|
|
135
|
+
name=agent_name,
|
|
136
|
+
description=agent_description,
|
|
137
|
+
mcp_server_id=mcp_server.id,
|
|
138
|
+
capabilities=capabilities,
|
|
139
|
+
status=UtilityAgentStatus.GENERATED,
|
|
140
|
+
code_path=str(generated_path),
|
|
141
|
+
created_at=datetime.now(),
|
|
142
|
+
metadata={
|
|
143
|
+
"generated_from": mcp_server.name,
|
|
144
|
+
"use_simple_server": use_simple_server,
|
|
145
|
+
"auth_required": auth_required,
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
logger.info(f"Generated utility agent '{agent_name}' at {generated_path}")
|
|
150
|
+
return utility_agent
|
|
151
|
+
|
|
152
|
+
def _generate_skill_examples(self, mcp_server: MCPServer) -> list:
|
|
153
|
+
"""Generate example prompts based on MCP server type.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
mcp_server: MCP server object
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
List of example prompts
|
|
160
|
+
"""
|
|
161
|
+
# Default examples
|
|
162
|
+
examples = [
|
|
163
|
+
f"Help me use {mcp_server.name}",
|
|
164
|
+
f"What can {mcp_server.name} do?",
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Add type-specific examples
|
|
168
|
+
server_type = mcp_server.name.lower()
|
|
169
|
+
|
|
170
|
+
if 'search' in server_type:
|
|
171
|
+
examples.extend([
|
|
172
|
+
"Search for information about AI",
|
|
173
|
+
"Find recent news about technology"
|
|
174
|
+
])
|
|
175
|
+
elif 'file' in server_type or 'filesystem' in server_type:
|
|
176
|
+
examples.extend([
|
|
177
|
+
"List files in the current directory",
|
|
178
|
+
"Read the contents of a file"
|
|
179
|
+
])
|
|
180
|
+
elif 'database' in server_type or 'db' in server_type:
|
|
181
|
+
examples.extend([
|
|
182
|
+
"Query the database for user information",
|
|
183
|
+
"Show me the database schema"
|
|
184
|
+
])
|
|
185
|
+
elif 'api' in server_type:
|
|
186
|
+
examples.extend([
|
|
187
|
+
"Call the API to get data",
|
|
188
|
+
"Send a request to the endpoint"
|
|
189
|
+
])
|
|
190
|
+
elif 'trading' in server_type or 'finance' in server_type:
|
|
191
|
+
examples.extend([
|
|
192
|
+
"Get the current market data",
|
|
193
|
+
"Show me trading opportunities"
|
|
194
|
+
])
|
|
195
|
+
else:
|
|
196
|
+
# Generic examples
|
|
197
|
+
examples.extend([
|
|
198
|
+
f"Execute a {mcp_server.name} operation",
|
|
199
|
+
f"Process this request using {mcp_server.name}"
|
|
200
|
+
])
|
|
201
|
+
|
|
202
|
+
return examples[:5] # Limit to 5 examples
|
|
203
|
+
|
|
204
|
+
def update_agent_files(
|
|
205
|
+
self,
|
|
206
|
+
agent_path: Path,
|
|
207
|
+
updates: Dict[str, Any]
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Update existing agent files with new configuration.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
agent_path: Path to the agent directory
|
|
213
|
+
updates: Dictionary of updates to apply
|
|
214
|
+
"""
|
|
215
|
+
# Update agent_config.json if it exists
|
|
216
|
+
config_path = agent_path / "agent_config.json"
|
|
217
|
+
if config_path.exists():
|
|
218
|
+
with open(config_path, 'r') as f:
|
|
219
|
+
config = json.load(f)
|
|
220
|
+
|
|
221
|
+
# Merge updates
|
|
222
|
+
config.update(updates)
|
|
223
|
+
|
|
224
|
+
with open(config_path, 'w') as f:
|
|
225
|
+
json.dump(config, f, indent=2)
|
|
226
|
+
|
|
227
|
+
logger.info(f"Updated agent configuration at {config_path}")
|
|
228
|
+
|
|
229
|
+
def cleanup_agent(self, agent_path: Path) -> None:
|
|
230
|
+
"""Clean up generated agent files.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
agent_path: Path to the agent directory
|
|
234
|
+
"""
|
|
235
|
+
if agent_path.exists():
|
|
236
|
+
shutil.rmtree(agent_path)
|
|
237
|
+
logger.info(f"Cleaned up agent at {agent_path}")
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Template generator for A2A utility agents.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to generate utility agent code from templates.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import shutil
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, List, Optional
|
|
11
|
+
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IATPServerTemplateGenerator:
|
|
18
|
+
"""Generate utility agent code from Jinja2 templates."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, templates_dir: Optional[Path] = None):
|
|
21
|
+
"""Initialize the template generator.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
templates_dir: Directory containing the Jinja2 templates
|
|
25
|
+
"""
|
|
26
|
+
if templates_dir is None:
|
|
27
|
+
templates_dir = Path(__file__).parent / "templates"
|
|
28
|
+
|
|
29
|
+
self.templates_dir = templates_dir
|
|
30
|
+
self.env = Environment(
|
|
31
|
+
loader=FileSystemLoader(self.templates_dir),
|
|
32
|
+
autoescape=select_autoescape(['html', 'xml']),
|
|
33
|
+
trim_blocks=True,
|
|
34
|
+
lstrip_blocks=True
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def generate_agent(
|
|
38
|
+
self,
|
|
39
|
+
output_dir: Path,
|
|
40
|
+
agent_name: str,
|
|
41
|
+
agent_id: str,
|
|
42
|
+
mcp_server_name: str,
|
|
43
|
+
mcp_server_url: str,
|
|
44
|
+
mcp_server_description: str,
|
|
45
|
+
mcp_server_capabilities: List[str],
|
|
46
|
+
agent_description: Optional[str] = None,
|
|
47
|
+
agent_version: str = "0.1.0",
|
|
48
|
+
mcp_server_type: str = "streamable-http",
|
|
49
|
+
mcp_server_metadata: Optional[Dict[str, Any]] = None,
|
|
50
|
+
expose_individual_tools: bool = False,
|
|
51
|
+
auth_required: bool = False,
|
|
52
|
+
auth_schemes: Optional[List[str]] = None,
|
|
53
|
+
skill_examples: Optional[List[str]] = None,
|
|
54
|
+
additional_dependencies: Optional[List[str]] = None,
|
|
55
|
+
environment_variables: Optional[List[Dict[str, str]]] = None,
|
|
56
|
+
use_simple_server: bool = True,
|
|
57
|
+
additional_ignores: Optional[List[str]] = None,
|
|
58
|
+
**kwargs
|
|
59
|
+
) -> Path:
|
|
60
|
+
"""Generate a complete utility agent from templates.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
output_dir: Directory to write the generated agent
|
|
64
|
+
agent_name: Human-readable name of the agent
|
|
65
|
+
agent_id: Unique identifier for the agent
|
|
66
|
+
mcp_server_name: Name of the MCP server
|
|
67
|
+
mcp_server_url: URL or path to the MCP server
|
|
68
|
+
mcp_server_description: Description of the MCP server
|
|
69
|
+
mcp_server_capabilities: List of capabilities provided by the MCP server
|
|
70
|
+
agent_description: Description of the agent (defaults to auto-generated)
|
|
71
|
+
agent_version: Version of the agent
|
|
72
|
+
mcp_server_type: Type of MCP server (stdio, http, etc.)
|
|
73
|
+
mcp_server_metadata: Additional metadata for the MCP server
|
|
74
|
+
expose_individual_tools: Whether to expose individual MCP tools as A2A skills
|
|
75
|
+
auth_required: Whether authentication is required
|
|
76
|
+
auth_schemes: List of authentication schemes if auth is required
|
|
77
|
+
skill_examples: Example prompts for the main skill
|
|
78
|
+
additional_dependencies: Extra Python dependencies
|
|
79
|
+
environment_variables: Environment variables for Dockerfile
|
|
80
|
+
use_simple_server: Whether to use the simplified server.py template
|
|
81
|
+
additional_ignores: Additional patterns to add to .gitignore
|
|
82
|
+
**kwargs: Additional template variables
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Path to the generated agency directory
|
|
86
|
+
"""
|
|
87
|
+
# Create output directory
|
|
88
|
+
output_dir = Path(output_dir)
|
|
89
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
# Generate default values
|
|
92
|
+
if agent_description is None:
|
|
93
|
+
agent_description = f"Utility agent that exposes {mcp_server_name} capabilities via A2A protocol"
|
|
94
|
+
|
|
95
|
+
if skill_examples is None:
|
|
96
|
+
skill_examples = [
|
|
97
|
+
f"Help me use {mcp_server_name}",
|
|
98
|
+
f"Process this request with {mcp_server_name}",
|
|
99
|
+
"Execute this operation"
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
if mcp_server_metadata is None:
|
|
103
|
+
mcp_server_metadata = {}
|
|
104
|
+
|
|
105
|
+
if additional_dependencies is None:
|
|
106
|
+
additional_dependencies = []
|
|
107
|
+
|
|
108
|
+
if environment_variables is None:
|
|
109
|
+
environment_variables = []
|
|
110
|
+
|
|
111
|
+
if additional_ignores is None:
|
|
112
|
+
additional_ignores = []
|
|
113
|
+
|
|
114
|
+
# Convert agent name to valid Python identifiers
|
|
115
|
+
class_name = ''.join(word.capitalize() for word in agent_name.replace('-', ' ').split())
|
|
116
|
+
package_name = agent_name.lower().replace(' ', '_').replace('-', '_')
|
|
117
|
+
module_name = package_name
|
|
118
|
+
docker_image = f"traia/{agent_id}:latest"
|
|
119
|
+
|
|
120
|
+
# Prepare template context
|
|
121
|
+
context = {
|
|
122
|
+
"agent_name": agent_name,
|
|
123
|
+
"agent_id": agent_id,
|
|
124
|
+
"agent_description": agent_description,
|
|
125
|
+
"agent_version": agent_version,
|
|
126
|
+
"mcp_server_name": mcp_server_name,
|
|
127
|
+
"mcp_server_url": mcp_server_url,
|
|
128
|
+
"mcp_server_description": mcp_server_description,
|
|
129
|
+
"mcp_server_type": mcp_server_type,
|
|
130
|
+
"mcp_server_capabilities": mcp_server_capabilities,
|
|
131
|
+
"mcp_server_metadata": mcp_server_metadata,
|
|
132
|
+
"expose_individual_tools": expose_individual_tools,
|
|
133
|
+
"auth_required": auth_required,
|
|
134
|
+
"auth_schemes": auth_schemes or [],
|
|
135
|
+
"skill_examples": skill_examples,
|
|
136
|
+
"class_name": class_name,
|
|
137
|
+
"package_name": package_name,
|
|
138
|
+
"module_name": module_name,
|
|
139
|
+
"docker_image": docker_image,
|
|
140
|
+
"additional_dependencies": additional_dependencies,
|
|
141
|
+
"environment_variables": environment_variables,
|
|
142
|
+
"additional_ignores": additional_ignores,
|
|
143
|
+
"use_uv_lock": False, # Will be true after first uv sync
|
|
144
|
+
**kwargs
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Generate files
|
|
148
|
+
files_to_generate = []
|
|
149
|
+
|
|
150
|
+
# Common files for both approaches
|
|
151
|
+
common_files = [
|
|
152
|
+
("agent_config.json.j2", "agent_config.json"),
|
|
153
|
+
("pyproject.toml.j2", "pyproject.toml"),
|
|
154
|
+
("Dockerfile.j2", "Dockerfile"),
|
|
155
|
+
("README.md.j2", "README.md"),
|
|
156
|
+
("docker-compose.yml.j2", "docker-compose.yml"),
|
|
157
|
+
("env.example.j2", ".env.example"),
|
|
158
|
+
("gitignore.j2", ".gitignore"),
|
|
159
|
+
(".dockerignore.j2", ".dockerignore"),
|
|
160
|
+
("run_local_docker.sh.j2", "run_local_docker.sh"),
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
if use_simple_server:
|
|
164
|
+
# Simple single-file approach
|
|
165
|
+
files_to_generate = [
|
|
166
|
+
("server.py.j2", "server.py"),
|
|
167
|
+
] + common_files
|
|
168
|
+
else:
|
|
169
|
+
# Modular approach with separate files
|
|
170
|
+
# Create package directory
|
|
171
|
+
pkg_dir = output_dir / package_name
|
|
172
|
+
pkg_dir.mkdir(exist_ok=True)
|
|
173
|
+
|
|
174
|
+
files_to_generate = [
|
|
175
|
+
("agent.py.j2", f"{package_name}/agent.py"),
|
|
176
|
+
("agent_executor.py.j2", f"{package_name}/agent_executor.py"),
|
|
177
|
+
("__main__.py.j2", f"{package_name}/__main__.py"),
|
|
178
|
+
] + common_files
|
|
179
|
+
|
|
180
|
+
# Create __init__.py
|
|
181
|
+
(output_dir / package_name / "__init__.py").write_text("")
|
|
182
|
+
|
|
183
|
+
# Render and write templates
|
|
184
|
+
for template_name, output_name in files_to_generate:
|
|
185
|
+
try:
|
|
186
|
+
template = self.env.get_template(template_name)
|
|
187
|
+
content = template.render(context)
|
|
188
|
+
output_path = output_dir / output_name
|
|
189
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
output_path.write_text(content)
|
|
191
|
+
logger.info(f"Generated {output_path}")
|
|
192
|
+
|
|
193
|
+
# Make run_local_docker.sh executable
|
|
194
|
+
if output_name == "run_local_docker.sh":
|
|
195
|
+
output_path.chmod(0o755)
|
|
196
|
+
logger.info(f"Made {output_path} executable")
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error(f"Error generating {output_name} from {template_name}: {e}")
|
|
199
|
+
raise
|
|
200
|
+
|
|
201
|
+
# # Copy mcp_agent_template.py
|
|
202
|
+
# self._copy_mcp_agent_template(output_dir)
|
|
203
|
+
|
|
204
|
+
logger.info(f"Successfully generated utility agent at {output_dir}")
|
|
205
|
+
return output_dir
|
|
206
|
+
|
|
207
|
+
# def _copy_mcp_agent_template(self, output_dir: Path):
|
|
208
|
+
# """Copy the mcp_agent_template.py file to the generated agent.
|
|
209
|
+
|
|
210
|
+
# Args:
|
|
211
|
+
# output_dir: Output directory for the agent
|
|
212
|
+
# """
|
|
213
|
+
# # Find the mcp_agent_template.py file
|
|
214
|
+
# mcp_template_path = Path(__file__).parent.parent / "mcp" / "mcp_agent_template.py"
|
|
215
|
+
# agent_adapter_path = Path(__file__).parent.parent / "mcp" / "traia_mcp_adapter.py"
|
|
216
|
+
|
|
217
|
+
# if mcp_template_path.exists():
|
|
218
|
+
# # Copy to output directory
|
|
219
|
+
# dest_path_one = output_dir / "mcp_agent_template.py"
|
|
220
|
+
# dest_path_two = output_dir / "traia_mcp_adapter.py"
|
|
221
|
+
# shutil.copy2(mcp_template_path, dest_path_one)
|
|
222
|
+
# shutil.copy2(agent_adapter_path, dest_path_two)
|
|
223
|
+
# logger.info(f"Copied mcp_agent_template.py to {dest_path_one}")
|
|
224
|
+
# logger.info(f"Copied traia_mcp_adapter.py to {dest_path_two}")
|
|
225
|
+
# else:
|
|
226
|
+
# logger.warning(f"Could not find mcp_agent_template.py at {mcp_template_path}")
|
|
227
|
+
# logger.warning(f"Could not find traia_mcp_adapter.py at {agent_adapter_path}")
|
|
228
|
+
|
|
229
|
+
def list_templates(self) -> List[str]:
|
|
230
|
+
"""List available templates.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List of template file names
|
|
234
|
+
"""
|
|
235
|
+
return [f.name for f in self.templates_dir.glob("*.j2")]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
# Install system dependencies
|
|
4
|
+
RUN apt-get update && apt-get install -y \
|
|
5
|
+
curl \
|
|
6
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
7
|
+
|
|
8
|
+
# Install uv
|
|
9
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
10
|
+
RUN chmod +x /usr/local/bin/uv
|
|
11
|
+
|
|
12
|
+
# Create non-root user
|
|
13
|
+
RUN useradd -m -u 1000 appuser
|
|
14
|
+
|
|
15
|
+
# Create app directory and set ownership
|
|
16
|
+
RUN mkdir -p /app && chown -R appuser:appuser /app
|
|
17
|
+
|
|
18
|
+
# Set working directory
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
|
|
21
|
+
# Switch to non-root user
|
|
22
|
+
USER appuser
|
|
23
|
+
|
|
24
|
+
# Copy project files
|
|
25
|
+
COPY --chown=appuser:appuser . .
|
|
26
|
+
|
|
27
|
+
# Install Python dependencies
|
|
28
|
+
RUN uv venv .venv && \
|
|
29
|
+
uv pip install -r pyproject.toml
|
|
30
|
+
|
|
31
|
+
# Set environment variables
|
|
32
|
+
ENV PATH="/app/.venv/bin:$PATH"
|
|
33
|
+
ENV PYTHONPATH=/app
|
|
34
|
+
ENV HOST=0.0.0.0
|
|
35
|
+
ENV PYTHONUNBUFFERED=1
|
|
36
|
+
ENV UV_SYSTEM_PYTHON=1
|
|
37
|
+
{% for env_var in environment_variables %}
|
|
38
|
+
ENV {{ env_var.name }}="{{ env_var.value }}"
|
|
39
|
+
{% endfor %}
|
|
40
|
+
|
|
41
|
+
# Health check
|
|
42
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
43
|
+
CMD curl -f http://localhost:${PORT:-8000}/.well-known/agent.json || exit 1
|
|
44
|
+
|
|
45
|
+
# Expose port (uses PORT environment variable with default)
|
|
46
|
+
EXPOSE ${PORT:-8000}
|
|
47
|
+
|
|
48
|
+
# Run the application
|
|
49
|
+
CMD ["uv", "run", "python", "-m", "{{ module_name }}"]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# A2A Utility Agency Templates
|
|
2
|
+
|
|
3
|
+
This directory contains Jinja2 templates for generating utility agencies that wrap MCP servers and expose them via the A2A (Agent-to-Agent) protocol.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
These templates follow the pattern established by the [A2A CrewAI examples](https://github.com/google-a2a/a2a-samples/tree/main/samples/python/agents/crewai) to create standardized A2A servers that:
|
|
8
|
+
|
|
9
|
+
1. **Wrap MCP Servers**: Connect to any MCP (Model Context Protocol) server
|
|
10
|
+
2. **Use CrewAI**: Leverage CrewAI agents to process requests intelligently
|
|
11
|
+
3. **Expose A2A Interface**: Provide a standard A2A protocol interface for agent-to-agent communication
|
|
12
|
+
|
|
13
|
+
## Templates
|
|
14
|
+
|
|
15
|
+
### Core Templates
|
|
16
|
+
|
|
17
|
+
- **`agent.py.j2`** - CrewAI agent implementation that wraps the MCP server
|
|
18
|
+
- **`agent_executor.py.j2`** - A2A executor that interfaces the CrewAI agent with A2A protocol
|
|
19
|
+
- **`__main__.py.j2`** - Main entry point that initializes and starts the A2A server
|
|
20
|
+
|
|
21
|
+
### Simple Server Template
|
|
22
|
+
|
|
23
|
+
- **`server.py.j2`** - All-in-one simplified implementation combining agent, executor, and server
|
|
24
|
+
|
|
25
|
+
### Configuration Templates
|
|
26
|
+
|
|
27
|
+
- **`agency_config.json.j2`** - Agency configuration file
|
|
28
|
+
- **`pyproject.toml.j2`** - Python project dependencies
|
|
29
|
+
- **`Dockerfile.j2`** - Docker container configuration
|
|
30
|
+
- **`README.md.j2`** - Documentation for the generated agency
|
|
31
|
+
|
|
32
|
+
## Template Variables
|
|
33
|
+
|
|
34
|
+
The templates expect the following variables:
|
|
35
|
+
|
|
36
|
+
### Required Variables
|
|
37
|
+
- `agency_name` - Human-readable name of the agency
|
|
38
|
+
- `agency_id` - Unique identifier for the agency
|
|
39
|
+
- `mcp_server_name` - Name of the MCP server
|
|
40
|
+
- `mcp_server_url` - URL or path to the MCP server
|
|
41
|
+
- `mcp_server_description` - Description of the MCP server
|
|
42
|
+
- `mcp_server_capabilities` - List of capabilities provided
|
|
43
|
+
|
|
44
|
+
### Optional Variables
|
|
45
|
+
- `agency_description` - Description of the agency (auto-generated if not provided)
|
|
46
|
+
- `agency_version` - Version of the agency (default: "0.1.0")
|
|
47
|
+
- `mcp_server_type` - Type of MCP server: "stdio", "http", etc. (default: "stdio")
|
|
48
|
+
- `expose_individual_tools` - Whether to expose each MCP tool as a separate A2A skill
|
|
49
|
+
- `auth_required` - Whether authentication is required
|
|
50
|
+
- `auth_schemes` - List of auth schemes if auth is required
|
|
51
|
+
- `skill_examples` - Example prompts for the main skill
|
|
52
|
+
- `additional_dependencies` - Extra Python dependencies
|
|
53
|
+
- `use_simple_server` - Whether to use the simplified server.py template
|
|
54
|
+
|
|
55
|
+
### Auto-generated Variables
|
|
56
|
+
- `class_name` - PascalCase class name derived from agency_name
|
|
57
|
+
- `package_name` - Python package name (snake_case)
|
|
58
|
+
- `module_name` - Python module name
|
|
59
|
+
- `docker_image` - Docker image name
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
Use the `UtilityAgencyTemplateGenerator` class to generate agencies:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from iatp.server.template_generator import UtilityAgencyTemplateGenerator
|
|
67
|
+
|
|
68
|
+
generator = UtilityAgencyTemplateGenerator()
|
|
69
|
+
output_path = generator.generate_agency(
|
|
70
|
+
output_dir="path/to/output",
|
|
71
|
+
agency_name="My MCP Agency",
|
|
72
|
+
agency_id="my-mcp-agency",
|
|
73
|
+
mcp_server_name="my-mcp-server",
|
|
74
|
+
mcp_server_url="http://localhost:3000",
|
|
75
|
+
mcp_server_description="Description of the MCP server",
|
|
76
|
+
mcp_server_capabilities=["capability1", "capability2"],
|
|
77
|
+
use_simple_server=True # For simpler deployments
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Generated Structure
|
|
82
|
+
|
|
83
|
+
### Simple Server (use_simple_server=True)
|
|
84
|
+
```
|
|
85
|
+
output_dir/
|
|
86
|
+
├── server.py # All-in-one A2A server
|
|
87
|
+
├── agency_config.json # Configuration
|
|
88
|
+
├── pyproject.toml # Dependencies
|
|
89
|
+
├── Dockerfile # Container config
|
|
90
|
+
├── README.md # Documentation
|
|
91
|
+
├── docker-compose.yml # Local development
|
|
92
|
+
├── .gitignore # Git ignore rules
|
|
93
|
+
└── .env.example # Environment variables example
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Modular Structure (use_simple_server=False)
|
|
97
|
+
```
|
|
98
|
+
output_dir/
|
|
99
|
+
├── package_name/
|
|
100
|
+
│ ├── __init__.py
|
|
101
|
+
│ ├── agent.py # CrewAI agent
|
|
102
|
+
│ ├── agent_executor.py # A2A executor
|
|
103
|
+
│ └── __main__.py # Entry point
|
|
104
|
+
├── agency_config.json
|
|
105
|
+
├── pyproject.toml
|
|
106
|
+
├── Dockerfile
|
|
107
|
+
├── README.md
|
|
108
|
+
├── docker-compose.yml
|
|
109
|
+
├── .gitignore
|
|
110
|
+
└── .env.example
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## A2A Protocol Compliance
|
|
114
|
+
|
|
115
|
+
The generated agencies follow the A2A protocol by:
|
|
116
|
+
|
|
117
|
+
1. **Agent Card**: Exposing agent capabilities at `/.well-known/agent.json`
|
|
118
|
+
2. **Task Processing**: Handling tasks via POST to `/tasks/send`
|
|
119
|
+
3. **Text I/O**: Supporting text input and output by default
|
|
120
|
+
4. **Request Context**: Processing request context and metadata
|
|
121
|
+
5. **Error Handling**: Proper error responses in A2A format
|
|
122
|
+
|
|
123
|
+
## Next Steps
|
|
124
|
+
|
|
125
|
+
After generating an agency:
|
|
126
|
+
|
|
127
|
+
1. **Test Locally**: Run with `uv run python -m package_name` or `python server.py`
|
|
128
|
+
2. **Build Docker**: `docker build -t agency-name .`
|
|
129
|
+
3. **Push to GitHub**: Use the push_agency_to_repo functionality
|
|
130
|
+
4. **Deploy to Cloud**: Use cloudrun_deployer for Google Cloud Run
|
|
131
|
+
5. **Register**: Add to MongoDB registry for discovery
|
|
132
|
+
|
|
133
|
+
## References
|
|
134
|
+
|
|
135
|
+
- [A2A Protocol Documentation](https://github.com/google-a2a/A2A)
|
|
136
|
+
- [A2A CrewAI Examples](https://github.com/google-a2a/a2a-samples/tree/main/samples/python/agents/crewai)
|
|
137
|
+
- [MCP Protocol](https://github.com/anthropics/model-context-protocol)
|