-
Notifications
You must be signed in to change notification settings - Fork 702
feat(mcp): add HTTP and Stdio client Roots feature
#620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yuehaii
wants to merge
13
commits into
mark3labs:main
Choose a base branch
from
yuehaii:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
1600dce
feat: client roots feature
yuehaii e1827a2
Merge branch 'mark3labs:main' into main
yuehaii a1d6e06
feat: finish client roots, pass unit and integration test
yuehaii bfe07cb
client roots http sample code
yuehaii d22932e
client roots for stdio and pass integration test
yuehaii b300907
Merge branch 'mark3labs:main' into main
yuehaii df7f0f8
update roots stio client example
yuehaii 896a4c2
add godoc and const of rootlist
yuehaii fadfa70
update godoc and data format
yuehaii 96b2183
update examples for client roots
yuehaii 7d2188b
add fallback for demonstration
yuehaii 77875f8
adjust roots path and signals of examples
yuehaii 2a1fa2e
update roots http client example
yuehaii File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,3 +5,4 @@ | |
| .claude | ||
| coverage.out | ||
| coverage.txt | ||
| .vscode/launch.json | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package client | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/mark3labs/mcp-go/mcp" | ||
| ) | ||
|
|
||
| // RootsHandler defines the interface for handling roots requests from servers. | ||
| // Clients can implement this interface to provide roots list to servers. | ||
| type RootsHandler interface { | ||
| // ListRoots handles a list root request from the server and returns the roots list. | ||
| // The implementation should: | ||
| // 1. Validate input against the requested schema | ||
| // 2. Return the appropriate response | ||
| ListRoots(ctx context.Context, request mcp.ListRootsRequest) (*mcp.ListRootsResult, error) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "log" | ||
| "net/url" | ||
| "os" | ||
| "os/signal" | ||
| "path/filepath" | ||
| "syscall" | ||
|
|
||
| "github.com/mark3labs/mcp-go/client" | ||
| "github.com/mark3labs/mcp-go/client/transport" | ||
| "github.com/mark3labs/mcp-go/mcp" | ||
| ) | ||
|
|
||
| // MockRootsHandler implements client.RootsHandler for demonstration. | ||
| // In a real implementation, this would enumerate workspace/project roots. | ||
| type MockRootsHandler struct{} | ||
|
|
||
| func (h *MockRootsHandler) ListRoots(ctx context.Context, request mcp.ListRootsRequest) (*mcp.ListRootsResult, error) { | ||
| home, err := os.UserHomeDir() | ||
| if err != nil { | ||
| log.Printf("Warning: failed to get home directory: %v", err) | ||
| home = "/tmp" // fallback for demonstration | ||
| } | ||
| app := filepath.ToSlash(filepath.Join(home, "app")) | ||
| proj := filepath.ToSlash(filepath.Join(home, "projects", "test-project")) | ||
| result := &mcp.ListRootsResult{ | ||
| Roots: []mcp.Root{ | ||
| { | ||
| Name: "app", | ||
| URI: (&url.URL{Scheme: "file", Path: app}).String(), | ||
| }, | ||
| { | ||
| Name: "test-project", | ||
| URI: (&url.URL{Scheme: "file", Path: proj}).String(), | ||
| }, | ||
| }, | ||
| } | ||
| return result, nil | ||
| } | ||
|
|
||
| // main starts a mock MCP roots client that communicates with a subprocess over stdio. | ||
| // It expects the server command as the first command-line argument, creates a stdio | ||
| // transport and an MCP client with a MockRootsHandler, starts and initializes the | ||
| // client, logs server info and available tools, notifies the server of root list | ||
| // changes, invokes the "roots" tool and prints any text content returned, and | ||
| // shuts down the client gracefully on SIGINT or SIGTERM. | ||
| func main() { | ||
| if len(os.Args) < 2 { | ||
| log.Fatal("Usage: roots_client <server_command>") | ||
| } | ||
|
|
||
| serverCommand := os.Args[1] | ||
| serverArgs := os.Args[2:] | ||
|
|
||
| // Create stdio transport to communicate with the server | ||
| stdio := transport.NewStdio(serverCommand, nil, serverArgs...) | ||
|
|
||
| // Create roots handler | ||
| rootsHandler := &MockRootsHandler{} | ||
|
|
||
| // Create client with roots capability | ||
| mcpClient := client.NewClient(stdio, client.WithRootsHandler(rootsHandler)) | ||
|
|
||
| ctx := context.Background() | ||
|
|
||
| // Start the client | ||
| if err := mcpClient.Start(ctx); err != nil { | ||
| log.Fatalf("Failed to start client: %v", err) | ||
| } | ||
|
|
||
| // Setup graceful shutdown | ||
| sigChan := make(chan os.Signal, 1) | ||
| signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||
|
|
||
| // Create a context that cancels on signal | ||
| ctx, cancel := context.WithCancel(ctx) | ||
| go func() { | ||
| <-sigChan | ||
| log.Println("Received shutdown signal, closing client...") | ||
| cancel() | ||
| }() | ||
|
|
||
| // Move defer after error checking | ||
| defer func() { | ||
| if err := mcpClient.Close(); err != nil { | ||
| log.Printf("Error closing client: %v", err) | ||
| } | ||
| }() | ||
|
|
||
| // Initialize the connection | ||
| initResult, err := mcpClient.Initialize(ctx, mcp.InitializeRequest{ | ||
| Params: mcp.InitializeParams{ | ||
| ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, | ||
| ClientInfo: mcp.Implementation{ | ||
| Name: "roots-stdio-client", | ||
| Version: "1.0.0", | ||
| }, | ||
| Capabilities: mcp.ClientCapabilities{ | ||
| // Roots capability will be automatically added by WithRootsHandler | ||
| }, | ||
| }, | ||
| }) | ||
| if err != nil { | ||
| log.Fatalf("Failed to initialize: %v", err) | ||
| } | ||
|
|
||
| log.Printf("Connected to server: %s v%s", initResult.ServerInfo.Name, initResult.ServerInfo.Version) | ||
| log.Printf("Server capabilities: %+v", initResult.Capabilities) | ||
|
|
||
| // list tools | ||
| toolsResult, err := mcpClient.ListTools(ctx, mcp.ListToolsRequest{}) | ||
| if err != nil { | ||
| log.Fatalf("Failed to list tools: %v", err) | ||
| } | ||
| log.Printf("Available tools:") | ||
| for _, tool := range toolsResult.Tools { | ||
| log.Printf(" - %s: %s", tool.Name, tool.Description) | ||
| } | ||
|
|
||
| // call server tool | ||
| request := mcp.CallToolRequest{} | ||
| request.Params.Name = "roots" | ||
| request.Params.Arguments = map[string]any{"testonly": "yes"} | ||
| result, err := mcpClient.CallTool(ctx, request) | ||
| if err != nil { | ||
| log.Fatalf("failed to call tool roots: %v", err) | ||
| } else if result.IsError { | ||
| log.Printf("tool reported error") | ||
| } else if len(result.Content) > 0 { | ||
| resultStr := "" | ||
| for _, content := range result.Content { | ||
| if textContent, ok := content.(mcp.TextContent); ok { | ||
| resultStr += fmt.Sprintf("%s\n", textContent.Text) | ||
| } | ||
| } | ||
| fmt.Printf("client call tool result: %s\n", resultStr) | ||
| } | ||
|
|
||
| // mock the root change | ||
| if err := mcpClient.RootListChanges(ctx); err != nil { | ||
| log.Printf("fail to notify root list change: %v", err) | ||
| } | ||
|
|
||
| // Keep running until cancelled by signal | ||
| <-ctx.Done() | ||
| log.Println("Client context cancelled") | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.