Skip to content
Open
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
28 changes: 26 additions & 2 deletions pkg/mcp/toolset/toolset.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"slices"
"strings"

"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/pkg/sftp"
Expand Down Expand Up @@ -109,6 +110,29 @@ func (ts *ToolSet) TranslateHostPath(hostPath string) (string, error) {
if !filepath.IsAbs(hostPath) {
return "", fmt.Errorf("expected an absolute path, got a relative path: %q", hostPath)
}
// TODO: make sure that hostPath is mounted
return hostPath, nil

guestPath, isMounted := ts.translateToGuestPath(hostPath)
if !isMounted {
logrus.Warnf("path %q is not under any mounted directory, using as guest path", hostPath)
}
return guestPath, nil
}

func (ts *ToolSet) translateToGuestPath(hostPath string) (string, bool) {
for _, mount := range ts.inst.Config.Mounts {
location := filepath.Clean(mount.Location)
cleanPath := filepath.Clean(hostPath)

if cleanPath == location {
return *mount.MountPoint, true
}

rel, err := filepath.Rel(location, cleanPath)
if err == nil && !strings.HasPrefix(rel, "..") && rel != ".." {
guestPath := filepath.Join(*mount.MountPoint, rel)
return guestPath, true
}
}

return hostPath, false
}
99 changes: 99 additions & 0 deletions pkg/mcp/toolset/toolset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package toolset

import (
"testing"

"gotest.tools/v3/assert"

"github.com/lima-vm/lima/v2/pkg/limatype"
)

func TestTranslateHostPath(t *testing.T) {
mountPoint1 := "/mnt/home-user"
mountPoint2 := "/mnt/tmp"

tests := []struct {
name string
hostPath string
toolSet ToolSet
wantGuestPath string
wantErr bool
}{
{
name: "file in mounted directory",
hostPath: "/home/user/documents/file.txt",
toolSet: ToolSet{
inst: &limatype.Instance{
Config: &limatype.LimaYAML{
Mounts: []limatype.Mount{
{Location: "/home/user", MountPoint: &mountPoint1},
},
},
},
},
wantGuestPath: "/mnt/home-user/documents/file.txt",
wantErr: false,
},
{
name: "path outside mount - fallback to guest path",
hostPath: "/other/path/file.txt",
toolSet: ToolSet{
inst: &limatype.Instance{
Config: &limatype.LimaYAML{
Mounts: []limatype.Mount{
{Location: "/home/user", MountPoint: &mountPoint1},
},
},
},
},
wantGuestPath: "/other/path/file.txt",
wantErr: false,
},
{
name: "similar prefix but not under mount",
hostPath: "/home/user2/file.txt",
toolSet: ToolSet{
inst: &limatype.Instance{
Config: &limatype.LimaYAML{
Mounts: []limatype.Mount{
{Location: "/home/user", MountPoint: &mountPoint1},
},
},
},
},
wantGuestPath: "/home/user2/file.txt",
wantErr: false,
},
{
name: "multiple mounts",
hostPath: "/tmp/myfile",
toolSet: ToolSet{
inst: &limatype.Instance{
Config: &limatype.LimaYAML{
Mounts: []limatype.Mount{
{Location: "/home/user", MountPoint: &mountPoint1},
{Location: "/tmp", MountPoint: &mountPoint2},
},
},
},
},
wantGuestPath: "/mnt/tmp/myfile",
wantErr: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got, err := test.toolSet.TranslateHostPath(test.hostPath)
if test.wantErr {
assert.Assert(t, err != nil)
} else {
assert.NilError(t, err)
assert.Equal(t, test.wantGuestPath, got)
}
})
}
}
Loading