返回 last30days-skill
preflight.go
根目录 / mcp / internal / tools / preflight.go
1 package tools
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7
8 mcplib "github.com/mark3labs/mcp-go/mcp"
9 "github.com/mark3labs/mcp-go/server"
10
11 "github.com/mvanhorn/last30days-skill/mcp/internal/engine"
12 )
13
14 func registerPreflightTool(s *server.MCPServer, cfg Config) {
15 s.AddTool(
16 mcplib.NewTool("preflight",
17 mcplib.WithDescription(
18 "Safely summarize what last30days would read, write, execute, and contact "+
19 "without running research, saving files, or reading browser cookies.",
20 ),
21 mcplib.WithString("format", mcplib.Description("Output shape: 'text' (default) for a concise summary or 'json' for structured details.")),
22 mcplib.WithReadOnlyHintAnnotation(true),
23 mcplib.WithDestructiveHintAnnotation(false),
24 mcplib.WithOpenWorldHintAnnotation(false),
25 ),
26 makePreflightHandler(cfg),
27 )
28 }
29
30 func makePreflightHandler(cfg Config) server.ToolHandlerFunc {
31 return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
32 format, err := preflightFormatArgument(req.GetArguments())
33 if err != nil {
34 return mcplib.NewToolResultError(err.Error()), nil
35 }
36
37 src, err := engine.EngineFS()
38 if err != nil {
39 return mcplib.NewToolResultError(fmt.Sprintf("engine source unavailable: %v", err)), nil
40 }
41 cacheDir, err := engine.EnsureUserCache(src, cfg.Version)
42 if err != nil {
43 return mcplib.NewToolResultError(fmt.Sprintf(
44 "engine extract failed: %v\nhint: set %s to a writable directory if the default cache location is locked down",
45 err, engine.CacheEnvOverride,
46 )), nil
47 }
48
49 res, runErr := engine.Run(ctx, engine.RunOptions{
50 CacheDir: cacheDir,
51 Args: preflightRunArgs(format),
52 })
53 if runErr != nil {
54 return mcplib.NewToolResultError(formatRunError(runErr, res)), nil
55 }
56 return mcplib.NewToolResultText(string(res.Stdout)), nil
57 }
58 }
59
60 func preflightRunArgs(format string) []string {
61 runArgs := []string{"--preflight", "--preflight-report-on-save-dir", mcpSaveDir()}
62 if format == "json" {
63 runArgs = append(runArgs, "--emit=json")
64 }
65 return runArgs
66 }
67
68 func preflightFormatArgument(args map[string]any) (string, error) {
69 raw, ok := args["format"]
70 if !ok {
71 return "text", nil
72 }
73 value, ok := raw.(string)
74 if !ok {
75 return "", errors.New("format must be a string")
76 }
77 switch value {
78 case "", "text":
79 return "text", nil
80 case "json":
81 return "json", nil
82 default:
83 return "", fmt.Errorf("format must be 'text' or 'json', got %q", value)
84 }
85 }
86
86 lines GO