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,78 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{ package_name }}"
|
|
3
|
+
version = "{{ agent_version }}"
|
|
4
|
+
description = "{{ agent_description }}"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"crewai>=0.203.1",
|
|
9
|
+
"crewai-tools[mcp]>=0.76.0",
|
|
10
|
+
"agentops>=0.4.21",
|
|
11
|
+
"a2a-sdk>=0.2.6",
|
|
12
|
+
"fastmcp>=2.12.5",
|
|
13
|
+
"hypercorn>=0.17.3", # HTTP/2 support via h2 dependency
|
|
14
|
+
"httpx[http2]>=0.28.1", # HTTP/2 support with http2 extra
|
|
15
|
+
"starlette>=0.48.0", # For SSE streaming support
|
|
16
|
+
"sse-starlette>=3.0.2", # Server-Sent Events support
|
|
17
|
+
"pydantic>=2.12.2",
|
|
18
|
+
"python-dotenv>=1.1.1",
|
|
19
|
+
"pymongo>=4.15.3", # Required for MCP registry
|
|
20
|
+
"traia-iatp>=0.1.27", # Core IATP functionality with d402 support
|
|
21
|
+
"eth-account>=0.11.0", # For x402 signing
|
|
22
|
+
"web3>=6.15.0", # For x402 blockchain interaction
|
|
23
|
+
{% for dep in additional_dependencies %}
|
|
24
|
+
"{{ dep }}",
|
|
25
|
+
{% endfor %}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
grpc = [
|
|
30
|
+
"grpcio>=1.73.0",
|
|
31
|
+
"grpcio-tools>=1.71.0",
|
|
32
|
+
"grpcio-reflection>=1.71.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.0.0",
|
|
37
|
+
"pytest-asyncio>=0.21.0",
|
|
38
|
+
"black>=23.0.0",
|
|
39
|
+
"ruff>=0.1.0",
|
|
40
|
+
"mypy>=1.0.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["setuptools>=64", "wheel"]
|
|
45
|
+
build-backend = "setuptools.build_meta"
|
|
46
|
+
|
|
47
|
+
[tool.black]
|
|
48
|
+
line-length = 100
|
|
49
|
+
target-version = ['py312']
|
|
50
|
+
include = '\.pyi?$'
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
line-length = 100
|
|
54
|
+
target-version = "py312"
|
|
55
|
+
select = [
|
|
56
|
+
"E", # pycodestyle errors
|
|
57
|
+
"W", # pycodestyle warnings
|
|
58
|
+
"F", # pyflakes
|
|
59
|
+
"I", # isort
|
|
60
|
+
"B", # flake8-bugbear
|
|
61
|
+
"C4", # flake8-comprehensions
|
|
62
|
+
"UP", # pyupgrade
|
|
63
|
+
]
|
|
64
|
+
ignore = [
|
|
65
|
+
"E501", # line too long
|
|
66
|
+
"B008", # do not perform function calls in argument defaults
|
|
67
|
+
"C901", # too complex
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.isort]
|
|
71
|
+
known-first-party = ["{{ package_name }}"]
|
|
72
|
+
|
|
73
|
+
[tool.mypy]
|
|
74
|
+
python_version = "3.12"
|
|
75
|
+
warn_return_any = true
|
|
76
|
+
warn_unused_configs = true
|
|
77
|
+
disallow_untyped_defs = true
|
|
78
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Script to build and run the {{ agent_name }} locally in Docker
|
|
4
|
+
|
|
5
|
+
set -e # Exit on error
|
|
6
|
+
|
|
7
|
+
# Color codes for output
|
|
8
|
+
GREEN='\033[0;32m'
|
|
9
|
+
BLUE='\033[0;34m'
|
|
10
|
+
YELLOW='\033[1;33m'
|
|
11
|
+
RED='\033[0;31m'
|
|
12
|
+
NC='\033[0m' # No Color
|
|
13
|
+
|
|
14
|
+
# Load environment variables from .env if it exists
|
|
15
|
+
if [ -f .env ]; then
|
|
16
|
+
echo -e "${BLUE}📋 Loading environment variables from .env file...${NC}"
|
|
17
|
+
set -a # Export all variables
|
|
18
|
+
source .env
|
|
19
|
+
set +a # Stop exporting
|
|
20
|
+
else
|
|
21
|
+
echo -e "${YELLOW}⚠️ No .env file found. Using defaults.${NC}"
|
|
22
|
+
echo -e "${YELLOW} Copy .env.example to .env and configure as needed.${NC}"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Configuration with defaults
|
|
26
|
+
IMAGE_NAME="{{ agent_id }}"
|
|
27
|
+
CONTAINER_NAME="{{ agent_id }}-local"
|
|
28
|
+
HOST_PORT=${PORT:-8000}
|
|
29
|
+
CONTAINER_PORT=${PORT:-8000}
|
|
30
|
+
|
|
31
|
+
echo -e "${BLUE}🚀 Building and running {{ agent_name }}...${NC}"
|
|
32
|
+
echo -e "${BLUE} Using port: ${HOST_PORT}${NC}"
|
|
33
|
+
echo
|
|
34
|
+
|
|
35
|
+
# Check if Docker is installed
|
|
36
|
+
if ! command -v docker &> /dev/null; then
|
|
37
|
+
echo -e "${RED}❌ Docker is not installed. Please install Docker first.${NC}"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Stop and remove existing container if it exists
|
|
42
|
+
if docker ps -a | grep -q $CONTAINER_NAME; then
|
|
43
|
+
echo -e "${YELLOW}🛑 Stopping existing container...${NC}"
|
|
44
|
+
docker stop $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
45
|
+
docker rm $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Build the Docker image
|
|
49
|
+
echo -e "${BLUE}🔨 Building Docker image...${NC}"
|
|
50
|
+
docker build -t $IMAGE_NAME .
|
|
51
|
+
|
|
52
|
+
# Run the container
|
|
53
|
+
echo -e "${BLUE}🏃 Starting container...${NC}"
|
|
54
|
+
docker run -d \
|
|
55
|
+
--name $CONTAINER_NAME \
|
|
56
|
+
-p $HOST_PORT:$CONTAINER_PORT \
|
|
57
|
+
--env-file .env \
|
|
58
|
+
-e DEBUG_PROTOCOL=${DEBUG_PROTOCOL:-false} \
|
|
59
|
+
$IMAGE_NAME
|
|
60
|
+
|
|
61
|
+
# Wait for the server to start
|
|
62
|
+
echo -e "${YELLOW}⏳ Waiting for server to start...${NC}"
|
|
63
|
+
sleep 3
|
|
64
|
+
|
|
65
|
+
# Check if container is running
|
|
66
|
+
if ! docker ps | grep -q $CONTAINER_NAME; then
|
|
67
|
+
echo -e "${RED}❌ Container failed to start. Checking logs:${NC}"
|
|
68
|
+
docker logs $CONTAINER_NAME
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Get container info
|
|
73
|
+
CONTAINER_ID=$(docker ps -q -f name=$CONTAINER_NAME)
|
|
74
|
+
|
|
75
|
+
# Output connection information
|
|
76
|
+
echo
|
|
77
|
+
echo -e "${GREEN}✅ {{ agent_name }} is running!${NC}"
|
|
78
|
+
echo
|
|
79
|
+
echo -e "${BLUE}📍 Connection Information:${NC}"
|
|
80
|
+
echo -e " A2A Endpoint: ${GREEN}http://localhost:${HOST_PORT}/a2a${NC}"
|
|
81
|
+
echo -e " Agent Info: ${GREEN}http://localhost:${HOST_PORT}/.well-known/agent.json${NC}"
|
|
82
|
+
echo -e " Container Name: ${GREEN}${CONTAINER_NAME}${NC}"
|
|
83
|
+
echo -e " Container ID: ${GREEN}${CONTAINER_ID:0:12}${NC}"
|
|
84
|
+
echo
|
|
85
|
+
echo -e "${BLUE}📝 Useful commands:${NC}"
|
|
86
|
+
echo -e " View logs: ${YELLOW}docker logs -f ${CONTAINER_NAME}${NC}"
|
|
87
|
+
echo -e " Stop server: ${YELLOW}docker stop ${CONTAINER_NAME}${NC}"
|
|
88
|
+
echo -e " Remove container: ${YELLOW}docker rm ${CONTAINER_NAME}${NC}"
|
|
89
|
+
echo -e " Shell access: ${YELLOW}docker exec -it ${CONTAINER_NAME} /bin/bash${NC}"
|
|
90
|
+
echo
|
|
91
|
+
|
|
92
|
+
# Check if the server is responding
|
|
93
|
+
echo -e "${YELLOW}🔍 Checking server health...${NC}"
|
|
94
|
+
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${HOST_PORT}/.well-known/agent.json" | grep -q "200"; then
|
|
95
|
+
echo -e "${GREEN}✅ Server is healthy!${NC}"
|
|
96
|
+
|
|
97
|
+
# Get and display agency info
|
|
98
|
+
echo
|
|
99
|
+
echo -e "${BLUE}📋 Agency Information:${NC}"
|
|
100
|
+
curl -s "http://localhost:${HOST_PORT}/.well-known/agent.json" | python -m json.tool || echo "Could not fetch agency info"
|
|
101
|
+
else
|
|
102
|
+
echo -e "${YELLOW}⚠️ Server may still be starting up. Check logs with: docker logs -f ${CONTAINER_NAME}${NC}"
|
|
103
|
+
fi
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{ agent_name }} - A2A Server Implementation
|
|
3
|
+
|
|
4
|
+
Auto-generated utility agent that exposes {{ mcp_server_name }} via A2A protocol.
|
|
5
|
+
This is a simplified all-in-one implementation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
from typing import Dict, Any, Optional
|
|
13
|
+
from a2a.server.apps import A2AStarletteApplication
|
|
14
|
+
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
15
|
+
from a2a.server.events.event_queue import EventQueue
|
|
16
|
+
from a2a.server.tasks import InMemoryTaskStore
|
|
17
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
|
18
|
+
from a2a.types import AgentCard, AgentSkill, AgentCapabilities
|
|
19
|
+
from a2a.utils import new_agent_text_message
|
|
20
|
+
import uvicorn
|
|
21
|
+
from crewai import Task
|
|
22
|
+
from fastapi import Request
|
|
23
|
+
|
|
24
|
+
# Import the MCP agent builder and run function
|
|
25
|
+
from traia_iatp.mcp import MCPServerConfig, MCPAgentBuilder, run_with_mcp_tools, MCPServerInfo
|
|
26
|
+
|
|
27
|
+
# Import D402 payment support
|
|
28
|
+
from traia_iatp.x402 import (
|
|
29
|
+
D402Config,
|
|
30
|
+
D402ServicePrice,
|
|
31
|
+
require_iatp_payment,
|
|
32
|
+
add_x402_info_to_agent_card
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
logging.basicConfig(level=logging.INFO)
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
# Load agent configuration
|
|
39
|
+
with open("agent_config.json", "r") as f:
|
|
40
|
+
config = json.load(f)
|
|
41
|
+
|
|
42
|
+
# Create MCP configuration
|
|
43
|
+
mcp_config = MCPServerConfig(
|
|
44
|
+
name=config["mcp_server"]["name"],
|
|
45
|
+
url=config["mcp_server"]["url"],
|
|
46
|
+
description=config["mcp_server"]["description"],
|
|
47
|
+
server_type=config["mcp_server"].get("server_type", "streamable-http"),
|
|
48
|
+
capabilities=config["mcp_server"].get("capabilities", []),
|
|
49
|
+
metadata=config["mcp_server"].get("metadata", {})
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Create MCP server info for use with run_with_mcp_tools
|
|
53
|
+
mcp_server_info = MCPServerInfo(
|
|
54
|
+
id="", # Not needed for direct usage
|
|
55
|
+
name=mcp_config.name,
|
|
56
|
+
url=mcp_config.url,
|
|
57
|
+
description=mcp_config.description,
|
|
58
|
+
server_type=mcp_config.server_type,
|
|
59
|
+
capabilities=mcp_config.capabilities,
|
|
60
|
+
metadata=mcp_config.metadata,
|
|
61
|
+
tags=mcp_config.metadata.get("tags", [])
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Get MCP server API key if required
|
|
65
|
+
{% if requires_api_key %}
|
|
66
|
+
# Check for API keys required by the MCP server
|
|
67
|
+
MCP_API_KEY = None
|
|
68
|
+
{% for api_key_name in api_keys %}
|
|
69
|
+
if not MCP_API_KEY and os.getenv("{{ api_key_name }}"):
|
|
70
|
+
MCP_API_KEY = os.getenv("{{ api_key_name }}")
|
|
71
|
+
logger.info(f"Using API key from {{ api_key_name }} environment variable")
|
|
72
|
+
{% endfor %}
|
|
73
|
+
|
|
74
|
+
if not MCP_API_KEY:
|
|
75
|
+
logger.warning("No API key found for MCP server authentication.")
|
|
76
|
+
logger.warning("The MCP server requires one of these environment variables to be set:")
|
|
77
|
+
{% for api_key_name in api_keys %}
|
|
78
|
+
logger.warning(" - {{ api_key_name }}")
|
|
79
|
+
{% endfor %}
|
|
80
|
+
{% else %}
|
|
81
|
+
MCP_API_KEY = None
|
|
82
|
+
{% endif %}
|
|
83
|
+
|
|
84
|
+
# D402 Configuration (if enabled)
|
|
85
|
+
x402_enabled = os.getenv("D402_ENABLED", "false").lower() == "true"
|
|
86
|
+
x402_config = None
|
|
87
|
+
|
|
88
|
+
if x402_enabled:
|
|
89
|
+
logger.info("D402 payments enabled")
|
|
90
|
+
|
|
91
|
+
contract_address = os.getenv("UTILITY_AGENT_CONTRACT_ADDRESS")
|
|
92
|
+
if not contract_address:
|
|
93
|
+
logger.error("D402 enabled but UTILITY_AGENT_CONTRACT_ADDRESS not set!")
|
|
94
|
+
x402_enabled = False
|
|
95
|
+
else:
|
|
96
|
+
x402_config = D402Config(
|
|
97
|
+
enabled=True,
|
|
98
|
+
pay_to_address=contract_address,
|
|
99
|
+
default_price=D402ServicePrice(
|
|
100
|
+
usd_amount=os.getenv("D402_PRICE_USD", "0.01"),
|
|
101
|
+
network=os.getenv("D402_NETWORK", "sepolia"),
|
|
102
|
+
asset_address=os.getenv("D402_TOKEN_ADDRESS", ""),
|
|
103
|
+
max_timeout_seconds=300
|
|
104
|
+
),
|
|
105
|
+
facilitator_url=os.getenv("D402_FACILITATOR_URL", "http://localhost:8080"),
|
|
106
|
+
service_description=config["description"],
|
|
107
|
+
protected_paths=["*"] # Protect all paths except .well-known
|
|
108
|
+
)
|
|
109
|
+
logger.info(f"D402 configured: ${x402_config.default_price.usd_amount} per request")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class {{ class_name }}AgentExecutor(AgentExecutor):
|
|
113
|
+
"""Agent executor for {{ agency_name }}."""
|
|
114
|
+
|
|
115
|
+
def __init__(self):
|
|
116
|
+
self.mcp_config = mcp_config
|
|
117
|
+
self.mcp_server_info = mcp_server_info
|
|
118
|
+
|
|
119
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
120
|
+
"""Process a request using the {{ agency_name }} capabilities."""
|
|
121
|
+
try:
|
|
122
|
+
# Get the user's request from context
|
|
123
|
+
request_text = context.get_user_input()
|
|
124
|
+
if not request_text:
|
|
125
|
+
await event_queue.enqueue_event(
|
|
126
|
+
new_agent_text_message("No user message provided")
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Get additional context if provided
|
|
131
|
+
task_context = {}
|
|
132
|
+
if hasattr(context, 'metadata'):
|
|
133
|
+
task_context = context.metadata or {}
|
|
134
|
+
|
|
135
|
+
# Create an agent for this request
|
|
136
|
+
agent = MCPAgentBuilder.create_agent(
|
|
137
|
+
role=f"{config['name']} Specialist",
|
|
138
|
+
goal=f"Process the request using {self.mcp_config.name} capabilities",
|
|
139
|
+
backstory=f"You are an expert at using {self.mcp_config.name}. {self.mcp_config.description}"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Create a task
|
|
143
|
+
task = Task(
|
|
144
|
+
description=request_text,
|
|
145
|
+
expected_output="The processed result based on the request",
|
|
146
|
+
agent=agent
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Run with MCP tools
|
|
150
|
+
result = run_with_mcp_tools(
|
|
151
|
+
tasks=[task],
|
|
152
|
+
mcp_server=self.mcp_server_info,
|
|
153
|
+
inputs=task_context,
|
|
154
|
+
skip_health_check=True, # Skip for production usage
|
|
155
|
+
api_key=MCP_API_KEY # Pass the API key if available
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Send the result as agent message
|
|
159
|
+
await event_queue.enqueue_event(
|
|
160
|
+
new_agent_text_message(str(result))
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"Error processing request: {e}")
|
|
165
|
+
await event_queue.enqueue_event(
|
|
166
|
+
new_agent_text_message(f"Error processing request: {str(e)}")
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def create_app():
|
|
171
|
+
"""Create the A2A application."""
|
|
172
|
+
# Create agent skill
|
|
173
|
+
skill = AgentSkill(
|
|
174
|
+
id="process_request",
|
|
175
|
+
name=f"Process request using {config['name']}",
|
|
176
|
+
description=config["description"],
|
|
177
|
+
examples=config.get("a2a_config", {}).get("skill_examples", [
|
|
178
|
+
"Help me with this request",
|
|
179
|
+
"Process this data"
|
|
180
|
+
])
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Create capabilities
|
|
184
|
+
capabilities = AgentCapabilities(
|
|
185
|
+
streaming=False,
|
|
186
|
+
pushNotifications=False,
|
|
187
|
+
stateTransitionHistory=False
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Create agent card
|
|
191
|
+
agent_card_dict = {
|
|
192
|
+
"name": config["agency_id"],
|
|
193
|
+
"description": config["description"],
|
|
194
|
+
"url": f"http://0.0.0.0:{os.environ.get('PORT', 8000)}",
|
|
195
|
+
"version": config["version"],
|
|
196
|
+
"capabilities": capabilities.model_dump(),
|
|
197
|
+
"skills": [skill.model_dump()]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Add x402 payment info to agent card if enabled
|
|
201
|
+
if x402_enabled and x402_config:
|
|
202
|
+
import asyncio
|
|
203
|
+
agent_card_dict = asyncio.run(add_x402_info_to_agent_card(agent_card_dict, x402_config))
|
|
204
|
+
|
|
205
|
+
# Create agent card from dict
|
|
206
|
+
agent_card = AgentCard(**agent_card_dict)
|
|
207
|
+
|
|
208
|
+
# Create executor
|
|
209
|
+
executor = {{ class_name }}AgentExecutor()
|
|
210
|
+
|
|
211
|
+
# Create task store and request handler
|
|
212
|
+
task_store = InMemoryTaskStore()
|
|
213
|
+
request_handler = DefaultRequestHandler(
|
|
214
|
+
agent_executor=executor,
|
|
215
|
+
task_store=task_store
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Create the A2A application
|
|
219
|
+
app = A2AStarletteApplication(
|
|
220
|
+
agent_card=agent_card,
|
|
221
|
+
http_handler=request_handler
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Add x402 middleware if enabled
|
|
225
|
+
if x402_enabled and x402_config:
|
|
226
|
+
logger.info("Adding x402 payment middleware")
|
|
227
|
+
|
|
228
|
+
@app.middleware("http")
|
|
229
|
+
async def payment_middleware(request: Request, call_next):
|
|
230
|
+
middleware = require_iatp_payment(x402_config)
|
|
231
|
+
return await middleware(request, call_next)
|
|
232
|
+
|
|
233
|
+
return app
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if __name__ == "__main__":
|
|
237
|
+
# Get port from environment variable (for Cloud Run compatibility)
|
|
238
|
+
port = int(os.environ.get("PORT", 8000))
|
|
239
|
+
logger.info(f"Starting {config['name']} on port {port}")
|
|
240
|
+
|
|
241
|
+
# Create and run the app
|
|
242
|
+
app = create_app()
|
|
243
|
+
uvicorn.run(app.build(), host="0.0.0.0", port=port)
|