webctx 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ package app
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ func TestFocusedExcerptPreservesWholeCurlExamples(t *testing.T) {
9
+ code := backtickFence + "bash\ncurl -X POST https://api.example.com/v2/scrape \\\n" +
10
+ " -H 'Content-Type: application/json' \\\n" +
11
+ " -d '{\"alexandria\":{\"capability\":\"find-tools\"}}'\n" + backtickFence
12
+ doc := "# Documentation\n\n" + strings.Repeat("Background prose unrelated to endpoints.\n", 200) +
13
+ "\n## Inspect tools over REST\n" + strings.Repeat("Introductory filler.\n", 70) +
14
+ "\n" + code + "\n" + strings.Repeat("Appendix filler.\n", 200)
15
+ got := focusExcerpt(doc, "How do I inspect Alexandria tools with a REST request example?", 1600)
16
+ if !strings.Contains(got, code) {
17
+ t.Fatalf("the complete REST example must survive extraction:\n%s", got)
18
+ }
19
+ if strings.Count(got, backtickFence) != 2 {
20
+ t.Fatalf("expected exactly one balanced code fence: %s", got)
21
+ }
22
+ if len(got) > 1800 {
23
+ t.Fatalf("excerpt exceeded budget: %d", len(got))
24
+ }
25
+ }
26
+
27
+ func TestOversizedCodeBlocksAreNeverPartiallyReturned(t *testing.T) {
28
+ huge := backtickFence + "json\n{\"payload\":\"" + strings.Repeat("a", 3000) + "\"}\n" + backtickFence
29
+ doc := "# API\n\n" + huge + "\n\n" + strings.Repeat("Further explanation.\n", 200)
30
+ got := atomicFocusedSection(doc, "What is the API payload?", 700)
31
+ if strings.Contains(got, "\"payload\"") || strings.Contains(got, backtickFence) {
32
+ t.Fatalf("oversized example should be omitted, not truncated:\n%s", got)
33
+ }
34
+ if !strings.Contains(got, "omitted") && !strings.Contains(got, "source URL") {
35
+ t.Fatalf("omission should be explicitly marked: %s", got)
36
+ }
37
+ }
38
+
39
+ func TestPlainCodeFenceOpeningNotTreatedAsClosing(t *testing.T) {
40
+ doc := "# API\n" + strings.Repeat("Preliminary text.\n", 80) + "\n" +
41
+ backtickFence + "\nPOST /v2/scrape\n{}\n" + backtickFence + "\n" +
42
+ strings.Repeat("End notes.\n", 150)
43
+ got := atomicFocusedSection(doc, "scrape request", 650)
44
+ if !strings.Contains(got, backtickFence+"\nPOST /v2/scrape\n{}\n"+backtickFence) {
45
+ t.Fatalf("plain opening fence was not handled atomically: %s", got)
46
+ }
47
+ }
@@ -0,0 +1,289 @@
1
+ package app
2
+
3
+ import (
4
+ "context"
5
+ "encoding/json"
6
+ "fmt"
7
+ "net/url"
8
+ "sort"
9
+ "strings"
10
+ "time"
11
+ )
12
+
13
+ // API contracts are public documentation, not calls to the documented API.
14
+ // When a question requires exact REST payloads, inspecting the site's linked
15
+ // OpenAPI spec is more faithful than extrapolating HTTP requests from SDK code.
16
+ type openAPIContext struct {
17
+ URL string
18
+ Content string
19
+ }
20
+
21
+ func asksForAPIContracts(question string) bool {
22
+ question = strings.ToLower(question)
23
+ return strings.Contains(question, "rest") || strings.Contains(question, "openapi") ||
24
+ (strings.Contains(question, "api") && (strings.Contains(question, "endpoint") || strings.Contains(question, "request") || strings.Contains(question, "schema") || strings.Contains(question, "contract"))) ||
25
+ (strings.Contains(question, "exact") && (strings.Contains(question, "curl") || strings.Contains(question, "request example")))
26
+ }
27
+
28
+ func discoverOpenAPIContract(rawURL, question string) openAPIContext {
29
+ if !asksForAPIContracts(question) || directSourceLanguage(rawURL) != "" {
30
+ return openAPIContext{}
31
+ }
32
+ u, err := url.Parse(rawURL)
33
+ if err != nil || validateSourceURL(rawURL) != nil || strings.EqualFold(u.Hostname(), "github.com") {
34
+ return openAPIContext{}
35
+ }
36
+ indexURL := u.Scheme + "://" + u.Host + "/llms.txt"
37
+ ctx, cancel := context.WithTimeout(context.Background(), 18*time.Second)
38
+ defer cancel()
39
+ index, err := fetchPublicText(ctx, indexURL, 300000)
40
+ if err != nil {
41
+ return openAPIContext{}
42
+ }
43
+ var specURLs []sourceCandidate
44
+ seen := make(map[string]bool)
45
+ for _, match := range mdLinkPattern.FindAllStringSubmatch(index, 300) {
46
+ dest, err := url.Parse(strings.TrimSpace(match[2]))
47
+ if err != nil {
48
+ continue
49
+ }
50
+ resolved := u.ResolveReference(dest)
51
+ name := strings.ToLower(resolved.Path)
52
+ if !strings.EqualFold(resolved.Hostname(), u.Hostname()) || resolved.Port() != u.Port() ||
53
+ !strings.HasSuffix(name, ".json") || !strings.Contains(name, "openapi") ||
54
+ validateSourceURL(resolved.String()) != nil || sensitiveURL(resolved) {
55
+ continue
56
+ }
57
+ sourceURL := canonicalSourceURL(resolved)
58
+ if seen[sourceURL] {
59
+ continue
60
+ }
61
+ seen[sourceURL] = true
62
+ score := 0
63
+ if strings.Contains(name, "v2-openapi") {
64
+ score += 10
65
+ }
66
+ if strings.HasPrefix(name, "/api-reference/") {
67
+ score += 5
68
+ }
69
+ if name == "/openapi.json" {
70
+ score += 5
71
+ }
72
+ score -= len(name) / 20 // Prefer the canonical path over translations.
73
+ specURLs = append(specURLs, sourceCandidate{url: sourceURL, score: score})
74
+ }
75
+ sort.SliceStable(specURLs, func(i, j int) bool { return specURLs[i].score > specURLs[j].score })
76
+ for i, item := range specURLs {
77
+ if i >= 2 { // Don't recursively fetch every language/version.
78
+ break
79
+ }
80
+ body, err := fetchPublicText(ctx, item.url, maxDirectSourceBytes)
81
+ if err != nil {
82
+ continue
83
+ }
84
+ if content := summarizeOpenAPIContract(body, question); content != "" {
85
+ return openAPIContext{URL: item.url, Content: content}
86
+ }
87
+ }
88
+ return openAPIContext{}
89
+ }
90
+
91
+ type openAPIOperation struct {
92
+ Method string
93
+ Path string
94
+ Summary string
95
+ Score int
96
+ Request map[string]any
97
+ Response map[string]any
98
+ }
99
+
100
+ func summarizeOpenAPIContract(source, question string) string {
101
+ var spec struct {
102
+ OpenAPI string `json:"openapi"`
103
+ Swagger string `json:"swagger"`
104
+ Servers []struct {
105
+ URL string `json:"url"`
106
+ } `json:"servers"`
107
+ Paths map[string]map[string]any `json:"paths"`
108
+ Components struct {
109
+ Schemas map[string]map[string]any `json:"schemas"`
110
+ } `json:"components"`
111
+ }
112
+ if json.Unmarshal([]byte(source), &spec) != nil || (spec.OpenAPI == "" && spec.Swagger == "") || len(spec.Paths) == 0 {
113
+ return ""
114
+ }
115
+ terms := queryTerms(question)
116
+ terms = append(terms, relatedAPITerms(question)...)
117
+ var operations []openAPIOperation
118
+ for path, methods := range spec.Paths {
119
+ // A broad question about Alexandria must not bring in unrelated API
120
+ // routes such as research papers or team activity merely because the
121
+ // path contains "search" or a summary happens to contain "API".
122
+ if strings.Contains(strings.ToLower(question), "alexandria") &&
123
+ strings.Contains(strings.Trim(path, "/"), "/") &&
124
+ !strings.Contains(strings.ToLower(path), "alexandria") {
125
+ continue
126
+ }
127
+ for method, rawDetail := range methods {
128
+ switch strings.ToUpper(method) {
129
+ case "GET", "POST", "PUT", "PATCH", "DELETE":
130
+ default:
131
+ continue
132
+ }
133
+ detail := mapValue(rawDetail)
134
+ summary := stringValue(detail["summary"])
135
+ req := mapValue(detail["requestBody"])
136
+ resp := mapValue(detail["responses"])
137
+ score := termScore(path+" "+summary, terms)*4 + termScore(stringValue(detail["description"]), terms)
138
+ if strings.Contains(strings.ToLower(question), "alexandria") &&
139
+ termScore(strings.Trim(path, "/"), []string{"search", "scrape", "find", "tools", "alexandria"}) == 0 {
140
+ continue
141
+ }
142
+ if examples := requestExamples(req); len(examples) > 0 {
143
+ for name, example := range examples {
144
+ score += termScore(name+" "+stringValue(example["summary"]), terms) * 3
145
+ }
146
+ }
147
+ if score > 0 {
148
+ operations = append(operations, openAPIOperation{
149
+ Method: strings.ToUpper(method), Path: path, Summary: summary, Score: score, Request: req, Response: resp,
150
+ })
151
+ }
152
+ }
153
+ }
154
+ sort.SliceStable(operations, func(i, j int) bool {
155
+ if operations[i].Score == operations[j].Score {
156
+ return operations[i].Path < operations[j].Path
157
+ }
158
+ return operations[i].Score > operations[j].Score
159
+ })
160
+ if len(operations) == 0 {
161
+ return ""
162
+ }
163
+ apiRoot := ""
164
+ for _, server := range spec.Servers {
165
+ if validateSourceURL(server.URL) == nil {
166
+ apiRoot = strings.TrimRight(server.URL, "/")
167
+ break
168
+ }
169
+ }
170
+ parts := []string{"OpenAPI-defined HTTP operations and example request bodies (not executed):"}
171
+ for i, operation := range operations {
172
+ if i >= 4 {
173
+ break
174
+ }
175
+ endpoint := operation.Path
176
+ if apiRoot != "" {
177
+ endpoint = apiRoot + operation.Path
178
+ }
179
+ parts = append(parts, "", fmt.Sprintf("### %s %s", operation.Method, endpoint))
180
+ if operation.Summary != "" {
181
+ parts = append(parts, operation.Summary)
182
+ }
183
+ if examples := requestExamples(operation.Request); len(examples) > 0 {
184
+ names := make([]string, 0, len(examples))
185
+ for name := range examples {
186
+ names = append(names, name)
187
+ }
188
+ sort.SliceStable(names, func(i, j int) bool {
189
+ a := termScore(names[i]+" "+stringValue(examples[names[i]]["summary"]), terms)
190
+ b := termScore(names[j]+" "+stringValue(examples[names[j]]["summary"]), terms)
191
+ if a == b {
192
+ return names[i] < names[j]
193
+ }
194
+ return a > b
195
+ })
196
+ emitted := 0
197
+ for _, name := range names {
198
+ value := examples[name]["value"]
199
+ body, err := json.MarshalIndent(value, "", " ")
200
+ if err != nil || len(body) > 9000 || len(body) == 0 {
201
+ continue
202
+ }
203
+ if strings.Contains(strings.ToLower(question), "alexandria") &&
204
+ !strings.Contains(strings.ToLower(name+" "+string(body)), "alexandria") &&
205
+ !strings.Contains(strings.ToLower(name+" "+string(body)), "findtools") &&
206
+ !strings.Contains(strings.ToLower(name+" "+string(body)), "find-tools") {
207
+ continue
208
+ }
209
+ if emitted >= 2 {
210
+ break
211
+ }
212
+ emitted++
213
+ parts = append(parts, "", "**Published request example:** "+stringValue(examples[name]["summary"]))
214
+ if apiRoot != "" && !strings.Contains(endpoint, "{") {
215
+ // Keep the published JSON body verbatim; the API key must expand in the shell.
216
+ parts = append(parts, fmt.Sprintf("```bash\ncurl -X %s %q \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n --data-binary @- <<'JSON_REQUEST'\n%s\nJSON_REQUEST\n```", operation.Method, endpoint, string(body)))
217
+ } else {
218
+ parts = append(parts, "```json\n"+string(body)+"\n```")
219
+ }
220
+ }
221
+ }
222
+ }
223
+ for _, name := range []string{"FindToolsOptions", "AlexandriaCall", "FindToolsData"} {
224
+ if termScore(question, []string{"alexandria", "tools", "inspect", "schema", "contracts"}) == 0 {
225
+ break
226
+ }
227
+ contract, ok := spec.Components.Schemas[name]
228
+ if !ok {
229
+ continue
230
+ }
231
+ parts = append(parts, "", "### "+name)
232
+ if desc := stringValue(contract["description"]); desc != "" {
233
+ parts = append(parts, desc)
234
+ }
235
+ if props, ok := contract["properties"].(map[string]any); ok {
236
+ keys := make([]string, 0, len(props))
237
+ for key := range props {
238
+ keys = append(keys, key)
239
+ }
240
+ sort.Strings(keys)
241
+ for _, key := range keys {
242
+ item := mapValue(props[key])
243
+ typ := stringValue(item["type"])
244
+ if typ == "" {
245
+ typ = "schema"
246
+ }
247
+ description := stringValue(item["description"])
248
+ if len(description) > 240 {
249
+ description = description[:240] + "…"
250
+ }
251
+ parts = append(parts, fmt.Sprintf("- `%s` (%s): %s", key, typ, description))
252
+ }
253
+ }
254
+ }
255
+ return strings.Join(parts, "\n")
256
+ }
257
+
258
+ func mapValue(value any) map[string]any {
259
+ v, _ := value.(map[string]any)
260
+ return v
261
+ }
262
+
263
+ func requestExamples(request map[string]any) map[string]map[string]any {
264
+ content := mapValue(request["content"])
265
+ jsonContent := mapValue(content["application/json"])
266
+ examples := mapValue(jsonContent["examples"])
267
+ out := make(map[string]map[string]any, len(examples))
268
+ for key, example := range examples {
269
+ if item := mapValue(example); item != nil && item["value"] != nil {
270
+ out[key] = item
271
+ }
272
+ }
273
+ return out
274
+ }
275
+
276
+ func relatedAPITerms(question string) []string {
277
+ question = strings.ToLower(question)
278
+ var terms []string
279
+ if strings.Contains(question, "discover") || strings.Contains(question, "locat") {
280
+ terms = append(terms, "search", "find", "list")
281
+ }
282
+ if strings.Contains(question, "inspect") || strings.Contains(question, "contract") {
283
+ terms = append(terms, "find", "tools", "schema")
284
+ }
285
+ if strings.Contains(question, "execut") || strings.Contains(question, "run ") {
286
+ terms = append(terms, "scrape", "call", "execute")
287
+ }
288
+ return terms
289
+ }
@@ -0,0 +1,140 @@
1
+ package app
2
+
3
+ import (
4
+ "encoding/json"
5
+ "net/http"
6
+ "strings"
7
+ "testing"
8
+ )
9
+
10
+ func exampleOpenAPISpec(t *testing.T) string {
11
+ t.Helper()
12
+ source := map[string]any{
13
+ "openapi": "3.0.0",
14
+ "servers": []any{map[string]any{"url": "https://api.example.com/v2"}},
15
+ "paths": map[string]any{
16
+ "/search": map[string]any{
17
+ "parameters": []any{map[string]any{"name": "unused"}}, // Legal non-operation path entry.
18
+ "post": map[string]any{"summary": "Search the Alexandria catalogue"},
19
+ },
20
+ "/scrape": map[string]any{
21
+ "post": map[string]any{
22
+ "summary": "Execute a provider capability or find tool contracts",
23
+ "requestBody": map[string]any{
24
+ "content": map[string]any{
25
+ "application/json": map[string]any{
26
+ "examples": map[string]any{
27
+ "findTools": map[string]any{
28
+ "summary": "Find Tools (free)",
29
+ "value": map[string]any{
30
+ "alexandria": map[string]any{
31
+ "provider": "firecrawl",
32
+ "capability": "find-tools",
33
+ "options": map[string]any{
34
+ "providers": []string{"particle"},
35
+ "expand": []string{"options", "response"},
36
+ },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ "components": map[string]any{"schemas": map[string]any{
48
+ "FindToolsOptions": map[string]any{
49
+ "description": "Browse providers and inspect tool contracts.",
50
+ "properties": map[string]any{
51
+ "expand": map[string]any{"type": "array", "description": "Contract sections to return"},
52
+ "providers": map[string]any{"type": "array", "description": "Provider IDs"},
53
+ },
54
+ },
55
+ }},
56
+ }
57
+ bytes, err := json.Marshal(source)
58
+ if err != nil {
59
+ t.Fatal(err)
60
+ }
61
+ return string(bytes)
62
+ }
63
+
64
+ func TestOpenAPIContractReturnsExactInspectionRESTExample(t *testing.T) {
65
+ result := summarizeOpenAPIContract(exampleOpenAPISpec(t),
66
+ "What are the Alexandria REST API endpoints for discovering, inspecting, and executing tools? Include exact request examples.")
67
+ for _, want := range []string{
68
+ "POST https://api.example.com/v2/search",
69
+ "POST https://api.example.com/v2/scrape",
70
+ "\"provider\": \"firecrawl\"",
71
+ "\"capability\": \"find-tools\"",
72
+ "\"expand\": [",
73
+ "FindToolsOptions",
74
+ "<<'JSON_REQUEST'",
75
+ "-H \"Authorization: Bearer $API_KEY\"",
76
+ } {
77
+ if !strings.Contains(result, want) {
78
+ t.Errorf("OpenAPI extraction lost %q:\n%s", want, result)
79
+ }
80
+ }
81
+ if count := strings.Count(result, "JSON_REQUEST\n"+strings.Repeat(string(rune(96)), 3)); count != 1 {
82
+ t.Fatalf("expected one complete REST example, got %d:\n%s", count, result)
83
+ }
84
+ }
85
+
86
+ func TestQuestionFocusedReadPullsOfficialOpenAPISpec(t *testing.T) {
87
+ oldClient := http.DefaultClient
88
+ t.Cleanup(func() { http.DefaultClient = oldClient })
89
+ spec := exampleOpenAPISpec(t)
90
+ http.DefaultClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
91
+ switch req.URL.Path {
92
+ case "/llms.txt":
93
+ return testHTTPResponse(req, http.StatusOK,
94
+ "# Docs\n\n## OpenAPI Specs\n\n- [v2-openapi](/api-reference/v2-openapi.json)\n", nil), nil
95
+ case "/api-reference/v2-openapi.json":
96
+ return testHTTPResponse(req, http.StatusOK, spec, nil), nil
97
+ default:
98
+ return testHTTPResponse(req, http.StatusNotFound, "", nil), nil
99
+ }
100
+ })}
101
+ primary := "# Alexandria\n\n## Discovery\n\ncurl https://api.example.com/v2/search\n" +
102
+ "\n## Execution\n\ncurl https://api.example.com/v2/scrape\n"
103
+ reader := func(url string) (string, error) { return primary, nil }
104
+ result, err := readLinkFocused("https://docs.example.com/features/alexandria",
105
+ "What are the Alexandria REST API endpoints for discovering, inspecting, and executing tools? Include exact request examples.",
106
+ reader)
107
+ if err != nil {
108
+ t.Fatal(err)
109
+ }
110
+ for _, want := range []string{
111
+ "## REST API contract",
112
+ "**Source:** https://docs.example.com/api-reference/v2-openapi.json",
113
+ "\"capability\": \"find-tools\"",
114
+ "curl https://api.example.com/v2/search",
115
+ "curl https://api.example.com/v2/scrape",
116
+ "**Coverage:** 2 source document(s)",
117
+ } {
118
+ if !strings.Contains(result, want) {
119
+ t.Errorf("focused read missing %q:\n%s", want, result)
120
+ }
121
+ }
122
+ }
123
+
124
+ func TestOpenAPIDiscoveryNeverCrossesToUnrelatedHost(t *testing.T) {
125
+ oldClient := http.DefaultClient
126
+ t.Cleanup(func() { http.DefaultClient = oldClient })
127
+ var calledOutsideIndex bool
128
+ http.DefaultClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
129
+ if req.URL.Path != "/llms.txt" {
130
+ calledOutsideIndex = true
131
+ }
132
+ return testHTTPResponse(req, http.StatusOK,
133
+ "- [External OpenAPI](https://evil.example.com/openapi.json)\n"+
134
+ "- [Local OpenAPI](http://127.0.0.1/openapi.json)\n", nil), nil
135
+ })}
136
+ if found := discoverOpenAPIContract("https://docs.example.com/reference",
137
+ "Find exact REST API endpoint request examples"); found.Content != "" || calledOutsideIndex {
138
+ t.Fatalf("unsafe OpenAPI discovery: %+v, off-index=%v", found, calledOutsideIndex)
139
+ }
140
+ }
@@ -0,0 +1,150 @@
1
+ package app
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+ "net/http"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ func TestReadLinkFocusedKeepsURLOnlyContract(t *testing.T) {
12
+ called := 0
13
+ reader := func(url string) (string, error) {
14
+ called++
15
+ if url != "https://docs.example.com/api" {
16
+ t.Fatalf("unexpected URL: %s", url)
17
+ }
18
+ return "# Entire document\n\nno focus needed", nil
19
+ }
20
+ got, err := readLinkFocused("https://docs.example.com/api", "", reader)
21
+ if err != nil || got != "# Entire document\n\nno focus needed" || called != 1 {
22
+ t.Fatalf("URL-only path changed: %q, %v, calls=%d", got, err, called)
23
+ }
24
+ }
25
+
26
+ func TestFocusedReadExpandsRelevantSameHostLinks(t *testing.T) {
27
+ old := http.DefaultClient
28
+ t.Cleanup(func() { http.DefaultClient = old })
29
+ http.DefaultClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
30
+ return testHTTPResponse(req, http.StatusNotFound, "", nil), nil
31
+ })}
32
+ urls := map[string]string{
33
+ "https://docs.example.com/guide": "# Guide\n[Authentication](./auth)\n[Pagination](/pagination)\n[Offsite](https://evil.example/pagination)\n[Secret](./auth?token=secret)\n[Self](#pagination)",
34
+ "https://docs.example.com/auth": "# Authentication\nSet the Authorization header with your token.",
35
+ "https://docs.example.com/pagination": "# Pagination\nPass next_cursor to read the next page.",
36
+ }
37
+ requested := make(map[string]int)
38
+ reader := func(url string) (string, error) {
39
+ requested[url]++
40
+ value, ok := urls[url]
41
+ if !ok {
42
+ return "", errors.New("missing URL")
43
+ }
44
+ return value, nil
45
+ }
46
+ got, err := readLinkFocused("https://docs.example.com/guide", "How do authentication and pagination work?", reader)
47
+ if err != nil {
48
+ t.Fatal(err)
49
+ }
50
+ for _, want := range []string{"next_cursor", "Authorization header", "https://docs.example.com/auth", "https://docs.example.com/pagination", "**Coverage:** 3 source document(s)"} {
51
+ if !strings.Contains(got, want) {
52
+ t.Errorf("missing %q in result:\n%s", want, got)
53
+ }
54
+ }
55
+ if len(requested) != 3 {
56
+ t.Fatalf("followed unsafe/irrelevant link: %+v", requested)
57
+ }
58
+ }
59
+
60
+ func TestFocusedExcerptPrioritizesRelevantHeadingAndCode(t *testing.T) {
61
+ large := "# Docs\n" + strings.Repeat("Large unrelated introduction.\n", 600) + "\n## Authenticated requests\nUse an Authorization header.\n```go\nheaders.Set(\"Authorization\", token)\n```\n\n## Other\n" + strings.Repeat("Unrelated text\n", 200)
62
+ got := focusExcerpt(large, "How to authenticate requests with Go?", 3000)
63
+ if !strings.Contains(got, "headers.Set") || strings.Contains(got, "Large unrelated introduction.Large") {
64
+ t.Fatalf("did not select API code example: %s", got)
65
+ }
66
+ if len(got) > 3500 {
67
+ t.Fatalf("excerpt unexpectedly large: %d", len(got))
68
+ }
69
+ }
70
+
71
+ func TestSearchResearchReturnsEvidenceAndSupportingSource(t *testing.T) {
72
+ results := []SearchDoc{
73
+ {URL: "https://docs.example.com/guide", Title: "Guide"},
74
+ {URL: "https://github.com/example/project", Title: "Source"},
75
+ {URL: "https://other.example.com/blog", Title: "Blog"},
76
+ }
77
+ reader := func(url string) (string, error) {
78
+ switch url {
79
+ case "https://docs.example.com/guide":
80
+ return "# Auth\nUse OAuth.\n[Pagination](/pagination)", nil
81
+ case "https://docs.example.com/pagination":
82
+ return "# Pagination\nnext_cursor is optional.", nil
83
+ case "https://github.com/example/project":
84
+ return "# Source\nOAuth settings are configurable.", nil
85
+ case "https://other.example.com/blog":
86
+ return "# Blog\nOAuth authorization examples.", nil
87
+ default:
88
+ return "", fmt.Errorf("unexpected URL: %s", url)
89
+ }
90
+ }
91
+ got := enrichSearchWithReader("OAuth pagination", results, reader)
92
+ if !strings.Contains(got, "Use OAuth.") || !strings.Contains(got, "next_cursor") || !strings.Contains(got, "Supporting reference") {
93
+ t.Fatalf("search lacked fetched source context: %s", got)
94
+ }
95
+ }
96
+
97
+ func TestFollowLinksRejectsOtherHostsSecretsAndAssets(t *testing.T) {
98
+ markdown := "[API auth](https://other.example.com/auth) [API auth](/auth?api_key=private) [API auth](/image.png) [API auth](http://127.0.0.1/auth) [API auth](/auth)"
99
+ got := relatedSourceLinks(markdown, "https://docs.example.com/guide", "api auth", 5)
100
+ if len(got) != 1 || got[0].url != "https://docs.example.com/auth" {
101
+ t.Fatalf("unsafe or irrelevant candidates: %+v", got)
102
+ }
103
+ }
104
+
105
+ func TestRelevanceMatchingDoesNotConfuseAPIWithScraping(t *testing.T) {
106
+ if termScore("advanced scraping guide", []string{"api"}) != 0 {
107
+ t.Fatal("API should not match the middle of the word scraping")
108
+ }
109
+ links := relatedSourceLinks("[Advanced scraping](/advanced-scraping-guide) [API endpoint reference](/api-reference/endpoint/scrape)", "https://docs.example.com/features/alexandria", "Alexandria API endpoints", 3)
110
+ if len(links) != 1 || !strings.Contains(links[0].url, "api-reference") {
111
+ t.Fatalf("irrelevant docs were followed: %+v", links)
112
+ }
113
+ }
114
+
115
+ func TestSearchPrefersTechnicalReferenceOverCommunityPages(t *testing.T) {
116
+ results := []SearchDoc{
117
+ {URL: "https://modelcontextprotocol.io/community/interest-groups/tool-annotations", Title: "Tool annotations interest group"},
118
+ {URL: "https://modelcontextprotocol.io/specification/2025-11-25/server/tools", Title: "Tools specification"},
119
+ {URL: "https://github.com/modelcontextprotocol/go-sdk", Title: "Go SDK for MCP"},
120
+ {URL: "https://pkg.go.dev/golang.org/x/tools/internal/mcp", Title: "Internal Go MCP"},
121
+ }
122
+ selected := chooseSearchSources("Go MCP SDK tool annotations", results, 2)
123
+ if len(selected) != 2 || selected[0].url != "https://github.com/modelcontextprotocol/go-sdk" || !strings.Contains(selected[1].url, "/specification/") {
124
+ t.Fatalf("selected irrelevant pages over official SDK/spec: %+v", selected)
125
+ }
126
+ }
127
+
128
+ func TestFollowLinksSkipGenericMCPLinksForSpecificQuestion(t *testing.T) {
129
+ content := "[Skills over MCP](/community/working-groups/skills-over-mcp) [Tool annotations](/specification/server/tools)"
130
+ links := relatedSourceLinks(content, "https://modelcontextprotocol.io/docs/tools", "Go MCP SDK tool annotations", 2)
131
+ if len(links) != 1 || !strings.Contains(links[0].url, "/specification/server/tools") {
132
+ t.Fatalf("followed generic cross-topic link: %+v", links)
133
+ }
134
+ }
135
+
136
+ func TestSelectJSONFieldsPreservesSyntaxAndPaths(t *testing.T) {
137
+ obj := map[string]any{"paths": map[string]any{"/v2/scrape": map[string]any{"post": map[string]any{"summary": "Scrape an API document"}}}, "metadata": strings.Repeat("x", 5000)}
138
+ got := selectedJSONFields(obj, "scrape", 1400)
139
+ if !strings.Contains(got, "/paths/~1v2~1scrape") || !strings.Contains(got, "```json") || !strings.Contains(got, "Scrape an API document") {
140
+ t.Fatalf("JSON selection missing source pointers: %s", got)
141
+ }
142
+ }
143
+
144
+ func TestLargeOpenAPISchemaUsesSourceJSONPointers(t *testing.T) {
145
+ source := `**URL:** https://docs.example.com/openapi.json` + "\n\n```json\n" + `{"paths":{"/v2/scrape":{"post":{"summary":"Scrape pages"}}},"large":"` + strings.Repeat("x", 12000) + `"}` + "\n```"
146
+ got := focusExcerpt(source, "scrape endpoint", 2500)
147
+ if !strings.Contains(got, "/paths/~1v2~1scrape") || !strings.Contains(got, "Scrape pages") || len(got) > 2700 {
148
+ t.Fatalf("large raw JSON should be selected by pointer: %s", got)
149
+ }
150
+ }
@@ -0,0 +1,25 @@
1
+ package app
2
+
3
+ import (
4
+ "fmt"
5
+ "net"
6
+ "net/url"
7
+ "strings"
8
+ )
9
+
10
+ // Reject non-public URL destinations before calling native readers or
11
+ // hosted scraping providers. Credentials embedded in URLs are never accepted.
12
+ func validateSourceURL(raw string) error {
13
+ u, err := url.Parse(raw)
14
+ if err != nil || u == nil || (u.Scheme != "http" && u.Scheme != "https") || u.Hostname() == "" || u.User != nil {
15
+ return fmt.Errorf("readable URL must be an absolute HTTP(S) URL without credentials")
16
+ }
17
+ host := strings.ToLower(strings.TrimSuffix(u.Hostname(), "."))
18
+ if host == "localhost" || strings.HasSuffix(host, ".localhost") || strings.HasSuffix(host, ".local") || strings.HasSuffix(host, ".internal") {
19
+ return fmt.Errorf("private/internal host is not a readable public URL")
20
+ }
21
+ if ip := net.ParseIP(host); ip != nil && (!ip.IsGlobalUnicast() || ip.IsPrivate() || ip.IsLoopback()) {
22
+ return fmt.Errorf("private or reserved IP address is not a readable public URL")
23
+ }
24
+ return nil
25
+ }