(a: z.infer<typeof Args>, _ctx: ToolContext)
| 44 | isReadOnly = true; isDestructive = false; argsSchema = Args; |
| 45 | |
| 46 | async execute(a: z.infer<typeof Args>, _ctx: ToolContext): Promise<ToolResult> { |
| 47 | let raw: string; |
| 48 | try { |
| 49 | if (/^https?:\/\//i.test(a.source)) { |
| 50 | const res = await proxyFetch(a.source, { redirect: 'follow' }); |
| 51 | if (!res.ok) return { content: `Failed to fetch spec: HTTP ${res.status}`, isError: true }; |
| 52 | raw = await res.text(); |
| 53 | } else { |
| 54 | raw = await fs.readFile(a.source, 'utf8'); |
| 55 | } |
| 56 | } catch (e: any) { |
| 57 | return { content: `Could not read spec source: ${e.message}`, isError: true }; |
| 58 | } |
| 59 | |
| 60 | let spec: any; |
| 61 | try { |
| 62 | spec = a.source.trim().endsWith('.json') || raw.trimStart().startsWith('{') |
| 63 | ? JSON.parse(raw) |
| 64 | : yaml.load(raw); |
| 65 | } catch (e: any) { |
| 66 | return { content: `Spec is not valid JSON/YAML: ${e.message}`, isError: true }; |
| 67 | } |
| 68 | |
| 69 | if (!spec || (!spec.paths && !spec.swagger && !spec.openapi)) { |
| 70 | return { content: 'This does not look like an OpenAPI/Swagger document (no paths/openapi/swagger fields).', isError: true }; |
| 71 | } |
| 72 | |
| 73 | const version = spec.openapi ?? spec.swagger ?? 'unknown'; |
| 74 | const title = spec.info?.title ?? '(untitled)'; |
| 75 | const servers: string[] = (spec.servers ?? []).map((s: any) => s.url).filter(Boolean); |
| 76 | const securitySchemes = spec.components?.securitySchemes ?? spec.securityDefinitions ?? {}; |
| 77 | |
| 78 | const endpoints: EndpointInfo[] = []; |
| 79 | const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']; |
| 80 | for (const [p, item] of Object.entries<any>(spec.paths ?? {})) { |
| 81 | if (a.filter && !p.includes(a.filter)) continue; |
| 82 | for (const m of methods) { |
| 83 | const op = item?.[m]; |
| 84 | if (!op) continue; |
| 85 | const params: ParamInfo[] = [...(item.parameters ?? []), ...(op.parameters ?? [])].map((pr: any) => ({ |
| 86 | name: pr.name, in: pr.in, required: !!pr.required, |
| 87 | type: schemaLabel(pr.schema ?? pr), |
| 88 | })); |
| 89 | let body: string | undefined; |
| 90 | if (op.requestBody) { |
| 91 | const content = op.requestBody.content ?? {}; |
| 92 | const first = content['application/json'] ?? Object.values(content)[0]; |
| 93 | body = schemaLabel((first as any)?.schema); |
| 94 | } else if (m === 'post' || m === 'put' || m === 'patch') { |
| 95 | // Swagger 2.0 carries body in parameters with in:'body'. |
| 96 | const b = params.find(pr => pr.in === 'body'); |
| 97 | if (b) body = b.type; |
| 98 | } |
| 99 | const responses = op.responses ?? {}; |
| 100 | const okKey = Object.keys(responses).find(k => k.startsWith('2')) ?? 'default'; |
| 101 | const okResp = responses[okKey]; |
| 102 | const okContent = okResp?.content?.['application/json'] ?? (okResp?.content ? Object.values(okResp.content)[0] : undefined); |
| 103 | const success = schemaLabel((okContent as any)?.schema ?? okResp?.schema); |
nothing calls this directly
no test coverage detected