traia-iatp 0.1.2__py3-none-any.whl → 0.1.67__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.
- traia_iatp/__init__.py +105 -8
- traia_iatp/cli/main.py +85 -1
- traia_iatp/client/__init__.py +28 -3
- traia_iatp/client/crewai_a2a_tools.py +32 -12
- traia_iatp/client/d402_a2a_client.py +348 -0
- traia_iatp/contracts/__init__.py +11 -0
- traia_iatp/contracts/data/abis/contract-abis-localhost.json +4091 -0
- traia_iatp/contracts/data/abis/contract-abis-sepolia.json +4890 -0
- traia_iatp/contracts/data/addresses/contract-addresses.json +17 -0
- traia_iatp/contracts/data/addresses/contract-proxies.json +12 -0
- traia_iatp/contracts/iatp_contracts_config.py +263 -0
- traia_iatp/contracts/wallet_creator.py +369 -0
- traia_iatp/core/models.py +17 -3
- traia_iatp/d402/MIDDLEWARE_ARCHITECTURE.md +205 -0
- traia_iatp/d402/PRICE_BUILDER_USAGE.md +249 -0
- traia_iatp/d402/README.md +489 -0
- traia_iatp/d402/__init__.py +54 -0
- traia_iatp/d402/asgi_wrapper.py +469 -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 +266 -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 +481 -0
- traia_iatp/d402/mcp_middleware.py +296 -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 +126 -0
- traia_iatp/d402/payment_signing.py +183 -0
- traia_iatp/d402/price_builder.py +164 -0
- traia_iatp/d402/servers/__init__.py +61 -0
- traia_iatp/d402/servers/base.py +139 -0
- traia_iatp/d402/servers/example_general_server.py +140 -0
- traia_iatp/d402/servers/fastapi.py +253 -0
- traia_iatp/d402/servers/mcp.py +304 -0
- traia_iatp/d402/servers/starlette.py +878 -0
- traia_iatp/d402/starlette_middleware.py +529 -0
- traia_iatp/d402/types.py +300 -0
- traia_iatp/mcp/D402_MCP_ADAPTER_FLOW.md +357 -0
- traia_iatp/mcp/__init__.py +3 -0
- traia_iatp/mcp/d402_mcp_tool_adapter.py +526 -0
- traia_iatp/mcp/mcp_agent_template.py +78 -13
- traia_iatp/mcp/templates/Dockerfile.j2 +27 -4
- traia_iatp/mcp/templates/README.md.j2 +104 -8
- traia_iatp/mcp/templates/cursor-rules.md.j2 +194 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +1 -2
- traia_iatp/mcp/templates/docker-compose.yml.j2 +13 -3
- traia_iatp/mcp/templates/env.example.j2 +60 -0
- traia_iatp/mcp/templates/mcp_health_check.py.j2 +2 -2
- traia_iatp/mcp/templates/pyproject.toml.j2 +11 -5
- traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +320 -10
- traia_iatp/mcp/templates/server.py.j2 +174 -197
- traia_iatp/mcp/traia_mcp_adapter.py +182 -20
- traia_iatp/registry/__init__.py +47 -12
- traia_iatp/registry/atlas_search_indexes.json +108 -54
- traia_iatp/registry/iatp_search_api.py +169 -39
- traia_iatp/registry/mongodb_registry.py +241 -69
- traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +1 -1
- traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +8 -8
- traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +1 -1
- traia_iatp/registry/readmes/README.md +3 -3
- traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +6 -6
- traia_iatp/scripts/__init__.py +2 -0
- traia_iatp/scripts/create_wallet.py +244 -0
- traia_iatp/server/a2a_server.py +22 -7
- traia_iatp/server/iatp_server_template_generator.py +23 -0
- traia_iatp/server/templates/.dockerignore.j2 +48 -0
- traia_iatp/server/templates/Dockerfile.j2 +23 -1
- traia_iatp/server/templates/README.md +2 -2
- traia_iatp/server/templates/README.md.j2 +5 -5
- traia_iatp/server/templates/__main__.py.j2 +374 -66
- traia_iatp/server/templates/agent.py.j2 +12 -11
- traia_iatp/server/templates/agent_config.json.j2 +3 -3
- traia_iatp/server/templates/agent_executor.py.j2 +45 -27
- traia_iatp/server/templates/env.example.j2 +32 -4
- traia_iatp/server/templates/gitignore.j2 +7 -0
- traia_iatp/server/templates/pyproject.toml.j2 +13 -12
- traia_iatp/server/templates/run_local_docker.sh.j2 +143 -11
- traia_iatp/server/templates/server.py.j2 +197 -10
- traia_iatp/special_agencies/registry_search_agency.py +1 -1
- traia_iatp/utils/iatp_utils.py +6 -6
- traia_iatp-0.1.67.dist-info/METADATA +320 -0
- traia_iatp-0.1.67.dist-info/RECORD +117 -0
- traia_iatp-0.1.2.dist-info/METADATA +0 -414
- traia_iatp-0.1.2.dist-info/RECORD +0 -72
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/WHEEL +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/entry_points.txt +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/licenses/LICENSE +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/top_level.txt +0 -0
|
@@ -25,7 +25,31 @@ USER appuser
|
|
|
25
25
|
# Copy project files
|
|
26
26
|
COPY --chown=appuser:appuser . .
|
|
27
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
|
+
|
|
28
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)
|
|
29
53
|
RUN uv venv .venv && \
|
|
30
54
|
uv pip install -r pyproject.toml
|
|
31
55
|
|
|
@@ -36,7 +60,6 @@ ENV HOST=0.0.0.0
|
|
|
36
60
|
ENV PYTHONUNBUFFERED=1
|
|
37
61
|
ENV UV_SYSTEM_PYTHON=1
|
|
38
62
|
ENV STAGE=MAINNET
|
|
39
|
-
ENV PORT=8000
|
|
40
63
|
ENV LOG_LEVEL=INFO
|
|
41
64
|
{% if environment_variables %}
|
|
42
65
|
# Additional environment variables
|
|
@@ -45,12 +68,12 @@ ENV {{ env_var.name }}="{{ env_var.value }}"
|
|
|
45
68
|
{% endfor %}
|
|
46
69
|
{% endif %}
|
|
47
70
|
|
|
48
|
-
# Health check
|
|
71
|
+
# Health check - uses dedicated health endpoint
|
|
49
72
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
50
|
-
CMD curl -f http://localhost:${PORT:-
|
|
73
|
+
CMD curl -f http://localhost:${PORT:-8080}/health || exit 1
|
|
51
74
|
|
|
52
75
|
# Expose port (uses PORT environment variable with default)
|
|
53
|
-
EXPOSE ${PORT:-
|
|
76
|
+
EXPOSE ${PORT:-8080}
|
|
54
77
|
|
|
55
78
|
# Run the application
|
|
56
79
|
CMD ["uv", "run", "python", "server.py"]
|
|
@@ -8,6 +8,8 @@ This is an MCP (Model Context Protocol) server that provides{{ auth_details }} a
|
|
|
8
8
|
- 🌐 **Full API Access**: Provides tools for interacting with {{ api_name }} endpoints
|
|
9
9
|
{% if requires_auth %}
|
|
10
10
|
- 🔐 **Secure Authentication**: Supports API key authentication via Bearer tokens
|
|
11
|
+
- 💳 **HTTP 402 Payment Protocol**: Dual-mode operation (authenticated or paid access)
|
|
12
|
+
- 🔗 **D402 Integration**: Uses traia_iatp.d402 for blockchain payment verification
|
|
11
13
|
{% endif %}
|
|
12
14
|
- 🐳 **Docker Support**: Easy deployment with Docker and Docker Compose
|
|
13
15
|
- ⚡ **Async Operations**: Built with FastMCP for efficient async handling
|
|
@@ -22,7 +24,6 @@ This is an MCP (Model Context Protocol) server that provides{{ auth_details }} a
|
|
|
22
24
|
This server provides the following tools:
|
|
23
25
|
|
|
24
26
|
- **`example_tool`**: Placeholder tool (to be implemented)
|
|
25
|
-
- **`get_api_info`**: Get information about the API service and authentication status
|
|
26
27
|
|
|
27
28
|
*Note: Replace `example_tool` with actual {{ api_name }} API tools based on the documentation.*
|
|
28
29
|
|
|
@@ -54,7 +55,18 @@ This server provides the following tools:
|
|
|
54
55
|
|
|
55
56
|
1. Create a `.env` file with your configuration:
|
|
56
57
|
```env
|
|
57
|
-
{% if requires_auth %}
|
|
58
|
+
{% if requires_auth %}# Server's internal API key (for payment mode)
|
|
59
|
+
{{ api_key_env_var }}=your-api-key-here
|
|
60
|
+
|
|
61
|
+
# Server payment address (for HTTP 402 protocol)
|
|
62
|
+
SERVER_ADDRESS=0x1234567890123456789012345678901234567890
|
|
63
|
+
|
|
64
|
+
# Operator keys (for signing settlement attestations)
|
|
65
|
+
MCP_OPERATOR_PRIVATE_KEY=0x1234567890abcdef...
|
|
66
|
+
MCP_OPERATOR_ADDRESS=0x9876543210fedcba...
|
|
67
|
+
|
|
68
|
+
# Optional: Testing mode (skip settlement for local dev)
|
|
69
|
+
D402_TESTING_MODE=false
|
|
58
70
|
{% endif %}PORT=8000
|
|
59
71
|
```
|
|
60
72
|
|
|
@@ -118,15 +130,100 @@ with create_mcp_adapter(
|
|
|
118
130
|
```
|
|
119
131
|
|
|
120
132
|
{% if requires_auth %}
|
|
121
|
-
## Authentication
|
|
133
|
+
## Authentication & Payment (HTTP 402 Protocol)
|
|
122
134
|
|
|
123
|
-
This server
|
|
135
|
+
This server supports **two modes of operation**:
|
|
124
136
|
|
|
137
|
+
### Mode 1: Authenticated Access (Free)
|
|
138
|
+
|
|
139
|
+
Clients with their own {{ api_name }} API key can use the server for free:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Request with client's API key
|
|
143
|
+
curl -X POST http://localhost:8000/mcp \
|
|
144
|
+
-H "Authorization: Bearer CLIENT_{{ api_key_env_var }}" \
|
|
145
|
+
-H "Content-Type: application/json" \
|
|
146
|
+
-d '{"method":"tools/call","params":{"name":"example_tool","arguments":{"query":"test"}}}'
|
|
125
147
|
```
|
|
126
|
-
|
|
148
|
+
|
|
149
|
+
**Flow**:
|
|
150
|
+
1. Client provides their {{ api_name }} API key
|
|
151
|
+
2. Server uses client's API key to call {{ api_name }} API
|
|
152
|
+
3. No payment required
|
|
153
|
+
4. Client pays {{ api_name }} directly
|
|
154
|
+
|
|
155
|
+
### Mode 2: Payment Required (Paid Access)
|
|
156
|
+
|
|
157
|
+
Clients without an API key can pay-per-use via HTTP 402 protocol:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Request with payment proof (x402/d402 protocol)
|
|
161
|
+
curl -X POST http://localhost:8000/mcp \
|
|
162
|
+
-H "X-PAYMENT: <base64_encoded_x402_payment>" \
|
|
163
|
+
-H "Content-Type: application/json" \
|
|
164
|
+
-d '{"method":"tools/call","params":{"name":"example_tool","arguments":{"query":"test"}}}'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Flow**:
|
|
168
|
+
1. Client makes initial request without payment
|
|
169
|
+
2. Server returns HTTP 402 with PaymentRequirements (token, network, amount)
|
|
170
|
+
3. Client creates EIP-3009 transferWithAuthorization payment signature
|
|
171
|
+
4. Client base64-encodes payment and sends in X-PAYMENT header
|
|
172
|
+
5. Server verifies payment via traia_iatp.d402.mcp_middleware
|
|
173
|
+
6. Server uses its INTERNAL {{ api_name }} API key to call the API
|
|
174
|
+
7. Client receives result
|
|
175
|
+
|
|
176
|
+
### D402 Protocol Details
|
|
177
|
+
|
|
178
|
+
This server uses the **traia_iatp.d402** module for payment verification:
|
|
179
|
+
|
|
180
|
+
- **Payment Method**: EIP-3009 transferWithAuthorization (gasless)
|
|
181
|
+
- **Supported Tokens**: USDC, TRAIA, or any ERC20 token
|
|
182
|
+
- **Default Price**: $0.001 per request (configurable via `DEFAULT_PRICE_USD`)
|
|
183
|
+
- **Networks**: Base Sepolia, Sepolia, Polygon, etc.
|
|
184
|
+
- **Facilitator**: d402.org (public) or custom facilitator
|
|
185
|
+
|
|
186
|
+
### Environment Variables for Payment Mode
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Required
|
|
190
|
+
{{ api_key_env_var }}=your_internal_{{ api_slug }}_api_key # Server's API key (for payment mode)
|
|
191
|
+
SERVER_ADDRESS=0x1234567890123456789012345678901234567890 # Server's payment address
|
|
192
|
+
|
|
193
|
+
# Required for Settlement (Production)
|
|
194
|
+
MCP_OPERATOR_PRIVATE_KEY=0x1234... # Private key for signing settlement attestations
|
|
195
|
+
MCP_OPERATOR_ADDRESS=0x5678... # Operator's public address (for verification)
|
|
196
|
+
|
|
197
|
+
# Optional
|
|
198
|
+
D402_FACILITATOR_URL=https://facilitator.d402.net # Facilitator service URL
|
|
199
|
+
D402_FACILITATOR_API_KEY=your_key # For private facilitator
|
|
200
|
+
D402_TESTING_MODE=false # Set to 'true' for local testing without settlement
|
|
127
201
|
```
|
|
128
202
|
|
|
129
|
-
|
|
203
|
+
**Operator Keys**:
|
|
204
|
+
- **MCP_OPERATOR_PRIVATE_KEY**: Used to sign settlement attestations (proof of service completion)
|
|
205
|
+
- **MCP_OPERATOR_ADDRESS**: Public address corresponding to the private key
|
|
206
|
+
- Required for on-chain settlement via IATP Settlement Layer
|
|
207
|
+
- Can be the same as SERVER_ADDRESS or a separate operator key
|
|
208
|
+
|
|
209
|
+
**Note on Per-Endpoint Configuration**:
|
|
210
|
+
Each endpoint's payment requirements (token address, network, price) are embedded in the tool code.
|
|
211
|
+
They come from the endpoint configuration when the server is generated.
|
|
212
|
+
|
|
213
|
+
### How It Works
|
|
214
|
+
|
|
215
|
+
1. **Client Decision**:
|
|
216
|
+
- Has {{ api_name }} API key? → Mode 1 (Authenticated)
|
|
217
|
+
- No API key but willing to pay? → Mode 2 (Payment)
|
|
218
|
+
|
|
219
|
+
2. **Server Response**:
|
|
220
|
+
- Mode 1: Uses client's API key (free for client)
|
|
221
|
+
- Mode 2: Uses server's API key (client pays server)
|
|
222
|
+
|
|
223
|
+
3. **Business Model**:
|
|
224
|
+
- Mode 1: No revenue (passthrough)
|
|
225
|
+
- Mode 2: Revenue from pay-per-use (monetize server's API subscription)
|
|
226
|
+
|
|
130
227
|
{% endif %}
|
|
131
228
|
|
|
132
229
|
## Development
|
|
@@ -163,8 +260,7 @@ The `deployment_params.json` file contains the deployment configuration for this
|
|
|
163
260
|
"api_key_header": "Authorization",
|
|
164
261
|
{% endif %}"capabilities": [
|
|
165
262
|
// List all implemented tool names here
|
|
166
|
-
"example_tool"
|
|
167
|
-
"get_api_info"
|
|
263
|
+
"example_tool"
|
|
168
264
|
]
|
|
169
265
|
},
|
|
170
266
|
"deployment_method": "cloud_run",
|
|
@@ -12,6 +12,200 @@ You are working on implementing the {{ api_name }} MCP (Model Context Protocol)
|
|
|
12
12
|
{% endif %}
|
|
13
13
|
- **Authentication**: {% if requires_auth %}Required - API key via `{{ api_key_env_var }}` environment variable{% else %}Not required{% endif %}
|
|
14
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
|
+
|
|
15
209
|
## Implementation Checklist
|
|
16
210
|
|
|
17
211
|
### 1. Update Deployment Configuration
|
|
@@ -5,19 +5,29 @@ services:
|
|
|
5
5
|
build: .
|
|
6
6
|
container_name: {{ api_slug }}-mcp-server
|
|
7
7
|
ports:
|
|
8
|
-
- "
|
|
8
|
+
- "8080:8080"
|
|
9
9
|
environment:
|
|
10
10
|
- PORT=8000
|
|
11
11
|
- STAGE=${STAGE:-MAINNET}
|
|
12
12
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
|
13
|
-
{% if
|
|
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)
|
|
14
16
|
- {{ api_key_env_var }}={% raw %}${{% endraw %}{{ api_key_env_var }}{% raw %}}{% endraw %}
|
|
15
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://test-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
|
+
- NETWORK=${NETWORK:-sepolia}
|
|
16
26
|
volumes:
|
|
17
27
|
- ./logs:/app/logs
|
|
18
28
|
restart: unless-stopped
|
|
19
29
|
healthcheck:
|
|
20
|
-
test: ["CMD", "curl", "-f", "http://localhost:
|
|
30
|
+
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
|
21
31
|
interval: 30s
|
|
22
32
|
timeout: 10s
|
|
23
33
|
retries: 3
|
|
@@ -0,0 +1,60 @@
|
|
|
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=8080
|
|
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
|
+
# Options:
|
|
37
|
+
# - Remote (production): https://test-facilitator.d402.net
|
|
38
|
+
# - Local (development): http://host.docker.internal:7070
|
|
39
|
+
D402_FACILITATOR_URL=https://test-facilitator.d402.net
|
|
40
|
+
D402_FACILITATOR_API_KEY=
|
|
41
|
+
|
|
42
|
+
# Default settlement token configuration
|
|
43
|
+
# Used as defaults for example tools and fallback for endpoints
|
|
44
|
+
DEFAULT_SETTLEMENT_TOKEN=0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238
|
|
45
|
+
DEFAULT_SETTLEMENT_NETWORK=sepolia
|
|
46
|
+
|
|
47
|
+
# Testing mode (set to 'true' to bypass facilitator for local testing)
|
|
48
|
+
D402_TESTING_MODE=false
|
|
49
|
+
|
|
50
|
+
# ============================================
|
|
51
|
+
# Deployment Process
|
|
52
|
+
# ============================================
|
|
53
|
+
# Phase 1 (Completed): Code generation
|
|
54
|
+
# Phase 2 (Next): Deployment via deploy_mcp_lambda_handler
|
|
55
|
+
# 1. Create IATP wallet contract → Get SERVER_ADDRESS
|
|
56
|
+
# 2. Generate operator keys → Get MCP_OPERATOR_PRIVATE_KEY & MCP_OPERATOR_ADDRESS
|
|
57
|
+
# 3. Get API key from creator{% if requires_auth %} → Set {{ api_key_env_var }}{% endif %}
|
|
58
|
+
# 4. Deploy to GCP with actual environment variables
|
|
59
|
+
# 5. Register in MongoDB
|
|
60
|
+
|
|
@@ -91,7 +91,7 @@ def check_mcp_server_health(url: str) -> bool:
|
|
|
91
91
|
print(f"📋 Available tools: {', '.join(tool_names)}")
|
|
92
92
|
|
|
93
93
|
# Check for expected tools
|
|
94
|
-
expected_tools = ["example_tool"
|
|
94
|
+
expected_tools = ["example_tool"]
|
|
95
95
|
missing_tools = [tool for tool in expected_tools if tool not in tool_names]
|
|
96
96
|
|
|
97
97
|
if missing_tools:
|
|
@@ -136,7 +136,7 @@ def main():
|
|
|
136
136
|
|
|
137
137
|
print(f"🚀 {{ api_name }} MCP Server Health Check")
|
|
138
138
|
print(f"📍 Server URL: {args.url}")
|
|
139
|
-
print(f"📰 Expected tools: example_tool
|
|
139
|
+
print(f"📰 Expected tools: example_tool")
|
|
140
140
|
print("="*50)
|
|
141
141
|
|
|
142
142
|
if check_mcp_server_health(args.url):
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "{{ api_slug }}-mcp-server"
|
|
3
3
|
version = "0.1.0"
|
|
4
|
-
description = "MCP server for {{ api_name }} API{% if auth_description %}
|
|
4
|
+
description = "MCP server for {{ api_name }} API with {% if auth_description %} authentication and {% endif %} HTTP 402 payment protocol support"
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"anyio>=4.0.0",
|
|
8
|
+
"mcp>=1.1.2",
|
|
9
9
|
"python-dotenv>=1.1.1",
|
|
10
|
-
"requests>=2.32.
|
|
11
|
-
"starlette>=0.
|
|
10
|
+
"requests>=2.32.5",
|
|
11
|
+
"starlette>=0.45.0",
|
|
12
12
|
"retry>=0.9.2",
|
|
13
|
+
"traia-iatp>=0.1.67", # For d402 payment protocol and RPC fallback support
|
|
14
|
+
"uvicorn>=0.37.0",
|
|
15
|
+
"web3>=6.15.0", # For blockchain payment verification
|
|
13
16
|
{% if sdk_package %}
|
|
14
17
|
"{{ sdk_package }}",
|
|
15
18
|
{% endif %}
|
|
@@ -19,6 +22,9 @@ dependencies = [
|
|
|
19
22
|
requires = ["hatchling"]
|
|
20
23
|
build-backend = "hatchling.build"
|
|
21
24
|
|
|
25
|
+
[tool.hatch.metadata]
|
|
26
|
+
allow-direct-references = true
|
|
27
|
+
|
|
22
28
|
[tool.hatch.build.targets.wheel]
|
|
23
29
|
include = [
|
|
24
30
|
"server.py",
|
|
@@ -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
|
+
|