swagger-digest 0.2.0 → 0.3.1
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.
- package/README.md +241 -133
- package/dist/{chunk-2NORV6KM.js → chunk-DJES77HX.js} +5 -1
- package/dist/{chunk-U7OLLL6J.js → chunk-MXTHI25H.js} +51 -7
- package/dist/cli.js +3 -2
- package/dist/index.d.ts +21 -1
- package/dist/index.js +4 -2
- package/dist/mcp/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,222 +1,337 @@
|
|
|
1
1
|
# swagger-digest
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/swagger-digest)
|
|
4
|
+
[](https://github.com/VladLegkowski/swagger-digest)
|
|
5
|
+
[](https://www.npmjs.com/package/swagger-digest)
|
|
6
|
+
[](https://modelcontextprotocol.io)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
**An API context engine for AI coding agents.** Turn large Swagger/OpenAPI specifications into compact, structured, searchable API context — as Markdown digests or through an on-demand MCP server.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
npx swagger-digest
|
|
9
|
-
```
|
|
10
|
+
A 600-operation spec like Stripe's is ~2M tokens of raw JSON — several times most models' context windows, and mostly noise even when it fits. swagger-digest reduces it to ~560k tokens of structured Markdown (benchmark below), and its MCP server lets your agent pull in only the endpoints and schemas it needs, when it needs them.
|
|
10
11
|
|
|
11
|
-
The
|
|
12
|
+
## The problem
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
- Real specifications contain hundreds of endpoints and thousands of lines of JSON/YAML.
|
|
15
|
+
- Pasting a raw spec into an AI coding agent burns context on boilerplate — descriptions repeated per operation, example payloads, schema trees.
|
|
16
|
+
- Finding the endpoints relevant to your task — and understanding their parameters and response shapes — means manually wading through the contract.
|
|
17
|
+
- Agents generating frontend code need accurate API contracts. Guessing field names from partial context produces broken integrations.
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
npx swagger-digest \
|
|
17
|
-
--url https://petstore.swagger.io/ \
|
|
18
|
-
--out-dir ./docs \
|
|
19
|
-
--filename petstore-digest.md
|
|
20
|
-
```
|
|
19
|
+
swagger-digest solves this at two levels: a **digest** (compact, complete, token-budgeted Markdown) and an **MCP server** (on-demand API context for agents).
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
## Two ways to use it
|
|
22
|
+
|
|
23
|
+
### Option 1: MCP server — recommended for AI coding agents
|
|
24
|
+
|
|
25
|
+
Run it as an [MCP](https://modelcontextprotocol.io) server and your agent explores APIs on its own: it calls the `swagger_digest` tool with a Swagger UI page or spec URL and receives a compact digest directly in its context — discovery, filtering and token budgets handled for it.
|
|
26
|
+
|
|
27
|
+
- **On-demand:** the agent fetches API context only when the task needs it.
|
|
28
|
+
- **Scoped:** filter to specific tags or paths so only relevant operations enter context.
|
|
29
|
+
- **Discovery built in:** point it at a Swagger UI page — it finds the spec automatically (initializer scripts, multi-spec dropdowns, common spec paths).
|
|
30
|
+
- **Ideal for ongoing development work**, where the agent revisits the same API repeatedly without you re-pasting documentation.
|
|
31
|
+
|
|
32
|
+
Install in your MCP client (stdio transport; the client spawns the process):
|
|
23
33
|
|
|
24
34
|
```bash
|
|
25
|
-
|
|
35
|
+
# OpenCode
|
|
36
|
+
opencode mcp add swagger-digest -- npx -y swagger-digest-mcp
|
|
26
37
|
```
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
```json
|
|
40
|
+
// JSON-configured clients (Claude Desktop, Cursor, …)
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"swagger-digest": {
|
|
44
|
+
"command": ["npx", "-y", "swagger-digest-mcp"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Tool arguments and environment variables are documented in [MCP configuration](#mcp-configuration).
|
|
51
|
+
|
|
52
|
+
### Option 2: Markdown digest
|
|
53
|
+
|
|
54
|
+
Generate a compact Markdown reference from any Swagger UI page or OpenAPI/Swagger spec — for documentation, sharing, or feeding API context into agents that don't use MCP.
|
|
29
55
|
|
|
30
56
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
# Interactive
|
|
58
|
+
npx swagger-digest
|
|
59
|
+
|
|
60
|
+
# Direct: fetch, digest, save
|
|
61
|
+
npx swagger-digest --url https://petstore.swagger.io/ --out-dir ./docs
|
|
62
|
+
|
|
63
|
+
# To stdout (pipes stay clean; progress goes to stderr)
|
|
64
|
+
npx swagger-digest --url https://petstore.swagger.io/ --stdout | pbcopy
|
|
65
|
+
|
|
66
|
+
# Keep only the operations you need
|
|
67
|
+
npx swagger-digest --url https://api.example.com/openapi.json \
|
|
68
|
+
--include-tag Subscriptions --path-prefix /v1/ --out-dir ./docs
|
|
33
69
|
```
|
|
34
70
|
|
|
35
|
-
|
|
71
|
+
Output is a structured Markdown document: endpoint index, per-tag operation details (auth, parameters, request/response bodies, examples), and TypeScript-like data type declarations. Save it next to your code, paste it into a PR, or drop it into your agent's context manually.
|
|
36
72
|
|
|
37
|
-
|
|
73
|
+
## Quick start
|
|
38
74
|
|
|
39
75
|
```bash
|
|
40
|
-
|
|
41
|
-
npx swagger-digest --url https://
|
|
76
|
+
# 1. Generate a digest from a public API (interactive prompts, saved to ~/Downloads)
|
|
77
|
+
npx swagger-digest --url https://petstore.swagger.io/
|
|
78
|
+
|
|
79
|
+
# 2. Or generate to stdout
|
|
80
|
+
npx swagger-digest --url https://petstore.swagger.io/ --stdout > petstore-digest.md
|
|
81
|
+
|
|
82
|
+
# 3. Register the MCP server (OpenCode example)
|
|
83
|
+
opencode mcp add swagger-digest -- npx -y swagger-digest-mcp
|
|
42
84
|
```
|
|
43
85
|
|
|
44
|
-
|
|
86
|
+
Requires Node.js 20+. Run `npx swagger-digest --help` for all options.
|
|
45
87
|
|
|
46
|
-
|
|
88
|
+
## Real-world example: building a subscriptions page
|
|
47
89
|
|
|
48
|
-
|
|
90
|
+
A React/TypeScript developer asks their AI coding agent:
|
|
49
91
|
|
|
50
|
-
|
|
92
|
+
> "Implement a subscription management page against our billing API — docs are at https://api.example.com/swagger-ui"
|
|
51
93
|
|
|
52
|
-
|
|
53
|
-
npx swagger-digest-mcp
|
|
54
|
-
```
|
|
94
|
+
**Without swagger-digest**, the agent either guesses the API contract or you paste thousands of lines of raw JSON into the conversation.
|
|
55
95
|
|
|
56
|
-
|
|
96
|
+
**With the MCP server**, the agent calls the tool itself:
|
|
57
97
|
|
|
58
98
|
```json
|
|
59
99
|
{
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
"command": ["npx", "-y", "swagger-digest-mcp"]
|
|
63
|
-
}
|
|
64
|
-
}
|
|
100
|
+
"url": "https://api.example.com/swagger-ui",
|
|
101
|
+
"include_tags": ["Subscriptions"]
|
|
65
102
|
}
|
|
66
103
|
```
|
|
67
104
|
|
|
68
|
-
|
|
105
|
+
and receives only the relevant slice — in this illustrative excerpt of digest output:
|
|
69
106
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
| `url` | string, required | Swagger UI page URL or direct OpenAPI spec URL |
|
|
73
|
-
| `spec_url` | string, optional | Explicit spec URL; overrides discovery from the page |
|
|
74
|
-
| `headers` | object, optional | Request headers (e.g. `Authorization`); sent only to the origin of `url` |
|
|
75
|
-
| `include_tags` | string[] | Keep only operations with these tags |
|
|
76
|
-
| `exclude_tags` | string[] | Drop operations with these tags |
|
|
77
|
-
| `path_prefix` | string | Keep only paths starting with this prefix |
|
|
78
|
-
| `max_tokens` | number | Trim the digest to an approximate token budget |
|
|
79
|
-
| `write_to_file` | object, optional | `{ directory, filename }` — save the digest instead of returning it inline |
|
|
107
|
+
```markdown
|
|
108
|
+
#### `GET /v1/subscriptions/{id}`
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
**Authentication:** Bearer
|
|
82
111
|
|
|
83
|
-
|
|
112
|
+
**Parameters**
|
|
84
113
|
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
114
|
+
- `id` (`path`, required) · `string` — Subscription identifier.
|
|
115
|
+
- `expand` (`query`, optional) · `string[]` — Fields to expand.
|
|
87
116
|
|
|
88
|
-
|
|
117
|
+
**Responses**
|
|
89
118
|
|
|
90
|
-
- **
|
|
91
|
-
- **No headless-browser fallback:** the optional Playwright fallback is disabled in the MCP server.
|
|
92
|
-
- Diagnostics go to stderr only; stdout carries protocol frames exclusively.
|
|
119
|
+
- **200 OK** — `Subscription` (application/json)
|
|
93
120
|
|
|
94
|
-
|
|
121
|
+
## Data types
|
|
95
122
|
|
|
96
|
-
|
|
123
|
+
export interface Subscription {
|
|
124
|
+
id?: string;
|
|
125
|
+
status?: "active" | "canceled" | "past_due";
|
|
126
|
+
current_period_end?: number;
|
|
127
|
+
items?: SubscriptionItems;
|
|
128
|
+
[key: string]: unknown;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
97
131
|
|
|
98
|
-
|
|
99
|
-
# Keep only operations tagged "Payments", plus everything under /invoices
|
|
100
|
-
npx swagger-digest --url https://api.example.com/openapi.json \
|
|
101
|
-
--include-tag Payments --path-prefix /payments/ --out-dir ./docs
|
|
132
|
+
From there the agent can: confirm the exact path, method and required parameters; read the `Subscription` schema and its referenced types; generate TypeScript types that match the contract; and implement the page without guessing. If the digest is too large, the agent re-calls with `path_prefix` or `max_tokens` to narrow it further.
|
|
102
133
|
|
|
103
|
-
|
|
104
|
-
npx swagger-digest --url https://api.example.com/openapi.json --exclude-tag Internal
|
|
134
|
+
## Benchmarks
|
|
105
135
|
|
|
106
|
-
|
|
107
|
-
npx swagger-digest --url https://api.example.com/openapi.json --max-tokens 50000
|
|
108
|
-
```
|
|
136
|
+
Ten public APIs, digest vs. raw spec JSON in an LLM context, ground truth from the parsed raw specs. Run in November 2026 against swagger-digest 0.2.0; token counts are heuristic estimates, not tokenizer-exact.
|
|
109
137
|
|
|
110
|
-
|
|
138
|
+
**7/10 wins · 100% operation coverage on every digest produced · 1.6–3.7× lighter · 1 tool call vs 1–5 fetch/guess attempts**
|
|
111
139
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
140
|
+
| Spec | Ops | Raw tokens | Digest tokens | Compression | Op coverage | Winner |
|
|
141
|
+
| ------------------- | ----- | ---------------------- | ----------------------- | ----------- | ----------- | ------- |
|
|
142
|
+
| Petstore v2 | 20 | 3,461 | 2,202 | 1.6× | 20/20 | 🟢 MCP |
|
|
143
|
+
| Petstore v3 | 19 | 4,277 | 2,624 | 1.6× | 19/19 | 🟢 MCP |
|
|
144
|
+
| Petstore v3 UI page | 19 | 4,277 (+4 URL guesses) | ❌ failed* | — | — | 🟡 Raw* |
|
|
145
|
+
| Azure Compute | 157 | 157,568 | 72,549 | 2.2× | 157/157 | 🟢 MCP |
|
|
146
|
+
| X/Twitter v2 | 259 | 333,985 | 105,719 | 3.2× | 259/259 | 🟢 MCP |
|
|
147
|
+
| Box | 297 | 453,200 | 162,829 | 2.8× | 297/297 | 🟢 MCP |
|
|
148
|
+
| Twilio | 197 | 469,913 | 155,709 | 3.0× | 197/197 | 🟢 MCP |
|
|
149
|
+
| Stripe | 612 | 2,079,211 | 564,308 | 3.7× | 612/612 | 🟢 MCP |
|
|
150
|
+
| Kubernetes | 1,172 | 1,085,939 | 1,341,885 (expanded!) | ❌ 0.8×* | 1172/1172 | 🔴 Raw |
|
|
151
|
+
| GitHub (full) | 1,272 | 3,551,639 | ❌ failed (14 MB fetch) | — | — | 🟡 Raw* |
|
|
116
152
|
|
|
117
|
-
|
|
153
|
+
> For its target use case — understanding an API in an LLM context — the digest wins on the spec class that actually matters (up to ~600 operations): 100% endpoint coverage, never truncated, one call, and 1.6–3.7× lighter. The bigger the spec, the bigger the win.
|
|
118
154
|
|
|
119
|
-
|
|
155
|
+
**Why token efficiency matters:** an agent's context window is shared between your codebase, the conversation, and every tool result. A raw spec that consumes 500k tokens crowds out everything else; the same API as a 150k-token digest leaves room to work — and smaller digests mean faster, cheaper, more focused generations.
|
|
120
156
|
|
|
121
|
-
|
|
157
|
+
\* Raw's three "wins" carry heavy caveats: the Petstore UI-page case needed brute-forced candidate URLs (the digest's discovery has since been fixed for that class of pages); GitHub's 3.55M-token raw spec would truncate in any model's context anyway; and the Kubernetes expansion is now prevented by the default token cap described below.
|
|
122
158
|
|
|
123
|
-
|
|
159
|
+
### Known failure modes (and their mitigations)
|
|
124
160
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
161
|
+
| Case | What happens | Mitigation |
|
|
162
|
+
| ------------------------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| JS-only Swagger UI pages with dynamically computed spec URLs | Literal extraction finds nothing after `url:` | **Fixed in 0.3.0** (embedded spec-URL scan). Otherwise pass `spec_url` / `--spec` explicitly |
|
|
164
|
+
| Mega-specs (Kubernetes-class, ≥1M tokens) | Digest can _expand_ past raw size — descriptions inlined per operation | **Default in 0.3.0**: automatic 250k-token cap (opt out with `--no-auto-trim` / `auto_trim: false`). Explicit `max_tokens` always wins |
|
|
165
|
+
| Huge fetches (14 MB GitHub spec) | Fetch fails at the 10 MB response cap | Use a filtered direct spec URL |
|
|
129
166
|
|
|
130
|
-
|
|
167
|
+
Not every specification benefits: very large kitchen-sink specs are where compression can invert — handled by the default cap, but worth knowing.
|
|
131
168
|
|
|
132
|
-
|
|
133
|
-
npm install playwright
|
|
134
|
-
npx playwright install chromium
|
|
135
|
-
```
|
|
169
|
+
## Feature overview
|
|
136
170
|
|
|
137
|
-
|
|
171
|
+
**Ingestion and discovery**
|
|
138
172
|
|
|
139
|
-
|
|
173
|
+
- Fetches direct OpenAPI JSON/YAML URLs and Swagger UI pages (OpenAPI 2.0, 3.0, 3.1).
|
|
174
|
+
- Multi-strategy discovery: direct spec URL → page script configuration (`url:`, `urls:` dropdown, inline `spec:`, dynamically computed initializer URLs) → common spec paths (`/v3/api-docs`, `/swagger.json`, …) → optional headless-browser fallback.
|
|
175
|
+
- Multi-spec Swagger UI dropdowns: probes candidates in order, uses the first that parses, names the pick.
|
|
176
|
+
- External `$ref` bundling: references to other HTTP(S) documents are fetched and inlined under namespaced names (one level deep, at most 5 documents).
|
|
140
177
|
|
|
141
|
-
|
|
178
|
+
**Markdown generation**
|
|
142
179
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
180
|
+
- Endpoint index plus per-tag operation details: auth requirements, path/query/header parameters, request bodies, success/error responses, examples.
|
|
181
|
+
- TypeScript-like type declarations for reachable schemas; `allOf`/`oneOf`/`anyOf`, enums, arrays, nullable fields, `additionalProperties` get compact representations.
|
|
182
|
+
- Unknown schema constructs render as `unknown` instead of failing the digest.
|
|
183
|
+
- Synthetic examples generated from schemas when none are declared, clearly marked as illustrative.
|
|
184
|
+
|
|
185
|
+
**MCP server**
|
|
186
|
+
|
|
187
|
+
- Single `swagger_digest` tool over stdio; digest returned inline in the tool result with metadata (retrieval method, operation/model counts, token estimate), or written to a file.
|
|
188
|
+
- Same discovery and filtering pipeline as the CLI; optional Playwright fallback disabled.
|
|
189
|
+
- Private-network (SSRF) guard on by default, because URLs come from the model.
|
|
190
|
+
|
|
191
|
+
**Filtering and token management**
|
|
192
|
+
|
|
193
|
+
- Filter by tags (`include_tags`/`exclude_tags`) and path prefix, before rendering.
|
|
194
|
+
- `max_tokens` budget with deterministic trim order; `## Trimmed` note records exactly what was removed.
|
|
195
|
+
- Automatic 250,000-token default cap for unbounded digests.
|
|
196
|
+
|
|
197
|
+
**Authentication and remote access**
|
|
198
|
+
|
|
199
|
+
- Origin-scoped custom headers and cookies: sent only to the origin you supply, never logged, never written to the digest.
|
|
200
|
+
- `SWAGGER_DIGEST_HEADER` environment variable for secrets outside the command line.
|
|
201
|
+
- Hardened fetching: response size cap (10 MB), redirect limit (5), timeouts, optional private-network blocking.
|
|
202
|
+
|
|
203
|
+
## Configuration and advanced usage
|
|
149
204
|
|
|
150
|
-
|
|
205
|
+
### CLI reference
|
|
206
|
+
|
|
207
|
+
| Option | Description |
|
|
208
|
+
| ------------------------------ | --------------------------------------------------------- |
|
|
209
|
+
| `-u, --url <url>` | Swagger UI page URL or direct OpenAPI spec URL |
|
|
210
|
+
| `-d, --out-dir <directory>` | Destination directory for the digest file |
|
|
211
|
+
| `-f, --filename <filename>` | Output filename (defaults to `<api-title>-digest.md`) |
|
|
212
|
+
| `--stdout` | Print to stdout instead of writing a file |
|
|
213
|
+
| `--spec <url>` | Explicit spec URL; overrides all discovery |
|
|
214
|
+
| `--include-tag <tag>` | Keep only operations with this tag (repeatable) |
|
|
215
|
+
| `--exclude-tag <tag>` | Drop operations with this tag (repeatable) |
|
|
216
|
+
| `--path-prefix <prefix>` | Keep only operations whose path starts with this prefix |
|
|
217
|
+
| `--max-tokens <n>` | Trim the digest until the token estimate fits this budget |
|
|
218
|
+
| `--no-auto-trim` | Disable the automatic 250,000-token default cap |
|
|
219
|
+
| `--max-bytes <bytes>` | Maximum response size (default 10,000,000) |
|
|
220
|
+
| `-H, --header <"Name: value">` | Custom request header, repeatable; origin-scoped |
|
|
221
|
+
| `--cookie <string>` | Convenience for a Cookie header; origin-scoped |
|
|
222
|
+
| `--no-browser` | Disable the optional Playwright fallback |
|
|
223
|
+
|
|
224
|
+
### MCP configuration
|
|
225
|
+
|
|
226
|
+
Register the server (stdio transport):
|
|
151
227
|
|
|
152
228
|
```bash
|
|
153
|
-
|
|
229
|
+
# OpenCode
|
|
230
|
+
opencode mcp add swagger-digest -- npx -y swagger-digest-mcp
|
|
154
231
|
```
|
|
155
232
|
|
|
156
|
-
|
|
233
|
+
```json
|
|
234
|
+
// JSON-configured clients
|
|
235
|
+
{
|
|
236
|
+
"mcpServers": {
|
|
237
|
+
"swagger-digest": {
|
|
238
|
+
"command": ["npx", "-y", "swagger-digest-mcp"]
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The `swagger_digest` tool:
|
|
245
|
+
|
|
246
|
+
| Argument | Type | Description |
|
|
247
|
+
| --------------- | ---------------- | ------------------------------------------------------------ |
|
|
248
|
+
| `url` | string, required | Swagger UI page URL or direct OpenAPI spec URL |
|
|
249
|
+
| `spec_url` | string | Explicit spec URL; overrides discovery |
|
|
250
|
+
| `headers` | object | Request headers; sent only to the origin of `url` |
|
|
251
|
+
| `include_tags` | string[] | Keep only operations with these tags |
|
|
252
|
+
| `exclude_tags` | string[] | Drop operations with these tags |
|
|
253
|
+
| `path_prefix` | string | Keep only paths starting with this prefix |
|
|
254
|
+
| `max_tokens` | number | Trim the digest to an approximate token budget |
|
|
255
|
+
| `auto_trim` | boolean | Default-cap digests without `max_tokens` (default true) |
|
|
256
|
+
| `write_to_file` | object | `{ directory, filename }` — save instead of returning inline |
|
|
257
|
+
|
|
258
|
+
MCP environment variables:
|
|
259
|
+
|
|
260
|
+
- `SWAGGER_DIGEST_HEADER` — newline-separated `Name: value` pairs, merged with (and overridden by) the call's `headers`. Keeps secrets out of the conversation.
|
|
261
|
+
- `SWAGGER_DIGEST_ALLOW_PRIVATE=1` — opt out of the private-network guard; only for hosts you trust, e.g. internal corporate APIs.
|
|
262
|
+
|
|
263
|
+
MCP-specific behavior: URLs resolving to loopback/private/link-local addresses are rejected by default (MCP clients supply URLs on the model's behalf); diagnostics go to stderr, protocol frames to stdout.
|
|
264
|
+
|
|
265
|
+
### Authentication headers (CLI)
|
|
157
266
|
|
|
158
267
|
```bash
|
|
268
|
+
npx swagger-digest --url https://api.example.com/swagger-ui \
|
|
269
|
+
--header "Authorization: Bearer eyJhbGciOi..." --out-dir ./docs
|
|
270
|
+
|
|
271
|
+
# or a session cookie
|
|
272
|
+
npx swagger-digest --url https://api.example.com/swagger-ui --cookie "session=abc123"
|
|
273
|
+
|
|
274
|
+
# or the environment variable (-H flags win on conflicts)
|
|
159
275
|
export SWAGGER_DIGEST_HEADER=$'Authorization: Bearer eyJ…\nX-Tenant: acme'
|
|
160
276
|
npx swagger-digest --url https://api.example.com/swagger-ui
|
|
161
277
|
```
|
|
162
278
|
|
|
163
|
-
**Scoping and safety:**
|
|
164
|
-
|
|
165
|
-
- Custom headers are sent **only** to the origin of the URL you supply. If the discovered spec, a redirect, or a Swagger UI script lives on a different origin, that request is made without your headers and a warning naming the other origin is printed to stderr.
|
|
166
|
-
- In the headless-browser fallback, headers are injected per-request (only for your origin) — they never reach CDNs or third-party scripts loaded by the page.
|
|
167
|
-
- Header values are never logged, written to the digest, or included in error messages.
|
|
168
|
-
- ⚠️ **Shell history:** secrets passed with `-H` can end up in your shell history file (`~/.bash_history`, `~/.zsh_history`). Prefer the `SWAGGER_DIGEST_HEADER` environment variable, or prefix the command with a space if your shell is configured to ignore leading-space entries.
|
|
279
|
+
**Scoping and safety:** custom headers are sent **only** to the origin of the URL you supply; cross-origin requests (redirects, discovered specs on other origins) proceed without them and produce a warning naming the other origin. In the headless-browser fallback, headers are injected per-request for your origin only — they never reach CDNs or third-party scripts. Header values are never logged, written to the digest, or included in error messages. Secrets passed with `-H` can end up in shell history — prefer the environment variable.
|
|
169
280
|
|
|
170
|
-
###
|
|
281
|
+
### Programmatic API
|
|
171
282
|
|
|
172
|
-
|
|
283
|
+
```ts
|
|
284
|
+
import { fetchOpenApiSpec, transformOpenApiToMarkdown, estimateTokens } from 'swagger-digest';
|
|
173
285
|
|
|
174
|
-
|
|
175
|
-
|
|
286
|
+
const { spec, sourceUrl } = await fetchOpenApiSpec('https://api.example.com/openapi.json', {
|
|
287
|
+
blockPrivateNetworks: true, // recommended when URLs come from untrusted input
|
|
288
|
+
headers: { Authorization: 'Bearer …' },
|
|
289
|
+
});
|
|
290
|
+
const markdown = transformOpenApiToMarkdown(spec, sourceUrl, {
|
|
291
|
+
includeTags: ['Subscriptions'],
|
|
292
|
+
maxTokens: 50_000,
|
|
293
|
+
onWarning: console.warn,
|
|
294
|
+
});
|
|
295
|
+
console.log(estimateTokens(markdown).models.claude.tokens);
|
|
176
296
|
```
|
|
177
297
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
## Security notes
|
|
298
|
+
`transformOpenApiToMarkdown` options: `includeTags`, `excludeTags`, `pathPrefix`, `maxTokens`, `autoTrim` (default true), `onWarning`. `fetchOpenApiSpec` options include `specUrl`, `headers`, `maxBytes`, `blockPrivateNetworks`, `useBrowserFallback`.
|
|
181
299
|
|
|
182
|
-
|
|
300
|
+
### Trimming order
|
|
183
301
|
|
|
184
|
-
|
|
302
|
+
When a budget applies (explicit `--max_tokens` or the default cap), the digest is trimmed in this fixed order, re-checked after each step: 1) drop declared example payloads → 2) shorten long descriptions → 3) collapse parameter/response detail per tag (the tag appearing last in the document is collapsed first) → 4) keep only the Quick index. A `## Trimmed` note records exactly which steps applied; schemas referenced only by removed operations are dropped. The estimator is a heuristic — treat budgets as a guide, not a guarantee.
|
|
185
303
|
|
|
186
|
-
|
|
304
|
+
## Supported specifications and limitations
|
|
187
305
|
|
|
188
|
-
|
|
306
|
+
**Supported:** OpenAPI 2.0, 3.0 and 3.1, in JSON or YAML, fetched over HTTP(S) from direct spec URLs or discovered through Swagger UI pages.
|
|
189
307
|
|
|
190
|
-
|
|
308
|
+
**Limitations:**
|
|
191
309
|
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
310
|
+
- The tool downloads specs exposed by the supplied server. It does not bypass authentication, browser access controls, or network restrictions. Ordinary HTTP fetches use Node.js networking — they do not reuse a browser's VPN extension, cookies, or signed-in session. If a page works in Chrome but not here, test `curl -v -L '<url>'` from the terminal; if that fails too, route the terminal through your VPN/proxy.
|
|
311
|
+
- External `$ref` documents are bundled best-effort (one level deep, at most 5 documents); unreachable references degrade to named type references and the digest still succeeds.
|
|
312
|
+
- Very large specs: fetches are capped at 10 MB by default (`--max-bytes`), and digests are capped at ~250k estimated tokens by default (see trimming above).
|
|
313
|
+
- A generated digest is a compact developer reference, not a replacement for the source OpenAPI contract.
|
|
196
314
|
|
|
197
|
-
|
|
315
|
+
**Security notes:** the tool fetches the URL you supply, same-origin Swagger UI initializer/config scripts, the spec URL referenced by that page, and a short list of common spec paths on the same origin. Cookies and Authorization headers are never sent automatically. If you call `fetchOpenApiSpec` from a server that accepts user-supplied URLs, enable `blockPrivateNetworks: true` — without it a caller can point the tool at cloud-metadata or other internal addresses (SSRF); the check rejects loopback, link-local, private, and unique-local addresses after redirects, but has a documented DNS-rebinding limitation — for hostile multi-tenant input, use a dedicated egress proxy. The MCP server enables this guard by default.
|
|
198
316
|
|
|
199
|
-
## Development
|
|
317
|
+
## Development and contribution
|
|
200
318
|
|
|
201
319
|
```bash
|
|
202
320
|
npm install
|
|
203
321
|
npm run build
|
|
204
|
-
npm test
|
|
322
|
+
npm test # typecheck + test suite (runs offline against fixtures)
|
|
205
323
|
npm run lint
|
|
206
|
-
npm link
|
|
207
|
-
swagger-digest
|
|
324
|
+
npm link # then: swagger-digest / swagger-digest-mcp
|
|
208
325
|
```
|
|
209
326
|
|
|
210
|
-
|
|
327
|
+
Manual check against a live API:
|
|
211
328
|
|
|
212
329
|
```bash
|
|
213
330
|
npm run build
|
|
214
331
|
node dist/cli.js --url https://petstore.swagger.io/ --out-dir ./tmp --filename petstore-digest.md
|
|
215
332
|
```
|
|
216
333
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
## Project layout
|
|
334
|
+
Project layout:
|
|
220
335
|
|
|
221
336
|
```text
|
|
222
337
|
src/
|
|
@@ -231,13 +346,6 @@ src/
|
|
|
231
346
|
digest.ts Tool handler: fetch → transform → tool result
|
|
232
347
|
```
|
|
233
348
|
|
|
234
|
-
## Limitations
|
|
235
|
-
|
|
236
|
-
- The tool downloads specs exposed by the supplied server. It does not bypass authentication, browser access controls, or network restrictions.
|
|
237
|
-
- External `$ref` documents are bundled best-effort: references to other HTTP(S) documents are fetched (one level deep, at most 5 documents) and their schemas inlined under namespaced names such as `meta_SharedMeta`. If an external document is unreachable or unparsable, the reference degrades to a named type reference and the digest still succeeds.
|
|
238
|
-
- Declared request/response examples are preserved. When an example is absent but a schema exists, a representative JSON example is generated from that schema and clearly marked as illustrative rather than spec-authored. The transformer includes the primary success response and applicable common error responses to balance useful payload context against digest size.
|
|
239
|
-
- A generated digest is a compact developer reference, not a replacement for the source OpenAPI contract.
|
|
240
|
-
|
|
241
349
|
## License
|
|
242
350
|
|
|
243
351
|
MIT
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
parseHeaderPairs,
|
|
7
7
|
transformOpenApiToMarkdown,
|
|
8
8
|
writeDigest
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MXTHI25H.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp/digest.ts
|
|
12
12
|
var TOKEN_HINT_THRESHOLD = 8e4;
|
|
@@ -69,6 +69,7 @@ async function runSwaggerDigest(args, options = {}) {
|
|
|
69
69
|
excludeTags: args.exclude_tags,
|
|
70
70
|
pathPrefix: args.path_prefix,
|
|
71
71
|
maxTokens: args.max_tokens,
|
|
72
|
+
autoTrim: args.auto_trim !== false,
|
|
72
73
|
onWarning: warn
|
|
73
74
|
});
|
|
74
75
|
const operationCount = countDigestOperations(markdown);
|
|
@@ -128,6 +129,9 @@ function createMcpServer(options = {}) {
|
|
|
128
129
|
max_tokens: z.number().int().positive().optional().describe(
|
|
129
130
|
"Trim the digest until an approximate token estimate fits this budget (heuristic, not tokenizer-accurate)"
|
|
130
131
|
),
|
|
132
|
+
auto_trim: z.boolean().optional().describe(
|
|
133
|
+
"Auto-trim digests exceeding the 250,000-token default budget when max_tokens is not given. Defaults to true."
|
|
134
|
+
),
|
|
131
135
|
write_to_file: z.object({ directory: z.string(), filename: z.string() }).optional().describe("Save the digest to a file instead of returning it inline")
|
|
132
136
|
}
|
|
133
137
|
},
|
|
@@ -586,6 +586,35 @@ function extractSpecUrlsFromScripts(scripts, pageUrl) {
|
|
|
586
586
|
}
|
|
587
587
|
return found;
|
|
588
588
|
}
|
|
589
|
+
function extractEmbeddedSpecUrlsFromScripts(scripts, pageUrl) {
|
|
590
|
+
const found = [];
|
|
591
|
+
const push = (raw) => {
|
|
592
|
+
try {
|
|
593
|
+
const url = new URL(raw);
|
|
594
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return;
|
|
595
|
+
if (!/\.(?:json|ya?ml)$/i.test(url.pathname)) return;
|
|
596
|
+
if (!/(?:openapi|swagger|api[-._]?docs)/i.test(url.pathname)) return;
|
|
597
|
+
const resolved = url.toString();
|
|
598
|
+
if (!found.includes(resolved)) found.push(resolved);
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
for (const script of scripts) {
|
|
603
|
+
for (const match of script.matchAll(/https?:\/\/[^\s"'`<>(){}[\]\\,=]+/gi)) {
|
|
604
|
+
push(match[0]);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const sameOrigin = found.filter((url) => {
|
|
608
|
+
try {
|
|
609
|
+
return new URL(url).origin === pageUrl.origin;
|
|
610
|
+
} catch {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
const crossOrigin = found.filter((url) => !sameOrigin.includes(url));
|
|
615
|
+
return [...sameOrigin, ...crossOrigin].slice(0, MAX_EMBEDDED_SPEC_URLS);
|
|
616
|
+
}
|
|
617
|
+
var MAX_EMBEDDED_SPEC_URLS = 8;
|
|
589
618
|
function findObjectLiteralArrayAfterKey(source, key) {
|
|
590
619
|
const keyPattern = new RegExp(`\\b${key}\\s*:`);
|
|
591
620
|
const match = keyPattern.exec(source);
|
|
@@ -703,7 +732,7 @@ async function fetchExternalConfigScripts(srcs, pageUrl, ctx, errors) {
|
|
|
703
732
|
try {
|
|
704
733
|
const response = await fetchText(candidate.toString(), ctx);
|
|
705
734
|
scripts.push(response.text);
|
|
706
|
-
if (extractSpecUrlFromScripts(scripts, pageUrl) || scripts.some((script) => findObjectLiteralAfterKey(script, "spec"))) {
|
|
735
|
+
if (extractSpecUrlFromScripts(scripts, pageUrl) || extractEmbeddedSpecUrlsFromScripts(scripts, pageUrl).length > 0 || scripts.some((script) => findObjectLiteralAfterKey(script, "spec"))) {
|
|
707
736
|
break;
|
|
708
737
|
}
|
|
709
738
|
} catch (error) {
|
|
@@ -983,6 +1012,13 @@ async function fetchOpenApiSpec(input, options = {}) {
|
|
|
983
1012
|
const externalUrls = extractSpecUrls(externalScripts, pageUrl).filter((url) => !inlineUrls.includes(url));
|
|
984
1013
|
const fromExternal = await probeSpecUrls([...inlineUrls, ...externalUrls], ctx, recordFailure, noteMultiple);
|
|
985
1014
|
if (fromExternal) return fromExternal;
|
|
1015
|
+
const triedUrls = /* @__PURE__ */ new Set([...inlineUrls, ...externalUrls]);
|
|
1016
|
+
const embeddedUrls = [
|
|
1017
|
+
...extractEmbeddedSpecUrlsFromScripts(scripts, pageUrl),
|
|
1018
|
+
...extractEmbeddedSpecUrlsFromScripts(externalScripts, pageUrl)
|
|
1019
|
+
].filter((url) => !triedUrls.has(url));
|
|
1020
|
+
const fromEmbedded = await probeSpecUrls(embeddedUrls, ctx, recordFailure);
|
|
1021
|
+
if (fromEmbedded) return fromEmbedded;
|
|
986
1022
|
for (const script of scripts) {
|
|
987
1023
|
const literal = findObjectLiteralAfterKey(script, "spec");
|
|
988
1024
|
if (!literal) continue;
|
|
@@ -1904,6 +1940,7 @@ function normalizeInlinePayloadSchemas(spec) {
|
|
|
1904
1940
|
}
|
|
1905
1941
|
|
|
1906
1942
|
// src/transformer/index.ts
|
|
1943
|
+
var DEFAULT_MAX_TOKENS = 25e4;
|
|
1907
1944
|
var HTTP_METHODS2 = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
|
|
1908
1945
|
function tagsOfEndpoint(endpoint) {
|
|
1909
1946
|
const tags = endpoint.operation.tags;
|
|
@@ -2076,16 +2113,22 @@ function transformOpenApiToMarkdown(spec, sourceUrl, options = {}) {
|
|
|
2076
2113
|
collapsedTags: /* @__PURE__ */ new Set(),
|
|
2077
2114
|
indexOnly: false
|
|
2078
2115
|
};
|
|
2079
|
-
if (options.maxTokens === void 0) {
|
|
2080
|
-
return renderDigest(normalizedSpec, sourceUrl, endpoints, tags, fullStage);
|
|
2081
|
-
}
|
|
2082
2116
|
const worst = (markdown2) => {
|
|
2083
2117
|
const report = estimateTokens(markdown2);
|
|
2084
2118
|
return Math.max(...Object.values(report.models).map((model) => model.tokens));
|
|
2085
2119
|
};
|
|
2086
|
-
const
|
|
2120
|
+
const effectiveMaxTokens = options.maxTokens ?? (options.autoTrim === false ? void 0 : DEFAULT_MAX_TOKENS);
|
|
2121
|
+
if (effectiveMaxTokens === void 0) {
|
|
2122
|
+
return renderDigest(normalizedSpec, sourceUrl, endpoints, tags, fullStage);
|
|
2123
|
+
}
|
|
2124
|
+
const fits = (markdown2) => worst(markdown2) <= effectiveMaxTokens;
|
|
2087
2125
|
const full = renderDigest(normalizedSpec, sourceUrl, endpoints, tags, fullStage);
|
|
2088
2126
|
if (fits(full)) return full;
|
|
2127
|
+
if (options.maxTokens === void 0) {
|
|
2128
|
+
options.onWarning?.(
|
|
2129
|
+
`Untrimmed digest \u2248 ${worst(full).toLocaleString()} tokens (estimated), over the ${DEFAULT_MAX_TOKENS.toLocaleString()} default budget; auto-trimming. Pass maxTokens to control the budget or autoTrim: false to disable.`
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2089
2132
|
const stages = buildTrimStages(endpoints, tags);
|
|
2090
2133
|
for (let i = 1; i < stages.length; i += 1) {
|
|
2091
2134
|
const stage = stages[i];
|
|
@@ -2094,14 +2137,14 @@ function transformOpenApiToMarkdown(spec, sourceUrl, options = {}) {
|
|
|
2094
2137
|
return appendTrimNote(
|
|
2095
2138
|
markdown2,
|
|
2096
2139
|
stages.slice(1, i + 1).map((entry) => entry.action).filter((action) => Boolean(action)),
|
|
2097
|
-
|
|
2140
|
+
effectiveMaxTokens
|
|
2098
2141
|
);
|
|
2099
2142
|
}
|
|
2100
2143
|
}
|
|
2101
2144
|
const last = stages[stages.length - 1];
|
|
2102
2145
|
const markdown = renderDigest(normalizedSpec, sourceUrl, endpoints, tags, last);
|
|
2103
2146
|
const allActions = stages.slice(1).map((stage) => stage.action).filter((action) => Boolean(action));
|
|
2104
|
-
return appendTrimNote(markdown, allActions,
|
|
2147
|
+
return appendTrimNote(markdown, allActions, effectiveMaxTokens);
|
|
2105
2148
|
}
|
|
2106
2149
|
|
|
2107
2150
|
// src/headers.ts
|
|
@@ -2196,6 +2239,7 @@ export {
|
|
|
2196
2239
|
parseSpecText,
|
|
2197
2240
|
fetchOpenApiSpec,
|
|
2198
2241
|
estimateTokens,
|
|
2242
|
+
DEFAULT_MAX_TOKENS,
|
|
2199
2243
|
transformOpenApiToMarkdown,
|
|
2200
2244
|
parseHeaderArgument,
|
|
2201
2245
|
parseHeaderPairs,
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
parseHeaderPairs,
|
|
11
11
|
transformOpenApiToMarkdown,
|
|
12
12
|
writeDigest
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MXTHI25H.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
16
16
|
import { input } from "@inquirer/prompts";
|
|
@@ -61,7 +61,7 @@ async function main() {
|
|
|
61
61
|
"Custom request header, repeatable. Sent only to the URL you supplied.",
|
|
62
62
|
collectOption,
|
|
63
63
|
[]
|
|
64
|
-
).option("--cookie <string>", "Convenience for a Cookie request header (same-origin scoping applies)").option("--include-tag <tag>", "Keep only operations with this tag, repeatable", collectOption, []).option("--exclude-tag <tag>", "Drop operations with this tag, repeatable", collectOption, []).option("--path-prefix <prefix>", "Keep only operations whose path starts with this prefix").option("--max-tokens <n>", "Trim the digest until the approximate token estimate fits this budget").option("--spec <url>", "Explicit OpenAPI spec URL; overrides discovery from the Swagger UI page").option("--stdout", "Print the digest to standard output instead of writing a file").addHelpText(
|
|
64
|
+
).option("--cookie <string>", "Convenience for a Cookie request header (same-origin scoping applies)").option("--include-tag <tag>", "Keep only operations with this tag, repeatable", collectOption, []).option("--exclude-tag <tag>", "Drop operations with this tag, repeatable", collectOption, []).option("--path-prefix <prefix>", "Keep only operations whose path starts with this prefix").option("--max-tokens <n>", "Trim the digest until the approximate token estimate fits this budget").option("--no-auto-trim", "Disable the automatic 250,000-token default cap for digests without --max-tokens").option("--spec <url>", "Explicit OpenAPI spec URL; overrides discovery from the Swagger UI page").option("--stdout", "Print the digest to standard output instead of writing a file").addHelpText(
|
|
65
65
|
"after",
|
|
66
66
|
[
|
|
67
67
|
"",
|
|
@@ -132,6 +132,7 @@ async function main() {
|
|
|
132
132
|
excludeTags: options.excludeTag,
|
|
133
133
|
pathPrefix: options.pathPrefix,
|
|
134
134
|
maxTokens: parseMaxTokens(options.maxTokens),
|
|
135
|
+
autoTrim: options.autoTrim !== false,
|
|
135
136
|
onWarning: (message) => console.error(chalk.yellow(`Warning: ${message}`))
|
|
136
137
|
});
|
|
137
138
|
spinner.succeed(`Parsed ${title} (${result.retrievalMethod})`);
|
package/dist/index.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ interface McpDigestArgs {
|
|
|
57
57
|
path_prefix?: string;
|
|
58
58
|
/** Trim the digest until the approximate token estimate fits this budget. */
|
|
59
59
|
max_tokens?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Auto-trim digests exceeding the 250,000-token default budget when
|
|
62
|
+
* max_tokens is not given. Defaults to true.
|
|
63
|
+
*/
|
|
64
|
+
auto_trim?: boolean;
|
|
60
65
|
/** Save the digest to a file instead of returning it inline. */
|
|
61
66
|
write_to_file?: {
|
|
62
67
|
directory: string;
|
|
@@ -114,9 +119,24 @@ interface TransformOptions {
|
|
|
114
119
|
* budget. The estimator is not tokenizer-accurate; treat the budget as a guide.
|
|
115
120
|
*/
|
|
116
121
|
maxTokens?: number;
|
|
122
|
+
/**
|
|
123
|
+
* When no explicit `maxTokens` is given, cap the digest at
|
|
124
|
+
* {@link DEFAULT_MAX_TOKENS} estimated tokens by running the same trim
|
|
125
|
+
* pipeline, warning when it engages. Defaults to true. Mega-specs
|
|
126
|
+
* (Kubernetes-class) otherwise produce digests *larger* than the raw spec.
|
|
127
|
+
*/
|
|
128
|
+
autoTrim?: boolean;
|
|
117
129
|
/** Receives non-fatal notices, e.g. unknown tag names. */
|
|
118
130
|
onWarning?: (message: string) => void;
|
|
119
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Default token budget applied when `maxTokens` is undefined and `autoTrim`
|
|
134
|
+
* is not disabled. Chosen from benchmark data: large real-world specs
|
|
135
|
+
* (Stripe-class, ~600 operations) digest to ~560k tokens untrimmed, while
|
|
136
|
+
* kitchen-sink specs expand past their raw size — 250k keeps the largest
|
|
137
|
+
* useful full-detail digests intact and trims only true outliers.
|
|
138
|
+
*/
|
|
139
|
+
declare const DEFAULT_MAX_TOKENS = 250000;
|
|
120
140
|
/** Convert an OpenAPI 2.0, 3.0, or 3.1 document to a compact Markdown digest. */
|
|
121
141
|
declare function transformOpenApiToMarkdown(spec: JsonObject, sourceUrl?: string, options?: TransformOptions): string;
|
|
122
142
|
|
|
@@ -153,5 +173,5 @@ declare function writeDigest(options: {
|
|
|
153
173
|
filename: string;
|
|
154
174
|
}): Promise<string>;
|
|
155
175
|
|
|
156
|
-
export { buildRequestHeaders, createMcpServer, defaultFilename, defaultOutputDirectory, estimateTokens, fetchOpenApiSpec, isOpenApiSpec, normalizeFilename, parseSpecText, resolveOutputDirectory, runSwaggerDigest, toTypeScriptIdentifier, transformOpenApiToMarkdown, writeDigest };
|
|
176
|
+
export { DEFAULT_MAX_TOKENS, buildRequestHeaders, createMcpServer, defaultFilename, defaultOutputDirectory, estimateTokens, fetchOpenApiSpec, isOpenApiSpec, normalizeFilename, parseSpecText, resolveOutputDirectory, runSwaggerDigest, toTypeScriptIdentifier, transformOpenApiToMarkdown, writeDigest };
|
|
157
177
|
export type { DigestServerOptions, DigestTokenReport, FetchOptions, FetchResult, JsonObject, McpDigestArgs, ModelTokenEstimate, TransformOptions };
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
buildRequestHeaders,
|
|
3
3
|
createMcpServer,
|
|
4
4
|
runSwaggerDigest
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DJES77HX.js";
|
|
6
6
|
import {
|
|
7
|
+
DEFAULT_MAX_TOKENS,
|
|
7
8
|
defaultFilename,
|
|
8
9
|
defaultOutputDirectory,
|
|
9
10
|
estimateTokens,
|
|
@@ -15,8 +16,9 @@ import {
|
|
|
15
16
|
toTypeScriptIdentifier,
|
|
16
17
|
transformOpenApiToMarkdown,
|
|
17
18
|
writeDigest
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-MXTHI25H.js";
|
|
19
20
|
export {
|
|
21
|
+
DEFAULT_MAX_TOKENS,
|
|
20
22
|
buildRequestHeaders,
|
|
21
23
|
createMcpServer,
|
|
22
24
|
defaultFilename,
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createMcpServer
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-DJES77HX.js";
|
|
5
|
+
import "../chunk-MXTHI25H.js";
|
|
6
6
|
|
|
7
7
|
// src/mcp/index.ts
|
|
8
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|