naco-api/testClient/mcp/test-mcp-endpoints.go

188 lines
5 KiB
Go

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)
}
}