traia-iatp 0.1.29__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 (107) hide show
  1. traia_iatp/README.md +368 -0
  2. traia_iatp/__init__.py +54 -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/d402_a2a_client.py +293 -0
  9. traia_iatp/client/grpc_a2a_tools.py +349 -0
  10. traia_iatp/client/root_path_a2a_client.py +1 -0
  11. traia_iatp/contracts/__init__.py +12 -0
  12. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  13. traia_iatp/contracts/wallet_creator.py +255 -0
  14. traia_iatp/core/__init__.py +43 -0
  15. traia_iatp/core/models.py +172 -0
  16. traia_iatp/d402/__init__.py +55 -0
  17. traia_iatp/d402/chains.py +102 -0
  18. traia_iatp/d402/client.py +150 -0
  19. traia_iatp/d402/clients/__init__.py +7 -0
  20. traia_iatp/d402/clients/base.py +218 -0
  21. traia_iatp/d402/clients/httpx.py +219 -0
  22. traia_iatp/d402/common.py +114 -0
  23. traia_iatp/d402/encoding.py +28 -0
  24. traia_iatp/d402/examples/client_example.py +197 -0
  25. traia_iatp/d402/examples/server_example.py +171 -0
  26. traia_iatp/d402/facilitator.py +453 -0
  27. traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
  28. traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
  29. traia_iatp/d402/fastmcp_middleware.py +147 -0
  30. traia_iatp/d402/mcp_middleware.py +434 -0
  31. traia_iatp/d402/middleware.py +193 -0
  32. traia_iatp/d402/models.py +116 -0
  33. traia_iatp/d402/networks.py +98 -0
  34. traia_iatp/d402/path.py +43 -0
  35. traia_iatp/d402/payment_introspection.py +104 -0
  36. traia_iatp/d402/payment_signing.py +178 -0
  37. traia_iatp/d402/paywall.py +119 -0
  38. traia_iatp/d402/starlette_middleware.py +326 -0
  39. traia_iatp/d402/template.py +1 -0
  40. traia_iatp/d402/types.py +300 -0
  41. traia_iatp/mcp/__init__.py +18 -0
  42. traia_iatp/mcp/client.py +201 -0
  43. traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
  44. traia_iatp/mcp/mcp_agent_template.py +481 -0
  45. traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
  46. traia_iatp/mcp/templates/README.md.j2 +310 -0
  47. traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
  48. traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
  49. traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
  50. traia_iatp/mcp/templates/dockerignore.j2 +47 -0
  51. traia_iatp/mcp/templates/env.example.j2 +57 -0
  52. traia_iatp/mcp/templates/gitignore.j2 +77 -0
  53. traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
  54. traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
  55. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  56. traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
  57. traia_iatp/mcp/templates/server.py.j2 +175 -0
  58. traia_iatp/mcp/traia_mcp_adapter.py +543 -0
  59. traia_iatp/preview_diagrams.html +181 -0
  60. traia_iatp/registry/__init__.py +26 -0
  61. traia_iatp/registry/atlas_search_indexes.json +280 -0
  62. traia_iatp/registry/embeddings.py +298 -0
  63. traia_iatp/registry/iatp_search_api.py +846 -0
  64. traia_iatp/registry/mongodb_registry.py +771 -0
  65. traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
  66. traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
  67. traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
  68. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
  69. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
  70. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
  71. traia_iatp/registry/readmes/README.md +251 -0
  72. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
  73. traia_iatp/scripts/__init__.py +2 -0
  74. traia_iatp/scripts/create_wallet.py +244 -0
  75. traia_iatp/server/__init__.py +15 -0
  76. traia_iatp/server/a2a_server.py +219 -0
  77. traia_iatp/server/example_template_usage.py +72 -0
  78. traia_iatp/server/iatp_server_agent_generator.py +237 -0
  79. traia_iatp/server/iatp_server_template_generator.py +235 -0
  80. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  81. traia_iatp/server/templates/Dockerfile.j2 +49 -0
  82. traia_iatp/server/templates/README.md +137 -0
  83. traia_iatp/server/templates/README.md.j2 +425 -0
  84. traia_iatp/server/templates/__init__.py +1 -0
  85. traia_iatp/server/templates/__main__.py.j2 +565 -0
  86. traia_iatp/server/templates/agent.py.j2 +94 -0
  87. traia_iatp/server/templates/agent_config.json.j2 +22 -0
  88. traia_iatp/server/templates/agent_executor.py.j2 +279 -0
  89. traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
  90. traia_iatp/server/templates/env.example.j2 +84 -0
  91. traia_iatp/server/templates/gitignore.j2 +78 -0
  92. traia_iatp/server/templates/grpc_server.py.j2 +218 -0
  93. traia_iatp/server/templates/pyproject.toml.j2 +78 -0
  94. traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
  95. traia_iatp/server/templates/server.py.j2 +243 -0
  96. traia_iatp/special_agencies/__init__.py +4 -0
  97. traia_iatp/special_agencies/registry_search_agency.py +392 -0
  98. traia_iatp/utils/__init__.py +10 -0
  99. traia_iatp/utils/docker_utils.py +251 -0
  100. traia_iatp/utils/general.py +64 -0
  101. traia_iatp/utils/iatp_utils.py +126 -0
  102. traia_iatp-0.1.29.dist-info/METADATA +423 -0
  103. traia_iatp-0.1.29.dist-info/RECORD +107 -0
  104. traia_iatp-0.1.29.dist-info/WHEEL +5 -0
  105. traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
  106. traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
  107. traia_iatp-0.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,481 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ MCP Agent Template
4
+
5
+ This template provides a structured way to create agents that use MCP server tools.
6
+ It offers a simplified interface for creating specialized agents that can leverage
7
+ any MCP server by providing the server configuration.
8
+
9
+ Features:
10
+ - Automatic detection of authentication requirements
11
+ - Support for both authenticated and non-authenticated MCP servers
12
+ - Flexible agent creation with optional tool filtering
13
+ - Health checks for MCP servers
14
+
15
+ Authentication:
16
+ The template automatically detects if an MCP server requires authentication
17
+ based on metadata fields:
18
+ - requires_api_key: boolean indicating if authentication is needed
19
+ - api_key_header: the header name to use (default: "Authorization")
20
+ - headers: dictionary containing the actual API key
21
+
22
+ Usage for MCP Agents:
23
+ 1. Import the MCPAgentBuilder from this module
24
+ 2. Create your specialized agent(s) with the builder
25
+ 3. Create tasks for your agents
26
+ 4. Run your agents as a CrewAI crew with MCP server configuration
27
+
28
+ Example:
29
+ ```python
30
+ from mcp_agent_template import MCPAgentBuilder, run_with_mcp_tools, MCPServerInfo
31
+
32
+ # Create MCP server info (you would get this from registry or configuration)
33
+ mcp_server = MCPServerInfo(
34
+ id="weather-123",
35
+ name="weather-mcp",
36
+ url="http://localhost:8080",
37
+ description="Weather information MCP server",
38
+ server_type="streamable-http",
39
+ capabilities=["get_weather", "get_forecast"],
40
+ metadata={},
41
+ tags=["weather", "api"]
42
+ )
43
+
44
+ # For authenticated servers, include auth info in metadata:
45
+ # metadata={
46
+ # "requires_api_key": True,
47
+ # "api_key_header": "Authorization",
48
+ # "headers": {"Authorization": "Bearer YOUR_API_KEY"}
49
+ # }
50
+
51
+ # Create an agent for the MCP server
52
+ analyst = MCPAgentBuilder.create_agent(
53
+ role="Weather Analyst",
54
+ goal="Analyze weather conditions and provide forecasts",
55
+ backstory="You are an expert meteorologist...",
56
+ verbose=True
57
+ )
58
+
59
+ # Create task for the agent
60
+ task = Task(
61
+ description="Analyze current weather conditions in New York...",
62
+ expected_output="A comprehensive weather report...",
63
+ agent=analyst
64
+ )
65
+
66
+ # Run the agent with MCP tools
67
+ result = run_with_mcp_tools([task], mcp_server=mcp_server)
68
+ print(result)
69
+ ```
70
+ """
71
+
72
+ import os
73
+ import sys
74
+ import json
75
+ import argparse
76
+ import logging
77
+ from typing import List, Dict, Any, Optional, Union
78
+ from dataclasses import dataclass
79
+ from pathlib import Path
80
+
81
+ # Import CrewAI components
82
+ from crewai import Agent, Task, Crew, Process, LLM
83
+ from crewai_tools import MCPServerAdapter
84
+
85
+ # Import our custom adapters for API key and d402 payment support
86
+ from .traia_mcp_adapter import create_mcp_adapter, create_mcp_adapter_with_auth, create_mcp_adapter_with_x402
87
+ from .d402_mcp_tool_adapter import create_d402_mcp_adapter
88
+
89
+
90
+ logger = logging.getLogger(__name__)
91
+
92
+ # Create default LLM instance
93
+ DEFAULT_LLM = LLM(model="openai/gpt-4.1", temperature=0.7)
94
+
95
+
96
+ @dataclass
97
+ class MCPServerInfo:
98
+ """Information about an MCP server."""
99
+ id: str
100
+ name: str
101
+ url: str
102
+ description: str
103
+ server_type: str
104
+ capabilities: List[str]
105
+ metadata: Dict[str, Any]
106
+ tags: List[str]
107
+
108
+
109
+ class MCPServerConfig:
110
+ """Configuration for an MCP server - used for utility agency creation."""
111
+
112
+ def __init__(
113
+ self,
114
+ name: str,
115
+ url: str,
116
+ description: str,
117
+ server_type: str = "streamable-http", # Only streamable-http is supported
118
+ capabilities: List[str] = None,
119
+ metadata: Dict[str, Any] = None
120
+ ):
121
+ self.name = name
122
+ self.url = url
123
+ self.description = description
124
+ self.server_type = server_type
125
+ self.capabilities = capabilities or []
126
+ self.metadata = metadata or {}
127
+
128
+ def to_dict(self) -> Dict[str, Any]:
129
+ """Convert to dictionary for storage."""
130
+ return {
131
+ "name": self.name,
132
+ "url": self.url,
133
+ "description": self.description,
134
+ "server_type": self.server_type,
135
+ "capabilities": self.capabilities,
136
+ "metadata": self.metadata
137
+ }
138
+
139
+
140
+ class MCPAgentBuilder:
141
+ """
142
+ Builder class for creating agents that use MCP server tools.
143
+ """
144
+
145
+ # Class variable to store tool subsets for agents (using agent id as key)
146
+ _agent_tool_subsets = {}
147
+
148
+ @staticmethod
149
+ def create_agent(
150
+ role: str,
151
+ goal: str,
152
+ backstory: str,
153
+ verbose: bool = True,
154
+ allow_delegation: bool = False,
155
+ llm: LLM = None,
156
+ tools_subset: List[str] = None
157
+ ) -> Agent:
158
+ """
159
+ Create a CrewAI agent for use with MCP tools.
160
+
161
+ Args:
162
+ role: The role of the agent
163
+ goal: The primary goal of the agent
164
+ backstory: Background story for the agent
165
+ verbose: Whether to enable verbose output
166
+ allow_delegation: Whether to allow the agent to delegate tasks
167
+ llm: The LLM instance to use (defaults to gpt-4.1 with temperature 0.7)
168
+ tools_subset: Optional list of specific tool names to include (if None, all tools are included)
169
+
170
+ Returns:
171
+ CrewAI Agent configured for MCP tools
172
+ """
173
+ # Use specified LLM or default
174
+ if llm is None:
175
+ llm = DEFAULT_LLM
176
+
177
+ # We'll set tools later when run_with_mcp_tools is called
178
+ # This is because tools require the MCP server connection
179
+ agent = Agent(
180
+ role=role,
181
+ goal=goal,
182
+ backstory=backstory,
183
+ verbose=verbose,
184
+ allow_delegation=allow_delegation,
185
+ llm=llm
186
+ )
187
+
188
+ # Store the tools_subset in the class dictionary
189
+ if tools_subset is not None:
190
+ MCPAgentBuilder._agent_tool_subsets[id(agent)] = tools_subset
191
+
192
+ return agent
193
+
194
+ @staticmethod
195
+ def get_tools_subset(agent: Agent) -> Optional[List[str]]:
196
+ """Get the tools subset for a specific agent"""
197
+ return MCPAgentBuilder._agent_tool_subsets.get(id(agent))
198
+
199
+
200
+ def check_server_health(server_info: MCPServerInfo, api_key: Optional[str] = None) -> bool:
201
+ """
202
+ Check if the MCP server is running and healthy by attempting to connect
203
+ and list available tools.
204
+
205
+ Args:
206
+ server_info: MCPServerInfo object with server details
207
+ api_key: Optional API key for authenticated servers
208
+
209
+ Returns:
210
+ True if the server is healthy, False otherwise
211
+ """
212
+ try:
213
+ # Check if authentication is required
214
+ requires_api_key = server_info.metadata.get("requires_api_key", False)
215
+ api_key_header = server_info.metadata.get("api_key_header", "Authorization")
216
+
217
+ # Create appropriate adapter
218
+ if requires_api_key and api_key:
219
+ # Use the provided API key directly (user provides raw key without Bearer prefix)
220
+ adapter = create_mcp_adapter_with_auth(
221
+ url=server_info.url,
222
+ api_key=api_key,
223
+ auth_header=api_key_header,
224
+ auth_prefix="Bearer" # We add the Bearer prefix
225
+ )
226
+ else:
227
+ # No authentication required or no API key provided
228
+ adapter = create_mcp_adapter(url=server_info.url)
229
+
230
+ # Try to connect and list tools
231
+ with adapter as mcp_tools:
232
+ tools = list(mcp_tools)
233
+ print(f"✓ MCP server '{server_info.name}' is healthy ({len(tools)} tools available)")
234
+ return True
235
+
236
+ except Exception as e:
237
+ print(f"✗ MCP server '{server_info.name}' health check failed: {e}")
238
+ return False
239
+
240
+
241
+ def run_with_mcp_tools(
242
+ tasks: List[Task],
243
+ mcp_server: MCPServerInfo,
244
+ agents: Optional[List[Agent]] = None,
245
+ process: Process = Process.sequential,
246
+ verbose: bool = True,
247
+ inputs: Optional[Dict[str, Any]] = None,
248
+ skip_health_check: bool = False,
249
+ api_key: Optional[str] = None,
250
+ d402_account: Optional[Any] = None,
251
+ d402_wallet_address: Optional[str] = None,
252
+ d402_max_value: Optional[int] = None,
253
+ d402_max_value_token: Optional[str] = None,
254
+ d402_max_value_network: Optional[str] = None
255
+ ) -> Any:
256
+ """
257
+ Run tasks with agents that have access to MCP server tools.
258
+
259
+ NOTE ON AUTHENTICATION AND PAYMENT:
260
+ This function supports three modes of operation:
261
+ 1. Authenticated mode: Provide api_key if server requires authentication
262
+ 2. Payment mode: Provide d402_account (CLIENT's account) for servers using HTTP 402 payment protocol
263
+ 3. Standard mode: No authentication or payment required
264
+
265
+ Args:
266
+ tasks: List of tasks to run
267
+ mcp_server: MCPServerInfo object with server details
268
+ agents: Optional list of agents (if None, will use agents from tasks)
269
+ process: CrewAI process type (sequential or hierarchical)
270
+ verbose: Whether to enable verbose output
271
+ inputs: Optional inputs for the crew
272
+ skip_health_check: Skip server health check
273
+ api_key: Optional API key for authenticated MCP servers
274
+ d402_account: CLIENT's operator account (EOA) with private key for signing payments.
275
+ This is the account that signs transactions on behalf of the wallet.
276
+ d402_wallet_address: CLIENT's IATPWallet contract address (holds funds).
277
+ If None, uses d402_account.address (for testing only).
278
+ In production, this must be the deployed IATPWallet contract address.
279
+ d402_max_value: Optional safety limit for maximum payment amount per request in base units.
280
+ This is a global safety check that prevents paying more than intended.
281
+ Typically, each MCP server uses one primary token, so this limit applies
282
+ to all endpoints using that token. Set it based on your most expensive
283
+ expected payment in the token's base units (e.g., for USDC with 6 decimals,
284
+ $1.00 = 1_000_000 base units).
285
+ If None, no limit is enforced (not recommended for production).
286
+ d402_max_value_token: Optional token address (e.g., "0x036CbD53842c5426634e7929541eC2318f3dCF7e" for USDC)
287
+ or token symbol (e.g., "USDC") that this max_value relates to.
288
+ Used for documentation/clarity - the actual validation is numeric only.
289
+ d402_max_value_network: Optional network name (e.g., "base-sepolia", "sepolia") that this
290
+ max_value relates to. Used for documentation/clarity.
291
+
292
+ Returns:
293
+ Result from the crew execution
294
+ """
295
+ # Check if the server is healthy (unless skipped)
296
+ if not skip_health_check:
297
+ # Pass the API key for health check if authentication is required
298
+ if not check_server_health(mcp_server, api_key):
299
+ print(f"MCP server '{mcp_server.name}' is not healthy.")
300
+ print(f"Server URL: {mcp_server.url}")
301
+ sys.exit(1)
302
+
303
+ # Check if authentication is required
304
+ requires_api_key = mcp_server.metadata.get("requires_api_key", False)
305
+ api_key_header = mcp_server.metadata.get("api_key_header", "Authorization")
306
+
307
+ # Determine connection mode: d402 payment takes precedence over auth
308
+ if d402_account:
309
+ # Payment mode: use new D402MCPToolAdapter (simpler, no background tasks)
310
+ # d402_account is the CLIENT's operator account for signing payments
311
+ # d402_wallet_address is the CLIENT's IATPWallet contract (if None, uses operator address for testing)
312
+ try:
313
+ adapter = create_d402_mcp_adapter(
314
+ url=mcp_server.url,
315
+ account=d402_account,
316
+ wallet_address=d402_wallet_address,
317
+ max_value=d402_max_value
318
+ )
319
+ max_value_info = ""
320
+ if d402_max_value is not None:
321
+ max_value_info = f" (max: {d402_max_value}"
322
+ if d402_max_value_token:
323
+ max_value_info += f" {d402_max_value_token}"
324
+ if d402_max_value_network:
325
+ max_value_info += f" on {d402_max_value_network}"
326
+ max_value_info += ")"
327
+
328
+ wallet_info = d402_wallet_address or d402_account.address
329
+ print(f"\n💳 Using d402 payment protocol:")
330
+ print(f" Operator account: {d402_account.address} (signs payments)")
331
+ print(f" Wallet address: {wallet_info} ({'IATPWallet' if d402_wallet_address else 'EOA for testing'})")
332
+ if max_value_info:
333
+ print(f" Max value: {max_value_info}")
334
+ print(f" Using D402MCPToolAdapter (simple request/response, no background tasks)")
335
+ except ImportError as e:
336
+ print(f"\n❌ Error: d402 payment hooks not available")
337
+ print("Ensure traia_iatp.d402 is installed")
338
+ sys.exit(1)
339
+ elif requires_api_key:
340
+ # Authenticated mode: use API key
341
+ if not api_key:
342
+ print(f"\n⚠️ WARNING: MCP server '{mcp_server.name}' requires authentication")
343
+ print(f"Expected header: {api_key_header}")
344
+ print("But no API key was provided.")
345
+ print("\nTo provide authentication:")
346
+ print("Pass your API key using the 'api_key' parameter")
347
+ print("Example: run_with_mcp_tools(tasks, mcp_server, api_key='YOUR_API_KEY')")
348
+ print("\nAlternatively, use d402 payment protocol:")
349
+ print("Example: run_with_mcp_tools(tasks, mcp_server, d402_account=client_account)")
350
+ sys.exit(1)
351
+
352
+ # Use the provided API key directly (user provides raw key without Bearer prefix)
353
+ adapter = create_mcp_adapter_with_auth(
354
+ url=mcp_server.url,
355
+ api_key=api_key,
356
+ auth_header=api_key_header,
357
+ auth_prefix="Bearer" # We add the Bearer prefix
358
+ )
359
+ print(f"\n🔐 Using authenticated connection (header: {api_key_header})")
360
+ else:
361
+ # Standard mode: no authentication or payment required
362
+ adapter = create_mcp_adapter(url=mcp_server.url)
363
+ print("\n🔓 Using standard connection (no authentication)")
364
+
365
+ # Get agents from tasks if not provided
366
+ if agents is None:
367
+ agents = [task.agent for task in tasks]
368
+ # Remove duplicates while preserving order
369
+ seen = set()
370
+ agents = [agent for agent in agents if not (agent in seen or seen.add(agent))]
371
+
372
+ try:
373
+ # Use context manager for MCP server connection
374
+ with adapter as all_tools:
375
+ print(f"Connected to MCP server '{mcp_server.name}'")
376
+ print(f"Available tools: {[tool.name for tool in all_tools]}")
377
+
378
+ # Assign tools to each agent based on their tools_subset if defined
379
+ for agent in agents:
380
+ # Get the tools subset from the class dictionary
381
+ tools_subset = MCPAgentBuilder.get_tools_subset(agent)
382
+ if tools_subset:
383
+ # Filter tools by name if a subset is specified
384
+ agent.tools = [tool for tool in all_tools if tool.name in tools_subset]
385
+ print(f"Agent '{agent.role}' assigned tools: {[tool.name for tool in agent.tools]}")
386
+ else:
387
+ # Use all tools if no subset is specified
388
+ agent.tools = all_tools
389
+ print(f"Agent '{agent.role}' assigned all available tools")
390
+
391
+ # Create and run the crew
392
+ crew = Crew(
393
+ agents=agents,
394
+ tasks=tasks,
395
+ verbose=verbose,
396
+ process=process,
397
+ tracing=True if os.getenv("AGENTOPS_API_KEY") else False,
398
+ )
399
+
400
+ # Kickoff the crew with inputs
401
+ result = crew.kickoff(inputs=inputs or {})
402
+ return result
403
+
404
+ except Exception as e:
405
+ print(f"Error during execution: {e}")
406
+ import traceback
407
+ traceback.print_exc()
408
+ sys.exit(1)
409
+
410
+
411
+ # Example usage when running this file directly
412
+ if __name__ == "__main__":
413
+ # This example shows how to use the template without registry
414
+ print("MCP Agent Template Example")
415
+ print("=" * 80)
416
+
417
+ # Example MCP server configuration (would normally come from registry or config)
418
+ example_server = MCPServerInfo(
419
+ id="example-123",
420
+ name="example-mcp",
421
+ url="http://localhost:8080/mcp/", # Add trailing slash
422
+ description="Example MCP server for demonstration",
423
+ server_type="streamable-http",
424
+ capabilities=["example_tool1", "example_tool2"],
425
+ metadata={},
426
+ tags=["example", "demo"]
427
+ )
428
+
429
+ # Example of MCP server that requires authentication:
430
+ # authenticated_server = MCPServerInfo(
431
+ # id="news-456",
432
+ # name="newsapi-mcp",
433
+ # url="http://localhost:8000/mcp/", # Add trailing slash
434
+ # description="NewsAPI MCP server",
435
+ # server_type="streamable-http",
436
+ # capabilities=["search_news", "get_headlines"],
437
+ # metadata={
438
+ # "requires_api_key": True,
439
+ # "api_key_header": "Authorization",
440
+ # "headers": {
441
+ # "Authorization": "Bearer YOUR_API_KEY" # Client API key
442
+ # }
443
+ # },
444
+ # tags=["news", "api"]
445
+ # )
446
+
447
+ print(f"Using MCP Server: {example_server.name}")
448
+ print(f"Description: {example_server.description}")
449
+ print(f"URL: {example_server.url}")
450
+ print(f"Capabilities: {example_server.capabilities}")
451
+ print()
452
+
453
+ # Create an example agent
454
+ analyst = MCPAgentBuilder.create_agent(
455
+ role="Example Analyst",
456
+ goal="Demonstrate the usage of MCP tools",
457
+ backstory="""
458
+ You are an expert in using MCP server tools.
459
+ Your job is to demonstrate how to use the available tools effectively.
460
+ """,
461
+ verbose=True
462
+ )
463
+
464
+ # Create a task
465
+ demo_task = Task(
466
+ description="""
467
+ Use the available MCP tools to perform a simple demonstration.
468
+ Show what the tools can do and provide a summary.
469
+ """,
470
+ expected_output="""
471
+ A demonstration report showing the capabilities of the MCP tools.
472
+ """,
473
+ agent=analyst
474
+ )
475
+
476
+ print("Note: This is a template example. To run actual MCP tools:")
477
+ print("1. Ensure an MCP server is running at the specified URL")
478
+ print("2. Get the server configuration from the registry or config")
479
+ print("3. Create agents and tasks specific to your use case")
480
+ print("4. Run with: run_with_mcp_tools([task], mcp_server)")
481
+ print("=" * 80)
@@ -0,0 +1,80 @@
1
+ # Use Python 3.12 slim image as base
2
+ FROM python:3.12-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y \
6
+ curl \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ # Install uv
10
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
11
+ RUN chmod +x /usr/local/bin/uv
12
+
13
+ # Create non-root user
14
+ RUN useradd -m -u 1000 appuser
15
+
16
+ # Create app directory and set ownership
17
+ RUN mkdir -p /app && chown -R appuser:appuser /app
18
+
19
+ # Set working directory
20
+ WORKDIR /app
21
+
22
+ # Switch to non-root user
23
+ USER appuser
24
+
25
+ # Copy project files
26
+ COPY --chown=appuser:appuser . .
27
+
28
+ # Copy IATP package if available (for local development only)
29
+ # This Dockerfile works for both local and remote deployments:
30
+ #
31
+ # LOCAL DEPLOYMENT (via run_local_docker.sh):
32
+ # - run_local_docker.sh detects local IATP path in pyproject.toml
33
+ # - Copies IATP to .docker-iatp/IATP before building
34
+ # - Temporarily modifies pyproject.toml to use file:///tmp/IATP
35
+ # - Dockerfile finds .docker-iatp/IATP and copies it to /tmp/IATP
36
+ # - uv install uses the local IATP from /tmp/IATP
37
+ #
38
+ # REMOTE DEPLOYMENT (Cloud Run, etc.):
39
+ # - .docker-iatp/IATP does NOT exist in build context
40
+ # - pyproject.toml has published version (traia-iatp>=0.1.27)
41
+ # - Dockerfile skips IATP copy, uv install uses published package
42
+ #
43
+ RUN if [ -d .docker-iatp/IATP ]; then \
44
+ cp -r .docker-iatp/IATP /tmp/IATP && \
45
+ echo "Using local IATP package (local development mode)"; \
46
+ else \
47
+ echo "Using published IATP package (remote deployment mode)"; \
48
+ fi
49
+
50
+ # Install Python dependencies
51
+ # - Local mode: pyproject.toml references file:///tmp/IATP (set by run_local_docker.sh)
52
+ # - Remote mode: pyproject.toml references traia-iatp>=0.1.27 (from template)
53
+ RUN uv venv .venv && \
54
+ uv pip install -r pyproject.toml
55
+
56
+ # Set environment variables
57
+ ENV PATH="/app/.venv/bin:$PATH"
58
+ ENV PYTHONPATH=/app
59
+ ENV HOST=0.0.0.0
60
+ ENV PYTHONUNBUFFERED=1
61
+ ENV UV_SYSTEM_PYTHON=1
62
+ ENV STAGE=MAINNET
63
+ ENV PORT=8000
64
+ ENV LOG_LEVEL=INFO
65
+ {% if environment_variables %}
66
+ # Additional environment variables
67
+ {% for env_var in environment_variables %}
68
+ ENV {{ env_var.name }}="{{ env_var.value }}"
69
+ {% endfor %}
70
+ {% endif %}
71
+
72
+ # Health check - uses dedicated health endpoint
73
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
74
+ CMD curl -f http://localhost:${PORT:-8000}/health || exit 1
75
+
76
+ # Expose port (uses PORT environment variable with default)
77
+ EXPOSE ${PORT:-8000}
78
+
79
+ # Run the application
80
+ CMD ["uv", "run", "python", "server.py"]