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,77 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual Environment
|
|
24
|
+
venv/
|
|
25
|
+
ENV/
|
|
26
|
+
env/
|
|
27
|
+
.venv/
|
|
28
|
+
|
|
29
|
+
# IDE
|
|
30
|
+
.vscode/
|
|
31
|
+
.idea/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
*~
|
|
35
|
+
|
|
36
|
+
# Environment variables
|
|
37
|
+
.env
|
|
38
|
+
.env.local
|
|
39
|
+
.env.*.local
|
|
40
|
+
|
|
41
|
+
# OS
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
44
|
+
|
|
45
|
+
# Logs
|
|
46
|
+
*.log
|
|
47
|
+
logs/
|
|
48
|
+
|
|
49
|
+
# Testing
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
.coverage
|
|
52
|
+
htmlcov/
|
|
53
|
+
.tox/
|
|
54
|
+
.hypothesis/
|
|
55
|
+
|
|
56
|
+
# Package managers
|
|
57
|
+
uv.lock
|
|
58
|
+
poetry.lock
|
|
59
|
+
pip-log.txt
|
|
60
|
+
|
|
61
|
+
# Docker
|
|
62
|
+
.dockerignore
|
|
63
|
+
|
|
64
|
+
# Output files
|
|
65
|
+
output_tests/
|
|
66
|
+
*.txt
|
|
67
|
+
*.json
|
|
68
|
+
|
|
69
|
+
# Temporary files
|
|
70
|
+
*.tmp
|
|
71
|
+
*.temp
|
|
72
|
+
*.bak
|
|
73
|
+
|
|
74
|
+
# {{ api_name }} specific
|
|
75
|
+
{% if additional_gitignore_entries -%}
|
|
76
|
+
{{ additional_gitignore_entries }}
|
|
77
|
+
{%- endif %}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
{{ api_name }} MCP Server Health Check Script
|
|
4
|
+
|
|
5
|
+
This script properly connects to the {{ api_name }} MCP server and checks its health by:
|
|
6
|
+
1. Establishing a session
|
|
7
|
+
2. Requesting server info
|
|
8
|
+
3. Listing available tools
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
import json
|
|
13
|
+
import requests
|
|
14
|
+
import argparse
|
|
15
|
+
from typing import Dict, Any
|
|
16
|
+
import uuid
|
|
17
|
+
|
|
18
|
+
def create_mcp_session(base_url: str) -> Dict[str, Any]:
|
|
19
|
+
"""Create an MCP session and return session info"""
|
|
20
|
+
# Generate a session ID
|
|
21
|
+
session_id = str(uuid.uuid4())
|
|
22
|
+
|
|
23
|
+
# MCP requires specific headers for streamable-http
|
|
24
|
+
headers = {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"Accept": "application/json, text/event-stream",
|
|
27
|
+
"X-Session-ID": session_id
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {"session_id": session_id, "headers": headers, "base_url": base_url}
|
|
31
|
+
|
|
32
|
+
def send_mcp_request(session: Dict[str, Any], method: str, params: Dict = None) -> Dict[str, Any]:
|
|
33
|
+
"""Send an MCP JSON-RPC request"""
|
|
34
|
+
request_data = {
|
|
35
|
+
"jsonrpc": "2.0",
|
|
36
|
+
"method": method,
|
|
37
|
+
"params": params or {},
|
|
38
|
+
"id": str(uuid.uuid4())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
response = requests.post(
|
|
43
|
+
f"{session['base_url']}/mcp/",
|
|
44
|
+
json=request_data,
|
|
45
|
+
headers=session['headers'],
|
|
46
|
+
timeout=5
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Handle both JSON and SSE responses
|
|
50
|
+
if response.headers.get('content-type', '').startswith('text/event-stream'):
|
|
51
|
+
# For SSE, we'd need to parse the event stream
|
|
52
|
+
return {"status": "ok", "message": "Server returned SSE stream"}
|
|
53
|
+
else:
|
|
54
|
+
return response.json()
|
|
55
|
+
|
|
56
|
+
except requests.exceptions.RequestException as e:
|
|
57
|
+
return {"error": str(e)}
|
|
58
|
+
|
|
59
|
+
def check_mcp_server_health(url: str) -> bool:
|
|
60
|
+
"""Check if {{ api_name }} MCP server is healthy"""
|
|
61
|
+
print(f"๐ Checking {{ api_name }} MCP server health at {url}")
|
|
62
|
+
|
|
63
|
+
# Create session
|
|
64
|
+
session = create_mcp_session(url)
|
|
65
|
+
print(f"๐ Created session: {session['session_id']}")
|
|
66
|
+
|
|
67
|
+
# Try to get server info
|
|
68
|
+
print("\n1๏ธโฃ Testing server.info method...")
|
|
69
|
+
result = send_mcp_request(session, "server.info")
|
|
70
|
+
|
|
71
|
+
if "error" in result and "session" not in str(result.get("error", "")):
|
|
72
|
+
print(f"โ Server info failed: {result}")
|
|
73
|
+
return False
|
|
74
|
+
else:
|
|
75
|
+
print(f"โ
Server responded: {json.dumps(result, indent=2)[:200]}...")
|
|
76
|
+
|
|
77
|
+
# Try to list tools
|
|
78
|
+
print("\n2๏ธโฃ Testing tools/list method...")
|
|
79
|
+
result = send_mcp_request(session, "tools/list")
|
|
80
|
+
|
|
81
|
+
if "error" in result and "session" not in str(result.get("error", "")):
|
|
82
|
+
print(f"โ List tools failed: {result}")
|
|
83
|
+
return False
|
|
84
|
+
else:
|
|
85
|
+
print(f"โ
Tools list responded: {json.dumps(result, indent=2)[:200]}...")
|
|
86
|
+
|
|
87
|
+
# Check if we have the expected tools
|
|
88
|
+
if "result" in result and "tools" in result["result"]:
|
|
89
|
+
tools = result["result"]["tools"]
|
|
90
|
+
tool_names = [tool.get("name", "") for tool in tools]
|
|
91
|
+
print(f"๐ Available tools: {', '.join(tool_names)}")
|
|
92
|
+
|
|
93
|
+
# Check for expected tools
|
|
94
|
+
expected_tools = ["example_tool", "get_api_info"]
|
|
95
|
+
missing_tools = [tool for tool in expected_tools if tool not in tool_names]
|
|
96
|
+
|
|
97
|
+
if missing_tools:
|
|
98
|
+
print(f"โ ๏ธ Missing expected tools: {', '.join(missing_tools)}")
|
|
99
|
+
else:
|
|
100
|
+
print("โ
All expected {{ api_name }} tools are available!")
|
|
101
|
+
|
|
102
|
+
# Alternative: Try connecting with CrewAI adapter
|
|
103
|
+
print("\n3๏ธโฃ Testing CrewAI adapter connection...")
|
|
104
|
+
try:
|
|
105
|
+
from crewai_tools import MCPServerAdapter
|
|
106
|
+
|
|
107
|
+
server_params = {
|
|
108
|
+
"url": f"{url}/mcp/",
|
|
109
|
+
"transport": "streamable-http"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
with MCPServerAdapter(server_params) as mcp_tools:
|
|
113
|
+
tools = list(mcp_tools)
|
|
114
|
+
print(f"โ
CrewAI connected successfully! Found {len(tools)} tools")
|
|
115
|
+
|
|
116
|
+
# Print first few tools
|
|
117
|
+
for i, tool in enumerate(tools[:3]):
|
|
118
|
+
print(f" - {tool.name}")
|
|
119
|
+
|
|
120
|
+
if len(tools) > 3:
|
|
121
|
+
print(f" ... and {len(tools) - 3} more")
|
|
122
|
+
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"โ ๏ธ CrewAI adapter test failed: {e}")
|
|
127
|
+
# This might fail but server could still be healthy
|
|
128
|
+
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
def main():
|
|
132
|
+
parser = argparse.ArgumentParser(description="Check {{ api_name }} MCP Server Health")
|
|
133
|
+
parser.add_argument("--url", default="http://localhost:8000",
|
|
134
|
+
help="{{ api_name }} MCP server URL (default: http://localhost:8000)")
|
|
135
|
+
args = parser.parse_args()
|
|
136
|
+
|
|
137
|
+
print(f"๐ {{ api_name }} MCP Server Health Check")
|
|
138
|
+
print(f"๐ Server URL: {args.url}")
|
|
139
|
+
print(f"๐ฐ Expected tools: example_tool, get_api_info")
|
|
140
|
+
print("="*50)
|
|
141
|
+
|
|
142
|
+
if check_mcp_server_health(args.url):
|
|
143
|
+
print("\nโ
{{ api_name }} MCP Server is healthy and responding!")
|
|
144
|
+
return 0
|
|
145
|
+
else:
|
|
146
|
+
print("\nโ {{ api_name }} MCP Server health check failed!")
|
|
147
|
+
return 1
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
sys.exit(main())
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{ api_slug }}-mcp-server"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server for {{ api_name }} API with {% if auth_description %} authentication and {% endif %} HTTP 402 payment protocol support"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"anyio>=4.0.0",
|
|
8
|
+
"mcp>=1.1.2",
|
|
9
|
+
"python-dotenv>=1.1.1",
|
|
10
|
+
"requests>=2.32.5",
|
|
11
|
+
"starlette>=0.45.0",
|
|
12
|
+
"retry>=0.9.2",
|
|
13
|
+
"traia-iatp>=0.1.27", # For d402 payment protocol support
|
|
14
|
+
"uvicorn>=0.37.0",
|
|
15
|
+
"web3>=6.15.0", # For blockchain payment verification
|
|
16
|
+
{% if sdk_package %}
|
|
17
|
+
"{{ sdk_package }}",
|
|
18
|
+
{% endif %}
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["hatchling"]
|
|
23
|
+
build-backend = "hatchling.build"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.metadata]
|
|
26
|
+
allow-direct-references = true
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
include = [
|
|
30
|
+
"server.py",
|
|
31
|
+
"mcp_health_check.py",
|
|
32
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": [
|
|
3
|
+
"server.py",
|
|
4
|
+
"mcp_health_check.py"
|
|
5
|
+
],
|
|
6
|
+
"exclude": [
|
|
7
|
+
"**/__pycache__",
|
|
8
|
+
".venv",
|
|
9
|
+
"**/*.pyc"
|
|
10
|
+
],
|
|
11
|
+
"extraPaths": [
|
|
12
|
+
"../../../../IATP/src",
|
|
13
|
+
"/Users/eitanlavi/workspace/traia/IATP/src"
|
|
14
|
+
],
|
|
15
|
+
"pythonVersion": "3.12",
|
|
16
|
+
"typeCheckingMode": "basic",
|
|
17
|
+
"reportMissingImports": true,
|
|
18
|
+
"reportMissingTypeStubs": false,
|
|
19
|
+
"venvPath": ".",
|
|
20
|
+
"venv": ".venv"
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Script to build and run the {{ api_name }} MCP Server 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
|
+
# Configuration (will be overridden by .env if present)
|
|
15
|
+
IMAGE_NAME="{{ api_slug }}-mcp-server"
|
|
16
|
+
CONTAINER_NAME="{{ api_slug }}-mcp-local"
|
|
17
|
+
HOST_PORT=${PORT:-8000}
|
|
18
|
+
CONTAINER_PORT=${PORT:-8000}
|
|
19
|
+
|
|
20
|
+
echo -e "${BLUE}๐ Building and running {{ api_name }} MCP Server...${NC}"
|
|
21
|
+
echo
|
|
22
|
+
|
|
23
|
+
# Setup .env file for local development
|
|
24
|
+
# Step 1: Copy .env.example to .env (if .env doesn't exist)
|
|
25
|
+
if [ ! -f .env ]; then
|
|
26
|
+
echo -e "${BLUE}๐ Setting up .env file for local development...${NC}"
|
|
27
|
+
|
|
28
|
+
if [ -f .env.example ]; then
|
|
29
|
+
echo -e "${BLUE} Copying .env.example to .env...${NC}"
|
|
30
|
+
cp .env.example .env
|
|
31
|
+
else
|
|
32
|
+
echo -e "${YELLOW}โ ๏ธ .env.example not found, creating minimal .env template...${NC}"
|
|
33
|
+
# Create minimal .env template
|
|
34
|
+
cat > .env << EOF
|
|
35
|
+
# {{ api_name }} MCP Server Configuration
|
|
36
|
+
PORT=8000
|
|
37
|
+
STAGE=MAINNET
|
|
38
|
+
LOG_LEVEL=INFO
|
|
39
|
+
|
|
40
|
+
{% if requires_auth %}
|
|
41
|
+
# API Authentication (server's internal key for payment mode)
|
|
42
|
+
{{ api_key_env_var }}=
|
|
43
|
+
{% endif %}
|
|
44
|
+
# D402 Payment Protocol Configuration
|
|
45
|
+
SERVER_ADDRESS=
|
|
46
|
+
MCP_OPERATOR_PRIVATE_KEY=
|
|
47
|
+
MCP_OPERATOR_ADDRESS=
|
|
48
|
+
D402_FACILITATOR_URL=https://facilitator.d402.net
|
|
49
|
+
D402_FACILITATOR_API_KEY=
|
|
50
|
+
DEFAULT_SETTLEMENT_TOKEN=0x036CbD53842c5426634e7929541eC2318f3dCF7e
|
|
51
|
+
DEFAULT_SETTLEMENT_NETWORK=sepolia
|
|
52
|
+
D402_TESTING_MODE=true
|
|
53
|
+
EOF
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Step 2: Fix pyproject.toml to use local IATP path for account generation
|
|
58
|
+
# This is needed because uv run python requires a valid pyproject.toml
|
|
59
|
+
echo -e "${BLUE}๐ง Fixing pyproject.toml for local development...${NC}"
|
|
60
|
+
|
|
61
|
+
# Find local IATP path
|
|
62
|
+
IATP_PATH=""
|
|
63
|
+
if [ -d "/Users/eitanlavi/workspace/traia/IATP" ]; then
|
|
64
|
+
IATP_PATH="/Users/eitanlavi/workspace/traia/IATP"
|
|
65
|
+
elif [ -d "$(dirname $(pwd))/IATP" ]; then
|
|
66
|
+
IATP_PATH="$(dirname $(pwd))/IATP"
|
|
67
|
+
elif [ -d "$HOME/workspace/traia/IATP" ]; then
|
|
68
|
+
IATP_PATH="$HOME/workspace/traia/IATP"
|
|
69
|
+
elif [ -n "$LOCAL_IATP_PATH" ] && [ -d "$LOCAL_IATP_PATH" ]; then
|
|
70
|
+
IATP_PATH="$LOCAL_IATP_PATH"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Temporarily update pyproject.toml to use local IATP path (if /tmp/IATP is present)
|
|
74
|
+
if grep -q "file:///tmp/IATP" pyproject.toml 2>/dev/null; then
|
|
75
|
+
if [ -n "$IATP_PATH" ] && [ -d "$IATP_PATH" ]; then
|
|
76
|
+
echo -e "${BLUE} Updating pyproject.toml to use: $IATP_PATH${NC}"
|
|
77
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
78
|
+
sed -i '' "s|file:///tmp/IATP|file://$IATP_PATH|g" pyproject.toml
|
|
79
|
+
else
|
|
80
|
+
sed -i "s|file:///tmp/IATP|file://$IATP_PATH|g" pyproject.toml
|
|
81
|
+
fi
|
|
82
|
+
else
|
|
83
|
+
echo -e "${RED}โ Local IATP not found. Cannot generate accounts.${NC}"
|
|
84
|
+
echo -e "${YELLOW} Please set LOCAL_IATP_PATH environment variable or ensure IATP is at /Users/eitanlavi/workspace/traia/IATP${NC}"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Step 3: Generate Ethereum accounts for local development (workaround)
|
|
90
|
+
# Always regenerate keys for local dev to ensure they're set
|
|
91
|
+
echo -e "${BLUE}๐ Generating Ethereum accounts for local testing (workaround for local dev)...${NC}"
|
|
92
|
+
|
|
93
|
+
# Generate accounts using Python (web3 library)
|
|
94
|
+
# Use uv run to ensure web3 is available from the project dependencies
|
|
95
|
+
uv run python << 'PYTHON_SCRIPT'
|
|
96
|
+
import sys
|
|
97
|
+
import secrets
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
from web3 import Web3
|
|
101
|
+
|
|
102
|
+
# Generate server address (EOA for local testing)
|
|
103
|
+
# This is a workaround - in production, SERVER_ADDRESS comes from IATP wallet contract
|
|
104
|
+
server_private_key = "0x" + secrets.token_hex(32)
|
|
105
|
+
server_account = Web3().eth.account.from_key(server_private_key)
|
|
106
|
+
server_address = server_account.address
|
|
107
|
+
|
|
108
|
+
# Generate operator private key and address
|
|
109
|
+
# This is a workaround - in production, these come from IATP wallet contract
|
|
110
|
+
operator_private_key = "0x" + secrets.token_hex(32)
|
|
111
|
+
operator_account = Web3().eth.account.from_key(operator_private_key)
|
|
112
|
+
operator_address = operator_account.address
|
|
113
|
+
|
|
114
|
+
# Write to temporary file
|
|
115
|
+
with open('.env.generated', 'w') as f:
|
|
116
|
+
f.write(f"SERVER_ADDRESS={server_address}\n")
|
|
117
|
+
f.write(f"MCP_OPERATOR_PRIVATE_KEY={operator_private_key}\n")
|
|
118
|
+
f.write(f"MCP_OPERATOR_ADDRESS={operator_address}\n")
|
|
119
|
+
|
|
120
|
+
print(f"Generated SERVER_ADDRESS: {server_address}")
|
|
121
|
+
print(f"Generated MCP_OPERATOR_ADDRESS: {operator_address}")
|
|
122
|
+
sys.exit(0)
|
|
123
|
+
except ImportError:
|
|
124
|
+
print("ERROR: web3 library not found. Install it with: pip install web3", file=sys.stderr)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
print(f"ERROR: Failed to generate accounts: {e}", file=sys.stderr)
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
PYTHON_SCRIPT
|
|
130
|
+
|
|
131
|
+
# Step 4: Fill in generated values into .env
|
|
132
|
+
if [ $? -eq 0 ] && [ -f .env.generated ]; then
|
|
133
|
+
# Load generated values
|
|
134
|
+
source .env.generated
|
|
135
|
+
|
|
136
|
+
# Update .env file with generated values (macOS-compatible sed)
|
|
137
|
+
# Use a temporary file approach for portability
|
|
138
|
+
if grep -q "^SERVER_ADDRESS=" .env; then
|
|
139
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
140
|
+
sed -i '' "s|^SERVER_ADDRESS=.*|SERVER_ADDRESS=$SERVER_ADDRESS|" .env
|
|
141
|
+
else
|
|
142
|
+
sed -i "s|^SERVER_ADDRESS=.*|SERVER_ADDRESS=$SERVER_ADDRESS|" .env
|
|
143
|
+
fi
|
|
144
|
+
else
|
|
145
|
+
echo "SERVER_ADDRESS=$SERVER_ADDRESS" >> .env
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
if grep -q "^MCP_OPERATOR_PRIVATE_KEY=" .env; then
|
|
149
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
150
|
+
sed -i '' "s|^MCP_OPERATOR_PRIVATE_KEY=.*|MCP_OPERATOR_PRIVATE_KEY=$MCP_OPERATOR_PRIVATE_KEY|" .env
|
|
151
|
+
else
|
|
152
|
+
sed -i "s|^MCP_OPERATOR_PRIVATE_KEY=.*|MCP_OPERATOR_PRIVATE_KEY=$MCP_OPERATOR_PRIVATE_KEY|" .env
|
|
153
|
+
fi
|
|
154
|
+
else
|
|
155
|
+
echo "MCP_OPERATOR_PRIVATE_KEY=$MCP_OPERATOR_PRIVATE_KEY" >> .env
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
if grep -q "^MCP_OPERATOR_ADDRESS=" .env; then
|
|
159
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
160
|
+
sed -i '' "s|^MCP_OPERATOR_ADDRESS=.*|MCP_OPERATOR_ADDRESS=$MCP_OPERATOR_ADDRESS|" .env
|
|
161
|
+
else
|
|
162
|
+
sed -i "s|^MCP_OPERATOR_ADDRESS=.*|MCP_OPERATOR_ADDRESS=$MCP_OPERATOR_ADDRESS|" .env
|
|
163
|
+
fi
|
|
164
|
+
else
|
|
165
|
+
echo "MCP_OPERATOR_ADDRESS=$MCP_OPERATOR_ADDRESS" >> .env
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Set D402_TESTING_MODE if not set
|
|
169
|
+
if ! grep -q "^D402_TESTING_MODE=" .env; then
|
|
170
|
+
echo "D402_TESTING_MODE=true" >> .env
|
|
171
|
+
else
|
|
172
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
173
|
+
sed -i '' "s|^D402_TESTING_MODE=.*|D402_TESTING_MODE=true|" .env
|
|
174
|
+
else
|
|
175
|
+
sed -i "s|^D402_TESTING_MODE=.*|D402_TESTING_MODE=true|" .env
|
|
176
|
+
fi
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
rm -f .env.generated
|
|
180
|
+
echo -e "${GREEN}โ
Generated Ethereum accounts and updated .env${NC}"
|
|
181
|
+
else
|
|
182
|
+
echo -e "${RED}โ Failed to generate accounts. Please install web3: pip install web3${NC}"
|
|
183
|
+
echo -e "${YELLOW} Or set SERVER_ADDRESS, MCP_OPERATOR_PRIVATE_KEY, and MCP_OPERATOR_ADDRESS manually in .env${NC}"
|
|
184
|
+
exit 1
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# Step 5: Prompt for API key if required (only if not already set)
|
|
188
|
+
{% if requires_auth %}
|
|
189
|
+
if ! grep -q "^{{ api_key_env_var }}=[^[:space:]]" .env; then
|
|
190
|
+
if [ -t 0 ]; then
|
|
191
|
+
echo ""
|
|
192
|
+
echo -e "${YELLOW}๐ Please enter your {{ api_name }} API key:${NC}"
|
|
193
|
+
echo -e "${YELLOW} (This is the server's internal API key used when clients pay via 402)${NC}"
|
|
194
|
+
read -p "{{ api_key_env_var }}: " API_KEY_VALUE
|
|
195
|
+
if [ -n "$API_KEY_VALUE" ]; then
|
|
196
|
+
if grep -q "^{{ api_key_env_var }}=" .env; then
|
|
197
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
198
|
+
sed -i '' "s|^{{ api_key_env_var }}=.*|{{ api_key_env_var }}=$API_KEY_VALUE|" .env
|
|
199
|
+
else
|
|
200
|
+
sed -i "s|^{{ api_key_env_var }}=.*|{{ api_key_env_var }}=$API_KEY_VALUE|" .env
|
|
201
|
+
fi
|
|
202
|
+
else
|
|
203
|
+
echo "{{ api_key_env_var }}=$API_KEY_VALUE" >> .env
|
|
204
|
+
fi
|
|
205
|
+
echo -e "${GREEN}โ
API key saved to .env${NC}"
|
|
206
|
+
else
|
|
207
|
+
echo -e "${YELLOW}โ ๏ธ No API key provided. You can set it later in .env${NC}"
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
echo -e "${YELLOW}โ ๏ธ Non-interactive mode: Please set {{ api_key_env_var }} in .env manually${NC}"
|
|
211
|
+
fi
|
|
212
|
+
else
|
|
213
|
+
echo -e "${GREEN}โ
{{ api_key_env_var }} already set in .env${NC}"
|
|
214
|
+
fi
|
|
215
|
+
{% endif %}
|
|
216
|
+
|
|
217
|
+
echo ""
|
|
218
|
+
echo -e "${GREEN}โ
.env file configured for local development!${NC}"
|
|
219
|
+
echo -e "${BLUE}๐ Review .env file if needed before continuing...${NC}"
|
|
220
|
+
if [ -t 0 ]; then
|
|
221
|
+
echo -e "${YELLOW}Press Enter to continue, or Ctrl+C to cancel...${NC}"
|
|
222
|
+
read
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
# Load environment variables from .env file
|
|
226
|
+
echo -e "${BLUE}๐ Loading environment variables from .env file...${NC}"
|
|
227
|
+
set -a # Export all variables
|
|
228
|
+
source .env
|
|
229
|
+
set +a # Stop exporting
|
|
230
|
+
|
|
231
|
+
# Update port variables after loading .env
|
|
232
|
+
HOST_PORT=${PORT:-8000}
|
|
233
|
+
CONTAINER_PORT=${PORT:-8000}
|
|
234
|
+
|
|
235
|
+
# Check if Docker is installed
|
|
236
|
+
if ! command -v docker &> /dev/null; then
|
|
237
|
+
echo -e "${RED}โ Docker is not installed. Please install Docker first.${NC}"
|
|
238
|
+
exit 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
# Stop and remove existing container if it exists
|
|
242
|
+
if docker ps -a | grep -q $CONTAINER_NAME; then
|
|
243
|
+
echo -e "${YELLOW}๐ Stopping existing container...${NC}"
|
|
244
|
+
docker stop $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
245
|
+
docker rm $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Build the Docker image
|
|
249
|
+
echo -e "${BLUE}๐จ Building Docker image...${NC}"
|
|
250
|
+
|
|
251
|
+
# Check if pyproject.toml uses local IATP path (for local development)
|
|
252
|
+
if grep -q "file://" pyproject.toml 2>/dev/null; then
|
|
253
|
+
# Extract IATP path from pyproject.toml (macOS-compatible, no Perl regex)
|
|
254
|
+
IATP_PATH_FROM_TOML=$(grep "file://" pyproject.toml | sed -E 's|.*file://([^"]+).*|\1|' | head -1)
|
|
255
|
+
|
|
256
|
+
# If path is /tmp/IATP (Docker path), find the actual local IATP path
|
|
257
|
+
if [ "$IATP_PATH_FROM_TOML" = "/tmp/IATP" ] || [ ! -d "$IATP_PATH_FROM_TOML" ]; then
|
|
258
|
+
# Try to find local IATP path (common locations)
|
|
259
|
+
if [ -d "/Users/eitanlavi/workspace/traia/IATP" ]; then
|
|
260
|
+
IATP_PATH="/Users/eitanlavi/workspace/traia/IATP"
|
|
261
|
+
elif [ -d "$(dirname $(pwd))/IATP" ]; then
|
|
262
|
+
IATP_PATH="$(dirname $(pwd))/IATP"
|
|
263
|
+
elif [ -d "$HOME/workspace/traia/IATP" ]; then
|
|
264
|
+
IATP_PATH="$HOME/workspace/traia/IATP"
|
|
265
|
+
elif [ -n "$LOCAL_IATP_PATH" ] && [ -d "$LOCAL_IATP_PATH" ]; then
|
|
266
|
+
IATP_PATH="$LOCAL_IATP_PATH"
|
|
267
|
+
else
|
|
268
|
+
IATP_PATH=""
|
|
269
|
+
fi
|
|
270
|
+
else
|
|
271
|
+
IATP_PATH="$IATP_PATH_FROM_TOML"
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
if [ -n "$IATP_PATH" ] && [ -d "$IATP_PATH" ]; then
|
|
275
|
+
echo -e "${BLUE}๐ฆ Using local IATP package: $IATP_PATH${NC}"
|
|
276
|
+
# Copy IATP into build context for Docker (exclude .venv to save space!)
|
|
277
|
+
mkdir -p .docker-iatp
|
|
278
|
+
|
|
279
|
+
# Use rsync if available (much better), otherwise cp with find
|
|
280
|
+
if command -v rsync &> /dev/null; then
|
|
281
|
+
rsync -a --exclude='.venv' --exclude='__pycache__' --exclude='.git' --exclude='build' --exclude='dist' --exclude='*.egg-info' "$IATP_PATH/" .docker-iatp/IATP/
|
|
282
|
+
echo -e "${BLUE} Copied IATP to build context (excluded .venv, ${GREEN}saved ~900MB${BLUE})${NC}"
|
|
283
|
+
else
|
|
284
|
+
# Fallback: use cp but warn about size
|
|
285
|
+
cp -r "$IATP_PATH" .docker-iatp/IATP
|
|
286
|
+
# Try to remove .venv after copy
|
|
287
|
+
rm -rf .docker-iatp/IATP/.venv .docker-iatp/IATP/__pycache__ .docker-iatp/IATP/.git 2>/dev/null
|
|
288
|
+
echo -e "${BLUE} Copied IATP to build context (cleaned .venv after copy)${NC}"
|
|
289
|
+
fi
|
|
290
|
+
# Temporarily update pyproject.toml to use Docker path
|
|
291
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
292
|
+
sed -i.bak 's|file://.*IATP|file:///tmp/IATP|g' pyproject.toml
|
|
293
|
+
else
|
|
294
|
+
sed -i.bak 's|file://.*IATP|file:///tmp/IATP|g' pyproject.toml
|
|
295
|
+
fi
|
|
296
|
+
docker build --no-cache -t $IMAGE_NAME .
|
|
297
|
+
BUILD_EXIT_CODE=$?
|
|
298
|
+
# Restore original pyproject.toml
|
|
299
|
+
mv pyproject.toml.bak pyproject.toml
|
|
300
|
+
rm -rf .docker-iatp
|
|
301
|
+
if [ $BUILD_EXIT_CODE -ne 0 ]; then
|
|
302
|
+
exit $BUILD_EXIT_CODE
|
|
303
|
+
fi
|
|
304
|
+
else
|
|
305
|
+
echo -e "${YELLOW}โ ๏ธ Local IATP path not found${NC}"
|
|
306
|
+
echo -e "${YELLOW} Falling back to published version (may fail if not published)${NC}"
|
|
307
|
+
# If pyproject.toml has /tmp/IATP, replace with published version
|
|
308
|
+
if grep -q "file:///tmp/IATP" pyproject.toml 2>/dev/null; then
|
|
309
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
310
|
+
sed -i.bak 's|file:///tmp/IATP|traia-iatp>=0.1.27|g' pyproject.toml
|
|
311
|
+
else
|
|
312
|
+
sed -i.bak 's|file:///tmp/IATP|traia-iatp>=0.1.27|g' pyproject.toml
|
|
313
|
+
fi
|
|
314
|
+
docker build --no-cache -t $IMAGE_NAME .
|
|
315
|
+
BUILD_EXIT_CODE=$?
|
|
316
|
+
mv pyproject.toml.bak pyproject.toml
|
|
317
|
+
if [ $BUILD_EXIT_CODE -ne 0 ]; then
|
|
318
|
+
exit $BUILD_EXIT_CODE
|
|
319
|
+
fi
|
|
320
|
+
else
|
|
321
|
+
docker build --no-cache -t $IMAGE_NAME .
|
|
322
|
+
fi
|
|
323
|
+
fi
|
|
324
|
+
else
|
|
325
|
+
# Using published version, normal build
|
|
326
|
+
docker build --no-cache -t $IMAGE_NAME .
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
# Run the container
|
|
330
|
+
echo -e "${BLUE}๐ Starting container...${NC}"
|
|
331
|
+
# Use --env-file to load all variables from .env file
|
|
332
|
+
docker run -d \
|
|
333
|
+
--name $CONTAINER_NAME \
|
|
334
|
+
-p $HOST_PORT:$CONTAINER_PORT \
|
|
335
|
+
--env-file .env \
|
|
336
|
+
$IMAGE_NAME
|
|
337
|
+
|
|
338
|
+
# Wait for the server to start
|
|
339
|
+
echo -e "${YELLOW}โณ Waiting for server to start...${NC}"
|
|
340
|
+
sleep 3
|
|
341
|
+
|
|
342
|
+
# Check if container is running
|
|
343
|
+
if ! docker ps | grep -q $CONTAINER_NAME; then
|
|
344
|
+
echo -e "${RED}โ Container failed to start. Checking logs:${NC}"
|
|
345
|
+
docker logs $CONTAINER_NAME
|
|
346
|
+
exit 1
|
|
347
|
+
fi
|
|
348
|
+
|
|
349
|
+
# Get container info
|
|
350
|
+
CONTAINER_ID=$(docker ps -q -f name=$CONTAINER_NAME)
|
|
351
|
+
CONTAINER_IP=$(docker inspect -f '{% raw %}{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}{% endraw %}' $CONTAINER_ID)
|
|
352
|
+
|
|
353
|
+
# Output connection information
|
|
354
|
+
echo
|
|
355
|
+
echo -e "${GREEN}โ
{{ api_name }} MCP Server is running!${NC}"
|
|
356
|
+
echo
|
|
357
|
+
echo -e "${BLUE}๐ Connection Information:${NC}"
|
|
358
|
+
echo -e " Local URL: ${GREEN}http://localhost:${HOST_PORT}/mcp${NC}"
|
|
359
|
+
echo -e " Container IP: ${GREEN}${CONTAINER_IP}:${CONTAINER_PORT}${NC}"
|
|
360
|
+
echo -e " Container Name: ${GREEN}${CONTAINER_NAME}${NC}"
|
|
361
|
+
echo -e " Container ID: ${GREEN}${CONTAINER_ID:0:12}${NC}"
|
|
362
|
+
echo
|
|
363
|
+
echo -e "${BLUE}๐ Useful commands:${NC}"
|
|
364
|
+
echo -e " View logs: ${YELLOW}docker logs -f ${CONTAINER_NAME}${NC}"
|
|
365
|
+
echo -e " Stop server: ${YELLOW}docker stop ${CONTAINER_NAME}${NC}"
|
|
366
|
+
echo -e " Remove container: ${YELLOW}docker rm ${CONTAINER_NAME}${NC}"
|
|
367
|
+
echo -e " Shell access: ${YELLOW}docker exec -it ${CONTAINER_NAME} /bin/bash${NC}"
|
|
368
|
+
echo
|
|
369
|
+
echo -e "${BLUE}๐ MCP Server Endpoint:${NC}"
|
|
370
|
+
echo -e " ${GREEN}http://localhost:${HOST_PORT}/mcp${NC}"
|
|
371
|
+
echo
|
|
372
|
+
|
|
373
|
+
# Check if the server is responding
|
|
374
|
+
echo -e "${YELLOW}๐ Checking server health...${NC}"
|
|
375
|
+
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${HOST_PORT}/mcp" | grep -q "200\|404\|405"; then
|
|
376
|
+
echo -e "${GREEN}โ
Server is responding!${NC}"
|
|
377
|
+
else
|
|
378
|
+
echo -e "${YELLOW}โ ๏ธ Server may still be starting up. Check logs with: docker logs -f ${CONTAINER_NAME}${NC}"
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
# Cleanup: Restore pyproject.toml to use /tmp/IATP (git-committed version)
|
|
382
|
+
echo -e "${BLUE}๐งน Cleaning up...${NC}"
|
|
383
|
+
if grep -q "file:///Users/eitanlavi/workspace/traia/IATP" pyproject.toml 2>/dev/null; then
|
|
384
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
385
|
+
sed -i '' "s|file:///Users/eitanlavi/workspace/traia/IATP|file:///tmp/IATP|g" pyproject.toml
|
|
386
|
+
else
|
|
387
|
+
sed -i "s|file:///Users/eitanlavi/workspace/traia/IATP|file:///tmp/IATP|g" pyproject.toml
|
|
388
|
+
fi
|
|
389
|
+
echo -e "${GREEN}โ
Restored pyproject.toml to git state${NC}"
|
|
390
|
+
fi
|