client

package
v0.0.0-...-889f6eb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Command = &cli.Command{
	Name:        "client",
	Aliases:     []string{"c"},
	Usage:       "Act as the proxy client",
	ArgsUsage:   fmt.Sprintf("ws(s)://proxy-server-address:%d", protocol.DefaultPort),
	Description: "It will connect to the proxy server and forward stdin to it while forwarding back the server's response to stdout. To be launched by your application expecting a stdio MCP server.",
	Flags: []cli.Flag{

		&cli.StringFlag{
			Name:     "cert",
			Usage:    "Path to the client certificate file",
			Aliases:  []string{"c"},
			Category: "mTLS",
			OnlyOnce: true,
		},
		&cli.StringFlag{
			Name:     "key",
			Usage:    "Path to the client key file",
			Aliases:  []string{"k"},
			Category: "mTLS",
			OnlyOnce: true,
		},
		&cli.StringFlag{
			Name:     "ca",
			Usage:    "Path to the CA certificate file",
			Aliases:  []string{"a"},
			Category: "mTLS",
			OnlyOnce: true,
		},
	},
	Before: func(ctx context.Context, cmd *cli.Command) (actionCtx context.Context, err error) {
		defer func() {
			if err != nil {
				err = cli.Exit(err, 1)
			}
		}()

		if cmd.Args().Len() != 1 {
			err = errors.New("expected exactly one argument: the websocket URL")
			return
		}
		if target, err = url.Parse(cmd.Args().Get(0)); err != nil {
			err = fmt.Errorf("invalid URL: %w", err)
			return
		}

		certFile := cmd.String("cert")
		keyFile := cmd.String("key")
		caFile := cmd.String("ca")
		if certFile != "" || keyFile != "" || caFile != "" {
			if certFile == "" || keyFile == "" || caFile == "" {
				err = errors.New("--cert, --key and --ca must be specified together")
				return
			}
			if tlsConf, err = protocol.GetClientTLSConfig(caFile, certFile, keyFile); err != nil {
				err = fmt.Errorf("failed to generate client TLS configuration: %w", err)
				return
			}
		}

		switch target.Scheme {
		case "ws":
			if tlsConf != nil {
				err = errors.New("TLS configuration is not supported for ws:// URLs")
				return
			}
		case "wss":
			if tlsConf == nil {
				err = errors.New("TLS configuration is required for wss:// URLs")
				return
			}
		default:
			err = fmt.Errorf("invalid URL scheme, expecting 'ws' or 'wss': %s", target.Scheme)
			return
		}

		if isLoopbackHost(target.Host) {
			compressionMode = websocket.CompressionDisabled
		} else {
			compressionMode = protocol.CompressionMode
		}

		actionCtx = ctx
		return
	},
	Action: func(ctx context.Context, cmd *cli.Command) (err error) {

		httpTransport := cleanhttp.DefaultTransport()
		httpTransport.TLSClientConfig = tlsConf
		conn, _, err := websocket.Dial(ctx, target.String(), &websocket.DialOptions{
			HTTPClient: &http.Client{
				Transport: httpTransport,
			},
			CompressionMode: compressionMode,
		})
		if err != nil {
			return cli.Exit(fmt.Errorf("failed to connect to server: %w", err), 2)
		}
		defer func() {

			if err := conn.CloseNow(); err != nil && !errors.Is(err, net.ErrClosed) {
				fmt.Fprintf(os.Stderr, "warning: failed to close websocket: %v\n", err)
			}
		}()

		if err = proxy(ctx, conn); err != nil {
			err = cli.Exit(err, 3)
		}
		return
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL