Skip to content

Commit 28302ff

Browse files
author
hnkatze
committed
feat(tools): add usage_guide tool and tighten tool descriptions
- New usage_guide tool (no params) returns an embedded guide for the LLM: workflow, token-saving rules, output notation ($ref/required/depth), and concrete example call sequences. Lets the server self-document when the client lacks CLAUDE.md/system-prompt context. - Embed the guide via go:embed (internal/tools/llm_guide.md). - Shorten all tool descriptions (~40% less) to reduce fixed per-request cost; get_endpoint/get_schema now explain $ref(Name) and resolve_depth. - Update README: tools table, LLM Context Guide, design decisions.
1 parent 4f8c7a2 commit 28302ff

4 files changed

Lines changed: 188 additions & 49 deletions

File tree

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ All output is **token-optimized by default** using TOON format (~40% fewer token
2929
| `fetch_spec` | Download and cache a spec. Returns title, version, endpoint/tag/schema counts. Supports `refresh=true`. |
3030
| `analyze_tags` | Tag summary with endpoint counts and method breakdown. **Start here** to understand the API. |
3131
| `list_endpoints` | List endpoints with filters (tag, method, path pattern). Auto-limited to 50 results. |
32-
| `get_endpoint` | Full detail for one endpoint — params, request body, responses, resolved schemas. |
33-
| `get_schema` | Get a named schema with all nested `$ref` fully resolved. |
32+
| `get_endpoint` | Full detail for one endpoint — params, request body, responses, resolved schemas (field descriptions inline; repeats shown as `$ref(Name)`). |
33+
| `get_schema` | Get a named schema with nested `$ref` resolved. Use to expand a `$ref(Name)` seen in `get_endpoint`. |
3434
| `search_spec` | Full-text search across paths, summaries, operation IDs, parameters, and body properties. Auto-limited to 50. |
3535
| `diff_endpoints` | Compare two spec versions. Shows added, removed, and changed endpoints. |
3636
| `spec_status` | Check cache status (memory/disk), fingerprint, age, ETag. No HTTP requests. |
3737
| `refresh_spec` | Force-refresh a cached spec. Returns change detection via fingerprint comparison. |
3838
| `generate_types` | Generate TypeScript interfaces or Go structs from an endpoint or named schema. |
39+
| `usage_guide` | Return this server's workflow, token-saving rules, output notation, and example call sequences. The LLM can call it to self-orient when it lacks setup context. |
3940

4041
### Recommended Workflow
4142

@@ -54,6 +55,8 @@ All tools accept `format`: `toon` (default, compact) or `json`.
5455

5556
`list_endpoints` and `search_spec` also accept `limit` (default: 50, 0 = unlimited).
5657

58+
`get_endpoint` and `get_schema` accept `resolve_depth` (0-10, default: 3). Raise it for deeply nested models; lower it (or 0 = names only) to cut tokens further.
59+
5760
## Installation
5861

5962
### Option 1: Go Install (requires Go 1.25+)
@@ -195,11 +198,15 @@ When working with external APIs via swagger-mcp:
195198
- If the API spec has changed, use `refresh_spec` to get fresh data
196199
- Default output is TOON format (compact, token-efficient) — use format=json only when needed
197200
- Use `generate_types` with language=typescript or language=go instead of manually translating schemas
201+
- Schemas show field descriptions inline (`field*: type — description`); a `*` marks a required field
202+
- A repeated schema appears once, then as `$ref(Name)` — call `get_schema Name` only if you need its fields expanded
203+
- Schemas resolve 3 levels deep by default; pass `resolve_depth` up to 10 for deeper models
198204

199205
### Anti-patterns
200206
- Don't call `list_endpoints` without filters on large APIs (wastes tokens)
201207
- Don't call `get_endpoint` for every endpoint — narrow down with tags/search first
202208
- Don't re-fetch specs that are already cached — use `spec_status` to check
209+
- Don't expand every `$ref(Name)` — only fetch the ones whose fields you actually need
203210
```
204211

205212
For **Cursor**, add the same content to `.cursorrules`. For **Windsurf** or other MCP clients, add it to your system prompt or project instructions file.
@@ -344,10 +351,12 @@ internal/
344351
### Key Design Decisions
345352

346353
- **Token-first defaults** — TOON format + auto-limits minimize LLM token consumption
354+
- **Per-endpoint `$ref` dedup** — a schema reused across responses (e.g. a shared error envelope) is expanded once, then referenced as `$ref(Name)`; cuts ~60% of tokens on real-world specs without losing meaning
355+
- **Description-forward schemas** — field descriptions are surfaced inline (`field*: type — description`) while noisy `example`/`default` payloads are dropped, so the model gets signal, not bulk
347356
- **Guided tool descriptions** — tool descriptions steer LLMs toward efficient filter-first workflows
348357
- **Two-level cache** — memory + disk with HTTP conditional requests for instant cross-session access
349358
- **kin-openapi** for parsing — handles OpenAPI 2.0/3.x with automatic `$ref` resolution
350-
- **Recursive `$ref` resolution** with depth limit (10) and circular reference protection
359+
- **Recursive `$ref` resolution** with a low default depth (3, override up to 10) and circular reference protection
351360
- **stdio transport** — universal compatibility with MCP clients
352361
- **Zero configuration** — sensible defaults, env vars for optional tuning
353362

internal/tools/llm_guide.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# swagger-mcp — usage guide
2+
3+
This server lets you explore an OpenAPI/Swagger spec without loading the whole
4+
document into context. It is built to minimize tokens: responses are compact
5+
(TOON), lists are auto-limited, and repeated schemas are de-duplicated. Follow
6+
the workflow below to stay efficient.
7+
8+
## Workflow
9+
10+
1. `fetch_spec` — once per spec URL. Returns title/version/counts. Establishes
11+
the cache so later calls are instant.
12+
2. `analyze_tags`**always start here**. Maps the API into tags with endpoint
13+
counts so you know where to look.
14+
3. `list_endpoints` — filter by `tag` (preferred), `method`, or `path_pattern`.
15+
Never browse unfiltered on a large API.
16+
4. `get_endpoint` — full detail for one endpoint: params, request body,
17+
responses, and resolved schemas.
18+
5. `get_schema` — only to expand a `$ref(Name)` you saw in `get_endpoint` and
19+
whose fields you actually need.
20+
6. `generate_types` — emit ready-to-paste TypeScript interfaces or Go structs
21+
instead of translating schemas by hand.
22+
23+
`search_spec` is the shortcut when you already know a keyword (it searches
24+
paths, summaries, operation IDs, params, and body field names).
25+
26+
## Reading the output (TOON)
27+
28+
- Endpoint list: `METHOD /path — summary [tag1, tag2]`, one per line.
29+
- Schema fields: `field: type` — a trailing `*` marks a **required** field, and
30+
`— text` after the type is the field's description. Example:
31+
`email*: string(email) — primary contact email`.
32+
- `$ref(Name)` means that schema was already shown once in this response (or is
33+
shared across responses). Don't re-expand it unless you need its fields — and
34+
if you do, call `get_schema Name`.
35+
- Schemas resolve **3 levels deep by default**. Pass `resolve_depth` (0–10) to
36+
go deeper on nested models, or `0` for names-only (cheapest).
37+
38+
## Token-efficiency rules
39+
40+
- Filter before listing. `analyze_tags` → filtered `list_endpoints` beats one
41+
unfiltered dump by ~30×.
42+
- Reuse the cache. Don't re-`fetch_spec` a URL you already loaded; check
43+
`spec_status` if unsure.
44+
- Default format is TOON (compact). Only pass `format=json` when a tool consumer
45+
needs structured JSON.
46+
- Lower `resolve_depth` (or 0) when you only need field names, not nested shapes.
47+
48+
## Example sequences
49+
50+
**"What can this API do with employees?"**
51+
```
52+
analyze_tags(url) → see an "Employee" tag, 40 endpoints
53+
list_endpoints(url, tag="Employee") → the 40 endpoints, one line each
54+
get_endpoint(url, "POST", "/api/v1/Employee") → full request/response shape
55+
```
56+
57+
**"I need to call the create-user endpoint."**
58+
```
59+
search_spec(url, query="create user") → ranked matches
60+
get_endpoint(url, "POST", "/users") → params + body + responses
61+
get_schema(url, "CreateUserDto") → only if a $ref(CreateUserDto) needs expanding
62+
```
63+
64+
**"Give me the TypeScript types for the orders response."**
65+
```
66+
generate_types(url, method="GET", path="/orders", language="typescript")
67+
```
68+
69+
**"Did the spec change since last time?"**
70+
```
71+
refresh_spec(url) → reports changed/unchanged via fingerprint, plus a fresh summary
72+
```
73+
74+
## Anti-patterns
75+
76+
- Don't `list_endpoints` without filters on a large API — it wastes tokens.
77+
- Don't `get_endpoint` every endpoint to "look around" — narrow with tags/search.
78+
- Don't expand every `$ref(Name)` — only the ones whose fields you actually need.
79+
- Don't re-fetch a cached spec — use `spec_status` to check first.
80+
- Don't raise `resolve_depth` to 10 by reflex — the default of 3 is enough for
81+
most models, and the dedup keeps shared schemas from repeating anyway.

0 commit comments

Comments
 (0)