package main import ( "context" "fmt" "log" "net/http" "net/http/httputil" "os" "strings" "time" "github.com/joho/godotenv" mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp" x402 "github.com/x402-foundation/x402/go/v2" x402mcp "github.com/x402-foundation/x402/go/v2/mcp" evm "github.com/x402-foundation/x402/go/v2/mechanisms/evm/exact/client" uptoclient "github.com/x402-foundation/x402/go/v2/mechanisms/evm/upto/client" evmsigners "github.com/x402-foundation/x402/go/v2/signers/evm" ) const USDCContractAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e" type LoggingRoundTripper struct { Proxied http.RoundTripper } func (lrt *LoggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { var err error if req.GetBody != nil { rc, _ := req.GetBody() req.Body = rc } reqDump, err := httputil.DumpRequestOut(req, true) if err != nil { log.Printf("Error dumping request: %v", err) } else { fmt.Printf("\n--- REQUEST ---\n%s\n----------------\n", reqDump) } resp, err := lrt.Proxied.RoundTrip(req) if err != nil { return nil, err } respDump, err := httputil.DumpResponse(resp, true) if err != nil { log.Printf("Error dumping response: %v", err) } else { fmt.Printf("\n--- RESPONSE ---\n%s\n----------------\n", respDump) } return resp, nil } func callTool(x402Mcp *x402mcp.X402MCPClient, ctx context.Context, name string, args map[string]any) error { result, err := x402Mcp.CallTool(ctx, name, args) if err != nil { return fmt.Errorf("%s MCP call failed: %w", name, err) } fmt.Printf("%s Response:\n", name) for _, item := range result.Content { fmt.Printf(" %s\n", item.Text) } if result.PaymentMade { fmt.Printf("%s Payment made\n", name) } else { fmt.Printf("%s No payment required\n", name) } if result.IsError { return fmt.Errorf("%s: tool returned an error result", name) } return nil } func getEnv(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value } return fallback } func main() { _ = godotenv.Load() privateKey := os.Getenv("PRIVATE_KEY") serverURL := getEnv("TEST_SERVER_URL", "http://localhost:8080") if privateKey == "" { log.Fatal("PRIVATE_KEY environment variable is required") } signer, err := evmsigners.NewClientSignerFromPrivateKey(privateKey) if err != nil { log.Fatal("Failed to create EVM signer:", err) } debugClient := &http.Client{ Transport: &LoggingRoundTripper{ Proxied: http.DefaultTransport, }, Timeout: 600 * time.Second, } ctx := context.Background() endpoint := strings.TrimRight(serverURL, "/") + "/mcp" transport := &mcpsdk.StreamableClientTransport{ Endpoint: endpoint, HTTPClient: debugClient, DisableStandaloneSSE: true, } mcpClient := mcpsdk.NewClient(&mcpsdk.Implementation{ Name: "test-mcp-endpoints", Version: "1.0.0", }, nil) session, err := mcpClient.Connect(ctx, transport, nil) if err != nil { log.Fatal("Failed to connect MCP client:", err) } defer func() { _ = session.Close() }() x402Mcp := x402mcp.NewX402MCPClientFromConfig(session, []x402mcp.SchemeRegistration{ {Network: x402.Network("eip155:84532"), Client: evm.NewExactEvmScheme(signer, nil)}, {Network: x402.Network("eip155:84532"), Client: uptoclient.NewUptoEvmScheme(signer, nil)}, }, x402mcp.Options{}) log.Printf("Connected to MCP endpoint %s", endpoint) log.Printf("Starting MCP endpoint tests against %s...", serverURL) testCases := []struct { name string args map[string]any }{ {"getFeed", map[string]any{"uri": "https://bsky.app/profile/did:plc:z72i7hdynmk6r22z27h6tvur/feed/whats-hot"}}, {"getActor", map[string]any{"author": "did:plc:z72i7hdynmk6r22z27h6tvur"}}, {"searchActors", map[string]any{"query": "test", "limit": 5}}, {"getPost", map[string]any{"author": "sharkgamiart.bsky.social", "rkey": "3m47y27c67c2b"}}, {"getLatestPosts", map[string]any{"author": "sharkgamiart.bsky.social", "limit": 5}}, {"getPostsInTime", map[string]any{"author": "sharkgamiart.bsky.social", "startTime": "2024-01-01T00:00:00Z", "endTime": "2024-12-31T23:59:59Z", "limit": 5}}, {"getStyleProfile", map[string]any{"author": "did:plc:z72i7hdynmk6r22z27h6tvur"}}, {"findSimilarUsers", map[string]any{"author": "did:plc:z72i7hdynmk6r22z27h6tvur", "limit": 5}}, } passed := 0 failed := 0 fmt.Printf("\n=== Testing listTools ===\n") toolsResponse, err := session.ListTools(ctx, nil) if err != nil { log.Printf("FAIL listTools: %v\n", err) failed++ } else { for _, tool := range toolsResponse.Tools { fmt.Printf(" tool: %s\n", tool.Name) } fmt.Printf("PASS listTools (%d tools listed)\n", len(toolsResponse.Tools)) passed++ } for _, tc := range testCases { fmt.Printf("\n=== Testing %s ===\n", tc.name) err := callTool(x402Mcp, ctx, tc.name, tc.args) if err != nil { log.Printf("FAIL %s: %v\n", tc.name, err) failed++ } else { fmt.Printf("PASS %s\n", tc.name) passed++ } } log.Printf("\n=== Test Summary ===") log.Printf("Passed: %d", passed) log.Printf("Failed: %d", failed) if failed > 0 { os.Exit(1) } }