| 
4 | 4 | package main  | 
5 | 5 | 
 
  | 
6 | 6 | import (  | 
 | 7 | +	"context"  | 
7 | 8 | 	"encoding/json"  | 
8 | 9 | 	"errors"  | 
9 | 10 | 	"fmt"  | 
 | 11 | +	"os"  | 
 | 12 | +	"path/filepath"  | 
10 | 13 | 	"runtime"  | 
11 | 14 | 	"strings"  | 
12 | 15 | 
 
  | 
@@ -38,6 +41,7 @@ func newApp() *cobra.Command {  | 
38 | 41 | 	cmd.AddCommand(  | 
39 | 42 | 		newMcpInfoCommand(),  | 
40 | 43 | 		newMcpServeCommand(),  | 
 | 44 | +		newMcpGenDocCommand(),  | 
41 | 45 | 		// TODO: `limactl-mcp configure gemini` ?  | 
42 | 46 | 	)  | 
43 | 47 | 	return cmd  | 
@@ -77,47 +81,51 @@ func newMcpInfoCommand() *cobra.Command {  | 
77 | 81 | 
 
  | 
78 | 82 | func mcpInfoAction(cmd *cobra.Command, _ []string) error {  | 
79 | 83 | 	ctx := cmd.Context()  | 
80 |  | -	limactl, err := limactlutil.Path()  | 
 | 84 | +	info, err := inspectInfo(ctx)  | 
81 | 85 | 	if err != nil {  | 
82 | 86 | 		return err  | 
83 | 87 | 	}  | 
84 |  | -	ts, err := toolset.New(limactl)  | 
 | 88 | +	j, err := json.MarshalIndent(info, "", "    ")  | 
85 | 89 | 	if err != nil {  | 
86 | 90 | 		return err  | 
87 | 91 | 	}  | 
 | 92 | +	_, err = fmt.Fprint(cmd.OutOrStdout(), string(j))  | 
 | 93 | +	return err  | 
 | 94 | +}  | 
 | 95 | + | 
 | 96 | +func inspectInfo(ctx context.Context) (*Info, error) {  | 
 | 97 | +	ts, err := toolset.New("")  | 
 | 98 | +	if err != nil {  | 
 | 99 | +		return nil, err  | 
 | 100 | +	}  | 
88 | 101 | 	server := newServer()  | 
89 | 102 | 	if err = ts.RegisterServer(server); err != nil {  | 
90 |  | -		return err  | 
 | 103 | +		return nil, err  | 
91 | 104 | 	}  | 
92 | 105 | 	serverTransport, clientTransport := mcp.NewInMemoryTransports()  | 
93 | 106 | 	serverSession, err := server.Connect(ctx, serverTransport, nil)  | 
94 | 107 | 	if err != nil {  | 
95 |  | -		return err  | 
 | 108 | +		return nil, err  | 
96 | 109 | 	}  | 
97 | 110 | 	client := mcp.NewClient(&mcp.Implementation{Name: "client"}, nil)  | 
98 | 111 | 	clientSession, err := client.Connect(ctx, clientTransport, nil)  | 
99 | 112 | 	if err != nil {  | 
100 |  | -		return err  | 
 | 113 | +		return nil, err  | 
101 | 114 | 	}  | 
102 | 115 | 	toolsResult, err := clientSession.ListTools(ctx, &mcp.ListToolsParams{})  | 
103 | 116 | 	if err != nil {  | 
104 |  | -		return err  | 
 | 117 | +		return nil, err  | 
105 | 118 | 	}  | 
106 | 119 | 	if err = clientSession.Close(); err != nil {  | 
107 |  | -		return err  | 
 | 120 | +		return nil, err  | 
108 | 121 | 	}  | 
109 | 122 | 	if err = serverSession.Wait(); err != nil {  | 
110 |  | -		return err  | 
 | 123 | +		return nil, err  | 
111 | 124 | 	}  | 
112 | 125 | 	info := &Info{  | 
113 | 126 | 		Tools: toolsResult.Tools,  | 
114 | 127 | 	}  | 
115 |  | -	j, err := json.MarshalIndent(info, "", "    ")  | 
116 |  | -	if err != nil {  | 
117 |  | -		return err  | 
118 |  | -	}  | 
119 |  | -	_, err = fmt.Fprint(cmd.OutOrStdout(), string(j))  | 
120 |  | -	return err  | 
 | 128 | +	return info, nil  | 
121 | 129 | }  | 
122 | 130 | 
 
  | 
123 | 131 | type Info struct {  | 
@@ -170,3 +178,76 @@ func mcpServeAction(cmd *cobra.Command, args []string) error {  | 
170 | 178 | 	transport := &mcp.StdioTransport{}  | 
171 | 179 | 	return server.Run(ctx, transport)  | 
172 | 180 | }  | 
 | 181 | + | 
 | 182 | +func newMcpGenDocCommand() *cobra.Command {  | 
 | 183 | +	cmd := &cobra.Command{  | 
 | 184 | +		Use:    "generate-doc DIR",  | 
 | 185 | +		Short:  "Generate documentation pages",  | 
 | 186 | +		Args:   cobra.MinimumNArgs(1),  | 
 | 187 | +		RunE:   mcpGenDocAction,  | 
 | 188 | +		Hidden: true,  | 
 | 189 | +	}  | 
 | 190 | +	return cmd  | 
 | 191 | +}  | 
 | 192 | + | 
 | 193 | +func mcpGenDocAction(cmd *cobra.Command, args []string) error {  | 
 | 194 | +	ctx := cmd.Context()  | 
 | 195 | +	dir := args[0]  | 
 | 196 | +	if err := os.MkdirAll(dir, 0o755); err != nil {  | 
 | 197 | +		return err  | 
 | 198 | +	}  | 
 | 199 | +	fName := filepath.Join(dir, "mcp.md")  | 
 | 200 | +	f, err := os.Create(fName)  | 
 | 201 | +	if err != nil {  | 
 | 202 | +		return err  | 
 | 203 | +	}  | 
 | 204 | +	defer f.Close()  | 
 | 205 | +	fmt.Fprint(f, `---  | 
 | 206 | +title: MCP tools  | 
 | 207 | +weight: 99  | 
 | 208 | +---  | 
 | 209 | +Lima implements the "MCP Sandbox Interface" (tentative name):  | 
 | 210 | +https://pkg.go.dev/github.com/lima-vm/lima/v2/pkg/mcp/msi  | 
 | 211 | +
  | 
 | 212 | +MCP Sandbox Interface defines MCP (Model Context Protocol) tools  | 
 | 213 | +that can be used for reading, writing, and executing local files  | 
 | 214 | +with an appropriate sandboxing technology, such as Lima.  | 
 | 215 | +
  | 
 | 216 | +The sandboxing technology can be more secure and/or efficient than  | 
 | 217 | +the default tools provided by an AI agent.  | 
 | 218 | +
  | 
 | 219 | +MCP Sandbox Interface was inspired by  | 
 | 220 | +[Google Gemini CLI's built-in tools](https://github.com/google-gemini/gemini-cli/tree/main/docs/tools).  | 
 | 221 | +
  | 
 | 222 | +`)  | 
 | 223 | +	info, err := inspectInfo(ctx)  | 
 | 224 | +	if err != nil {  | 
 | 225 | +		return err  | 
 | 226 | +	}  | 
 | 227 | +	for _, tool := range info.Tools {  | 
 | 228 | +		fmt.Fprintf(f, "## `%s`\n\n", tool.Name)  | 
 | 229 | +		if tool.Title != "" {  | 
 | 230 | +			fmt.Fprintf(f, "### Title\n\n%s\n\n", tool.Title)  | 
 | 231 | +		}  | 
 | 232 | +		if tool.Description != "" {  | 
 | 233 | +			fmt.Fprintf(f, "### Description\n\n%s\n\n", tool.Description)  | 
 | 234 | +		}  | 
 | 235 | +		if tool.InputSchema != nil {  | 
 | 236 | +			fmt.Fprint(f, "### Input Schema\n\n")  | 
 | 237 | +			schema, err := json.MarshalIndent(tool.InputSchema, "", "    ")  | 
 | 238 | +			if err != nil {  | 
 | 239 | +				return err  | 
 | 240 | +			}  | 
 | 241 | +			fmt.Fprintf(f, "```json\n%s\n```\n\n", string(schema))  | 
 | 242 | +		}  | 
 | 243 | +		if tool.OutputSchema != nil {  | 
 | 244 | +			fmt.Fprint(f, "### Output Schema\n\n")  | 
 | 245 | +			schema, err := json.MarshalIndent(tool.OutputSchema, "", "    ")  | 
 | 246 | +			if err != nil {  | 
 | 247 | +				return err  | 
 | 248 | +			}  | 
 | 249 | +			fmt.Fprintf(f, "```json\n%s\n```\n\n", string(schema))  | 
 | 250 | +		}  | 
 | 251 | +	}  | 
 | 252 | +	return f.Close()  | 
 | 253 | +}  | 
0 commit comments