fetchSpec retrieves and parses the OpenAPI specification from the configured URL.
(ctx context.Context)
| 115 | |
| 116 | // fetchSpec retrieves and parses the OpenAPI specification from the configured URL. |
| 117 | func (t *ToolSet) fetchSpec(ctx context.Context) (*v3.Document, error) { |
| 118 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, t.specURL, http.NoBody) |
| 119 | if err != nil { |
| 120 | return nil, fmt.Errorf("failed to create request: %w", err) |
| 121 | } |
| 122 | |
| 123 | req.Header.Set("Accept", "application/json") |
| 124 | setHeaders(req, t.headers) |
| 125 | |
| 126 | resp, err := httpclient.NewSafeClient(t.timeout, t.allowPrivateIPs).Do(req) |
| 127 | if err != nil { |
| 128 | return nil, fmt.Errorf("request failed: %w", err) |
| 129 | } |
| 130 | defer resp.Body.Close() |
| 131 | |
| 132 | if resp.StatusCode != http.StatusOK { |
| 133 | return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode) |
| 134 | } |
| 135 | |
| 136 | limitedReader := io.LimitReader(resp.Body, 10<<20) // 10MB limit |
| 137 | body, err := io.ReadAll(limitedReader) |
| 138 | if err != nil { |
| 139 | return nil, fmt.Errorf("failed to read response: %w", err) |
| 140 | } |
| 141 | |
| 142 | // Check if the spec was truncated. |
| 143 | if len(body) >= 10<<20 { |
| 144 | return nil, errors.New("OpenAPI spec exceeds 10MB size limit") |
| 145 | } |
| 146 | |
| 147 | doc, err := libopenapi.NewDocument(body) |
| 148 | if err != nil { |
| 149 | return nil, fmt.Errorf("failed to parse OpenAPI spec: %w", err) |
| 150 | } |
| 151 | |
| 152 | model, buildErr := doc.BuildV3Model() |
| 153 | if buildErr != nil { |
| 154 | // Log validation issues but don't fail — some valid OpenAPI 3.1 |
| 155 | // features may not be fully supported by the validator. |
| 156 | slog.WarnContext(ctx, "OpenAPI spec validation reported issues; proceeding anyway", "url", t.specURL, "error", buildErr) |
| 157 | } |
| 158 | |
| 159 | if model == nil { |
| 160 | return nil, errors.New("failed to build OpenAPI V3 model from spec") |
| 161 | } |
| 162 | |
| 163 | return &model.Model, nil |
| 164 | } |
| 165 | |
| 166 | // buildTools converts an OpenAPI spec into a list of tools. |
| 167 | func (t *ToolSet) buildTools(spec *v3.Document) ([]tools.Tool, error) { |
no test coverage detected