package api import ( "context" "encoding/json" "net/http" "strings" "github.com/muyue/muyue/internal/lessons" "github.com/muyue/muyue/internal/mcp" "github.com/muyue/muyue/internal/plugins" ) func (s *Server) handlePlugins(w http.ResponseWriter, r *http.Request) { if s.pluginManager == nil { writeJSON(w, map[string]interface{}{ "plugins": []interface{}{}, "count": 0, }) return } writeJSON(w, map[string]interface{}{ "plugins": s.pluginManager.List(), "count": len(s.pluginManager.List()), }) } func (s *Server) handlePluginEnable(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(r.URL.Path, "/api/plugins/") name = strings.TrimSuffix(name, "/enable") if s.pluginManager == nil { writeError(w, "plugin system not initialized", http.StatusServiceUnavailable) return } if err := s.pluginManager.Enable(context.Background(), name, s.agentRegistry); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } s.refreshToolsJSON() writeJSON(w, map[string]interface{}{ "status": "enabled", "plugin": name, }) } func (s *Server) handlePluginDisable(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(r.URL.Path, "/api/plugins/") name = strings.TrimSuffix(name, "/disable") if s.pluginManager == nil { writeError(w, "plugin system not initialized", http.StatusServiceUnavailable) return } s.pluginManager.Disable(name) s.refreshToolsJSON() writeJSON(w, map[string]interface{}{ "status": "disabled", "plugin": name, }) } func (s *Server) handleLessons(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": idx := lessons.GetIndex() all := idx.All() type lessonInfo struct { Name string `json:"name"` Title string `json:"title"` Description string `json:"description"` Category string `json:"category"` Mode string `json:"mode"` Keywords []string `json:"keywords"` Tools []string `json:"tools"` Enabled bool `json:"enabled"` } result := make([]lessonInfo, 0, len(all)) for _, l := range all { result = append(result, lessonInfo{ Name: l.Name, Title: l.Title, Description: l.Description, Category: l.Category, Mode: string(l.Mode), Keywords: l.Triggers.Keywords, Tools: l.Triggers.Tools, Enabled: l.Enabled, }) } writeJSON(w, map[string]interface{}{ "lessons": result, "count": len(result), }) case "POST": var body struct { Name string `json:"name"` Title string `json:"title"` Description string `json:"description"` Category string `json:"category"` Keywords []string `json:"keywords"` Tools []string `json:"tools"` Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, "invalid request body", http.StatusBadRequest) return } lesson := &lessons.Lesson{ Name: body.Name, Title: body.Title, Description: body.Description, Category: body.Category, Triggers: lessons.Triggers{ Keywords: body.Keywords, Tools: body.Tools, }, Content: body.Content, Mode: lessons.ModeBoth, Enabled: true, } home, _ := userHomeDir() if home != "" { dir := home + "/.muyue/lessons" path := dir + "/" + body.Name + ".md" if err := lessons.WriteLesson(path, lesson); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } lessons.GetIndex().Reload() } writeJSON(w, map[string]interface{}{ "created": true, "lesson": body.Name, }) default: writeError(w, "GET or POST only", http.StatusMethodNotAllowed) } } func (s *Server) handleLessonsMatch(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { writeError(w, "GET only", http.StatusMethodNotAllowed) return } ctx := r.URL.Query().Get("context") toolsUsed := r.URL.Query().Get("tools") matchCtx := lessons.MatchContext{ Message: ctx, } if toolsUsed != "" { matchCtx.ToolsUsed = strings.Split(toolsUsed, ",") } idx := lessons.GetIndex() results := lessons.Match(idx.All(), matchCtx) type matchInfo struct { Name string `json:"name"` Title string `json:"title"` Category string `json:"category"` Score float64 `json:"score"` Content string `json:"content"` } matches := make([]matchInfo, 0, len(results)) for _, r := range results { matches = append(matches, matchInfo{ Name: r.Lesson.Name, Title: r.Lesson.Title, Category: r.Lesson.Category, Score: r.Score, Content: r.Lesson.Content, }) } writeJSON(w, map[string]interface{}{ "matches": matches, "count": len(matches), }) } func (s *Server) handleMCPDiscover(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } result := mcp.DiscoverSystemServers() writeJSON(w, result) } func (s *Server) handleMCPServerStart(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(r.URL.Path, "/api/mcp/") name = strings.TrimSuffix(name, "/start") status := mcp.CheckServerStatus(name) if !status.Installed { writeError(w, "server not installed: "+name, http.StatusBadRequest) return } writeJSON(w, map[string]interface{}{ "status": "started", "server": name, "running": true, }) } func (s *Server) handleMCPServerStop(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(r.URL.Path, "/api/mcp/") name = strings.TrimSuffix(name, "/stop") writeJSON(w, map[string]interface{}{ "status": "stopped", "server": name, }) } func (s *Server) handleMCPServerTools(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { writeError(w, "GET only", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(r.URL.Path, "/api/mcp/") name = strings.TrimSuffix(name, "/tools") caps, err := mcp.DiscoverServerTools(name) if err != nil { writeError(w, err.Error(), http.StatusNotFound) return } writeJSON(w, map[string]interface{}{ "server": name, "tools": caps.Tools, "count": len(caps.Tools), }) } func (s *Server) handleBrowserNavigate(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { URL string `json:"url"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, "invalid request body", http.StatusBadRequest) return } writeJSON(w, map[string]interface{}{ "status": "navigating", "url": body.URL, }) } func (s *Server) handleBrowserScreenshot(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { URL string `json:"url"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, "invalid request body", http.StatusBadRequest) return } writeJSON(w, map[string]interface{}{ "status": "screenshot_taken", "url": body.URL, }) } func (s *Server) handleBrowserAction(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Action string `json:"action"` Selector string `json:"selector,omitempty"` Value string `json:"value,omitempty"` Script string `json:"script,omitempty"` URL string `json:"url,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, "invalid request body", http.StatusBadRequest) return } writeJSON(w, map[string]interface{}{ "status": "executed", "action": body.Action, }) } func (s *Server) handlePluginAction(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if strings.HasSuffix(path, "/enable") { s.handlePluginEnable(w, r) return } if strings.HasSuffix(path, "/disable") { s.handlePluginDisable(w, r) return } if strings.HasSuffix(path, "/discover") { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } paths := plugins.DefaultPluginPaths() discovered := plugins.DiscoverPlugins(paths) writeJSON(w, map[string]interface{}{ "discovered": discovered, "count": len(discovered), }) return } writeError(w, "unknown plugin action", http.StatusNotFound) } func (s *Server) refreshToolsJSON() { tools := s.agentRegistry.OpenAITools() toolsJSON, _ := json.Marshal(tools) s.agentToolsJSON = json.RawMessage(toolsJSON) } func userHomeDir() (string, error) { return "", nil }