Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions cmd/vt/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var (
registryRunEnv []string
registryRunNoClientConfig bool
registryRunForeground bool
registryRunVolumes []string
)

func init() {
Expand Down Expand Up @@ -124,6 +125,13 @@ func init() {
false,
"Run in foreground mode (block until container exits)",
)
registryRunCmd.Flags().StringArrayVarP(
&registryRunVolumes,
"volume",
"v",
[]string{},
"Mount a volume into the container (format: host-path:container-path[:ro])",
)

// Add OIDC validation flags
AddOIDCFlags(registryRunCmd)
Expand Down Expand Up @@ -462,6 +470,7 @@ func registryRunCmdFunc(cmd *cobra.Command, args []string) error {
OIDCJwksURL: oidcJwksURL,
OIDCClientID: oidcClientID,
Debug: debugMode,
Volumes: registryRunVolumes,
}

// Create context
Expand Down
9 changes: 9 additions & 0 deletions cmd/vt/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
runEnv []string
runNoClientConfig bool
runForeground bool
runVolumes []string
)

func init() {
Expand Down Expand Up @@ -51,6 +52,13 @@ func init() {
"Do not update client configuration files with the MCP server URL",
)
runCmd.Flags().BoolVarP(&runForeground, "foreground", "f", false, "Run in foreground mode (block until container exits)")
runCmd.Flags().StringArrayVarP(
&runVolumes,
"volume",
"v",
[]string{},
"Mount a volume into the container (format: host-path:container-path[:ro])",
)

// Add OIDC validation flags
AddOIDCFlags(runCmd)
Expand Down Expand Up @@ -92,6 +100,7 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
OIDCJwksURL: oidcJwksURL,
OIDCClientID: oidcClientID,
Debug: debugMode,
Volumes: runVolumes,
}

// Run the MCP server
Expand Down
56 changes: 56 additions & 0 deletions cmd/vt/run_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -68,6 +69,10 @@ type RunOptions struct {

// Debug indicates whether debug mode is enabled
Debug bool

// Volumes are the directory mounts to pass to the container
// Format: "host-path:container-path[:ro]"
Volumes []string
}

// RunMCPServer runs an MCP server with the specified options
Expand Down Expand Up @@ -188,6 +193,11 @@ func RunMCPServer(ctx context.Context, cmd *cobra.Command, options RunOptions) e
}
}

// Process volume mounts if provided
if err := processVolumeMounts(options.Volumes, permProfile); err != nil {
return err
}

// Set up the transport
fmt.Printf("Setting up %s transport...\n", transportType)
if err := transportHandler.Setup(
Expand Down Expand Up @@ -283,6 +293,47 @@ func RunMCPServer(ctx context.Context, cmd *cobra.Command, options RunOptions) e
return nil
}

// processVolumeMounts processes volume mounts and adds them to the permission profile
// Volume format: "host-path:container-path[:ro]"
func processVolumeMounts(volumes []string, profile *permissions.Profile) error {
if len(volumes) == 0 {
return nil
}

for _, volume := range volumes {
// Check if the volume has a read-only flag
readOnly := false
volumeSpec := volume

if strings.HasSuffix(volume, ":ro") {
readOnly = true
volumeSpec = strings.TrimSuffix(volume, ":ro")
}

// Create a mount declaration
mount := permissions.MountDeclaration(volumeSpec)
source, target, err := mount.Parse()
if err != nil {
return fmt.Errorf("invalid volume format: %s (%v)", volume, err)
}

// Add the mount to the appropriate permission list
if readOnly {
// For read-only mounts, add to Read list
profile.Read = append(profile.Read, mount)
} else {
// For read-write mounts, add to Write list
profile.Write = append(profile.Write, mount)
}

fmt.Printf("Adding volume mount: %s -> %s (%s)\n",
source, target,
map[bool]string{true: "read-only", false: "read-write"}[readOnly])
}

return nil
}

// updateClientConfigurations updates client configuration files with the MCP server URL
func updateClientConfigurations(containerName, host string, port int) error {
// Find client configuration files
Expand Down Expand Up @@ -369,6 +420,11 @@ func detachProcess(_ *cobra.Command, options RunOptions) error {
detachedArgs = append(detachedArgs, "--no-client-config")
}

// Add volume mounts if they were provided
for _, volume := range options.Volumes {
detachedArgs = append(detachedArgs, "--volume", volume)
}

// Add OIDC flags if they were provided
if options.OIDCIssuer != "" {
detachedArgs = append(detachedArgs, "--oidc-issuer", options.OIDCIssuer)
Expand Down
50 changes: 39 additions & 11 deletions pkg/container/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,33 +544,61 @@ func (c *Client) PullImage(ctx context.Context, imageName string) error {
// getPermissionConfigFromProfile converts a permission profile to a container permission config
// with transport-specific settings (internal function)
// addReadOnlyMounts adds read-only mounts to the permission config
func (*Client) addReadOnlyMounts(config *PermissionConfig, paths []string) {
for _, path := range paths {
func (*Client) addReadOnlyMounts(config *PermissionConfig, mounts []permissions.MountDeclaration) {
for _, mountDecl := range mounts {
source, target, err := mountDecl.Parse()
if err != nil {
// Skip invalid mounts
fmt.Printf("Warning: Skipping invalid mount declaration: %s (%v)\n", mountDecl, err)
continue
}

// Skip resource URIs for now (they need special handling)
if strings.Contains(source, "://") {
fmt.Printf("Warning: Resource URI mounts not yet supported: %s\n", source)
continue
}

// Skip relative paths
if !filepath.IsAbs(path) {
if !filepath.IsAbs(source) {
fmt.Printf("Warning: Skipping relative source path: %s\n", source)
continue
}

config.Mounts = append(config.Mounts, Mount{
Source: path,
Target: path,
Source: source,
Target: target,
ReadOnly: true,
})
}
}

// addReadWriteMounts adds read-write mounts to the permission config
func (*Client) addReadWriteMounts(config *PermissionConfig, paths []string) {
for _, path := range paths {
func (*Client) addReadWriteMounts(config *PermissionConfig, mounts []permissions.MountDeclaration) {
for _, mountDecl := range mounts {
source, target, err := mountDecl.Parse()
if err != nil {
// Skip invalid mounts
fmt.Printf("Warning: Skipping invalid mount declaration: %s (%v)\n", mountDecl, err)
continue
}

// Skip resource URIs for now (they need special handling)
if strings.Contains(source, "://") {
fmt.Printf("Warning: Resource URI mounts not yet supported: %s\n", source)
continue
}

// Skip relative paths
if !filepath.IsAbs(path) {
if !filepath.IsAbs(source) {
fmt.Printf("Warning: Skipping relative source path: %s\n", source)
continue
}

// Check if the path is already mounted read-only
alreadyMounted := false
for i, m := range config.Mounts {
if m.Target == path {
if m.Target == target {
// Update the mount to be read-write
config.Mounts[i].ReadOnly = false
alreadyMounted = true
Expand All @@ -581,8 +609,8 @@ func (*Client) addReadWriteMounts(config *PermissionConfig, paths []string) {
// If not already mounted, add a new mount
if !alreadyMounted {
config.Mounts = append(config.Mounts, Mount{
Source: path,
Target: path,
Source: source,
Target: target,
ReadOnly: false,
})
}
Expand Down
Loading