Files
sherlock/cmd/searxng-mcp/tools.go
T
amacocian 59eaf9e47d
Release / release (push) Failing after 6s
SearXNG support
2026-06-14 21:33:22 +02:00

85 lines
2.9 KiB
Go

package main
import (
"context"
"errors"
"strings"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func registerTools(server *mcp.Server, api *searxngAPI) {
mcp.AddTool(server, &mcp.Tool{
Name: "search_web",
Description: "Search the operator's OAuth-protected SearXNG instance and return JSON search results.",
}, searchWebHandler(api))
}
type searchWebInput struct {
Query string `json:"query" jsonschema:"search query"`
Categories string `json:"categories,omitempty" jsonschema:"optional comma-separated SearXNG categories, e.g. general, images, news"`
Engines string `json:"engines,omitempty" jsonschema:"optional comma-separated SearXNG engines"`
Language string `json:"language,omitempty" jsonschema:"optional SearXNG language code"`
TimeRange string `json:"time_range,omitempty" jsonschema:"optional time range: day, month, year"`
SafeSearch *int `json:"safesearch,omitempty" jsonschema:"optional SearXNG safesearch value: 0, 1, or 2"`
Page int `json:"page,omitempty" jsonschema:"optional result page number, default 1"`
Limit int `json:"limit,omitempty" jsonschema:"max results to return (1-50, default 10)"`
}
type searchWebOutput struct {
Query string `json:"query"`
NumberOfResults int `json:"number_of_results,omitempty"`
Results []searxngResult `json:"results"`
Suggestions []string `json:"suggestions,omitempty"`
Answers []string `json:"answers,omitempty"`
Corrections []string `json:"corrections,omitempty"`
}
func searchWebHandler(api *searxngAPI) mcp.ToolHandlerFor[searchWebInput, searchWebOutput] {
return func(ctx context.Context, _ *mcp.CallToolRequest, in searchWebInput) (*mcp.CallToolResult, searchWebOutput, error) {
query := strings.TrimSpace(in.Query)
if query == "" {
return nil, searchWebOutput{}, errors.New("search_web requires query")
}
limit := clampInt(in.Limit, 1, 50, 10)
page := clampInt(in.Page, 1, 100, 1)
if in.SafeSearch != nil && (*in.SafeSearch < 0 || *in.SafeSearch > 2) {
return nil, searchWebOutput{}, errors.New("safesearch must be 0, 1, or 2")
}
resp, err := api.search(ctx, searchParams{
Query: query,
Categories: strings.TrimSpace(in.Categories),
Engines: strings.TrimSpace(in.Engines),
Language: strings.TrimSpace(in.Language),
TimeRange: strings.TrimSpace(in.TimeRange),
SafeSearch: in.SafeSearch,
Page: page,
Limit: limit,
})
if err != nil {
return nil, searchWebOutput{}, err
}
return nil, searchWebOutput{
Query: resp.Query,
NumberOfResults: resp.NumberOfResults,
Results: resp.Results,
Suggestions: resp.Suggestions,
Answers: resp.Answers,
Corrections: resp.Corrections,
}, nil
}
}
func clampInt(v, min, max, dflt int) int {
if v <= 0 {
return dflt
}
if v < min {
return min
}
if v > max {
return max
}
return v
}