Skip to content

Commit 1d5a8b6

Browse files
committed
x402 integration into aop
1 parent 1bb4dab commit 1d5a8b6

File tree

3 files changed

+244
-18
lines changed

3 files changed

+244
-18
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
AOP x402 Payment Integration Example
3+
4+
This example demonstrates how to enable x402 cryptocurrency payments
5+
for all agents in an AOP cluster. Users will need to pay in cryptocurrency
6+
before accessing any agent endpoints.
7+
8+
Requirements:
9+
pip install swarms x402
10+
11+
Usage:
12+
python aop_payment_example.py
13+
14+
Then test with your MCP client or directly via HTTP.
15+
"""
16+
17+
from dotenv import load_dotenv
18+
19+
from swarms import Agent
20+
from swarms.structs.aop import AOP, PaymentConfig
21+
22+
# Load environment variables
23+
load_dotenv()
24+
25+
26+
def main():
27+
# Create multiple agents for the cluster
28+
research_agent = Agent(
29+
agent_name="Research-Agent",
30+
system_prompt="You are an expert research analyst. Conduct thorough research on the given topic and provide comprehensive insights.",
31+
model_name="gpt-4o-mini",
32+
max_loops=1,
33+
)
34+
35+
analysis_agent = Agent(
36+
agent_name="Analysis-Agent",
37+
system_prompt="You are a data analysis expert. Analyze the provided information and extract key insights.",
38+
model_name="gpt-4o-mini",
39+
max_loops=1,
40+
)
41+
42+
writing_agent = Agent(
43+
agent_name="Writing-Agent",
44+
system_prompt="You are a professional writer. Create well-structured, engaging content based on the given requirements.",
45+
model_name="gpt-4o-mini",
46+
max_loops=1,
47+
)
48+
49+
# Configure x402 payment settings
50+
# Replace with your actual Solana wallet address!
51+
payment_config = PaymentConfig(
52+
pay_to_address="YourSolanaWalletAddressHere", # REQUIRED: Your Solana wallet
53+
price="$0.01", # Price per agent request
54+
network_id="solana", # Solana mainnet (use "solana-devnet" for testing)
55+
description="AI Agent Marketplace - Pay per use",
56+
)
57+
58+
# Create AOP cluster with payment enabled
59+
aop = AOP(
60+
server_name="Paid AI Agent Cluster",
61+
description="A marketplace of AI agents with x402 cryptocurrency payments",
62+
agents=[research_agent, analysis_agent, writing_agent],
63+
port=8000,
64+
host="localhost",
65+
payment=True, # Enable x402 payment
66+
payment_config=payment_config, # Payment configuration
67+
verbose=True,
68+
log_level="INFO",
69+
)
70+
71+
print("\n" + "=" * 60)
72+
print("🚀 AOP Server with x402 Payment Integration (Solana)")
73+
print("=" * 60)
74+
print(f"✅ Payment enabled: {payment_config.price} per request")
75+
print(f"💰 Solana wallet: {payment_config.pay_to_address}")
76+
print(f"🌐 Network: {payment_config.network_id}")
77+
print(f"📍 Server: http://{aop.host}:{aop.port}")
78+
print("\n💡 All MCP endpoints require Solana payment via x402!")
79+
print("🎯 Available Agents:")
80+
for agent_name in aop.list_agents():
81+
print(f" - {agent_name}")
82+
print("\n" + "=" * 60)
83+
print("Starting server...\n")
84+
85+
# Start the server
86+
aop.run()
87+
88+
89+
if __name__ == "__main__":
90+
main()

examples/guides/x402_examples/env.example

Lines changed: 0 additions & 17 deletions
This file was deleted.

swarms/structs/aop.py

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,114 @@
1212

1313
from loguru import logger
1414
from mcp.server.fastmcp import FastMCP
15+
from pydantic import BaseModel, Field
1516

1617
from swarms.structs.agent import Agent
1718
from swarms.structs.omni_agent_types import AgentType
1819
from swarms.tools.mcp_client_tools import (
1920
get_tools_for_multiple_mcp_servers,
2021
)
2122

23+
# Optional x402 and Starlette imports
24+
try:
25+
from starlette.middleware.base import BaseHTTPMiddleware
26+
from starlette.requests import Request
27+
from x402.fastapi.middleware import require_payment
28+
29+
X402_AVAILABLE = True
30+
except ImportError:
31+
X402_AVAILABLE = False
32+
33+
34+
class PaymentConfig(BaseModel):
35+
"""
36+
Configuration for x402 cryptocurrency payment integration.
37+
38+
This enables monetization of AI agent endpoints by requiring payment
39+
before execution. Payments are processed via x402 protocol on Solana blockchain.
40+
41+
Attributes:
42+
pay_to_address: Solana wallet address to receive cryptocurrency payments
43+
price: Cost per agent request (e.g., "$0.01", "$0.05")
44+
network_id: Blockchain network identifier (default: "solana" for Solana mainnet)
45+
description: Optional description of the payment service for users
46+
"""
47+
48+
pay_to_address: str = Field(
49+
...,
50+
description="Solana wallet address to receive payments",
51+
)
52+
price: str = Field(
53+
default="$0.01",
54+
description="Price per agent request in USD (e.g., '$0.01')",
55+
)
56+
network_id: str = Field(
57+
default="solana",
58+
description="Blockchain network ID (default: 'solana' for mainnet, or 'solana-devnet' for testing)",
59+
)
60+
description: Optional[str] = Field(
61+
None,
62+
description="Service description shown to users during payment",
63+
)
64+
65+
66+
if X402_AVAILABLE:
67+
68+
class X402PaymentMiddleware(BaseHTTPMiddleware):
69+
"""
70+
Starlette middleware that wraps x402 payment validation.
71+
72+
Intercepts HTTP requests and applies x402 payment requirement
73+
to specified paths (MCP tool execution paths).
74+
"""
75+
76+
def __init__(self, app, payment_config: PaymentConfig):
77+
"""
78+
Initialize x402 payment middleware.
79+
80+
Args:
81+
app: Starlette app instance
82+
payment_config: Payment configuration with wallet, price, network
83+
"""
84+
super().__init__(app)
85+
self.payment_config = payment_config
86+
# Create x402 payment middleware for MCP path
87+
self.payment_validator = require_payment(
88+
path="/mcp", # FastMCP streamable-http path
89+
price=payment_config.price,
90+
pay_to_address=payment_config.pay_to_address,
91+
network_id=payment_config.network_id,
92+
description=payment_config.description
93+
or "AI Agent execution via MCP",
94+
input_schema={
95+
"type": "object",
96+
"properties": {"task": {"type": "string"}},
97+
},
98+
output_schema={
99+
"type": "object",
100+
"properties": {"result": {"type": "string"}},
101+
},
102+
)
103+
104+
async def dispatch(self, request: Request, call_next):
105+
"""
106+
Intercept requests and apply x402 payment validation.
107+
108+
Args:
109+
request: Incoming HTTP request
110+
call_next: Next middleware in chain
111+
112+
Returns:
113+
Response from payment validator or next middleware
114+
"""
115+
# Check if request path requires payment (MCP paths)
116+
if request.url.path.startswith("/mcp"):
117+
# Apply x402 payment validation
118+
return await self.payment_validator(request, call_next)
119+
120+
# Other paths don't require payment
121+
return await call_next(request)
122+
22123

23124
class TaskStatus(Enum):
24125
"""Status of a task in the queue."""
@@ -597,6 +698,8 @@ def __init__(
597698
max_network_retries: int = 5,
598699
network_retry_delay: float = 10.0,
599700
network_timeout: float = 30.0,
701+
payment: bool = False,
702+
payment_config: Optional[PaymentConfig] = None,
600703
log_level: Literal[
601704
"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
602705
] = "INFO",
@@ -628,7 +731,22 @@ def __init__(
628731
max_network_retries: Maximum number of network reconnection attempts
629732
network_retry_delay: Delay between network retry attempts in seconds
630733
network_timeout: Network connection timeout in seconds
734+
payment: Enable x402 cryptocurrency payment for agent endpoints
735+
payment_config: Configuration for x402 payment (required if payment=True)
631736
"""
737+
# Validate payment configuration
738+
if payment and not payment_config:
739+
raise ValueError(
740+
"payment_config is required when payment=True. "
741+
"Please provide a PaymentConfig instance with pay_to_address."
742+
)
743+
744+
if payment and not X402_AVAILABLE:
745+
raise ImportError(
746+
"x402 package is required for payment functionality. "
747+
"Install it with: pip install x402"
748+
)
749+
632750
self.server_name = server_name
633751
self.description = description
634752
self.verbose = verbose
@@ -648,6 +766,8 @@ def __init__(
648766
self.max_network_retries = max_network_retries
649767
self.network_retry_delay = network_retry_delay
650768
self.network_timeout = network_timeout
769+
self.payment = payment
770+
self.payment_config = payment_config
651771

652772
# Persistence state tracking
653773
self._restart_count = 0
@@ -671,6 +791,39 @@ def __init__(
671791
**kwargs,
672792
)
673793

794+
# Patch streamable_http_app to add x402 payment middleware
795+
if self.payment and self.payment_config:
796+
if X402_AVAILABLE:
797+
# Store original streamable_http_app
798+
original_streamable_http_app = (
799+
self.mcp_server.streamable_http_app
800+
)
801+
payment_config = self.payment_config
802+
803+
# Create patched version that adds middleware
804+
def patched_streamable_http_app():
805+
app = original_streamable_http_app()
806+
app.add_middleware(
807+
X402PaymentMiddleware,
808+
payment_config=payment_config,
809+
)
810+
return app
811+
812+
# Replace streamable_http_app with patched version
813+
self.mcp_server.streamable_http_app = (
814+
patched_streamable_http_app
815+
)
816+
817+
logger.info(
818+
f"Added x402 payment middleware (Solana): "
819+
f"price={self.payment_config.price}, "
820+
f"wallet={self.payment_config.pay_to_address}"
821+
)
822+
else:
823+
logger.error(
824+
"x402 payment enabled but middleware not available"
825+
)
826+
674827
# Configure logger
675828
logger.remove() # Remove default handler
676829
logger.add(
@@ -681,7 +834,7 @@ def __init__(
681834
)
682835

683836
logger.info(
684-
f"Initialized AOP with server name: {server_name}, verbose: {verbose}, traceback: {traceback_enabled}, persistence: {persistence}, network_monitoring: {network_monitoring}"
837+
f"Initialized AOP with server name: {server_name}, verbose: {verbose}, traceback: {traceback_enabled}, persistence: {persistence}, network_monitoring: {network_monitoring}, payment: {payment}"
685838
)
686839

687840
# Add initial agents if provided

0 commit comments

Comments
 (0)