mirror of
https://github.com/lima-vm/lima.git
synced 2026-10-28 11:44:35 +08:00
docs: generate MCP docs
Fix issue 4071 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
3 files changed
+123
-33
No files matched your search
@@ -478,7 +478,7 @@ endif
|
||||
# Set limactl.md as explicit dependency.
|
||||
# It's uncertain how many docsy pages will be generated by `make`,
|
||||
# because `limactl` generates them without corresponding source code for the docsy pages.
|
||||
docsy: website/_output/docsy/limactl.md
|
||||
docsy: website/_output/docsy/limactl.md website/_output/docsy-mcp/mcp.md
|
||||
website/_output/docsy/limactl.md: _output/bin/limactl$(exe)
|
||||
@mkdir -p website/_output/docsy
|
||||
ifeq ($(native_compiling),true)
|
||||
@@ -486,6 +486,11 @@ ifeq ($(native_compiling),true)
|
||||
$< generate-doc --type docsy website/_output/docsy \
|
||||
--output _output --prefix $(PREFIX)
|
||||
endif
|
||||
website/_output/docsy-mcp/mcp.md: _output/libexec/lima/limactl-mcp$(exe)
|
||||
@mkdir -p website/_output/docsy-mcp
|
||||
ifeq ($(native_compiling),true)
|
||||
$< generate-doc website/_output/docsy-mcp
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
default-template.yaml: _output/bin/limactl$(exe)
|
||||
|
||||
+113
-32
@@ -4,9 +4,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -38,6 +41,7 @@ func newApp() *cobra.Command {
|
||||
cmd.AddCommand(
|
||||
newMcpInfoCommand(),
|
||||
newMcpServeCommand(),
|
||||
newMcpGenDocCommand(),
|
||||
// TODO: `limactl-mcp configure gemini` ?
|
||||
)
|
||||
return cmd
|
||||
@@ -77,41 +81,10 @@ func newMcpInfoCommand() *cobra.Command {
|
||||
|
||||
func mcpInfoAction(cmd *cobra.Command, _ []string) error {
|
||||
ctx := cmd.Context()
|
||||
limactl, err := limactlutil.Path()
|
||||
info, err := inspectInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts, err := toolset.New(limactl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server := newServer()
|
||||
if err = ts.RegisterServer(server); err != nil {
|
||||
return err
|
||||
}
|
||||
serverTransport, clientTransport := mcp.NewInMemoryTransports()
|
||||
serverSession, err := server.Connect(ctx, serverTransport, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := mcp.NewClient(&mcp.Implementation{Name: "client"}, nil)
|
||||
clientSession, err := client.Connect(ctx, clientTransport, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toolsResult, err := clientSession.ListTools(ctx, &mcp.ListToolsParams{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = clientSession.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = serverSession.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
info := &Info{
|
||||
Tools: toolsResult.Tools,
|
||||
}
|
||||
j, err := json.MarshalIndent(info, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -120,6 +93,41 @@ func mcpInfoAction(cmd *cobra.Command, _ []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func inspectInfo(ctx context.Context) (*Info, error) {
|
||||
ts, err := toolset.New("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := newServer()
|
||||
if err = ts.RegisterServer(server); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverTransport, clientTransport := mcp.NewInMemoryTransports()
|
||||
serverSession, err := server.Connect(ctx, serverTransport, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := mcp.NewClient(&mcp.Implementation{Name: "client"}, nil)
|
||||
clientSession, err := client.Connect(ctx, clientTransport, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toolsResult, err := clientSession.ListTools(ctx, &mcp.ListToolsParams{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = clientSession.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = serverSession.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := &Info{
|
||||
Tools: toolsResult.Tools,
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
Tools []*mcp.Tool `json:"tools"`
|
||||
}
|
||||
@@ -170,3 +178,76 @@ func mcpServeAction(cmd *cobra.Command, args []string) error {
|
||||
transport := &mcp.StdioTransport{}
|
||||
return server.Run(ctx, transport)
|
||||
}
|
||||
|
||||
func newMcpGenDocCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "generate-doc DIR",
|
||||
Short: "Generate documentation pages",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: mcpGenDocAction,
|
||||
Hidden: true,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func mcpGenDocAction(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
dir := args[0]
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
fName := filepath.Join(dir, "mcp.md")
|
||||
f, err := os.Create(fName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fmt.Fprint(f, `---
|
||||
title: MCP tools
|
||||
weight: 99
|
||||
---
|
||||
Lima implements the "MCP Sandbox Interface" (tentative name):
|
||||
https://pkg.go.dev/github.com/lima-vm/lima/v2/pkg/mcp/msi
|
||||
|
||||
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, such as Lima.
|
||||
|
||||
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
|
||||
[Google Gemini CLI's built-in tools](https://github.com/google-gemini/gemini-cli/tree/main/docs/tools).
|
||||
|
||||
`)
|
||||
info, err := inspectInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tool := range info.Tools {
|
||||
fmt.Fprintf(f, "## `%s`\n\n", tool.Name)
|
||||
if tool.Title != "" {
|
||||
fmt.Fprintf(f, "### Title\n\n%s\n\n", tool.Title)
|
||||
}
|
||||
if tool.Description != "" {
|
||||
fmt.Fprintf(f, "### Description\n\n%s\n\n", tool.Description)
|
||||
}
|
||||
if tool.InputSchema != nil {
|
||||
fmt.Fprint(f, "### Input Schema\n\n")
|
||||
schema, err := json.MarshalIndent(tool.InputSchema, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(f, "```json\n%s\n```\n\n", string(schema))
|
||||
}
|
||||
if tool.OutputSchema != nil {
|
||||
fmt.Fprint(f, "### Output Schema\n\n")
|
||||
schema, err := json.MarshalIndent(tool.OutputSchema, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(f, "```json\n%s\n```\n\n", string(schema))
|
||||
}
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
@@ -204,6 +204,10 @@ enable = false
|
||||
source="docsy"
|
||||
target="content/docs/Reference"
|
||||
language="en"
|
||||
[[module.imports.mounts]]
|
||||
source="docsy-mcp"
|
||||
target="content/docs/config/ai/outside"
|
||||
language="en"
|
||||
# Import templates README.md from templates folder
|
||||
[[module.imports]]
|
||||
path="../.."
|
||||
|
||||
Reference in new issue
Block a user