package main import ( "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "strconv" ) // MCP Tool Implementations // These call the internal API endpoints to get data const apiBaseURL = "http://localhost:8082" // Internal API server func mcpAPICall(accessToken, path string, params map[string]string) ([]byte, error) { u := apiBaseURL + path if params != nil && len(params) > 0 { v := url.Values{} for k, val := range params { if val != "" { v.Set(k, val) } } u += "?" + v.Encode() } log.Printf("[MCP] API call: %s", u) req, err := http.NewRequest("GET", u, nil) if err != nil { log.Printf("[MCP] Request error: %v", err) return nil, err } // Use the access token for auth req.Header.Set("Authorization", "Bearer "+accessToken) client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Printf("[MCP] HTTP error: %v", err) return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Printf("[MCP] Read error: %v", err) return nil, err } if resp.StatusCode != 200 { errMsg := fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) log.Printf("[MCP] API error: %v", errMsg) return nil, errMsg } log.Printf("[MCP] API success: %d bytes", len(body)) return body, nil } func mcpListDossiers(accessToken string) (string, error) { body, err := mcpAPICall(accessToken, "/api/v1/dossiers", nil) if err != nil { return "", err } // Pretty print JSON var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpListStudies(accessToken, dossier string) (string, error) { body, err := mcpAPICall(accessToken, "/api/v1/dossiers/"+dossier+"/entries", map[string]string{"category": "imaging"}) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpListSeries(accessToken, dossier, study string) (string, error) { body, err := mcpAPICall(accessToken, "/api/v1/dossiers/"+dossier+"/entries", map[string]string{"parent": study}) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpListSlices(accessToken, dossier, series string) (string, error) { body, err := mcpAPICall(accessToken, "/api/v1/dossiers/"+dossier+"/entries", map[string]string{"parent": series}) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpFetchImage(accessToken, dossier, slice string, wc, ww float64) (map[string]interface{}, error) { params := map[string]string{} if wc != 0 { params["wc"] = strconv.FormatFloat(wc, 'f', 0, 64) } if ww != 0 { params["ww"] = strconv.FormatFloat(ww, 'f', 0, 64) } body, err := mcpAPICall(accessToken, "/image/"+slice, params) if err != nil { return nil, err } b64 := base64.StdEncoding.EncodeToString(body) return mcpImageContent(b64, "image/webp", fmt.Sprintf("Slice %s (%d bytes)", slice[:8], len(body))), nil } func mcpFetchContactSheet(accessToken, dossier, series string, wc, ww float64) (map[string]interface{}, error) { params := map[string]string{} if wc != 0 { params["wc"] = strconv.FormatFloat(wc, 'f', 0, 64) } if ww != 0 { params["ww"] = strconv.FormatFloat(ww, 'f', 0, 64) } body, err := mcpAPICall(accessToken, "/contact-sheet.webp/"+series, params) if err != nil { return nil, err } b64 := base64.StdEncoding.EncodeToString(body) return mcpImageContent(b64, "image/webp", fmt.Sprintf("Contact sheet %s (%d bytes)", series[:8], len(body))), nil } func mcpQueryEntries(accessToken, dossier, category, typ, searchKey, parent, from, to string, limit int) (string, error) { params := map[string]string{} if category != "" { params["category"] = category } if typ != "" { params["type"] = typ } if searchKey != "" { params["search_key"] = searchKey } if parent != "" { params["parent"] = parent } if from != "" { params["from"] = from } if to != "" { params["to"] = to } if limit > 0 { params["limit"] = strconv.Itoa(limit) } body, err := mcpAPICall(accessToken, "/api/v1/dossiers/"+dossier+"/entries", params) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpGetCategories(accessToken, dossier, typ, category string) (string, error) { params := map[string]string{"dossier": dossier} if typ != "" { params["type"] = typ } if category != "" { params["category"] = category } body, err := mcpAPICall(accessToken, "/api/categories", params) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil } func mcpQueryGenome(accessToken, dossier, gene, search, category, rsids string, minMag float64, includeHidden bool) (string, error) { params := map[string]string{"dossier": dossier} if gene != "" { params["gene"] = gene } if search != "" { params["search"] = search } if category != "" { params["category"] = category } if rsids != "" { params["rsids"] = rsids } if minMag > 0 { params["min_magnitude"] = strconv.FormatFloat(minMag, 'f', -1, 64) } if includeHidden { params["include_hidden"] = "true" } body, err := mcpAPICall(accessToken, "/api/genome", params) if err != nil { return "", err } var data interface{} json.Unmarshal(body, &data) pretty, _ := json.MarshalIndent(data, "", " ") return string(pretty), nil }