Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/thv/app/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ func init() {
if err := proxyCmd.MarkFlagRequired("target-uri"); err != nil {
logger.Warnf("Warning: Failed to mark flag as required: %v", err)
}

// Attach the subcommand to the main proxy command
// Attach the subcommands to the main proxy command
proxyCmd.AddCommand(proxyTunnelCmd)
proxyCmd.AddCommand(proxyStdioCmd)

}

func proxyCmdFunc(cmd *cobra.Command, args []string) error {
Expand Down
57 changes: 57 additions & 0 deletions cmd/thv/app/proxy_stdio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package app

import (
"fmt"
"os/signal"
"syscall"

"github.com/spf13/cobra"

"github.com/stacklok/toolhive/pkg/logger"
"github.com/stacklok/toolhive/pkg/transport"
"github.com/stacklok/toolhive/pkg/workloads"
)

var proxyStdioCmd = &cobra.Command{
Use: "stdio WORKLOAD-NAME SERVER_NAME",
Short: "Create a stdio-based proxy for an MCP server",
Long: `Create a stdio-based proxy that connects stdin/stdout to a target MCP server.

Example:
thv proxy stdio my-workload my-server-proxy
`,
Args: cobra.ExactArgs(2),
RunE: proxyStdioCmdFunc,
}

func proxyStdioCmdFunc(cmd *cobra.Command, args []string) error {
ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

workloadName := args[0]
serverName := args[1]

workloadManager, err := workloads.NewManager(ctx)
if err != nil {
return fmt.Errorf("failed to create workload manager: %w", err)
}
stdioWorkload, err := workloadManager.GetWorkload(ctx, workloadName)
if err != nil {
return fmt.Errorf("failed to get workload %q: %w", workloadName, err)
}
logger.Infof("Starting stdio proxy for server=%q -> %s", serverName, workloadName)

bridge, err := transport.NewStdioBridge(stdioWorkload.URL)
if err != nil {
return fmt.Errorf("failed to create stdio bridge: %w", err)
}
bridge.InitReady = make(chan struct{})
bridge.Start(ctx)

// Consume until interrupt
close(bridge.InitReady)
<-ctx.Done()
logger.Info("Shutting down bridge")
bridge.Shutdown()
return nil
}
1 change: 1 addition & 0 deletions docs/cli/thv_proxy.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions docs/cli/thv_proxy_stdio.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/container/images/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/docker/docker/client"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/google/go-containerregistry/pkg/v1/remote"

Expand Down
Loading
Loading