{
  "markdown": "<!-- mcp-name: io.github.bvenkata/legacy2mcp -->\n\n<div align=\"center\">\n\n<img src=\"assets/banner.svg\" alt=\"legacy2mcp — legacy SOAP/WSDL turned into safe, typed MCP tools\" width=\"820\">\n\n<p>\n  <a href=\"https://github.com/bvenkata/legacy2mcp/actions/workflows/ci.yml\"><img src=\"https://github.com/bvenkata/legacy2mcp/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"https://pypi.org/project/legacy2mcp/\"><img src=\"https://img.shields.io/pypi/v/legacy2mcp?color=3775A9&logo=pypi&logoColor=white\" alt=\"PyPI\"></a>\n  <a href=\"https://pypi.org/project/legacy2mcp/\"><img src=\"https://img.shields.io/pypi/pyversions/legacy2mcp?color=3776AB&logo=python&logoColor=white\" alt=\"Python versions\"></a>\n  <a href=\"https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.bvenkata/legacy2mcp\"><img src=\"https://img.shields.io/badge/MCP_Registry-io.github.bvenkata%2Flegacy2mcp-6f42c1\" alt=\"MCP Registry\"></a>\n  <a href=\"https://glama.ai/mcp/servers/bvenkata/legacy2mcp\"><img src=\"https://glama.ai/mcp/servers/bvenkata/legacy2mcp/badges/score.svg\" alt=\"Glama score\"></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/license-Apache%202.0-blue\" alt=\"License\"></a>\n</p>\n\n<p><b>Point it at a WSDL. Get an MCP server whose tools can't drift from the service, can't send unvalidated arguments, and can't call write operations you didn't opt into.</b></p>\n\n<img src=\"demo/legacy2mcp.gif\" alt=\"legacy2mcp turning a Calculator WSDL into four typed, schema-validated MCP tools\" width=\"820\">\n\n</div>\n\n---\n\n> [!NOTE]\n> `legacy2mcp` introspects every operation in a WSDL, builds a real JSON Schema for each one **from the WSDL's own XSD types**, and exposes them as [MCP](https://modelcontextprotocol.io/) tools — with **every call schema-validated before it reaches your SOAP endpoint**, **write-like operations excluded by default**, and **every call audit-logged**. No hand-written adapter code, no hand-maintained schemas.\n\n## Contents\n\n- [Install](#install)\n- [Quick start](#quick-start)\n- [How it works](#how-it-works)\n- [Point it at your own WSDL](#point-it-at-your-own-wsdl)\n- [Use it from Claude Desktop](#use-it-from-claude-desktop-or-any-mcp-client)\n- [What's handled](#whats-handled)\n- [Safety model](#safety-model)\n- [Use cases](#use-cases)\n- [Real-world usage](#real-world-usage)\n- [Configuration reference](#configuration-reference)\n- [Roadmap](#roadmap)\n- [Development](#development) · [Contributing](#contributing) · [License](#license)\n\n## Install\n\n```bash\npip install legacy2mcp\n# or:  uv tool install legacy2mcp   ·   pipx install legacy2mcp\n```\n\nAlso published to the **[MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.bvenkata/legacy2mcp)** as `io.github.bvenkata/legacy2mcp`, so registry-aware MCP clients can discover it directly.\n\n## Quick start\n\nTry it end-to-end against the bundled mock SOAP service — no external network, no real backend:\n\n```bash\ngit clone https://github.com/bvenkata/legacy2mcp.git\ncd legacy2mcp\npip install -e \".[dev]\"\n\n# 1. start the demo SOAP service (dneonline-style Calculator WSDL)\npython examples/soap/run_mock_calculator.py &\n\n# 2. see the MCP tools generated from its WSDL\nlegacy2mcp inspect --config examples/soap/config.calculator.yaml\n```\n\n<details>\n<summary>Or with Docker</summary>\n\n```bash\ndocker compose up demo-soap-service -d\ndocker compose run --rm legacy2mcp legacy2mcp inspect \\\n  --config examples/soap/config.calculator.docker.yaml\n```\n</details>\n\n## How it works\n\n```mermaid\nflowchart LR\n  WSDL[\"WSDL / XSD\"] --> GEN[\"legacy2mcp<br/>schema generation\"]\n  GEN --> TOOLS[\"Typed MCP tools<br/>one per operation\"]\n  AGENT[\"AI agent /<br/>MCP client\"] -->|tool call| VAL{\"schema<br/>validation\"}\n  TOOLS -. defines .-> VAL\n  VAL -->|invalid args| REJ[\"rejected, never<br/>reaches SOAP\"]\n  VAL -->|valid and allowed| SOAP[\"SOAP endpoint\"]\n  SOAP --> RESP[\"plain JSON<br/>back to the agent\"]\n  VAL --> LOG[(\"audit log\")]\n```\n\n1. Loads the WSDL with [`zeep`](https://docs.python-zeep.org/), a mature, widely-used Python SOAP client.\n2. For every operation on every port/binding, converts the XSD input type into a JSON Schema ([`schema/xsd_to_jsonschema.py`](src/legacy2mcp/schema/xsd_to_jsonschema.py)) — simple types, nested complex types, enums and arrays, recursively, depth-limited for pathological WSDLs.\n3. Registers one MCP tool per operation, named `<adapter_id>_<OperationName>`.\n4. On a tool call: validates arguments with `jsonschema` (schemas use `additionalProperties: false`), calls the operation via `zeep`, serializes the response to plain JSON, and writes an audit entry.\n5. Operations whose names look like writes are excluded unless `allow_write_operations: true`.\n\n## Point it at your own WSDL\n\n```yaml\n# config.yaml\nserver:\n  name: my-legacy-mcp\n\nadapters:\n  - id: legacy\n    type: soap\n    config:\n      wsdl_url: \"https://service.example.com/LegacyService?wsdl\"\n      auth:\n        type: basic\n        username: \"svc-account\"\n        password_env: \"SERVICE_PASSWORD\"   # value read from the environment, never the file\n      allow_write_operations: false        # Create*/Update*/Delete*/… stay hidden\n      include_operations: [\"GetRecord\", \"GetRecordDetails\", \"SearchRecords\"]\n\nsecurity:\n  audit:\n    enabled: true\n    path: \"./legacy-mcp-audit.log\"\n```\n\n```bash\nexport SERVICE_PASSWORD=...\nlegacy2mcp inspect --config config.yaml   # review the generated tools\nlegacy2mcp run     --config config.yaml   # start the MCP server (stdio)\n```\n\nA production-shaped template with comments lives at [`examples/soap/config.template.yaml`](examples/soap/config.template.yaml).\n\n## Use it from Claude Desktop (or any MCP client)\n\n```json\n{\n  \"mcpServers\": {\n    \"legacy\": {\n      \"command\": \"legacy2mcp\",\n      \"args\": [\"run\", \"--config\", \"/absolute/path/to/config.yaml\"]\n    }\n  }\n}\n```\n\n## What's handled\n\n| Area | Covered |\n|---|---|\n| **Type mapping** | `string` / `int` / `long` / `decimal` / `boolean` / `date` / `dateTime` / `base64Binary` / … → JSON Schema types + formats |\n| **Structure** | nested complex types, repeated elements → arrays, `xsd:enumeration` → `enum`, recursion depth-limited |\n| **Discovery** | every service → port → binding → operation; duplicate tool names rejected at startup |\n| **Invocation** | argument validation, `zeep` call, response serialized to plain JSON, single-field responses re-wrapped to a named result |\n| **Errors** | SOAP faults and transport errors caught and returned as clean messages — no stack traces to the caller |\n| **Auth** | HTTP basic (username + `*_env` password); anonymous |\n| **Transport** | `stdio` (the transport Claude Desktop and most agent frameworks spawn) |\n\nSee [docs/security.md](docs/security.md) for the full, honest security model — what's covered today and what isn't yet.\n\n## Safety model\n\n| Layer | What it does |\n|---|---|\n| **Schema validation** | No arguments reach the SOAP layer without passing `jsonschema.validate` against that operation's generated schema. |\n| **Read-only by default** | Operation names are matched against write-verb prefixes (`Create`, `Update`, `Delete`, `Cancel`, `Void`, `Submit`, `Pay`, …); those tools aren't exposed unless you set `allow_write_operations: true`. |\n| **Explicit allow / deny** | `include_operations` (allowlist) and `exclude_operations` (denylist) on top of the heuristic. |\n| **Audit log** | One JSON line per call — tool, arguments, timestamp, outcome, duration. |\n| **Secret hygiene** | Credentials come from named environment variables; the YAML stays safe to commit. |\n\n> [!WARNING]\n> The write-operation filter is a **name heuristic, not semantic analysis** — an operation called `ProcessRecord` that deletes data would not be caught. For any system where a wrong call has real consequences, set `include_operations` explicitly and don't rely on the heuristic. There is also no auth/authz on the MCP server itself yet — don't expose a v0.1 server to untrusted callers. Details in [docs/security.md](docs/security.md).\n\n## Use cases\n\n| Domain | Shape |\n|---|---|\n| **Systems of record** | An agent reads status/detail records from a legacy back-office platform, read-only, every lookup logged. |\n| **Financial services** | Expose account and transaction *reads* without exposing transfers or adjustments. |\n| **Supply chain / ERP** | Surface order status, inventory, shipment tracking from an old SOAP middleware layer. |\n| **Internal support tooling** | A support copilot gets safe, typed access to the system of record instead of a scraped UI. |\n| **Migration & modernization** | Put an MCP layer in front of a legacy service now; swap the backend later without touching the agent. |\n\n## Real-world usage\n\n### In CI/CD — catch WSDL drift before it reaches production\n\n`legacy2mcp inspect` loads the config, contacts the WSDL, builds every schema, and exits non-zero if anything fails:\n\n```yaml\n- name: Check the WSDL still generates valid MCP tools\n  env:\n    SERVICE_PASSWORD: ${{ secrets.SERVICE_PASSWORD }}\n  run: |\n    pip install legacy2mcp\n    legacy2mcp inspect --config config/legacy.yaml > tools.json\n    git diff --exit-code --no-index tools/legacy.snapshot.json tools.json  # optional: pin the contract\n```\n\n### As a sidecar / long-running MCP server\n\n`legacy2mcp run` speaks MCP over stdio. Package it with your config using the provided [`Dockerfile`](Dockerfile) and let your MCP client launch it.\n\n### In a data pipeline\n\nCall the same generated, validated tools from your own code via any MCP client library to pull records on a schedule — the audit log records exactly what was fetched.\n\n## Configuration reference\n\n| Key | Default | Meaning |\n|---|---|---|\n| `server.name` | `legacy2mcp` | MCP server name reported to clients |\n| `server.transport` | `stdio` | only `stdio` is implemented in v0.1 |\n| `adapters[].id` | — | prefix for this adapter's tool names |\n| `adapters[].type` | — | `soap` (implemented); `db` / `queue` are on the roadmap |\n| `adapters[].config.wsdl_url` | — | WSDL location (`http(s)://…?wsdl` or a file path) |\n| `adapters[].config.auth` | `{type: none}` | `none` or `{type: basic, username, password_env}` |\n| `adapters[].config.allow_write_operations` | `false` | expose write-like operations |\n| `adapters[].config.include_operations` | all | allowlist of operation names |\n| `adapters[].config.exclude_operations` | `[]` | denylist of operation names |\n| `adapters[].config.timeout_seconds` | `15` | per-call SOAP timeout |\n| `security.audit.enabled` | `true` | write the audit log |\n| `security.audit.path` | `./legacy2mcp-audit.log` | audit log location |\n\n## Roadmap\n\n| Version | Scope | Status |\n|---|---|---|\n| **v0.1** | SOAP/WSDL adapter, schema generation, validation, read-only default, audit log, `run` + `inspect` CLI, basic auth, stdio | ✅ shipped |\n| v0.2 | Database adapter (parameterized-query-only, table/operation allowlists), HTTP/SSE transport, role→tool authorization, OAuth2 for SOAP | planned |\n| v0.3+ | Queue adapter (Kafka/RabbitMQ/SQS), workflow composition with approval gates, OpenTelemetry export | ideas |\n\nFull detail in [docs/roadmap.md](docs/roadmap.md). The [`BaseAdapter`](src/legacy2mcp/adapters/base.py) interface (`discover_tools()` + `invoke()`) is the extension point — the server core handles validation, dispatch and audit for any adapter.\n\n## Development\n\n```bash\npip install -e \".[dev]\"\npytest tests/ -v          # runs against an in-process mock SOAP service — no network\n```\n\nCI runs the suite on Python 3.10–3.12 ([`ci.yml`](.github/workflows/ci.yml)). Releases to PyPI and the MCP Registry are tag-triggered — see [docs/releasing.md](docs/releasing.md). The demo GIF is regenerated with `vhs demo/demo.tape` ([`demo/`](demo/)).\n\n## Contributing\n\nAdapters for new legacy systems are the highest-value contribution — implement `BaseAdapter` and the core handles the rest. Issues and PRs welcome.\n\n## License\n\n[Apache 2.0](LICENSE)\n\n<div align=\"center\"><sub>legacy2mcp · <a href=\"https://github.com/bvenkata/legacy2mcp\">github.com/bvenkata/legacy2mcp</a></sub></div>\n",
  "bytes": 12019,
  "sha": "68937c286e621ea88ddd6949e8fb13137ab538045c1ed863b634986c6769586b",
  "repo_slug": "bvenkata/legacy2mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_bvenkata_legacy2mcp_a877a8d2/readme"
}