Skip to main content

Convert legacy API specs (SOAP/WSDL, Swagger 2.0) into FastMCP-ready OpenAPI 3.x documents

Project description

spec2openapi

Convert legacy API specifications — SOAP/WSDL and Swagger 2.0 — into FastMCP-ready OpenAPI 3.x documents.

CI PyPI Python License: Apache-2.0 Code of Conduct

한국어 문서 (Korean README)


MCP (Model Context Protocol) tooling such as FastMCP can turn an OpenAPI 3.x document into an MCP server automatically — but enterprises are full of services described only by WSDL or Swagger 2.0. spec2openapi closes that gap:

WSDL ─────────┐
              ├──(spec2openapi)──> OpenAPI 3.x (+ x-soap extensions) ──> FastMCP.from_openapi() ──> MCP tools
Swagger 2.0 ──┘

The output is a single, ordinary OpenAPI document. It is designed for a fixed-runtime deployment model: build one FastMCP-based container image, then swap the spec (e.g. a Kubernetes ConfigMap) to mass-produce MCP servers without rebuilding anything.

Features

  • WSDL → OpenAPI 3.0/3.1 — document/literal and rpc/literal bindings, SOAP 1.1/1.2, nested complex types, arrays, attributes, nillable, inheritance (flattened complexContent extensions), simpleContent (text value + attributes), choice (members become optional + x-soap-choice), default values, recursive types, multi-service/multi-port WSDLs with automatic dedup.
  • XSD facets & docs carried into tool schemas — enumerations, pattern, length and numeric bounds, fractionDigits (→ multipleOf), and xsd:annotation documentation are extracted (including from xsd:import-ed schemas) so LLMs see well-described, well-constrained tool arguments.
  • x-soap contract — SOAPAction, SOAP version, endpoint, wrapper element QNames, soap:header parts and declared faults are embedded as vendor extensions; OpenAPI xml annotations carry everything a call layer needs to serialize JSON ↔ literal XML.
  • Swagger 2.0 → OpenAPI 3.x upgrade — full mechanical mapping (servers, requestBody, formData/multipart, parameter schema wrapping, collectionFormatstyle/explode, $ref rewriting, security schemes, type: file, x-nullable, discriminator). Every assumption made for missing information is recorded in x-s2o.assumptions; untranslatable constructs are preserved as x- extensions and listed in x-s2o.lossy.
  • FastMCP compatibility, guaranteed and verifiable — operationIds are generated in FastMCP's tool-name alphabet ([A-Za-z0-9_], unique, ≤64 chars) so tool name == operationId. spec2openapi validate proves it: static checks, openapi-spec-validator, and a real FastMCP.from_openapi() round-trip listing the resulting tools.
  • Reference MCP runtime (optional)pip install "spec2openapi[mcp]" adds a verified SOAP bridge (custom httpx transport) + FastMCP glue, a fixed Dockerfile, and Kubernetes examples. SOAP faults map to MCP tool errors; plain REST specs are served by the same runtime.

Installation

pip install spec2openapi          # converter only (zeep, lxml, PyYAML)
pip install "spec2openapi[mcp]"   # + reference MCP runtime (fastmcp, httpx)

Quick start

CLI

# See what a WSDL contains (operations, headers, faults, style)
spec2openapi inspect https://legacy-host/OrderService?wsdl

# WSDL -> OpenAPI
spec2openapi convert https://legacy-host/OrderService?wsdl -o orders.openapi.yaml

# Swagger 2.0 -> OpenAPI 3.x (assumptions reported on stderr)
spec2openapi upgrade swagger2.json -o service.openapi.yaml

# Prove the spec converts cleanly into MCP tools
spec2openapi validate orders.openapi.yaml

# Reference MCP runtime (requires the [mcp] extra)
spec2openapi serve orders.openapi.yaml --transport http --port 8000
$ spec2openapi validate orders.openapi.yaml
operations        : 2
component schemas : 3
openapi-spec-validator: OK
FastMCP round-trip: OK (2 tools)
  - CreateOrder(customer, items, note)
  - GetOrder(orderId)

OK: spec is FastMCP-convertible

Library

import spec2openapi

# WSDL -> OpenAPI dict
spec = spec2openapi.convert_wsdl("https://legacy-host/OrderService?wsdl")

# Swagger 2.0 -> OpenAPI dict
legacy = spec2openapi.load_spec("swagger2.json")
spec = spec2openapi.convert_swagger(legacy, openapi_version="3.1")

print(spec2openapi.dump_spec(spec))            # YAML text

# Optional [mcp] extra: run it as an MCP server right away
mcp = spec2openapi.from_openapi_spec(spec)
mcp.run(transport="http", host="0.0.0.0", port=8000)

How SOAP calls work (the x-soap contract)

The generated paths (/operations/...) are not real REST endpoints — a SOAP translation layer must build the actual call. Everything it needs ships inside the spec:

Field (paths.*.post.x-soap) Meaning
operation / service / port WSDL names
soapAction, soapVersion, style "1.1"/"1.2", document/rpc
endpoint soap:address (override at runtime)
input / output wrapper element QNames
headers[] soap:header parts with schema refs
faults[] declared faults with schema refs

Serialization rules (schema xml annotations): xml.name/xml.namespace (absent namespace = unqualified), xml.attribute: true, xml.x-text: true (simpleContent text), arrays repeat the element, and property order = XSD sequence order (do not alphabetize the document). x-soap-choice lists mutually exclusive property groups.

The [mcp] extra contains a verified implementation of this contract (src/spec2openapi/bridge.py) — use it directly or as the reference for your own runtime.

Handling missing information (Swagger 2.0)

Upgrading is favorable: OpenAPI 3.x is a superset of Swagger 2.0, so almost nothing must be invented. Where documents are genuinely underspecified, a three-tier policy applies:

  1. Deterministic, documented defaults — missing consumes/producesapplication/json; missing operationId{method}_{path}; missing host → relative server /; missing schemeshttps. All recorded in x-s2o.assumptions.
  2. Preserve, never drop — constructs with no OpenAPI 3 equivalent (e.g. collectionFormat: tsv) are kept as x- extensions and listed in x-s2o.lossy.
  3. Verify the outcomespec2openapi validate runs the actual FastMCP round-trip; assumptions never block tool generation because tools only need paths and schemas.

Kubernetes: one image, many MCP servers

docker build -t spec2openapi:0.1.0 .
spec2openapi convert <wsdl> -o openapi.yaml
kubectl create configmap my-mcp-spec --from-file=openapi.yaml
kubectl apply -f k8s/example.yaml    # Deployment mounts /config/openapi.yaml

Only the ConfigMap changes per service; credentials live in a Secret (SPEC2OPENAPI_ENDPOINT, SPEC2OPENAPI_AUTH = basic|wsse, SPEC2OPENAPI_USERNAME/PASSWORD, SPEC2OPENAPI_TIMEOUT, SPEC2OPENAPI_VERIFY, SPEC2OPENAPI_TRUST_ENV). The MCP endpoint is http://<service>:8000/mcp (streamable HTTP).

Limitations

rpc/encoded (skipped and recorded in x-soap.skippedOperations), MTOM/attachments, WS-Policy/WS-Addressing, and substitution groups are not supported. WS-Security support in the reference runtime is UsernameToken (PasswordText).

Security

All XML parsing disables DTD loading, entity resolution, and parser-level network access. When converting WSDLs from untrusted sources, add --forbid-external (CLI) or forbid_external=True (API) to refuse fetching remote wsdl:/xsd: imports (SSRF mitigation; local relative imports still work). See SECURITY.md for the full notes and how to report vulnerabilities.

Development

git clone https://github.com/Seo-yul/spec2openapi.git
cd spec2openapi
pip install -e ".[dev]"
python -m pytest tests/

The suite (70 tests) covers conversion units, the Swagger upgrader, envelope (de)serialization, end-to-end MCP-tool-call → mock-SOAP-server round-trips (rpc, simpleContent, choice, recursive trees, unqualified forms), FastMCP round-trips for every fixture × OpenAPI 3.0/3.1, and stress patterns (circular $refs, deep nesting, large enums, cross-namespace name collisions, duplicate operation names across services, odd path characters, deep allOf chains). Generated samples live in examples/.

Project layout

src/spec2openapi/
  parser.py    WSDL parsing (zeep) + raw XSD scraping (facets/docs)
  schema.py    XSD -> JSON Schema (xml annotations, choice, simpleContent)
  openapi.py   OpenAPI 3.0/3.1 assembly + x-soap extensions
  swagger.py   Swagger 2.0 -> OpenAPI 3.x upgrader (x-s2o report)
  convert.py   core public API
  cli.py       convert / upgrade / inspect / validate / serve
  bridge.py    [mcp] SOAP bridge (httpx transport)
  server.py    [mcp] FastMCP glue

Contributing

Contributions are welcome — see CONTRIBUTING.md. This project follows the Contributor Covenant Code of Conduct; by participating you agree to uphold it. Security issues should be reported privately per SECURITY.md.

License

Apache-2.0 © Seoyul Yoon

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spec2openapi-0.1.0.tar.gz (47.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spec2openapi-0.1.0-py3-none-any.whl (39.0 kB view details)

Uploaded Python 3

File details

Details for the file spec2openapi-0.1.0.tar.gz.

File metadata

  • Download URL: spec2openapi-0.1.0.tar.gz
  • Upload date:
  • Size: 47.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for spec2openapi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5c7fd3d8654651eaae3daa82683b883b1b41469f9d8991d553c449b57e464bd9
MD5 0020c5f9ee23b375ec7f31e6a4abe46b
BLAKE2b-256 3e6d832305d2a59f011ee1b14c33f758870504490758134501f0795ec0634e3d

See more details on using hashes here.

File details

Details for the file spec2openapi-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: spec2openapi-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for spec2openapi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 af1f23c65950a09a0f7d884745591963b2a3a547800b19d650e751b9d768e3a0
MD5 e54402006cee2408a51d24a0f3659ed4
BLAKE2b-256 3e4dcdcf6ce05e11a7af2192ca6aaa25ed39b52526a4407820ca0e98139772e8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page