Skip to content

Commit a78c8b3

Browse files
committed
feat: enhance thv mcp list to accept server names in addition to URLs
- Modified --server flag to accept both URLs and server names from running workloads - Added resolveServerURL function to look up running servers by name - Uses efficient GetWorkload method instead of listing all workloads - Improved error messages for better user experience - Removed unnecessary logger calls that could corrupt JSON output This allows users to conveniently reference MCP servers by their friendly names (e.g., 'thv mcp list --server fetch') instead of having to remember or look up their URLs, while maintaining backward compatibility with direct URL usage. Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent 3f5f52d commit a78c8b3

File tree

5 files changed

+64
-13
lines changed

5 files changed

+64
-13
lines changed

cmd/thv/app/mcp.go

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/stacklok/toolhive/pkg/transport/streamable"
2020
"github.com/stacklok/toolhive/pkg/transport/types"
2121
"github.com/stacklok/toolhive/pkg/versions"
22+
"github.com/stacklok/toolhive/pkg/workloads"
2223
)
2324

2425
var (
@@ -86,7 +87,7 @@ func newMCPCommand() *cobra.Command {
8687
}
8788

8889
func addMCPFlags(cmd *cobra.Command) {
89-
cmd.Flags().StringVar(&mcpServerURL, "server", "", "MCP server URL (required)")
90+
cmd.Flags().StringVar(&mcpServerURL, "server", "", "MCP server URL or name from ToolHive registry (required)")
9091
cmd.Flags().StringVar(&mcpFormat, "format", FormatText, "Output format (json or text)")
9192
cmd.Flags().DurationVar(&mcpTimeout, "timeout", 30*time.Second, "Connection timeout")
9293
cmd.Flags().StringVar(&mcpTransport, "transport", "auto", "Transport type (auto, sse, streamable-http)")
@@ -98,7 +99,13 @@ func mcpListCmdFunc(cmd *cobra.Command, _ []string) error {
9899
ctx, cancel := context.WithTimeout(cmd.Context(), mcpTimeout)
99100
defer cancel()
100101

101-
mcpClient, err := createMCPClient()
102+
// Resolve server URL if it's a name
103+
serverURL, err := resolveServerURL(ctx, mcpServerURL)
104+
if err != nil {
105+
return err
106+
}
107+
108+
mcpClient, err := createMCPClient(serverURL)
102109
if err != nil {
103110
return err
104111
}
@@ -143,7 +150,13 @@ func mcpListToolsCmdFunc(cmd *cobra.Command, _ []string) error {
143150
ctx, cancel := context.WithTimeout(cmd.Context(), mcpTimeout)
144151
defer cancel()
145152

146-
mcpClient, err := createMCPClient()
153+
// Resolve server URL if it's a name
154+
serverURL, err := resolveServerURL(ctx, mcpServerURL)
155+
if err != nil {
156+
return err
157+
}
158+
159+
mcpClient, err := createMCPClient(serverURL)
147160
if err != nil {
148161
return err
149162
}
@@ -166,7 +179,13 @@ func mcpListResourcesCmdFunc(cmd *cobra.Command, _ []string) error {
166179
ctx, cancel := context.WithTimeout(cmd.Context(), mcpTimeout)
167180
defer cancel()
168181

169-
mcpClient, err := createMCPClient()
182+
// Resolve server URL if it's a name
183+
serverURL, err := resolveServerURL(ctx, mcpServerURL)
184+
if err != nil {
185+
return err
186+
}
187+
188+
mcpClient, err := createMCPClient(serverURL)
170189
if err != nil {
171190
return err
172191
}
@@ -189,7 +208,13 @@ func mcpListPromptsCmdFunc(cmd *cobra.Command, _ []string) error {
189208
ctx, cancel := context.WithTimeout(cmd.Context(), mcpTimeout)
190209
defer cancel()
191210

192-
mcpClient, err := createMCPClient()
211+
// Resolve server URL if it's a name
212+
serverURL, err := resolveServerURL(ctx, mcpServerURL)
213+
if err != nil {
214+
return err
215+
}
216+
217+
mcpClient, err := createMCPClient(serverURL)
193218
if err != nil {
194219
return err
195220
}
@@ -207,19 +232,45 @@ func mcpListPromptsCmdFunc(cmd *cobra.Command, _ []string) error {
207232
return outputMCPData(map[string]interface{}{"prompts": result.Prompts}, mcpFormat)
208233
}
209234

235+
// resolveServerURL resolves a server name to a URL or returns the URL if it's already a URL
236+
func resolveServerURL(ctx context.Context, serverInput string) (string, error) {
237+
// Check if it's already a URL
238+
if strings.HasPrefix(serverInput, "http://") || strings.HasPrefix(serverInput, "https://") {
239+
return serverInput, nil
240+
}
241+
242+
// Try to get the workload by name
243+
manager, err := workloads.NewManager(ctx)
244+
if err != nil {
245+
return "", fmt.Errorf("failed to create workload manager: %w", err)
246+
}
247+
248+
workload, err := manager.GetWorkload(ctx, serverInput)
249+
if err != nil {
250+
return "", fmt.Errorf("server '%s' not found in running workloads. Please ensure the server is running or provide a valid URL", serverInput)
251+
}
252+
253+
// Check if the workload is running
254+
if workload.Status != "running" {
255+
return "", fmt.Errorf("server '%s' is not running (status: %s). Please start it first using 'thv run %s'", serverInput, workload.Status, serverInput)
256+
}
257+
258+
return workload.URL, nil
259+
}
260+
210261
// createMCPClient creates an MCP client based on the server URL and transport type
211-
func createMCPClient() (*client.Client, error) {
212-
transportType := determineTransportType(mcpServerURL, mcpTransport)
262+
func createMCPClient(serverURL string) (*client.Client, error) {
263+
transportType := determineTransportType(serverURL, mcpTransport)
213264

214265
switch transportType {
215266
case types.TransportTypeSSE:
216-
mcpClient, err := client.NewSSEMCPClient(mcpServerURL)
267+
mcpClient, err := client.NewSSEMCPClient(serverURL)
217268
if err != nil {
218269
return nil, fmt.Errorf("failed to create SSE MCP client: %w", err)
219270
}
220271
return mcpClient, nil
221272
case types.TransportTypeStreamableHTTP:
222-
mcpClient, err := client.NewStreamableHttpClient(mcpServerURL)
273+
mcpClient, err := client.NewStreamableHttpClient(serverURL)
223274
if err != nil {
224275
return nil, fmt.Errorf("failed to create Streamable HTTP MCP client: %w", err)
225276
}

docs/cli/thv_mcp_list.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_mcp_list_prompts.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_mcp_list_resources.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_mcp_list_tools.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)