generated from wesen/wesen-go-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
801 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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,22 @@ | ||
package cmds | ||
|
||
import ( | ||
"github.com/go-go-golems/glazed/pkg/help" | ||
"github.com/go-go-golems/go-go-mcp/cmd/mcp-server/cmds/client" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ClientCmd = &cobra.Command{ | ||
Use: "client", | ||
Short: "MCP client functionality", | ||
Long: `Client commands for interacting with MCP servers`, | ||
} | ||
|
||
func InitClientCommand(helpSystem *help.HelpSystem) error { | ||
// Add client subcommands | ||
ClientCmd.AddCommand(client.ToolsCmd) | ||
ClientCmd.AddCommand(client.ResourcesCmd) | ||
ClientCmd.AddCommand(client.PromptsCmd) | ||
|
||
return nil | ||
} |
This file contains 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,85 @@ | ||
package helpers | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" | ||
"github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" | ||
"github.com/go-go-golems/go-go-mcp/pkg/client" | ||
"github.com/go-go-golems/go-go-mcp/pkg/protocol" | ||
"github.com/rs/zerolog/log" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
type ClientSettings = layers.ClientSettings | ||
|
||
// CreateClient initializes and returns a new MCP client based on the provided flags. | ||
func CreateClient(cmd *cobra.Command) (*client.Client, error) { | ||
transport, err := cmd.Flags().GetString("transport") | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get transport flag: %w", err) | ||
} | ||
|
||
serverURL, err := cmd.Flags().GetString("server") | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get server flag: %w", err) | ||
} | ||
|
||
cmdArgs, err := cmd.Flags().GetStringSlice("command") | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get command flag: %w", err) | ||
} | ||
|
||
return createClient(&ClientSettings{ | ||
Transport: transport, | ||
Server: serverURL, | ||
Command: cmdArgs, | ||
}) | ||
} | ||
|
||
// CreateClientFromSettings initializes and returns a new MCP client based on the provided settings. | ||
func CreateClientFromSettings(parsedLayers *glazed_layers.ParsedLayers) (*client.Client, error) { | ||
s := &ClientSettings{} | ||
if err := parsedLayers.InitializeStruct(layers.ClientLayerSlug, s); err != nil { | ||
return nil, err | ||
} | ||
|
||
return createClient(s) | ||
} | ||
|
||
func createClient(s *ClientSettings) (*client.Client, error) { | ||
var t client.Transport | ||
var err error | ||
|
||
switch s.Transport { | ||
case "command": | ||
if len(s.Command) == 0 { | ||
return nil, fmt.Errorf("command is required for command transport") | ||
} | ||
log.Debug().Msgf("Creating command transport with args: %v", s.Command) | ||
t, err = client.NewCommandStdioTransport(log.Logger, s.Command[0], s.Command[1:]...) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create command transport: %w", err) | ||
} | ||
case "sse": | ||
log.Debug().Msgf("Creating SSE transport with server URL: %s", s.Server) | ||
t = client.NewSSETransport(s.Server, log.Logger) | ||
default: | ||
return nil, fmt.Errorf("invalid transport type: %s", s.Transport) | ||
} | ||
|
||
// Create and initialize client | ||
c := client.NewClient(log.Logger, t) | ||
log.Debug().Msgf("Initializing client") | ||
err = c.Initialize(context.Background(), protocol.ClientCapabilities{ | ||
Sampling: &protocol.SamplingCapability{}, | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialize client: %w", err) | ||
} | ||
|
||
log.Debug().Msgf("Client initialized") | ||
|
||
return c, nil | ||
} |
This file contains 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,39 @@ | ||
package layers | ||
|
||
import ( | ||
"github.com/go-go-golems/glazed/pkg/cmds/layers" | ||
"github.com/go-go-golems/glazed/pkg/cmds/parameters" | ||
) | ||
|
||
type ClientSettings struct { | ||
Transport string `glazed.parameter:"transport"` | ||
Server string `glazed.parameter:"server"` | ||
Command []string `glazed.parameter:"command"` | ||
} | ||
|
||
const ClientLayerSlug = "mcp-client" | ||
|
||
func NewClientParameterLayer() (layers.ParameterLayer, error) { | ||
return layers.NewParameterLayer(ClientLayerSlug, "MCP Client Settings", | ||
layers.WithParameterDefinitions( | ||
parameters.NewParameterDefinition( | ||
"transport", | ||
parameters.ParameterTypeString, | ||
parameters.WithHelp("Transport type (command or sse)"), | ||
parameters.WithDefault("command"), | ||
), | ||
parameters.NewParameterDefinition( | ||
"server", | ||
parameters.ParameterTypeString, | ||
parameters.WithHelp("Server URL for SSE transport"), | ||
parameters.WithDefault("http://localhost:3001"), | ||
), | ||
parameters.NewParameterDefinition( | ||
"command", | ||
parameters.ParameterTypeStringList, | ||
parameters.WithHelp("Command and arguments for command transport"), | ||
parameters.WithDefault([]string{"mcp-server", "start", "--transport", "stdio"}), | ||
), | ||
), | ||
) | ||
} |
This file contains 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,203 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/go-go-golems/glazed/pkg/cli" | ||
"github.com/go-go-golems/glazed/pkg/cmds" | ||
glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" | ||
"github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" | ||
|
||
"github.com/go-go-golems/glazed/pkg/cmds/parameters" | ||
"github.com/go-go-golems/glazed/pkg/middlewares" | ||
"github.com/go-go-golems/glazed/pkg/settings" | ||
"github.com/go-go-golems/glazed/pkg/types" | ||
"github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/helpers" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
promptArgs string | ||
) | ||
|
||
// PromptsCmd handles the "prompts" command group | ||
var PromptsCmd = &cobra.Command{ | ||
Use: "prompts", | ||
Short: "Interact with prompts", | ||
Long: `List available prompts and execute specific prompts.`, | ||
} | ||
|
||
type ListPromptsCommand struct { | ||
*cmds.CommandDescription | ||
} | ||
|
||
type ExecutePromptCommand struct { | ||
*cmds.CommandDescription | ||
} | ||
|
||
type ExecutePromptSettings struct { | ||
Args string `glazed.parameter:"args"` | ||
PromptName string `glazed.parameter:"prompt-name"` | ||
} | ||
|
||
func NewListPromptsCommand() (*ListPromptsCommand, error) { | ||
glazedParameterLayer, err := settings.NewGlazedParameterLayers() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not create Glazed parameter layer") | ||
} | ||
|
||
clientLayer, err := layers.NewClientParameterLayer() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not create client parameter layer") | ||
} | ||
|
||
return &ListPromptsCommand{ | ||
CommandDescription: cmds.NewCommandDescription( | ||
"list", | ||
cmds.WithShort("List available prompts"), | ||
cmds.WithLayersList( | ||
glazedParameterLayer, | ||
clientLayer, | ||
), | ||
), | ||
}, nil | ||
} | ||
|
||
func NewExecutePromptCommand() (*ExecutePromptCommand, error) { | ||
clientLayer, err := layers.NewClientParameterLayer() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not create client parameter layer") | ||
} | ||
|
||
return &ExecutePromptCommand{ | ||
CommandDescription: cmds.NewCommandDescription( | ||
"execute", | ||
cmds.WithShort("Execute a specific prompt"), | ||
cmds.WithFlags( | ||
parameters.NewParameterDefinition( | ||
"args", | ||
parameters.ParameterTypeString, | ||
parameters.WithHelp("Prompt arguments as JSON string"), | ||
parameters.WithDefault(""), | ||
), | ||
), | ||
cmds.WithArguments( | ||
parameters.NewParameterDefinition( | ||
"prompt-name", | ||
parameters.ParameterTypeString, | ||
parameters.WithHelp("Name of the prompt to execute"), | ||
parameters.WithRequired(true), | ||
), | ||
), | ||
cmds.WithLayersList( | ||
clientLayer, | ||
), | ||
), | ||
}, nil | ||
} | ||
|
||
func (c *ListPromptsCommand) RunIntoGlazeProcessor( | ||
ctx context.Context, | ||
parsedLayers *glazed_layers.ParsedLayers, | ||
gp middlewares.Processor, | ||
) error { | ||
client, err := helpers.CreateClientFromSettings(parsedLayers) | ||
if err != nil { | ||
return err | ||
} | ||
defer client.Close(ctx) | ||
|
||
prompts, cursor, err := client.ListPrompts(ctx, "") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, prompt := range prompts { | ||
row := types.NewRow( | ||
types.MRP("name", prompt.Name), | ||
types.MRP("description", prompt.Description), | ||
) | ||
|
||
// Create a JSON array of arguments | ||
args := make([]map[string]interface{}, len(prompt.Arguments)) | ||
for i, arg := range prompt.Arguments { | ||
args[i] = map[string]interface{}{ | ||
"name": arg.Name, | ||
"required": arg.Required, | ||
"description": arg.Description, | ||
} | ||
} | ||
row.Set("arguments", args) | ||
|
||
if err := gp.AddRow(ctx, row); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if cursor != "" { | ||
// Add cursor as a final row | ||
cursorRow := types.NewRow( | ||
types.MRP("cursor", cursor), | ||
) | ||
if err := gp.AddRow(ctx, cursorRow); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c *ExecutePromptCommand) RunIntoWriter( | ||
ctx context.Context, | ||
parsedLayers *glazed_layers.ParsedLayers, | ||
w io.Writer, | ||
) error { | ||
s := &ExecutePromptSettings{} | ||
if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { | ||
return err | ||
} | ||
|
||
client, err := helpers.CreateClientFromSettings(parsedLayers) | ||
if err != nil { | ||
return err | ||
} | ||
defer client.Close(ctx) | ||
|
||
// Parse prompt arguments | ||
promptArgMap := make(map[string]string) | ||
if s.Args != "" { | ||
if err := json.Unmarshal([]byte(s.Args), &promptArgMap); err != nil { | ||
return fmt.Errorf("invalid prompt arguments JSON: %w", err) | ||
} | ||
} | ||
|
||
message, err := client.GetPrompt(ctx, s.PromptName, promptArgMap) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Write formatted output to writer | ||
_, err = fmt.Fprintf(w, "Role: %s\nContent: %s\n", message.Role, message.Content.Text) | ||
return err | ||
} | ||
|
||
func init() { | ||
listCmd, err := NewListPromptsCommand() | ||
cobra.CheckErr(err) | ||
|
||
executeCmd, err := NewExecutePromptCommand() | ||
cobra.CheckErr(err) | ||
|
||
listCobraCmd, err := cli.BuildCobraCommandFromGlazeCommand(listCmd) | ||
cobra.CheckErr(err) | ||
|
||
executeCobraCmd, err := cli.BuildCobraCommandFromWriterCommand(executeCmd) | ||
cobra.CheckErr(err) | ||
|
||
PromptsCmd.AddCommand(listCobraCmd) | ||
PromptsCmd.AddCommand(executeCobraCmd) | ||
} |
Oops, something went wrong.