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,520 @@
|
|
|
1
|
+
# {{ api_name }} MCP Server Implementation Guide
|
|
2
|
+
|
|
3
|
+
You are working on implementing the {{ api_name }} MCP (Model Context Protocol) server. The basic structure has been set up, and your task is to implement the actual API integration.
|
|
4
|
+
|
|
5
|
+
## API Information
|
|
6
|
+
|
|
7
|
+
- **API Name**: {{ api_name }}
|
|
8
|
+
- **Documentation**: [{{ docs_url }}]({{ docs_url }})
|
|
9
|
+
- **Website**: [{{ api_url }}]({{ api_url }})
|
|
10
|
+
{% if sdk_package %}
|
|
11
|
+
- **SDK**: `{{ sdk_package }}` (already included in pyproject.toml)
|
|
12
|
+
{% endif %}
|
|
13
|
+
- **Authentication**: {% if requires_auth %}Required - API key via `{{ api_key_env_var }}` environment variable{% else %}Not required{% endif %}
|
|
14
|
+
|
|
15
|
+
{% if requires_auth %}
|
|
16
|
+
## 🔒 HTTP 402 Payment Protocol - Dual-Mode Operation
|
|
17
|
+
|
|
18
|
+
This MCP server implements the **HTTP 402 Payment Required** protocol using the **traia_iatp.d402** module with dual-mode support:
|
|
19
|
+
|
|
20
|
+
### Mode 1: Authenticated (Free Access)
|
|
21
|
+
|
|
22
|
+
When a client provides their own {{ api_name }} API key:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Request Headers:
|
|
26
|
+
Authorization: Bearer CLIENT_API_KEY
|
|
27
|
+
|
|
28
|
+
Flow:
|
|
29
|
+
1. Client connects with their {{ api_name }} API key
|
|
30
|
+
2. MCP server uses client's API key to call {{ api_name }} API
|
|
31
|
+
3. No payment required
|
|
32
|
+
4. Client pays {{ api_name }} directly (not the MCP server)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Use Case**: Clients who already have a {{ api_name }} subscription/API key
|
|
36
|
+
|
|
37
|
+
### Mode 2: Payment Required (Paid Access)
|
|
38
|
+
|
|
39
|
+
When a client doesn't have their own API key but pays via x402/d402 protocol:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Request Headers:
|
|
43
|
+
X-PAYMENT: <base64_encoded_x402_payment>
|
|
44
|
+
|
|
45
|
+
X402 Payment Header Format (created by x402.clients.base.create_payment_header()):
|
|
46
|
+
{
|
|
47
|
+
"x402Version": 1,
|
|
48
|
+
"scheme": "exact",
|
|
49
|
+
"network": "base-sepolia",
|
|
50
|
+
"payload": {
|
|
51
|
+
"signature": "0x...",
|
|
52
|
+
"authorization": {
|
|
53
|
+
"from": "0xCLIENT_ADDRESS",
|
|
54
|
+
"to": "0xSERVER_ADDRESS",
|
|
55
|
+
"value": "1000", // atomic units (wei)
|
|
56
|
+
"validAfter": "1700000000",
|
|
57
|
+
"validBefore": "1700000300",
|
|
58
|
+
"nonce": "hex..."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Flow:
|
|
64
|
+
1. Client creates EIP-3009 transferWithAuthorization signature
|
|
65
|
+
2. Client sends Payment header with encoded payment payload
|
|
66
|
+
3. MCP server verifies payment using traia_iatp.d402.facilitator
|
|
67
|
+
4. MCP server uses its INTERNAL {{ api_name }} API key to call the API
|
|
68
|
+
5. Client pays the MCP server (not {{ api_name }})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Use Case**: Pay-per-use clients without their own {{ api_name }} subscription
|
|
72
|
+
|
|
73
|
+
### D402 Protocol Integration
|
|
74
|
+
|
|
75
|
+
This server uses the **traia_iatp.d402** module which implements:
|
|
76
|
+
- EIP-3009 transferWithAuthorization for gasless payments
|
|
77
|
+
- Payment verification via IATP Settlement Facilitator
|
|
78
|
+
- On-chain transaction verification
|
|
79
|
+
- Multiple token support (USDC, TRAIA, etc.)
|
|
80
|
+
|
|
81
|
+
**Dependencies** (already in pyproject.toml):
|
|
82
|
+
- `traia-iatp>=0.1.27` - Provides d402 module
|
|
83
|
+
- `web3>=6.15.0` - For blockchain verification
|
|
84
|
+
|
|
85
|
+
### Implementation Pattern for Tools
|
|
86
|
+
|
|
87
|
+
**For auto-generated tools** (from OpenAPI), the endpoint implementer will generate code like this:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from traia_iatp.d402.mcp_middleware import EndpointPaymentInfo, verify_endpoint_payment
|
|
91
|
+
|
|
92
|
+
@mcp.tool()
|
|
93
|
+
async def your_tool_name(context: Context, param1: str) -> Dict[str, Any]:
|
|
94
|
+
"""Your tool description."""
|
|
95
|
+
|
|
96
|
+
# Get client's API key (if provided)
|
|
97
|
+
api_key = get_session_api_key(context)
|
|
98
|
+
|
|
99
|
+
# If no API key, verify payment for this specific endpoint
|
|
100
|
+
if not api_key:
|
|
101
|
+
# Each endpoint has specific payment requirements
|
|
102
|
+
endpoint_payment = EndpointPaymentInfo(
|
|
103
|
+
settlement_token_address="0xUSDC...", # From endpoint config
|
|
104
|
+
settlement_token_network="base-sepolia", # From endpoint config
|
|
105
|
+
payment_price_float=0.001, # From endpoint config
|
|
106
|
+
payment_price_wei="1000", # From endpoint config
|
|
107
|
+
server_address="0xSERVER..." # Server's payment address
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Verify payment matches this endpoint's requirements
|
|
111
|
+
if not verify_endpoint_payment(context, endpoint_payment):
|
|
112
|
+
return {
|
|
113
|
+
"error": "Payment required or insufficient",
|
|
114
|
+
"code": 402,
|
|
115
|
+
"required_payment": {
|
|
116
|
+
"token": "0xUSDC...",
|
|
117
|
+
"network": "base-sepolia",
|
|
118
|
+
"amount": 0.001
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Dual-mode: determine which API key to use
|
|
123
|
+
if api_key:
|
|
124
|
+
# MODE 1: Use client's API key (free for client)
|
|
125
|
+
api_key_to_use = api_key
|
|
126
|
+
else:
|
|
127
|
+
# MODE 2: Use server's internal API key (client paid)
|
|
128
|
+
api_key_to_use = os.getenv("{{ api_key_env_var }}")
|
|
129
|
+
|
|
130
|
+
# Call API
|
|
131
|
+
headers = {"Authorization": f"Bearer {api_key_to_use}"}
|
|
132
|
+
response = requests.get("{{ api_url }}/endpoint", headers=headers)
|
|
133
|
+
return response.json()
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Key points**:
|
|
137
|
+
1. Each tool verifies payment against **its specific endpoint requirements**
|
|
138
|
+
2. Different endpoints can have different tokens, networks, and prices
|
|
139
|
+
3. Payment amount/token/network are verified per-endpoint
|
|
140
|
+
4. The middleware just extracts payment payload globally
|
|
141
|
+
|
|
142
|
+
### Middleware Chain
|
|
143
|
+
|
|
144
|
+
The server has TWO middleware in sequence:
|
|
145
|
+
|
|
146
|
+
1. **AuthMiddleware**: Extracts client's API key from Authorization header
|
|
147
|
+
2. **D402MCPMiddleware**: Extracts and validates payment payload from Payment header
|
|
148
|
+
|
|
149
|
+
### How Per-Endpoint Payment Works
|
|
150
|
+
|
|
151
|
+
Unlike FastAPI where middleware can be applied per-route, FastMCP has global middleware.
|
|
152
|
+
Therefore:
|
|
153
|
+
|
|
154
|
+
1. **D402MCPMiddleware**: Extracts payment payload globally, stores in `context.state.payment_payload`
|
|
155
|
+
2. **Each Tool**: Calls `verify_endpoint_payment()` with its specific requirements
|
|
156
|
+
- Verifies payment token matches endpoint's settlement token
|
|
157
|
+
- Verifies payment amount meets endpoint's price
|
|
158
|
+
- Verifies payment network matches endpoint's network
|
|
159
|
+
|
|
160
|
+
**Result**: Different endpoints can accept different tokens, on different networks, at different prices!
|
|
161
|
+
|
|
162
|
+
### Environment Variables
|
|
163
|
+
|
|
164
|
+
**Required**:
|
|
165
|
+
- `{{ api_key_env_var }}`: Server's internal {{ api_name }} API key (used when clients pay via 402)
|
|
166
|
+
- `SERVER_ADDRESS`: MCP server's payment address (where 402 payments are sent)
|
|
167
|
+
|
|
168
|
+
**Required for Settlement (Production)**:
|
|
169
|
+
- `MCP_OPERATOR_PRIVATE_KEY`: Private key for signing settlement attestations (proof of service completion)
|
|
170
|
+
- `MCP_OPERATOR_ADDRESS`: Public address corresponding to operator private key (for verification)
|
|
171
|
+
|
|
172
|
+
**Optional**:
|
|
173
|
+
- `D402_FACILITATOR_URL`: Custom d402 facilitator URL (default: "https://facilitator.d402.net")
|
|
174
|
+
- `D402_FACILITATOR_API_KEY`: API key for private facilitator
|
|
175
|
+
- `D402_TESTING_MODE`: Set to "true" for local testing without settlement (default: "false")
|
|
176
|
+
|
|
177
|
+
**Example .env file**:
|
|
178
|
+
```bash
|
|
179
|
+
# API Authentication (server's internal key for payment mode)
|
|
180
|
+
{{ api_key_env_var }}=your_{{ api_slug }}_api_key_here
|
|
181
|
+
|
|
182
|
+
# Server Payment Address (where 402 payments are received)
|
|
183
|
+
SERVER_ADDRESS=0x1234567890123456789012345678901234567890
|
|
184
|
+
|
|
185
|
+
# Operator Keys (for signing settlement attestations)
|
|
186
|
+
MCP_OPERATOR_PRIVATE_KEY=0x1234567890abcdef... # Keep secure!
|
|
187
|
+
MCP_OPERATOR_ADDRESS=0x9876543210fedcba... # Derived from private key
|
|
188
|
+
|
|
189
|
+
# Optional: Custom facilitator
|
|
190
|
+
D402_FACILITATOR_URL=https://facilitator.d402.net
|
|
191
|
+
D402_FACILITATOR_API_KEY=facilitator_api_key
|
|
192
|
+
|
|
193
|
+
# Optional: Testing mode (skip settlement for local dev)
|
|
194
|
+
D402_TESTING_MODE=false # Set to 'true' for testing without facilitator
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**About Operator Keys**:
|
|
198
|
+
- The operator signs settlement attestations after completing each paid request
|
|
199
|
+
- Attestation proves: service was completed + output hash is valid
|
|
200
|
+
- Can use the same key as SERVER_ADDRESS or a separate signing key
|
|
201
|
+
- Required for on-chain settlement via IATP Settlement Layer
|
|
202
|
+
|
|
203
|
+
**Note on Endpoint-Specific Configuration**:
|
|
204
|
+
Each endpoint's payment requirements (token, network, price) are embedded in the tool code.
|
|
205
|
+
These come from the endpoint configuration in the database/OpenAPI schema.
|
|
206
|
+
|
|
207
|
+
{% endif %}
|
|
208
|
+
|
|
209
|
+
## Implementation Checklist
|
|
210
|
+
|
|
211
|
+
### 1. Update Deployment Configuration
|
|
212
|
+
|
|
213
|
+
**IMPORTANT**: Update the `deployment_params.json` file with all implemented capabilities:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcp_server": {
|
|
218
|
+
"capabilities": [
|
|
219
|
+
// Replace these with your actual implemented tool names
|
|
220
|
+
"search_{{ api_slug.replace('-', '_') }}",
|
|
221
|
+
"get_{{ api_slug.replace('-', '_') }}_info",
|
|
222
|
+
// Add all other tools you implement
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
"tags": ["{{ api_name_lower }}", "api", /* add relevant tags like "search", "data", etc. */]
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 2. Study the API Documentation
|
|
230
|
+
|
|
231
|
+
First, thoroughly review the API documentation at {{ docs_url }} to understand:
|
|
232
|
+
- Available endpoints
|
|
233
|
+
- Request/response formats
|
|
234
|
+
- Rate limits
|
|
235
|
+
- Error handling
|
|
236
|
+
{% if requires_auth %}- Authentication method (API key placement in headers, query params, etc.){% endif %}
|
|
237
|
+
- Specific features and capabilities to expose as tools
|
|
238
|
+
|
|
239
|
+
### 3. Implement API Client Functions
|
|
240
|
+
|
|
241
|
+
Add functions to call the {{ api_name }} API with retry support. Example pattern:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from retry import retry
|
|
245
|
+
|
|
246
|
+
{% if sdk_package %}# Using the SDK with retry decorator
|
|
247
|
+
@retry(tries=2, delay=1, backoff=2, jitter=(1, 3))
|
|
248
|
+
def call_{{ api_slug.replace('-', '_') }}_api(endpoint: str, params: Dict[str, Any], api_key: str) -> Dict[str, Any]:
|
|
249
|
+
"""Call {{ api_name }} API using the SDK with automatic retry."""
|
|
250
|
+
# Initialize the SDK client
|
|
251
|
+
client = {{ sdk_module }}.Client(api_key=api_key)
|
|
252
|
+
|
|
253
|
+
# Make the API call - will retry once on any exception
|
|
254
|
+
try:
|
|
255
|
+
response = client.call_endpoint(params)
|
|
256
|
+
return response.to_dict()
|
|
257
|
+
except Exception as e:
|
|
258
|
+
raise Exception(f"{{ api_name }} API error: {str(e)}")
|
|
259
|
+
{% else %}# Using requests library with retry decorator
|
|
260
|
+
@retry(tries=2, delay=1, backoff=2, jitter=(1, 3))
|
|
261
|
+
def call_{{ api_slug.replace('-', '_') }}_api(endpoint: str, params: Dict[str, Any], api_key: str) -> Dict[str, Any]:
|
|
262
|
+
"""Call {{ api_name }} API endpoint with automatic retry."""
|
|
263
|
+
base_url = "https://api.example.com/v1" # TODO: Get actual base URL from docs
|
|
264
|
+
|
|
265
|
+
headers = {
|
|
266
|
+
{% if requires_auth %}"Authorization": f"Bearer {api_key}", # Or "X-API-Key": api_key
|
|
267
|
+
{% endif %}"Content-Type": "application/json"
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Will retry once on any requests.RequestException
|
|
271
|
+
try:
|
|
272
|
+
response = requests.get(f"{base_url}/{endpoint}",
|
|
273
|
+
params=params,
|
|
274
|
+
headers=headers,
|
|
275
|
+
timeout=30)
|
|
276
|
+
response.raise_for_status()
|
|
277
|
+
return response.json()
|
|
278
|
+
except requests.RequestException as e:
|
|
279
|
+
raise Exception(f"{{ api_name }} API error: {str(e)}")
|
|
280
|
+
{% endif %}```
|
|
281
|
+
|
|
282
|
+
#### Retry Configuration Explained
|
|
283
|
+
|
|
284
|
+
- `tries=2`: Total attempts (1 original + 1 retry)
|
|
285
|
+
- `delay=1`: Wait 1 second before retry
|
|
286
|
+
- `backoff=2`: Multiply delay by 2 for subsequent retries (if more than 2 tries)
|
|
287
|
+
- `jitter=(1, 3)`: Add random delay between 1-3 seconds to avoid thundering herd
|
|
288
|
+
|
|
289
|
+
### 4. Create MCP Tools
|
|
290
|
+
|
|
291
|
+
Replace the `example_tool` placeholder with actual tools. **Each tool you implement MUST be added to the `capabilities` array in `deployment_params.json`**.
|
|
292
|
+
|
|
293
|
+
#### Search/Query Tool
|
|
294
|
+
```python
|
|
295
|
+
@mcp.tool()
|
|
296
|
+
async def search_{{ api_slug.replace('-', '_') }}(
|
|
297
|
+
context: Context,
|
|
298
|
+
query: str,
|
|
299
|
+
limit: int = 10
|
|
300
|
+
) -> Dict[str, Any]:
|
|
301
|
+
"""
|
|
302
|
+
Search {{ api_name }} for [specific data type].
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
context: MCP context (injected automatically)
|
|
306
|
+
query: Search query
|
|
307
|
+
limit: Maximum number of results
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Dictionary with search results
|
|
311
|
+
"""
|
|
312
|
+
{% if requires_auth %}
|
|
313
|
+
api_key = get_session_api_key(context)
|
|
314
|
+
if not api_key:
|
|
315
|
+
return {"error": "No API key found. Please authenticate with Authorization: Bearer YOUR_API_KEY"}
|
|
316
|
+
{% endif %}
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
# The call_{{ api_slug.replace('-', '_') }}_api function already has retry logic
|
|
320
|
+
results = call_{{ api_slug.replace('-', '_') }}_api(
|
|
321
|
+
"search", # TODO: Use actual endpoint
|
|
322
|
+
{"q": query, "limit": limit},
|
|
323
|
+
{% if requires_auth %}api_key{% else %}None{% endif %})
|
|
324
|
+
return {
|
|
325
|
+
"status": "success",
|
|
326
|
+
"results": results
|
|
327
|
+
}
|
|
328
|
+
except Exception as e:
|
|
329
|
+
return {"error": str(e)}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Get/Fetch Tool
|
|
333
|
+
```python
|
|
334
|
+
@mcp.tool()
|
|
335
|
+
async def get_{{ api_slug.replace('-', '_') }}_info(
|
|
336
|
+
context: Context,
|
|
337
|
+
item_id: str
|
|
338
|
+
) -> Dict[str, Any]:
|
|
339
|
+
"""
|
|
340
|
+
Get detailed information about a specific item.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
context: MCP context (injected automatically)
|
|
344
|
+
item_id: ID of the item to fetch
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Dictionary with item details
|
|
348
|
+
"""
|
|
349
|
+
# Similar implementation pattern
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### Create/Update Tool (if applicable)
|
|
353
|
+
```python
|
|
354
|
+
@mcp.tool()
|
|
355
|
+
async def create_{{ api_slug.replace('-', '_') }}_item(
|
|
356
|
+
context: Context,
|
|
357
|
+
name: str,
|
|
358
|
+
data: Dict[str, Any]
|
|
359
|
+
) -> Dict[str, Any]:
|
|
360
|
+
"""
|
|
361
|
+
Create a new item in {{ api_name }}.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
context: MCP context (injected automatically)
|
|
365
|
+
name: Name of the item
|
|
366
|
+
data: Additional data for the item
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Dictionary with creation result
|
|
370
|
+
"""
|
|
371
|
+
# Implementation here
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### 5. Best Practices
|
|
375
|
+
|
|
376
|
+
1. **Error Handling**: Always wrap API calls in try-except blocks and return user-friendly error messages
|
|
377
|
+
2. **Input Validation**: Validate parameters before making API calls
|
|
378
|
+
3. **Rate Limiting**: Respect API rate limits, implement delays if needed
|
|
379
|
+
4. **Response Formatting**: Return consistent response structures across all tools
|
|
380
|
+
5. **Logging**: Use the logger for debugging but don't log sensitive data like API keys
|
|
381
|
+
6. **Documentation**: Write clear docstrings for each tool explaining parameters and return values
|
|
382
|
+
7. **Retry Strategy**: Use the `@retry` decorator on API client functions for resilience
|
|
383
|
+
|
|
384
|
+
#### Retry Best Practices
|
|
385
|
+
|
|
386
|
+
- **Only use retry for external API calls** - Don't add retry to internal functions, database operations, or file operations
|
|
387
|
+
- **Apply retry to API client functions**, not MCP tool functions directly
|
|
388
|
+
- **Use `tries=2`** (1 original attempt + 1 retry) to balance reliability and responsiveness
|
|
389
|
+
- **Add jitter** to prevent thundering herd when multiple clients retry simultaneously
|
|
390
|
+
- **Don't retry on authentication errors** - use conditional retry if needed:
|
|
391
|
+
```python
|
|
392
|
+
from retry import retry
|
|
393
|
+
import requests
|
|
394
|
+
|
|
395
|
+
def retry_on_server_error(exception):
|
|
396
|
+
"""Only retry on server errors (5xx), not client errors (4xx)"""
|
|
397
|
+
if isinstance(exception, requests.HTTPError):
|
|
398
|
+
return 500 <= exception.response.status_code < 600
|
|
399
|
+
return True # Retry on other exceptions like network errors
|
|
400
|
+
|
|
401
|
+
@retry(tries=2, delay=1, backoff=2, jitter=(1, 3), exceptions=retry_on_server_error)
|
|
402
|
+
def call_api_with_smart_retry(endpoint, params, api_key):
|
|
403
|
+
# API implementation
|
|
404
|
+
pass
|
|
405
|
+
```
|
|
406
|
+
- **Log retry attempts** for debugging:
|
|
407
|
+
```python
|
|
408
|
+
@retry(tries=2, delay=1, logger=logger)
|
|
409
|
+
def call_{{ api_slug.replace('-', '_') }}_api(...):
|
|
410
|
+
# Implementation
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### When to Use Retry
|
|
414
|
+
|
|
415
|
+
**✅ DO use retry for:**
|
|
416
|
+
- HTTP requests to external APIs
|
|
417
|
+
- Network operations that can fail temporarily
|
|
418
|
+
- Remote service calls that may experience transient errors
|
|
419
|
+
|
|
420
|
+
**❌ DON'T use retry for:**
|
|
421
|
+
- MCP tool functions (they should handle errors gracefully instead)
|
|
422
|
+
- Local file operations
|
|
423
|
+
- Database queries (unless specifically needed for connection issues)
|
|
424
|
+
- Authentication/validation logic
|
|
425
|
+
- Data processing or computation functions
|
|
426
|
+
|
|
427
|
+
### 6. Testing
|
|
428
|
+
|
|
429
|
+
After implementing tools, test them:
|
|
430
|
+
|
|
431
|
+
1. Run the server locally:
|
|
432
|
+
```bash
|
|
433
|
+
./run_local_docker.sh
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
2. Use the health check script:
|
|
437
|
+
```bash
|
|
438
|
+
python mcp_health_check.py
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
3. Test with CrewAI:
|
|
442
|
+
```python
|
|
443
|
+
{% if requires_auth %}from traia_iatp.mcp.traia_mcp_adapter import create_mcp_adapter_with_auth
|
|
444
|
+
|
|
445
|
+
# Authenticated connection
|
|
446
|
+
with create_mcp_adapter_with_auth(
|
|
447
|
+
url="http://localhost:8000/mcp/",
|
|
448
|
+
api_key="your-api-key"
|
|
449
|
+
) as tools:
|
|
450
|
+
# Test your tools
|
|
451
|
+
for tool in tools:
|
|
452
|
+
print(f"Tool: {tool.name}")
|
|
453
|
+
{% else %}from traia_iatp.mcp.traia_mcp_adapter import create_mcp_adapter
|
|
454
|
+
|
|
455
|
+
# Standard connection (no authentication)
|
|
456
|
+
with create_mcp_adapter(
|
|
457
|
+
url="http://localhost:8000/mcp/"
|
|
458
|
+
) as tools:
|
|
459
|
+
# Test your tools
|
|
460
|
+
for tool in tools:
|
|
461
|
+
print(f"Tool: {tool.name}")
|
|
462
|
+
{% endif %}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 7. Update Documentation
|
|
466
|
+
|
|
467
|
+
After implementing the tools:
|
|
468
|
+
|
|
469
|
+
1. **Update README.md**:
|
|
470
|
+
- List all implemented tools with descriptions
|
|
471
|
+
- Add usage examples for each tool
|
|
472
|
+
- Include any specific setup instructions
|
|
473
|
+
|
|
474
|
+
2. **Update deployment_params.json**:
|
|
475
|
+
- Ensure ALL tool names are in the `capabilities` array
|
|
476
|
+
- Add relevant tags based on functionality
|
|
477
|
+
- Verify authentication settings match implementation
|
|
478
|
+
|
|
479
|
+
3. **Add Tool Examples** in README.md:
|
|
480
|
+
```python
|
|
481
|
+
# Example usage of each tool
|
|
482
|
+
result = await tool.search_{{ api_slug.replace('-', '_') }}(query="example", limit=5)
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### 8. Pre-Deployment Checklist
|
|
486
|
+
|
|
487
|
+
Before marking the implementation as complete:
|
|
488
|
+
|
|
489
|
+
- [ ] All placeholder code has been replaced with actual implementation
|
|
490
|
+
- [ ] All tools are properly documented with docstrings
|
|
491
|
+
- [ ] Error handling is implemented for all API calls
|
|
492
|
+
- [ ] `deployment_params.json` contains all tool names in capabilities
|
|
493
|
+
- [ ] README.md has been updated with usage examples
|
|
494
|
+
- [ ] Server runs successfully with `./run_local_docker.sh`
|
|
495
|
+
- [ ] Health check passes
|
|
496
|
+
- [ ] At least one tool works end-to-end
|
|
497
|
+
|
|
498
|
+
### 9. Common {{ api_name }} Use Cases
|
|
499
|
+
|
|
500
|
+
Based on the API documentation, consider implementing tools for these common use cases:
|
|
501
|
+
|
|
502
|
+
1. **TODO**: List specific use cases based on {{ api_name }} capabilities
|
|
503
|
+
2. **TODO**: Add more relevant use cases
|
|
504
|
+
3. **TODO**: Include any special features of this API
|
|
505
|
+
|
|
506
|
+
### 10. Example API Calls
|
|
507
|
+
|
|
508
|
+
Here are some example API calls from the {{ api_name }} documentation that you should implement:
|
|
509
|
+
|
|
510
|
+
```
|
|
511
|
+
TODO: Add specific examples from the API docs
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## Need Help?
|
|
515
|
+
|
|
516
|
+
- Check the {{ api_name }} API documentation: {{ docs_url }}
|
|
517
|
+
- Review the MCP specification: https://modelcontextprotocol.io
|
|
518
|
+
- Look at other MCP server examples in the Traia-IO organization
|
|
519
|
+
|
|
520
|
+
Remember: The goal is to make {{ api_name }}'s capabilities easily accessible to AI agents through standardized MCP tools.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"github_url": "https://github.com/Traia-IO/{{ api_slug }}-mcp-server",
|
|
3
|
+
"mcp_server": {
|
|
4
|
+
"name": "{{ api_slug }}-mcp",
|
|
5
|
+
"description": "{{ api_description|capitalize }}",
|
|
6
|
+
"server_type": "streamable-http",
|
|
7
|
+
"requires_api_key": {{ requires_auth|lower }},
|
|
8
|
+
{% if requires_auth %}"api_key_header": "Authorization",
|
|
9
|
+
"api_keys": ["{{ api_key_env_var }}"],
|
|
10
|
+
{% endif %}"capabilities": [
|
|
11
|
+
"example_tool",
|
|
12
|
+
"get_api_info"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"deployment_method": "cloud_run",
|
|
16
|
+
"gcp_project_id": "traia-mcp-servers",
|
|
17
|
+
"gcp_region": "us-central1",
|
|
18
|
+
"tags": ["{{ api_name_lower }}", "api", "mcp"],
|
|
19
|
+
"ref": "main"
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
{{ api_slug }}-mcp:
|
|
5
|
+
build: .
|
|
6
|
+
container_name: {{ api_slug }}-mcp-server
|
|
7
|
+
ports:
|
|
8
|
+
- "8000:8000"
|
|
9
|
+
environment:
|
|
10
|
+
- PORT=8000
|
|
11
|
+
- STAGE=${STAGE:-MAINNET}
|
|
12
|
+
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
|
13
|
+
{% if requires_auth %}
|
|
14
|
+
# API Authentication (server's internal key for payment mode)
|
|
15
|
+
# Set this in .env file when deploying (creator provides their master API key)
|
|
16
|
+
- {{ api_key_env_var }}={% raw %}${{% endraw %}{{ api_key_env_var }}{% raw %}}{% endraw %}
|
|
17
|
+
{% endif %}
|
|
18
|
+
# D402 Payment Protocol Configuration
|
|
19
|
+
- SERVER_ADDRESS=${SERVER_ADDRESS:-}
|
|
20
|
+
- MCP_OPERATOR_PRIVATE_KEY=${MCP_OPERATOR_PRIVATE_KEY:-} # For signing settlement attestations
|
|
21
|
+
- MCP_OPERATOR_ADDRESS=${MCP_OPERATOR_ADDRESS:-} # Operator's public address (for verification)
|
|
22
|
+
- D402_FACILITATOR_URL=${D402_FACILITATOR_URL:-https://facilitator.d402.net}
|
|
23
|
+
- D402_FACILITATOR_API_KEY=${D402_FACILITATOR_API_KEY:-}
|
|
24
|
+
- D402_TESTING_MODE=${D402_TESTING_MODE:-false} # Set to 'true' for local testing without facilitator
|
|
25
|
+
volumes:
|
|
26
|
+
- ./logs:/app/logs
|
|
27
|
+
restart: unless-stopped
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
30
|
+
interval: 30s
|
|
31
|
+
timeout: 10s
|
|
32
|
+
retries: 3
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Python cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
ENV/
|
|
11
|
+
env/
|
|
12
|
+
|
|
13
|
+
# IDE
|
|
14
|
+
.vscode/
|
|
15
|
+
.idea/
|
|
16
|
+
*.swp
|
|
17
|
+
*.swo
|
|
18
|
+
|
|
19
|
+
# Git
|
|
20
|
+
.git/
|
|
21
|
+
.gitignore
|
|
22
|
+
|
|
23
|
+
# Documentation
|
|
24
|
+
README.md
|
|
25
|
+
docs/
|
|
26
|
+
|
|
27
|
+
# Tests
|
|
28
|
+
tests/
|
|
29
|
+
pytest_cache/
|
|
30
|
+
.coverage
|
|
31
|
+
|
|
32
|
+
# OS
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
35
|
+
|
|
36
|
+
# Temporary files
|
|
37
|
+
*.log
|
|
38
|
+
*.tmp
|
|
39
|
+
.cache/
|
|
40
|
+
|
|
41
|
+
# uv
|
|
42
|
+
uv.lock
|
|
43
|
+
|
|
44
|
+
# Docker
|
|
45
|
+
Dockerfile
|
|
46
|
+
docker-compose.yml
|
|
47
|
+
.dockerignore
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# {{ api_name }} MCP Server Environment Configuration
|
|
2
|
+
#
|
|
3
|
+
# IMPORTANT: These values will be set during deployment (Phase 2)
|
|
4
|
+
# This file is a template - actual .env will be created with real values
|
|
5
|
+
|
|
6
|
+
# ============================================
|
|
7
|
+
# Server Configuration
|
|
8
|
+
# ============================================
|
|
9
|
+
PORT=8000
|
|
10
|
+
STAGE=MAINNET
|
|
11
|
+
LOG_LEVEL=INFO
|
|
12
|
+
|
|
13
|
+
{% if requires_auth %}
|
|
14
|
+
# ============================================
|
|
15
|
+
# API Authentication (Set during deployment)
|
|
16
|
+
# ============================================
|
|
17
|
+
# Server's internal {{ api_name }} API key
|
|
18
|
+
# This is used when clients pay via HTTP 402 (payment mode)
|
|
19
|
+
# Will be provided by MCP server creator during deployment
|
|
20
|
+
{{ api_key_env_var }}=
|
|
21
|
+
|
|
22
|
+
{% endif %}
|
|
23
|
+
# ============================================
|
|
24
|
+
# D402 Payment Protocol (Set during deployment)
|
|
25
|
+
# ============================================
|
|
26
|
+
# MCP server's payment address (IATP wallet contract address)
|
|
27
|
+
# Generated during deployment when IATP wallet contract is created
|
|
28
|
+
SERVER_ADDRESS=
|
|
29
|
+
|
|
30
|
+
# Operator keys for signing settlement attestations
|
|
31
|
+
# Generated during deployment from IATP wallet contract
|
|
32
|
+
MCP_OPERATOR_PRIVATE_KEY=
|
|
33
|
+
MCP_OPERATOR_ADDRESS=
|
|
34
|
+
|
|
35
|
+
# Facilitator configuration
|
|
36
|
+
D402_FACILITATOR_URL=https://facilitator.d402.net
|
|
37
|
+
D402_FACILITATOR_API_KEY=
|
|
38
|
+
|
|
39
|
+
# Default settlement token configuration
|
|
40
|
+
# Used as defaults for example tools and fallback for endpoints
|
|
41
|
+
DEFAULT_SETTLEMENT_TOKEN=0x036CbD53842c5426634e7929541eC2318f3dCF7e
|
|
42
|
+
DEFAULT_SETTLEMENT_NETWORK=sepolia
|
|
43
|
+
|
|
44
|
+
# Testing mode
|
|
45
|
+
D402_TESTING_MODE=true
|
|
46
|
+
|
|
47
|
+
# ============================================
|
|
48
|
+
# Deployment Process
|
|
49
|
+
# ============================================
|
|
50
|
+
# Phase 1 (Completed): Code generation
|
|
51
|
+
# Phase 2 (Next): Deployment via deploy_mcp_lambda_handler
|
|
52
|
+
# 1. Create IATP wallet contract → Get SERVER_ADDRESS
|
|
53
|
+
# 2. Generate operator keys → Get MCP_OPERATOR_PRIVATE_KEY & MCP_OPERATOR_ADDRESS
|
|
54
|
+
# 3. Get API key from creator{% if requires_auth %} → Set {{ api_key_env_var }}{% endif %}
|
|
55
|
+
# 4. Deploy to GCP with actual environment variables
|
|
56
|
+
# 5. Register in MongoDB
|
|
57
|
+
|