Skip to content

Commit 43992be

Browse files
committed
Create sample client
1 parent 5c19710 commit 43992be

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed

examples/simple_client/main.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"io"
8+
"log"
9+
"os"
10+
"time"
11+
12+
"github.com/mark3labs/mcp-go/client"
13+
"github.com/mark3labs/mcp-go/client/transport"
14+
"github.com/mark3labs/mcp-go/mcp"
15+
)
16+
17+
func main() {
18+
// Define command line flags
19+
stdioCmd := flag.String("stdio", "", "Command to execute for stdio transport (e.g. 'python server.py')")
20+
sseURL := flag.String("sse", "", "URL for SSE transport (e.g. 'http://localhost:8080/sse')")
21+
flag.Parse()
22+
23+
// Validate flags
24+
if (*stdioCmd == "" && *sseURL == "") || (*stdioCmd != "" && *sseURL != "") {
25+
fmt.Println("Error: You must specify exactly one of --stdio or --sse")
26+
flag.Usage()
27+
os.Exit(1)
28+
}
29+
30+
// Create a context with timeout
31+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
32+
defer cancel()
33+
34+
// Create client based on transport type
35+
var c *client.Client
36+
var err error
37+
38+
if *stdioCmd != "" {
39+
fmt.Println("Initializing stdio client...")
40+
// Parse command and arguments
41+
args := parseCommand(*stdioCmd)
42+
if len(args) == 0 {
43+
fmt.Println("Error: Invalid stdio command")
44+
os.Exit(1)
45+
}
46+
47+
// Create command and stdio transport
48+
command := args[0]
49+
cmdArgs := args[1:]
50+
51+
// Create stdio transport with verbose logging
52+
stdioTransport := transport.NewStdio(command, nil, cmdArgs...)
53+
54+
// Start the transport
55+
if err := stdioTransport.Start(ctx); err != nil {
56+
log.Fatalf("Failed to start stdio transport: %v", err)
57+
}
58+
59+
// Create client with the transport
60+
c = client.NewClient(stdioTransport)
61+
62+
// Set up logging for stderr if available
63+
if stderr, ok := client.GetStderr(c); ok {
64+
go func() {
65+
buf := make([]byte, 4096)
66+
for {
67+
n, err := stderr.Read(buf)
68+
if err != nil {
69+
if err != io.EOF {
70+
log.Printf("Error reading stderr: %v", err)
71+
}
72+
return
73+
}
74+
if n > 0 {
75+
fmt.Fprintf(os.Stderr, "[Server] %s", buf[:n])
76+
}
77+
}
78+
}()
79+
}
80+
} else {
81+
fmt.Println("Initializing SSE client...")
82+
// Create SSE transport
83+
sseTransport, err := transport.NewSSE(*sseURL)
84+
if err != nil {
85+
log.Fatalf("Failed to create SSE transport: %v", err)
86+
}
87+
88+
// Start the transport
89+
if err := sseTransport.Start(ctx); err != nil {
90+
log.Fatalf("Failed to start SSE transport: %v", err)
91+
}
92+
93+
// Create client with the transport
94+
c = client.NewClient(sseTransport)
95+
}
96+
97+
// Set up notification handler
98+
c.OnNotification(func(notification mcp.JSONRPCNotification) {
99+
fmt.Printf("Received notification: %s\n", notification.Method)
100+
})
101+
102+
// Initialize the client
103+
fmt.Println("Initializing client...")
104+
initRequest := mcp.InitializeRequest{}
105+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
106+
initRequest.Params.ClientInfo = mcp.Implementation{
107+
Name: "MCP-Go Simple Client Example",
108+
Version: "1.0.0",
109+
}
110+
initRequest.Params.Capabilities = mcp.ClientCapabilities{}
111+
112+
serverInfo, err := c.Initialize(ctx, initRequest)
113+
if err != nil {
114+
log.Fatalf("Failed to initialize: %v", err)
115+
}
116+
117+
// Display server information
118+
fmt.Printf("Connected to server: %s (version %s)\n",
119+
serverInfo.ServerInfo.Name,
120+
serverInfo.ServerInfo.Version)
121+
fmt.Printf("Server capabilities: %+v\n", serverInfo.Capabilities)
122+
123+
// List available tools if the server supports them
124+
if serverInfo.Capabilities.Tools != nil {
125+
fmt.Println("Fetching available tools...")
126+
toolsRequest := mcp.ListToolsRequest{}
127+
toolsResult, err := c.ListTools(ctx, toolsRequest)
128+
if err != nil {
129+
log.Printf("Failed to list tools: %v", err)
130+
} else {
131+
fmt.Printf("Server has %d tools available\n", len(toolsResult.Tools))
132+
for i, tool := range toolsResult.Tools {
133+
fmt.Printf(" %d. %s - %s\n", i+1, tool.Name, tool.Description)
134+
}
135+
}
136+
}
137+
138+
// List available resources if the server supports them
139+
if serverInfo.Capabilities.Resources != nil {
140+
fmt.Println("Fetching available resources...")
141+
resourcesRequest := mcp.ListResourcesRequest{}
142+
resourcesResult, err := c.ListResources(ctx, resourcesRequest)
143+
if err != nil {
144+
log.Printf("Failed to list resources: %v", err)
145+
} else {
146+
fmt.Printf("Server has %d resources available\n", len(resourcesResult.Resources))
147+
for i, resource := range resourcesResult.Resources {
148+
fmt.Printf(" %d. %s - %s\n", i+1, resource.URI, resource.Name)
149+
}
150+
}
151+
}
152+
153+
fmt.Println("Client initialized successfully. Shutting down...")
154+
c.Close()
155+
}
156+
157+
// parseCommand splits a command string into command and arguments
158+
func parseCommand(cmd string) []string {
159+
// This is a simple implementation that doesn't handle quotes or escapes
160+
// For a more robust solution, consider using a shell parser library
161+
var result []string
162+
var current string
163+
var inQuote bool
164+
var quoteChar rune
165+
166+
for _, r := range cmd {
167+
switch {
168+
case r == ' ' && !inQuote:
169+
if current != "" {
170+
result = append(result, current)
171+
current = ""
172+
}
173+
case (r == '"' || r == '\''):
174+
if inQuote && r == quoteChar {
175+
inQuote = false
176+
quoteChar = 0
177+
} else if !inQuote {
178+
inQuote = true
179+
quoteChar = r
180+
} else {
181+
current += string(r)
182+
}
183+
default:
184+
current += string(r)
185+
}
186+
}
187+
188+
if current != "" {
189+
result = append(result, current)
190+
}
191+
192+
return result
193+
}

0 commit comments

Comments
 (0)