Initial commit: Search MCP Server
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GrepAppClient struct {
|
||||
BaseURL string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func NewGrepAppClient() *GrepAppClient {
|
||||
return &GrepAppClient{
|
||||
BaseURL: "https://grep.app",
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type GrepSearchResult struct {
|
||||
Repo string `json:"repo"`
|
||||
Path string `json:"path"`
|
||||
Filename string `json:"filename"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Lines []struct {
|
||||
Content string `json:"content"`
|
||||
LineNumber int `json:"line_number"`
|
||||
} `json:"lines,omitempty"`
|
||||
URL string `json:"url"`
|
||||
RepoURL string `json:"repo_url"`
|
||||
BlobURL string `json:"blob_url"`
|
||||
}
|
||||
|
||||
type GrepSearchResponse struct {
|
||||
Hits struct {
|
||||
Total int `json:"total"`
|
||||
Hits []GrepSearchHit `json:"hits"`
|
||||
Metadata map[string]int `json:"metadata,omitempty"`
|
||||
} `json:"hits"`
|
||||
}
|
||||
|
||||
type GrepSearchHit struct {
|
||||
Source GrepSearchResult `json:"_source"`
|
||||
}
|
||||
|
||||
type GrepSearchParams struct {
|
||||
Query string
|
||||
Language string
|
||||
Repo string
|
||||
Path string
|
||||
Regex bool
|
||||
CaseSensitive bool
|
||||
WholeWords bool
|
||||
}
|
||||
|
||||
func (c *GrepAppClient) Search(params GrepSearchParams) (*GrepSearchResponse, error) {
|
||||
searchURL := fmt.Sprintf("%s/search", c.BaseURL)
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("q", params.Query)
|
||||
values.Set("limit", "25")
|
||||
|
||||
if params.Language != "" {
|
||||
values.Set("lang", params.Language)
|
||||
}
|
||||
if params.Repo != "" {
|
||||
values.Set("repo", params.Repo)
|
||||
}
|
||||
if params.Path != "" {
|
||||
values.Set("path", params.Path)
|
||||
}
|
||||
if params.Regex {
|
||||
values.Set("regex", "true")
|
||||
}
|
||||
if params.CaseSensitive {
|
||||
values.Set("case", "true")
|
||||
}
|
||||
if params.WholeWords {
|
||||
values.Set("word", "true")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", searchURL+"?"+values.Encode(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Go-GrepApp-MCP/1.0")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Accept-Language", "en-US,en;q=0.5")
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error executing request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response: %w", err)
|
||||
}
|
||||
|
||||
var searchResponse GrepSearchResponse
|
||||
if err := json.Unmarshal(body, &searchResponse); err != nil {
|
||||
return nil, fmt.Errorf("error parsing JSON: %w", err)
|
||||
}
|
||||
|
||||
return &searchResponse, nil
|
||||
}
|
||||
|
||||
func (c *GrepAppClient) GetFile(owner, repo, path, ref string) (string, error) {
|
||||
apiURL := fmt.Sprintf("https://api.github.com/repos/%s/contents/%s", owner, path)
|
||||
if ref != "" {
|
||||
apiURL += "?ref=" + ref
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Go-GrepApp-MCP/1.0")
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error executing request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return "", fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var fileData map[string]interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&fileData); err != nil {
|
||||
return "", fmt.Errorf("error parsing response: %w", err)
|
||||
}
|
||||
|
||||
content, ok := fileData["content"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no content found in response")
|
||||
}
|
||||
|
||||
decoded, err := decodeBase64(content)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decoding content: %w", err)
|
||||
}
|
||||
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
func decodeBase64(encoded string) (string, error) {
|
||||
encoded = strings.ReplaceAll(encoded, "\n", "")
|
||||
decoded, err := strconv.Unquote(`"` + encoded + `"`)
|
||||
if err != nil {
|
||||
return encoded, nil
|
||||
}
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
type GitHubFileParams struct {
|
||||
Owner string
|
||||
Repo string
|
||||
Path string
|
||||
Ref string
|
||||
}
|
||||
|
||||
func (c *GrepAppClient) GetFilesBatch(files []GitHubFileParams) ([]map[string]string, error) {
|
||||
results := make([]map[string]string, 0, len(files))
|
||||
|
||||
for _, file := range files {
|
||||
content, err := c.GetFile(file.Owner, file.Repo, file.Path, file.Ref)
|
||||
if err != nil {
|
||||
results = append(results, map[string]string{
|
||||
"owner": file.Owner,
|
||||
"repo": file.Repo,
|
||||
"path": file.Path,
|
||||
"error": err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, map[string]string{
|
||||
"owner": file.Owner,
|
||||
"repo": file.Repo ,
|
||||
"path": file.Path,
|
||||
"content": content,
|
||||
})
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
Reference in New Issue
Block a user