A Model Context Protocol (MCP) server that provides granular access to OpenAPI/Swagger specifications. Built for LLM-powered development workflows — query endpoints, schemas, and diffs without leaving your editor.
Large APIs can have hundreds of endpoints. When building a frontend module, you need to understand request/response shapes, required fields, auth schemes, and error codes — scattered across a massive Swagger UI.
swagger-mcp gives your LLM direct access to the spec so it can:
- Fetch and cache any OpenAPI 2.0/3.x spec from a URL
- List and filter endpoints by tag, method, or path
- Extract full endpoint details with all
$refpointers resolved - Pull individual schemas with nested references expanded
- Search across paths, summaries, descriptions, and parameters
- Compare two spec versions to see what changed
- Check cache status without making HTTP requests
All output is token-optimized by default using TOON format (~40% fewer tokens than JSON), with auto-limiting and guided tool descriptions that reduce typical workflows from ~41K tokens to ~1.3K tokens.
| Tool | Description |
|---|---|
fetch_spec |
Download and cache a spec. Returns title, version, endpoint/tag/schema counts. Supports refresh=true. |
analyze_tags |
Tag summary with endpoint counts and method breakdown. Start here to understand the API. |
list_endpoints |
List endpoints with filters (tag, method, path pattern). Auto-limited to 50 results. |
get_endpoint |
Full detail for one endpoint — params, request body, responses, resolved schemas (field descriptions inline; repeats shown as $ref(Name)). |
get_schema |
Get a named schema with nested $ref resolved. Use to expand a $ref(Name) seen in get_endpoint. |
search_spec |
Full-text search across paths, summaries, operation IDs, parameters, and body properties. Auto-limited to 50. |
diff_endpoints |
Compare two spec versions. Shows added, removed, and changed endpoints. |
spec_status |
Check cache status (memory/disk), fingerprint, age, ETag. No HTTP requests. |
refresh_spec |
Force-refresh a cached spec. Returns change detection via fingerprint comparison. |
generate_types |
Generate TypeScript interfaces or Go structs from an endpoint or named schema. |
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. |
For large APIs, the tools guide the LLM toward an efficient pattern:
1. analyze_tags → Understand API structure (~500 tokens)
2. list_endpoints → Filter by tag/method (~200 tokens)
3. get_endpoint → Full details for one endpoint (~500 tokens)
Total: ~1,200 tokens vs ~41,000+ without filters
All tools accept format: toon (default, compact) or json.
list_endpoints and search_spec also accept limit (default: 50, 0 = unlimited).
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.
go install github.com/hnkatze/swagger-mcp-go/cmd/swagger-mcp@latestThe binary will be placed in $GOPATH/bin (usually ~/go/bin). Make sure it's on your PATH.
Download the latest release for your platform from GitHub Releases.
# Linux / macOS
tar -xzf swagger-mcp_<version>_<os>_<arch>.tar.gz
sudo mv swagger-mcp /usr/local/bin/
# Windows
# Extract the .zip and add the directory to your PATHgit clone https://github.com/hnkatze/swagger-mcp-go.git
cd swagger-mcp-go
go build -o swagger-mcp ./cmd/swagger-mcp/claude mcp add swagger-mcp --transport stdio -- swagger-mcpAdd to your project root or ~/.claude/.mcp.json:
{
"mcpServers": {
"swagger-mcp": {
"type": "stdio",
"command": "swagger-mcp",
"args": []
}
}
}If the binary is not on your PATH, use the full path:
{
"mcpServers": {
"swagger-mcp": {
"type": "stdio",
"command": "/path/to/swagger-mcp",
"args": []
}
}
}Add to your claude_desktop_config.json:
{
"mcpServers": {
"swagger-mcp": {
"command": "swagger-mcp",
"args": []
}
}
}Once configured, the tools are available to the LLM automatically. You interact naturally:
"Fetch the spec at https://petstore.swagger.io/v2/swagger.json and tell me what's in it"
The LLM calls fetch_spec and returns a summary with endpoint count, tags, and schemas.
"List all endpoints tagged 'pet' and show me the POST /pet details"
The LLM calls analyze_tags to discover tags, then list_endpoints filtered by tag, then get_endpoint for the full request/response shape.
"Search for anything related to 'authentication' in the spec"
The LLM calls search_spec and returns matching endpoints ranked by relevance, auto-limited to 50 results.
"What changed between v1 and v2 of this API?"
The LLM calls diff_endpoints with both spec URLs and reports added, removed, and changed endpoints.
"I need to build the user management module. What endpoints and schemas should I know about?"
The LLM calls analyze_tags + list_endpoints + get_endpoint + get_schema to map out the full data model, dependencies, and API contract.
Add this snippet to your CLAUDE.md, .cursorrules, or system prompt to help your LLM use swagger-mcp efficiently:
## API Exploration (swagger-mcp)
When working with external APIs via swagger-mcp:
### Workflow
1. `fetch_spec` with the spec URL (only needed once per session)
2. `analyze_tags` to understand the API structure — always start here
3. `list_endpoints` with tag/method/path filters to find relevant endpoints
4. `get_endpoint` for full details on a specific endpoint
5. `generate_types` to get TypeScript interfaces or Go structs ready to use
6. `get_schema` when you need a specific model's structure
### Rules
- Always filter by tag before listing endpoints on large APIs
- Use `search_spec` to find endpoints by keyword (searches paths, summaries, params, and body properties)
- If the API spec has changed, use `refresh_spec` to get fresh data
- Default output is TOON format (compact, token-efficient) — use format=json only when needed
- Use `generate_types` with language=typescript or language=go instead of manually translating schemas
- Schemas show field descriptions inline (`field*: type — description`); a `*` marks a required field
- A repeated schema appears once, then as `$ref(Name)` — call `get_schema Name` only if you need its fields expanded
- Schemas resolve 3 levels deep by default; pass `resolve_depth` up to 10 for deeper models
### Anti-patterns
- Don't call `list_endpoints` without filters on large APIs (wastes tokens)
- Don't call `get_endpoint` for every endpoint — narrow down with tags/search first
- Don't re-fetch specs that are already cached — use `spec_status` to check
- Don't expand every `$ref(Name)` — only fetch the ones whose fields you actually needFor Cursor, add the same content to .cursorrules. For Windsurf or other MCP clients, add it to your system prompt or project instructions file.
Token-Optimized Object Notation — a compact, human-readable format that reduces token usage by ~40% compared to JSON:
# showing 50 of 790 — use tag/method/path_pattern filters to narrow results
GET /pets — List all pets [pets]
POST /pets — Create a pet [pets]
GET /pets/{petId} — Get a pet by ID [pets]
Detailed endpoint view:
GET /pets/{petId} — Get a pet by ID [pets]
parameters:
- petId (path, required): integer(int64) — The pet ID
responses:
200: Successful response
application/json:
id*: integer(int64)
name*: string
tag: string
404: Pet not found
Key conventions:
*marks required fields- Types use compact notation:
string(email),[]Pet,enum(active, inactive) - List views strip description (summary is enough for browsing)
- Truncated results show a metadata header with filter guidance
- Diff output uses
+(added),-(removed),~(changed) prefixes
Standard indented JSON output. Use format=json when you need programmatic consumption. List/search responses include truncation metadata:
{
"total": 790,
"showing": 50,
"truncated": true,
"endpoints": [...]
}swagger-mcp uses a two-level cache for fast spec access:
- TTL: 5 minutes
- Max entries: 20 specs
- Instant access within a session
- Location:
~/.swagger-mcp/cache/ - TTL: 24 hours
- Max entries: 50 specs
- Persists across sessions
- SHA-256 filenames for safe storage
- Atomic writes prevent corruption
When disk-cached data is stale, swagger-mcp sends conditional HTTP requests using ETag/If-None-Match and Last-Modified/If-Modified-Since. If the server returns 304 Not Modified, the cached data is reused without re-downloading.
Request → L1 Memory (hit? return)
→ L2 Disk (fresh? promote to L1, return)
→ Conditional HTTP (304? refresh L2, promote to L1, return)
→ Full HTTP fetch (update L1 + L2, return)
- Disk errors: graceful degradation to memory-only cache
- Network errors: fall back to stale disk data
- HTTP 4xx: error (no stale data fallback)
- HTTP 5xx: fall back to stale disk data
All settings have sensible defaults. Environment variables are optional overrides:
| Variable | Default | Description |
|---|---|---|
SWAGGER_MCP_DEFAULT_FORMAT |
toon |
Default output format (toon or json) |
SWAGGER_MCP_DEFAULT_LIMIT |
50 |
Default max results for list/search (0 = unlimited) |
SWAGGER_MCP_CACHE_DIR |
~/.swagger-mcp/cache |
Disk cache directory |
SWAGGER_MCP_DISK_CACHE_TTL |
24h |
Disk cache TTL (Go duration format) |
SWAGGER_MCP_CONDITIONAL_FETCH |
true |
Enable HTTP conditional requests |
SWAGGER_MCP_MAX_DISK_ENTRIES |
50 |
Max specs cached on disk |
| Setting | Value |
|---|---|
| Default format | TOON |
| Default limit | 50 results |
| Cache TTL (memory) | 5 minutes |
| Cache TTL (disk) | 24 hours |
| Max cached specs (memory) | 20 |
| Max cached specs (disk) | 50 |
| Max spec size | 20 MB |
| Fetch timeout | 30 seconds |
| Conditional fetch | Enabled |
cmd/swagger-mcp/main.go Entry point — config, MCP server, stdio
internal/
├── config/config.go Configuration with env var overrides
├── loader/
│ ├── loader.go L1→L2→HTTP fetch flow, kin-openapi parsing
│ ├── cache.go L1 in-memory LRU cache (mutex-protected)
│ └── diskcache.go L2 disk cache (SHA-256 keys, atomic writes)
├── analyzer/analyzer.go List, search, tag analysis, spec diffing
├── extractor/extractor.go Endpoint detail, schema extraction, $ref resolution
├── generator/
│ ├── generator.go Schema collection, $ref walking, transitive deps
│ ├── typescript.go TypeScript interface/type emitter
│ └── golang.go Go struct/const emitter
├── formatter/
│ ├── json.go JSON output (with ListResult wrapper)
│ └── toon.go TOON output (headers, strip descriptions)
├── tools/tools.go MCP tool definitions, handlers, format/limit logic
└── types/types.go Shared types, error codes, ListResult
- Token-first defaults — TOON format + auto-limits minimize LLM token consumption
- Per-endpoint
$refdedup — 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 - Description-forward schemas — field descriptions are surfaced inline (
field*: type — description) while noisyexample/defaultpayloads are dropped, so the model gets signal, not bulk - Guided tool descriptions — tool descriptions steer LLMs toward efficient filter-first workflows
- Two-level cache — memory + disk with HTTP conditional requests for instant cross-session access
- kin-openapi for parsing — handles OpenAPI 2.0/3.x with automatic
$refresolution - Recursive
$refresolution with a low default depth (3, override up to 10) and circular reference protection - stdio transport — universal compatibility with MCP clients
- Zero configuration — sensible defaults, env vars for optional tuning
# Run locally
go run ./cmd/swagger-mcp/
# Build
go build -o swagger-mcp ./cmd/swagger-mcp/
# Run tests
go test ./...
# Run tests with verbose output
go test -v ./...
# Run only unit tests (skip integration)
go test -short ./...
# Run tests with coverage
go test -cover ./...- Go 1.25+
- mcp-go — MCP server SDK
- kin-openapi — OpenAPI spec parsing and validation
MIT