-
Notifications
You must be signed in to change notification settings - Fork 699
Define "MCP Sandbox Interface" and implement limactl mcp serve
#3744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
// - [RunShellCommandParams].Directory must not be empty | ||
// | ||
// Eventually, this package may be split to a separate repository. | ||
package msi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RFC: "MCP Sandbox Interface" might be a misnomer; it might rather sound like an interface for sandboxing MCP servers, e.g., docker run -i --rm example.com/some-mcp-server /usr/bin/some-mcp-server
https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#docker-based-mcp-server
Alternative names:
- AI Sandbox Interface
- Agent Sandbox Interface
- AI Protection Interface
- ...
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @lima-vm/maintainers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the name is fine. It is an interface for MCP to tools that run in a sandbox.
dadfb7c
to
ab6c5dc
Compare
0892d60
to
2967ffe
Compare
I've only browsed the code, but if I understand this correctly, the "protection" relies just on the fact that the VM has mounted the filesystem readonly. AFAICT there is no sandboxing of the filesystem, so the ReadFile tool could still read e.g. credentials from Beyond that, I wonder if this isn't outside the scope of Lima itself. It is an application built on top of Lima, but for some reason compiled into it. It could just as well be built as a separate tool. Or as a So I wonder if it wouldn't be good to include it as an example on how to create plugins for Lima? The reason to keep it in the Lima repo would be that MCP is a hot topic, and having it bundled makes it easier to discover. |
The home directory except the pwd can be hidden with:
Maybe |
$ cat ~/bin/limactl-hello
#!/bin/bash
echo "Hello world from $BASH_SOURCE"
$ limactl hello
Hello world from /Users/jan/bin/limactl-hello We could also look for plugins in |
11ff941
to
d8c61ec
Compare
Reimplemented as |
42eb5c5
to
206003b
Compare
It doesn't seem to be working for me: ❯ l mcp -v
limactl-mcp version 2.0.0-alpha.0-39-gf71031e6
❯ l ls default qemu
NAME STATUS SSH VMTYPE ARCH CPUS MEMORY DISK DIR
default Stopped 127.0.0.1:0 vz aarch64 4 4GiB 100GiB ~/.lima/default
qemu Running 127.0.0.1:63754 qemu aarch64 4 4GiB 100GiB ~/.lima/qemu
❯ l mcp serve default
FATA[0000] expected status of instance "default" to be "Running", got ""
❯ l mcp serve qemu
FATA[0000] expected status of instance "qemu" to be "Running", got ""
❯ l shell qemu uname -a
Linux lima-qemu 6.14.0-23-generic #23-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 13 22:12:54 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux |
Works for me. |
No, I have the default setup. My suspicion is that I would have expected to see something like Are you sure you don't have external I think as a plugin the code has to call |
Confirmed: The error goes away after running ADDITIONAL_DRIVERS="qemu vz" make native additional-drivers limactl-plugins install |
And it turns out to be #2668 once again wasting time. We really should fix this non-idiomatic interface. When I add @@ -151,6 +152,9 @@ func mcpServeAction(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ if len(inst.Errors) != 0 {
+ return errors.Join(inst.Errors...)
+ }
if err = ts.RegisterInstance(ctx, inst); err != nil {
return err
} then I get a more meaningful error: ❯ l mcp serve default
FATA[0000] failed to resolve vm for "/Users/jan/.lima/default/lima.yaml": vmType "vz" is not a registered driver |
Is it a bug that |
I guess for a dev env, |
You can just look it up in the
The |
Oh, got it, thanks! I've raised a fix for the same #4034 |
Fixed the incompatibility with built-in drivers |
=== Interface === `pkg/mcp/msi` defines "MCP Sandbox Interface" (tentative) that should be reusable for other projects too. MCP Sandbox Interface defines MCP (Model Context Protocol) tools that can be used for reading, writing, and executing local files with an appropriate sandboxing technology. The sandboxing technology can be more secure and/or efficient than the default tools provided by an AI agent. MCP Sandbox Interface was inspired by Gemini CLI's built-in tools. https://github.com/google-gemini/gemini-cli/tree/v0.1.12/docs/tools === Implementation === `limactl mcp serve INSTANCE` launches an MCP server that implements the MCP Sandbox Interface. Use <https://github.com/modelcontextprotocol/inspector> to play around with the server. ```bash limactl start default brew install mcp-inspector mcp-inspector ``` In the web browser, - Set `Command` to `limactl` - Set `Arguments` to `mcp serve default` - Click `▶️ Connect` === Usage with Gemni CLI === 1. Create `.gemini/extensions/lima/gemini-extension.json` as follows: ```json { "name": "lima", "version": "2.0.0", "mcpServers": { "lima": { "command": "limactl", "args": [ "mcp", "serve", "default" ] } } } ``` 2. Modify `.gemini/settings.json` so as to disable Gemini CLI's built-in tools except ones that do not relate to local command execution and file I/O: ```json { "coreTools": ["WebFetchTool", "WebSearchTool", "MemoryTool"] } ``` Signed-off-by: Akihiro Suda <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, LGTM
@@ -112,6 +112,8 @@ help-targets: | |||
@echo '- limactl : Build limactl, and lima' | |||
@echo '- lima : Copy lima, and lima.bat' | |||
@echo '- helpers : Copy nerdctl.lima, apptainer.lima, docker.lima, podman.lima, and kubectl.lima' | |||
# TODO: move CLI plugins to _output/libexec/lima/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't have to be in this PR, but should be done before 2.0 release.
cmd.AddCommand( | ||
newMcpInfoCommand(), | ||
newMcpServeCommand(), | ||
// TODO: `limactl-mcp install-gemini` ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that is out-of-scope for an MCP server. And gemini-cli
seems to be available in package managers already anyways.
|
||
// Path returns the path to the `limactl` executable. | ||
func Path() (string, error) { | ||
limactl := cmp.Or(os.Getenv("LIMACTL"), "limactl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? It needs to be documented, if we do need it. And then it should be a pattern for all plugins.
Right now we go to extra effort that the version of limactl
that was used to invoke the plugin is first on the PATH
before the plugin is started.
If this is purely for development/testing, then the variable should start with an underscore.
// Inspect runs `limactl list --json INST` and parses the output. | ||
func Inspect(ctx context.Context, limactl, instName string) (*limatype.Instance, error) { | ||
var stdout, stderr bytes.Buffer | ||
cmd := exec.CommandContext(ctx, limactl, "list", "--json", instName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we document --json
as a legacy flag that is equal to --format json
.
I know "legacy" does not necessarily mean "deprecated", but it is pointing that way.
I'm in favour of keeping --json
as a shorthand, and not calling it "legacy". But if it is considered legacy, then we should use the modern form here.
// Offset *int `json:"offset,omitempty" jsonschema:"For text files, the 0-based line number to start reading from. Requires limit to be set."` | ||
// Limit *int `json:"limit,omitempty" jsonschema:"For text files, the maximum number of lines to read. If omitted, reads a default maximum (e.g., 2000 lines) or the entire file if feasible."` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not have commented out code unless it also has a TODO explanation.
|
||
var SearchFileContent = &mcp.Tool{ | ||
Name: "search_file_content", | ||
Description: `Searches for a regular expression pattern within the content of files in a specified directory. Internally calls 'git grep -n --no-index'.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the server test that git
is available in the instance and not provide the search tool if it isn't?
This should be documented in the limactl mcp serve --help
that the instance needs to have git
installed.
// - the output format is JSON, not a plain text | ||
// - the output of [SearchFileContent] always corresponds to `git grep -n --no-index` | ||
// - [RunShellCommandParams].Command is a string slice, not a string | ||
// - [RunShellCommandParams].Directory is a absolute path, not a relative path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// - [RunShellCommandParams].Directory is a absolute path, not a relative path | |
// - [RunShellCommandParams].Directory is an absolute path, not a relative path |
// - [RunShellCommandParams].Directory must not be empty | ||
// | ||
// Eventually, this package may be split to a separate repository. | ||
package msi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the name is fine. It is an interface for MCP to tools that run in a sandbox.
if err != nil { | ||
return nil, nil, err | ||
} | ||
return &mcp.CallToolResult{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be some post-processing for binary files?
Either reject them, or show a hexdump style output for the beginning of the file?
It may be better to reject binary output and provide a separate hexdump tool. The ReadFile tool could explain why the output was not shown and recommend to use the hexdump tool if the binary content really is needed.
There are two kinds of scenario to use Lima with AI: | ||
|
||
- [AI in Lima](./ai-in-lima): running an AI agent inside a VM | ||
- [Lima in AI](./lima-in-ai): calling Lima's MCP tools from an AI agent running outside a VM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this "MCP tools in Lima".
Because "Lima in AI" means to me that the AI tool itself will run limactl
commands on the host, and not via MCP.
This PR allows AI agents such as Gemini CLI to wrap local file operations (read/write/execute) inside Lima VM.
It should work with Claude Code, Codex, etc. too, but they might need a modification to disable their built-in local file operation tools. (Help wanted for testing)
This feature will be available in Lima v2.0
Preview of the documentation: https://deploy-preview-3744--lima-vm.netlify.app/docs/config/ai/
Interface
pkg/mcp/msi
defines "MCP Sandbox Interface" (tentative) that should be reusable for other projects too.MCP Sandbox Interface defines MCP (Model Context Protocol) tools that can be used for reading, writing, and executing local files with an appropriate sandboxing technology. The sandboxing technology can be more secure and/or efficient than the default tools provided by an AI agent.
MCP Sandbox Interface was inspired by Gemini CLI's built-in tools. https://github.com/google-gemini/gemini-cli/tree/v0.1.12/docs/tools
Implementation
limactl mcp serve INSTANCE
launches an MCP server that implements the MCP Sandbox Interface.Use https://github.com/modelcontextprotocol/inspector to play around with the server.
In the web browser,
Command
tolimactl
Arguments
tomcp serve default
▶️Connect
Usage with Gemni CLI
.gemini/extensions/lima/gemini-extension.json
as follows:.gemini/settings.json
so as to disable Gemini CLI's built-in tools except ones that do not relate to local command execution and file I/O:TODOs