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