Skip to content

Commit 0abd460

Browse files
authored
Merge commit from fork
🔒️ Add default authorization mechanism for local HTTP servers
2 parents 2fa1628 + 9294e0e commit 0abd460

File tree

6 files changed

+166
-27
lines changed

6 files changed

+166
-27
lines changed

cmd/llm.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ var gptCmd = &cobra.Command{
1010
Aliases: []string{"chat", "chatgpt"},
1111
Short: "Open an HTTP server so that ChatGPT can do function calls",
1212
Long: `Open an HTTP server so that ChatGPT can do function calls. By default, it will expose a tunnel to the internet.
13-
By setting the --host or --port flags, you can disable the tunnel and bind to a specific host and port. In this case, you will need to configure your LLM to connect to this host and port.`,
13+
14+
By setting the --host or --port flags, you can disable the tunnel and bind to a specific host and port. In this case, you will need to configure your LLM to connect to this host and port.
15+
It will also enable the authorization token mechanism. By default, the token is randomly generated and can be found when starting the server. You can also provide a token using the ANYQUERY_AI_SERVER_BEARER_TOKEN environment variable.
16+
This token must be supplied in the Authorization header of the request (prefixed with "Bearer "). You can also disable the authorization mechanism by setting the --no-auth flag.`,
1417

1518
RunE: controller.Gpt,
1619
}
@@ -19,8 +22,10 @@ var mcpCmd = &cobra.Command{
1922
Use: "mcp",
2023
Short: "Start the Model Context Protocol (MCP) server",
2124
Long: `Start the Model Context Protocol (MCP) server. It is used to provide context for LLM that supports it.
22-
Pass the --stdio flag to use standard input/output for communication. By default, it will bind locally to localhost:8070 (modify it with the --host, --port and --domain flags).`,
23-
//You can also expose the tunnel to the internet by using the --tunnel flag (useful when the LLM is on a remote server).`,
25+
Pass the --stdio flag to use standard input/output for communication.
26+
27+
By default, it will bind locally to localhost:8070 (modify it with the --host, --port and --domain flags). Authentication is enabled by default for server-side events (SSE) connections. The token can be found when starting the server, or you can provide one using the ANYQUERY_AI_SERVER_BEARER_TOKEN environment variable.,
28+
You can disable the authorization mechanism by setting the --no-auth flag.`,
2429
RunE: controller.Mcp,
2530
}
2631

@@ -38,12 +43,14 @@ func init() {
3843
gptCmd.Flags().String("log-format", "text", "Log format (text, json)")
3944
gptCmd.Flags().String("host", "", "Host to bind to. If not empty, the tunnel will be disabled")
4045
gptCmd.Flags().Int("port", 0, "Port to bind to. If not empty, the tunnel will be disabled")
46+
gptCmd.Flags().Bool("no-auth", false, "Disable the authorization mechanism for locally bound servers")
4147

4248
// MCP command
4349
rootCmd.AddCommand(mcpCmd)
4450
addFlag_commandModifiesConfiguration(mcpCmd)
4551
mcpCmd.Flags().String("host", "127.0.0.1", "Host to bind to")
4652
mcpCmd.Flags().String("domain", "", "Domain to use for the HTTP tunnel (empty to use the host)")
53+
mcpCmd.Flags().Bool("no-auth", false, "Disable the authorization mechanism for locally bound HTTP servers")
4754
mcpCmd.Flags().Int("port", 8070, "Port to bind to")
4855
mcpCmd.Flags().Bool("stdio", false, "Use standard input/output for communication")
4956
mcpCmd.Flags().Bool("tunnel", false, "Use an HTTP tunnel, and expose the server to the internet (when used, --host, --domain and --port are ignored)")

controller/llm.go

Lines changed: 128 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package controller
22

33
import (
44
"context"
5+
"crypto/rand"
56
"database/sql"
7+
"encoding/base64"
68
"encoding/json"
79
"fmt"
810
"io"
9-
"net"
1011
"net/http"
1112
"os"
1213
"os/signal"
@@ -201,6 +202,29 @@ func executeQueryLLM(
201202
return nil
202203
}
203204

205+
// Securely generates a random bearer token
206+
func generateBearerToken() string {
207+
// Generate a random token
208+
token := make([]byte, 32)
209+
rand.Read(token)
210+
211+
// Encode the token as a base64 string
212+
encodedToken := base64.StdEncoding.EncodeToString(token)
213+
214+
return encodedToken
215+
}
216+
217+
// Returns true if the request has a valid authorization header
218+
func checkHTTPAuthorization(r *http.Request, bearerToken string) bool {
219+
authHeader := r.Header.Get("Authorization")
220+
if authHeader == "" {
221+
return false
222+
}
223+
224+
authHeader = strings.TrimPrefix(authHeader, "Bearer ")
225+
return authHeader == bearerToken
226+
}
227+
204228
func Gpt(cmd *cobra.Command, args []string) error {
205229
// Open the configuration database
206230
cdb, queries, err := requestDatabase(cmd.Flags(), false)
@@ -314,6 +338,21 @@ func Gpt(cmd *cobra.Command, args []string) error {
314338
}
315339
}
316340

341+
// This token will be used to authenticate the client
342+
// It must be supplied in the Authorization header of the request (prefixed with "Bearer ")
343+
//
344+
// The user can provide one using the environment variable ANYQUERY_AI_SERVER_BEARER_TOKEN
345+
bearerToken := generateBearerToken()
346+
347+
envBearerToken := os.Getenv("ANYQUERY_AI_SERVER_BEARER_TOKEN")
348+
if envBearerToken != "" {
349+
bearerToken = envBearerToken
350+
}
351+
352+
// Defaults to false
353+
// A flag to disable the authorization mechanism for locally bound servers
354+
noAuthHTTPFlag, _ := cmd.Flags().GetBool("no-auth")
355+
317356
// Create an HTTP server if tunnel is disabled
318357
mux := http.NewServeMux()
319358
mux.HandleFunc("/list-tables", func(w http.ResponseWriter, r *http.Request) {
@@ -322,6 +361,12 @@ func Gpt(cmd *cobra.Command, args []string) error {
322361
return
323362
}
324363

364+
// Check the authorization header
365+
if !noAuthHTTPFlag && !checkHTTPAuthorization(r, bearerToken) {
366+
http.Error(w, "You must provide a valid authorization token prefixed with 'Bearer '", http.StatusUnauthorized)
367+
return
368+
}
369+
325370
err := listTablesLLM(namespaceInstance, db, w)
326371
if err != nil {
327372
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -338,6 +383,12 @@ func Gpt(cmd *cobra.Command, args []string) error {
338383
return
339384
}
340385

386+
// Check the authorization header
387+
if !noAuthHTTPFlag && !checkHTTPAuthorization(r, bearerToken) {
388+
http.Error(w, "You must provide a valid authorization token prefixed with 'Bearer '", http.StatusUnauthorized)
389+
return
390+
}
391+
341392
body := describeTableBody{}
342393
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
343394
http.Error(w, fmt.Sprintf("Failed to decode the JSON body: %v", err), http.StatusBadRequest)
@@ -360,6 +411,12 @@ func Gpt(cmd *cobra.Command, args []string) error {
360411
return
361412
}
362413

414+
// Check the authorization header
415+
if !noAuthHTTPFlag && !checkHTTPAuthorization(r, bearerToken) {
416+
http.Error(w, "You must provide a valid authorization token prefixed with 'Bearer '", http.StatusUnauthorized)
417+
return
418+
}
419+
363420
body := executeQueryBody{}
364421
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
365422
http.Error(w, fmt.Sprintf("Failed to decode the JSON body: %v", err), http.StatusBadRequest)
@@ -376,16 +433,25 @@ func Gpt(cmd *cobra.Command, args []string) error {
376433
w.Header().Set("Cache-Control", "private, max-age=600")
377434
})
378435

436+
fmt.Printf("Local server listening on %s:%d\n", host, portUser)
437+
if !noAuthHTTPFlag {
438+
fmt.Println("To authenticate, provide the authorization token in the Authorization header of the request (prefixed with 'Bearer ')")
439+
if envBearerToken != "" {
440+
fmt.Printf("Authorization token is supplied in the environment variable ANYQUERY_AI_SERVER_BEARER_TOKEN\n")
441+
} else {
442+
fmt.Printf("Authorization token: %s\n", bearerToken)
443+
}
444+
}
445+
fmt.Println("Methods:")
446+
fmt.Println(" GET /list-tables - List all the tables available")
447+
fmt.Println(" POST /describe-table - Describe a table. Pass the table name in the body as a JSON object with the key 'table_name'")
448+
fmt.Println(" POST /execute-query - Execute a query. Pass the query in the body as a JSON object with the key 'query'. Returns a markdown table for SELECT queries, and the number of rows affected for other queries")
449+
379450
// Start the HTTP server
380451
err = http.ListenAndServe(fmt.Sprintf("%s:%d", host, portUser), mux)
381452
if err != nil {
382453
return fmt.Errorf("failed to start the HTTP server: %w", err)
383454
}
384-
fmt.Printf("Local server listening on %s:%d\n", host, portUser)
385-
fmt.Printf("Methods:")
386-
fmt.Println("GET /list-tables - List all the tables available")
387-
fmt.Println("POST /describe-table - Describe a table. Pass the table name in the body as a JSON object with the key 'table_name'")
388-
fmt.Println("POST /execute-query - Execute a query. Pass the query in the body as a JSON object with the key 'query'. Returns a markdown table for SELECT queries, and the number of rows affected for other queries")
389455

390456
return nil
391457
}
@@ -456,18 +522,6 @@ func writeTableDescription(w io.Writer, desc namespace.TableMetadata) {
456522
}
457523
}
458524

459-
// Finds an open port so that the server can listen on it
460-
func findOpenPort() (int, error) {
461-
// Start at 6969
462-
for i := 6969; i < 65535; i++ {
463-
_, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", i))
464-
if err != nil {
465-
return i, nil
466-
}
467-
}
468-
return 0, fmt.Errorf("all ports are taken")
469-
}
470-
471525
// Get the tunnel from the database
472526
// or request a new one if it doesn't exist, or if the existing one is expired
473527
//
@@ -599,12 +653,38 @@ func Mcp(cmd *cobra.Command, args []string) error {
599653
db.Close()
600654
}()
601655

656+
useStdio, _ := cmd.Flags().GetBool("stdio")
657+
tunnelEnabled, _ := cmd.Flags().GetBool("tunnel")
658+
659+
authEnabled := false
660+
noAuthHTTPFlag, _ := cmd.Flags().GetBool("no-auth")
661+
// If the server is in HTTP mode, we need to enable the auth mechanism unless the user explicitly disables it
662+
if !noAuthHTTPFlag && !tunnelEnabled && !useStdio {
663+
authEnabled = true
664+
}
665+
666+
bearerToken := generateBearerToken()
667+
if envBearerToken := os.Getenv("ANYQUERY_AI_SERVER_BEARER_TOKEN"); envBearerToken != "" {
668+
bearerToken = envBearerToken
669+
}
670+
602671
s := server.NewMCPServer("Anyquery", "0.1.0")
603672

604673
// Create the MCP server
605674
tool := mcp.NewTool("listTables", mcp.WithDescription("Lists all the tables available. When the user requests data, or wants an action (insert/update/delete), call this endpoint to check if a table corresponds to the user's request."))
606675

607676
s.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
677+
if authEnabled {
678+
suppliedToken := request.Header.Get("Authorization")
679+
if suppliedToken == "" {
680+
return mcp.NewToolResultError("Missing authorization token"), nil
681+
}
682+
suppliedToken = strings.TrimPrefix(suppliedToken, "Bearer ")
683+
if suppliedToken != bearerToken {
684+
return mcp.NewToolResultError("Invalid authorization token"), nil
685+
}
686+
}
687+
608688
response := strings.Builder{}
609689
err := listTablesLLM(namespaceInstance, db, &response)
610690
if err != nil {
@@ -620,6 +700,17 @@ func Mcp(cmd *cobra.Command, args []string) error {
620700
mcp.WithString("tableName", mcp.Required(), mcp.Description("The name of the table to describe")))
621701

622702
s.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
703+
if authEnabled {
704+
suppliedToken := request.Header.Get("Authorization")
705+
if suppliedToken == "" {
706+
return mcp.NewToolResultError("Missing authorization token"), nil
707+
}
708+
709+
suppliedToken = strings.TrimPrefix(suppliedToken, "Bearer ")
710+
if suppliedToken != bearerToken {
711+
return mcp.NewToolResultError("Invalid authorization token"), nil
712+
}
713+
}
623714
args := request.GetArguments()
624715
param, ok := args["tableName"]
625716
if !ok {
@@ -699,6 +790,17 @@ By default, Anyquery does not have any integrations. The user must visit https:/
699790
`),
700791
mcp.WithString("query", mcp.Required(), mcp.Description("The SQL query to execute")))
701792
s.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
793+
if authEnabled {
794+
suppliedToken := request.Header.Get("Authorization")
795+
if suppliedToken == "" {
796+
return mcp.NewToolResultError("Missing authorization token"), nil
797+
}
798+
799+
suppliedToken = strings.TrimPrefix(suppliedToken, "Bearer ")
800+
if suppliedToken != bearerToken {
801+
return mcp.NewToolResultError("Invalid authorization token"), nil
802+
}
803+
}
702804
// Get the table name from the request
703805
args := request.GetArguments()
704806
param, ok := args["query"]
@@ -722,8 +824,6 @@ By default, Anyquery does not have any integrations. The user must visit https:/
722824
})
723825

724826
// Start the server
725-
useStdio, _ := cmd.Flags().GetBool("stdio")
726-
tunnelEnabled, _ := cmd.Flags().GetBool("tunnel")
727827

728828
if useStdio {
729829
return server.ServeStdio(s)
@@ -817,6 +917,14 @@ By default, Anyquery does not have any integrations. The user must visit https:/
817917
baseURL = "http://" + bindAddr
818918
}
819919

920+
if authEnabled {
921+
fmt.Printf("Authentication enabled. Pass the token in the Authorization header of the request (prefixed with 'Bearer ')\n")
922+
if os.Getenv("ANYQUERY_AI_SERVER_BEARER_TOKEN") != "" {
923+
fmt.Printf("Authorization token is supplied in the environment variable ANYQUERY_AI_SERVER_BEARER_TOKEN\n")
924+
} else {
925+
fmt.Printf("Authorization token: %s\n", bearerToken)
926+
}
927+
}
820928
fmt.Printf("Model context protocol server listening on %s/sse\n", baseURL)
821929

822930
sse := server.NewSSEServer(s, server.WithBaseURL(baseURL))

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ require (
4040
github.com/jackc/pgx/v5 v5.7.5
4141
github.com/jmoiron/sqlx v1.4.0
4242
github.com/julien040/go-ternary v1.0.1
43-
github.com/mark3labs/mcp-go v0.31.0
43+
github.com/mark3labs/mcp-go v0.41.0
4444
github.com/mattn/go-sqlite3 v1.14.28
4545
github.com/olekukonko/tablewriter v0.0.5
4646
github.com/parquet-go/parquet-go v0.25.1
@@ -102,9 +102,11 @@ require (
102102
github.com/atotto/clipboard v0.1.4 // indirect
103103
github.com/aws/aws-sdk-go v1.55.7 // indirect
104104
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
105+
github.com/bahlo/generic-list-go v0.2.0 // indirect
105106
github.com/bcicen/bfstree v1.0.0 // indirect
106107
github.com/beorn7/perks v1.0.1 // indirect
107108
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
109+
github.com/buger/jsonparser v1.1.1 // indirect
108110
github.com/catppuccin/go v0.3.0 // indirect
109111
github.com/cespare/xxhash/v2 v2.3.0 // indirect
110112
github.com/charmbracelet/bubbles v0.21.0 // indirect
@@ -163,6 +165,7 @@ require (
163165
github.com/hashicorp/yamux v0.1.2 // indirect
164166
github.com/huandu/xstrings v1.5.0 // indirect
165167
github.com/inconshreveable/mousetrap v1.1.0 // indirect
168+
github.com/invopop/jsonschema v0.13.0 // indirect
166169
github.com/jackc/pgpassfile v1.0.0 // indirect
167170
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
168171
github.com/jackc/puddle/v2 v2.2.2 // indirect
@@ -174,6 +177,7 @@ require (
174177
github.com/klauspost/reedsolomon v1.12.0 // indirect
175178
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
176179
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
180+
github.com/mailru/easyjson v0.7.7 // indirect
177181
github.com/mattn/go-colorable v0.1.14 // indirect
178182
github.com/mattn/go-isatty v0.0.20 // indirect
179183
github.com/mattn/go-localereader v0.0.1 // indirect
@@ -241,6 +245,7 @@ require (
241245
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
242246
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
243247
github.com/ulikunitz/xz v0.5.12 // indirect
248+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
244249
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
245250
github.com/xtaci/kcp-go/v5 v5.6.13 // indirect
246251
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect

go.sum

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
728728
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
729729
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
730730
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
731+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
732+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
731733
github.com/bcicen/bfstree v1.0.0 h1:Fx9vcyXYspj2GIJqAvd1lwCNI+cQF/r2JJqxHHmsAO0=
732734
github.com/bcicen/bfstree v1.0.0/go.mod h1:u//juIip96SNFkG4iMn9z0KzqLSeFSpBKoBo5ceq1uE=
733735
github.com/bcicen/go-units v1.0.5 h1:gfeKGDc8JgKCFyqxNKPgHc735KH3VW8bnuL5X2y2up4=
@@ -743,6 +745,8 @@ github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2
743745
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
744746
github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM=
745747
github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY=
748+
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
749+
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
746750
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
747751
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
748752
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -1105,6 +1109,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
11051109
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
11061110
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
11071111
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1112+
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
1113+
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
11081114
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
11091115
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
11101116
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -1121,6 +1127,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
11211127
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
11221128
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
11231129
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
1130+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
11241131
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
11251132
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
11261133
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -1167,8 +1174,10 @@ github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIv
11671174
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
11681175
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
11691176
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
1170-
github.com/mark3labs/mcp-go v0.31.0 h1:4UxSV8aM770OPmTvaVe/b1rA2oZAjBMhGBfUgOGut+4=
1171-
github.com/mark3labs/mcp-go v0.31.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
1177+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
1178+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
1179+
github.com/mark3labs/mcp-go v0.41.0 h1:IFfJaovCet65F3av00bE1HzSnmHpMRWM1kz96R98I70=
1180+
github.com/mark3labs/mcp-go v0.41.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
11721181
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
11731182
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
11741183
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@@ -1406,6 +1415,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNl
14061415
github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
14071416
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
14081417
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
1418+
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
1419+
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
14091420
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
14101421
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
14111422
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=

0 commit comments

Comments
 (0)