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.
- traia_iatp/README.md +368 -0
- traia_iatp/__init__.py +54 -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/d402_a2a_client.py +293 -0
- traia_iatp/client/grpc_a2a_tools.py +349 -0
- traia_iatp/client/root_path_a2a_client.py +1 -0
- traia_iatp/contracts/__init__.py +12 -0
- traia_iatp/contracts/iatp_contracts_config.py +263 -0
- traia_iatp/contracts/wallet_creator.py +255 -0
- traia_iatp/core/__init__.py +43 -0
- traia_iatp/core/models.py +172 -0
- traia_iatp/d402/__init__.py +55 -0
- traia_iatp/d402/chains.py +102 -0
- traia_iatp/d402/client.py +150 -0
- traia_iatp/d402/clients/__init__.py +7 -0
- traia_iatp/d402/clients/base.py +218 -0
- traia_iatp/d402/clients/httpx.py +219 -0
- traia_iatp/d402/common.py +114 -0
- traia_iatp/d402/encoding.py +28 -0
- traia_iatp/d402/examples/client_example.py +197 -0
- traia_iatp/d402/examples/server_example.py +171 -0
- traia_iatp/d402/facilitator.py +453 -0
- traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
- traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
- traia_iatp/d402/fastmcp_middleware.py +147 -0
- traia_iatp/d402/mcp_middleware.py +434 -0
- traia_iatp/d402/middleware.py +193 -0
- traia_iatp/d402/models.py +116 -0
- traia_iatp/d402/networks.py +98 -0
- traia_iatp/d402/path.py +43 -0
- traia_iatp/d402/payment_introspection.py +104 -0
- traia_iatp/d402/payment_signing.py +178 -0
- traia_iatp/d402/paywall.py +119 -0
- traia_iatp/d402/starlette_middleware.py +326 -0
- traia_iatp/d402/template.py +1 -0
- traia_iatp/d402/types.py +300 -0
- traia_iatp/mcp/__init__.py +18 -0
- traia_iatp/mcp/client.py +201 -0
- traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
- traia_iatp/mcp/mcp_agent_template.py +481 -0
- traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
- traia_iatp/mcp/templates/README.md.j2 +310 -0
- traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
- traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
- traia_iatp/mcp/templates/dockerignore.j2 +47 -0
- traia_iatp/mcp/templates/env.example.j2 +57 -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 +32 -0
- traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
- traia_iatp/mcp/templates/server.py.j2 +175 -0
- traia_iatp/mcp/traia_mcp_adapter.py +543 -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 +846 -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/scripts/__init__.py +2 -0
- traia_iatp/scripts/create_wallet.py +244 -0
- traia_iatp/server/__init__.py +15 -0
- traia_iatp/server/a2a_server.py +219 -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/.dockerignore.j2 +48 -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 +565 -0
- traia_iatp/server/templates/agent.py.j2 +94 -0
- traia_iatp/server/templates/agent_config.json.j2 +22 -0
- traia_iatp/server/templates/agent_executor.py.j2 +279 -0
- traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
- traia_iatp/server/templates/env.example.j2 +84 -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 +78 -0
- traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
- traia_iatp/server/templates/server.py.j2 +243 -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.29.dist-info/METADATA +423 -0
- traia_iatp-0.1.29.dist-info/RECORD +107 -0
- traia_iatp-0.1.29.dist-info/WHEEL +5 -0
- traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
- traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
- 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"]
|