跳到正文
Hacker News· balajijoe5·· 6 天前AI 评分62

legacy2mcp 发布:60 秒把 SOAP 或 REST API 转成 MCP server

Legacy2mcp – Convert your SOAP or REST API to an MCP server in 60 seconds

AI 导读

legacy2mcp 发布,可将遗留 SOAP/WSDL 服务、数据库或 REST API 在 60 秒内转换为可用的 MCP server。

正文 · AI 翻译

legacy2mcp — legacy SOAP/WSDL turned into safe, typed MCP tools

CI PyPI Python versions MCP Registry Glama score License

从遗留的 SOAP/WSDL 服务、数据库或 REST API 到可运行的 MCP 服务器,只需 60 秒。默认只读,每次调用都有审计日志。

Three-step quick start: pip install legacy2mcp, then legacy2mcp init --rest-url against the public Swagger Petstore, then legacy2mcp inspect to see the generated MCP tools


注意

legacy2mcp 会内省遗留系统——WSDL 中的每个操作,或数据库的实时 schema——基于该内省出的 schema为每个工具构建真正的 JSON Schema,并将它们暴露为 MCP 工具——每次调用在到达你的 SOAP 端点或数据库之前都会经过 schema 校验,默认排除类似写入/写入操作,并且每次调用都有审计日志。无需手写适配器代码,无需手工维护 schema,LLM 也无法执行任意 SQL。

目录

安装

# Full install (recommended)
pip install "legacy2mcp[ai]"

# Or the identical, scan-only-ready core -- AI-assisted `legacy2mcp init`
# needs no extra package either way, just an API key at runtime; `[ai]`
# above installs nothing extra today, it's just a stable, familiar name
pip install legacy2mcp
# or:  uv tool install legacy2mcp   ·   pipx install legacy2mcp

也已作为 io.github.bvenkata/legacy2mcp 发布到 MCP Registry,因此支持注册表的 MCP 客户端可以直接发现它。

快速开始

无需 API key,无需克隆——针对一个公开演示 API:

pip install legacy2mcp
legacy2mcp init --rest-url "https://petstore3.swagger.io/api/v3" --out config.yaml
legacy2mcp inspect --config config.yaml   # see the generated MCP tools

接下来,将它连接到你的 AI 客户端——参见 在 Claude Desktop 中使用。

你有自己的 WSDL、REST API 或代码库?legacy2mcp init(不带 参数)会引导你完成——参见 自动生成配置。更多 演示(本地 SOAP 服务、Docker、通过原始 URL 提供的 WSDL)列在 examples/ 中。

工作原理

flowchart LR
  WSDL["WSDL / XSD"] --> GEN["legacy2mcp<br/>schema generation"]
  DB["DB schema<br/>(allowlisted tables)"] --> GEN
  GEN --> TOOLS["Typed MCP tools<br/>one per operation / table / query"]
  AGENT["AI agent /<br/>MCP client"] -->|tool call| VAL{"schema<br/>validation"}
  TOOLS -. defines .-> VAL
  VAL -->|invalid args| REJ["rejected, never<br/>reaches SOAP/DB"]
  VAL -->|valid and allowed| BACKEND["SOAP endpoint<br/>or database"]
  BACKEND --> RESP["plain JSON<br/>back to the agent"]
  VAL --> LOG[("audit log")]

加载中

SOAP:

  1. 使用 zeep 加载 WSDL,这是一个成熟且广泛使用的 Python SOAP 客户端。
  2. 对于每个端口/绑定上的每个操作,将 XSD 输入类型转换为 JSON Schema(schema/xsd_to_jsonschema.py)——简单类型、嵌套复杂类型、枚举和数组,递归处理,针对病态 WSDL 进行深度限制。
  3. 为每个操作注册一个 MCP 工具,命名为 <adapter_id>_<OperationName>。
  4. 在工具调用时:使用 jsonschema 校验参数(schema 使用 additionalProperties: false),通过 zeep 调用操作,将响应序列化为纯 JSON,并写入审计条目。

数据库:

  1. 使用对应方言的驱动(psycopg / pyodbc / oracledb,延迟导入)连接,并将每个允许列表中的表的实时列类型(adapters/db_dialects.py)内省为 JSON Schema(schema/sql_to_jsonschema.py)。
  2. 为每个允许列表中的表和操作注册 select_<table> / insert_<table>,并为配置中的每个命名查询注册 query_<name>。
  3. 在工具调用时:校验参数,使用一种方言中立的 :param 占位符约定,从允许列表中的表/列构建 SQL(或运行配置提供的命名查询),执行它,并写入审计条目。LLM 永远不会看到或编写 SQL 文本。
  4. 名称看起来像写入的操作会被排除,除非 allow_write_operations: true。

为什么不直接手写一个适配器?

因为手写适配器是你系统 schema 的第二份副本,没人会记得去更新它。一旦某个 WSDL 操作的签名发生变化,或者某一列发生改变,你工具的 JSON Schema 就会悄无声息地偏离现实——而调用它的 LLM 会通过发送一些错误得无人察觉的参数,或者从 SOAP/DB 客户端深处收到一个令人困惑的运行时错误,才发现这一点。legacy2mcp 每次启动时都从实时系统构建 schema,因此发生漂移的调用会在验证阶段失败,在到达你的后端之前——而不是之后。

手写适配器(FastMCP / 原生 SDK) 通用 OpenAPI 转 MCP 代理 legacy2mcp
Schema 来源 按工具手工编写 OpenAPI 规范(如果存在的话) 实时 WSDL / DB schema,或发现的 OpenAPI 规范
可能偏离真实系统 是——没有任何东西会重新检查它 仅当规范本身已过时 否——启动时从实时源重建
覆盖 SOAP / 数据库 你自己编写那部分客户端代码 否——仅支持 REST/OpenAPI 是,外加 REST
写操作默认隐藏 否——你得自己记得对每个操作设门禁 否——通常会暴露规范定义的一切 是——显式选择加入 allow_write_operations
每次调用的审计日志 自己动手 通常没有 内置
到可运行服务器所需时间 每个系统数小时到数天 对于有规范的 REST API,只需几分钟 几分钟——legacy2mcp init

将它指向你自己的 WSDL

# config.yaml
server:
  name: my-legacy-mcp

adapters:
  - id: legacy
    type: soap
    config:
      wsdl_url: "https://service.example.com/LegacyService?wsdl"
      auth:
        type: basic
        username: "svc-account"
        password_env: "SERVICE_PASSWORD"   # value read from the environment, never the file
      allow_write_operations: false        # Create*/Update*/Delete*/… stay hidden
      include_operations: ["GetRecord", "GetRecordDetails", "SearchRecords"]

security:
  audit:
    enabled: true
    path: "./legacy-mcp-audit.log"
export SERVICE_PASSWORD=...
legacy2mcp inspect --config config.yaml   # review the generated tools
legacy2mcp run     --config config.yaml   # start the MCP server (stdio)

一个带注释的生产形态模板位于 examples/soap/config.template.yaml。

将它指向你自己的数据库

pip install "legacy2mcp[db-postgres]"   # or db-mssql / db-oracle
# config.yaml
adapters:
  - id: orders
    type: db
    config:
      dialect: postgres             # postgres | mssql | oracle
      dsn_env: "ORDERS_DB_DSN"      # connection string, read from the environment
      allow_write_operations: false # insert_<table> / write: true queries stay hidden
      tables:
        - name: orders
          operations: [select]
          columns: [id, status, total, created_at]   # column allowlist
          filterable_columns: [status]                # equality filters on select_orders
          max_rows: 200
      queries:
        - name: orders_by_status_since
          sql: "SELECT id, status, total FROM orders WHERE status = :status AND created_at >= :since"
          params:
            - { name: status, type: string, required: true }
            - { name: since, type: string, required: true }
export ORDERS_DB_DSN=...
legacy2mcp inspect --config config.yaml   # review the generated tools

列在启动时从实时 schema 内省得到,就像 SOAP 操作 schema 来自 WSDL 一样——没有任何需要手工维护的东西。从 LLM 到任意 SQL 永远没有通路:它只能对 tables: 下列出的表调用 select_<table> / insert_<table>,或者调用你在 queries: 下编写了 SQL 的 query_<name> 工具——LLM 只提供查询的 params 所声明的占位符值,绝不提供 SQL 文本。一个生产形态模板位于 examples/db/config.template.yaml。

将它指向你自己的 REST API

普通的 REST/JSON API 没有 WSDL 或实时数据库 schema 可供内省,因此 rest 适配器的端点列表是显式配置——手工编写,或者用 legacy2mcp init(下一节)生成一个起点。

# config.yaml
adapters:
  - id: orders
    type: rest
    config:
      base_url: "https://api.example.com"
      auth:
        type: bearer
        token_env: "ORDERS_API_TOKEN"
      allow_write_operations: false
      endpoints:
        - name: get_order
          method: GET
          path: "/orders/{order_id}"
          params:
            - { name: order_id, location: path, type: integer, required: true }
        - name: create_order
          method: POST
          path: "/orders"
          write: true   # stays hidden unless allow_write_operations: true
          params:
            - { name: total, location: body, type: number, required: true }

一个生产形态模板位于 examples/rest/config.template.yaml。

注意

非 GET 方法和 LLM 辅助分类都是启发式方法,并不能保证该端点实际上可以安全地无监督调用——在针对真实后端运行之前,请检查 write: 标志和 allow_write_operations,这与下面 SOAP 适配器的写动词启发式方法有同样的注意事项。

自动生成配置

legacy2mcp init 会询问要指向什么,然后为你写一个起始的 config.yaml:

legacy2mcp init
What do you want to point legacy2mcp at?
  1) WSDL URL (SOAP service)
  2) REST API URL (tries OpenAPI/Swagger discovery)
  3) GitHub repo URL or a local path to its source code
Choice [1-3]:
选项 会发生什么 需要 LLM 密钥吗?
WSDL URL 直接将 soap 适配器指向它——zeep 在 inspect/run 时无论如何都会内省实时 WSDL,因此无需预先扫描任何东西。 否
REST API URL 针对该 URL 尝试常见的 OpenAPI/Swagger 规范位置(/openapi.json、/swagger.json……),如果找到真实规范,就据此构建 rest 适配器的端点列表;否则生成一个空脚手架。 否
GitHub 仓库 / 本地路径 克隆(或读取)该仓库,并静态扫描其 Python 源代码(仅 AST,绝不执行),查找 zeep.Client(...) 调用、SQLAlchemy/Flask-SQLAlchemy 模型,或 Flask/FastAPI 路由——相应地选择 soap/db/rest。 可选

每个工具也可通过标志 -- --wsdl-url、--rest-url、--github-url 或 --local-path 以非交互方式运行,用于脚本或 CI。只有 GitHub/源代码路径可以使用 LLM,且仅用于优化静态分析已经发现的内容(模糊的参数类型、读与写的分类)——绝不会转储你的仓库。它不需要额外依赖,只需通过 --llm-api-key-env 提供 OpenAI 兼容的 API 密钥,或者如果你省略它,则使用交互式隐藏提示(绝不会写入磁盘);传入 --scan-only 可直接跳过它并仅运行静态分析,与不输入密钥相同。

legacy2mcp init --github-url https://github.com/you/your-api --scan-only --out config.yaml

无论你选择哪条路径,在运行生成的文件之前都要先审查它——对于任何无法确信推断出的内容(未解析的 base_url、未验证的参数类型、哪些端点被归类为 write: true),它都会用 # TODO 注释标记出来,而且在你这样做之前,其中没有任何内容会针对真实后端进行验证:

legacy2mcp inspect --config config.yaml   # review the generated tools
legacy2mcp run     --config config.yaml   # start the MCP server

在将其指向任何真实环境之前,请参阅 docs/security.md,了解此启发式方法确切保证和不保证什么。

从 Claude Desktop(或任何 MCP 客户端)使用它

每个客户端都通过 stdio 启动 uvx legacy2mcp run --config <absolute path>。如果你使用 pip 或 pipx 安装,请用 legacy2mcp 代替 uvx legacy2mcp。

Claude Desktop(claude_desktop_config.json)

{
  "mcpServers": {
    "legacy2mcp": {
      "command": "uvx",
      "args": ["legacy2mcp", "run", "--config", "/absolute/path/to/config.yaml"]
    }
  }
}

Claude Code

claude mcp add legacy2mcp -- uvx legacy2mcp run --config /absolute/path/to/config.yaml

Cursor(.cursor/mcp.json)

{
  "mcpServers": {
    "legacy2mcp": {
      "command": "uvx",
      "args": ["legacy2mcp", "run", "--config", "/absolute/path/to/config.yaml"]
    }
  }
}

VS Code、传递密钥和故障排除:docs/clients.md。

已处理的内容

领域 覆盖范围
类型映射 string / int / long / decimal / boolean / date / dateTime / base64Binary / … → JSON Schema 类型 + 格式
结构 嵌套复杂类型、重复元素 → 数组,xsd:enumeration → enum,递归深度受限
发现 每个服务 → 端口 → 绑定 → 操作;重复的工具名称在启动时被拒绝
调用 参数验证、zeep 调用、响应序列化为纯 JSON、单字段响应重新包装为命名结果
错误 SOAP 故障和传输错误被捕获并作为干净的消息返回——不会向调用方暴露堆栈跟踪
认证 HTTP 基本认证(用户名 + *_env 密码);持有者令牌(*_env);匿名
传输 stdio(Claude Desktop 和大多数代理框架启动时使用的传输方式)
数据库方言 Postgres、SQL Server、Oracle——表允许列表(select/insert)配合实时模式内省,以及命名参数化查询
REST 来自配置或 legacy2mcp init 的显式端点允许列表(方法 + 路径 + 类型化参数)——没有可内省的实时模式,因此在运行之前这里没有任何内容经过验证

请参阅 docs/security.md 了解完整、诚实的安全模型——今天覆盖了什么,以及尚未覆盖什么。

安全模型

层 作用
模式验证 任何参数都必须针对该工具生成的模式通过 jsonschema.validate,才能到达 SOAP/DB 层。
默认只读 SOAP:操作名称会与写动词前缀(Create、Update、Delete、Cancel、Void、Submit、Pay、…)进行匹配。DB:表/查询需显式选择加入,且 insert/标记为写的查询还需要额外确认。除非你设置 allow_write_operations: true,否则两者都不会暴露。
显式允许 / 拒绝 SOAP:include_operations / exclude_operations。DB:表/列允许列表——没有“所有表”或任意 SQL 的等价物。
审计日志 每次调用一行 JSON——工具、参数、时间戳、结果、持续时间。
密钥卫生 凭据和数据库连接字符串来自命名的环境变量;YAML 保持可安全提交。

警告

SOAP 写操作过滤器是一种名称启发式规则,而非语义分析——一个名为 ProcessRecord 却会删除数据的操作不会被拦截。对于任何错误调用会造成实际后果的系统,请显式设置 include_operations,不要依赖该启发式规则。DB 适配器的允许列表在构造上就是显式的,但没有列级数据脱敏——允许列表中列的值始终按原样返回。MCP 服务器本身目前也还没有认证/授权——不要将服务器暴露给不受信任的调用方。详情见 docs/security.md。

用例

领域 形态
记录系统 智能体从遗留后台平台读取状态/详情记录,只读,每次查询都记录日志。
金融服务 暴露账户和交易的读取,而不暴露转账或调整。
供应链 / ERP 从旧的 SOAP 中间件层呈现订单状态、库存、物流跟踪。
内部支持工具 支持副驾驶获得对记录系统的安全、类型化访问,而不是抓取 UI。
迁移与现代化 现在就在遗留服务前放置一个 MCP 层;以后更换后端而无需改动智能体。

实际使用

在 CI/CD 中——在 WSDL 漂移到达生产环境之前捕获它

legacy2mcp inspect 加载配置、连接 WSDL、构建每个 schema,如果任何环节失败则以非零状态退出:

- name: Check the WSDL still generates valid MCP tools
  env:
    SERVICE_PASSWORD: ${{ secrets.SERVICE_PASSWORD }}
  run: |
    pip install legacy2mcp
    legacy2mcp inspect --config config/legacy.yaml > tools.json
    git diff --exit-code --no-index tools/legacy.snapshot.json tools.json  # optional: pin the contract

作为 sidecar / 长期运行的 MCP 服务器

legacy2mcp run 通过 stdio 使用 MCP 通信。使用提供的 Dockerfile 将其与你的配置打包,让你的 MCP 客户端启动它。

在数据管道中

通过任意 MCP 客户端库从你自己的代码中调用相同的、已生成并验证的工具,按计划拉取记录——审计日志会准确记录获取了什么。

配置参考

键 默认值 含义
server.name legacy2mcp 向客户端报告的 MCP 服务器名称
server.transport stdio 目前仅实现了 stdio
adapters[].id — 此适配器工具名称的前缀
adapters[].type — soap、db 或 rest(已实现);queue 在路线图中

SOAP 适配器配置:

键 默认值 含义
adapters[].config.wsdl_url — WSDL 位置(http(s)://…?wsdl 或文件路径)
adapters[].config.auth {type: none} none 或 {type: basic, username, password_env}
adapters[].config.allow_write_operations false 暴露类似写操作的操作
adapters[].config.include_operations 全部 操作名称的允许列表
adapters[].config.exclude_operations [] 操作名称的拒绝列表
adapters[].config.timeout_seconds 15 每次调用的 SOAP 超时

DB 适配器配置:

键 默认值 含义
adapters[].config.dialect — postgres、mssql 或 oracle
adapters[].config.dsn_env — 保存连接字符串/DSN 的环境变量
adapters[].config.allow_write_operations false 暴露 insert_<table> 和 write: true 查询
adapters[].config.timeout_seconds 15 连接超时
adapters[].config.tables[].name / .schema — 要加入允许列表的表(以及可选的 schema/owner)
adapters[].config.tables[].operations [select] select 和/或 insert
adapters[].config.tables[].columns 所有已内省项 列允许列表
adapters[].config.tables[].filterable_columns [] 可用作 select_<table> 上等值过滤器的列
adapters[].config.tables[].max_rows 200 行数上限,在查询运行后强制执行
adapters[].config.queries[].name / .sql / .params — 一个命名查询;sql 使用 :param_name 占位符
adapters[].config.queries[].write false 将此查询置于 allow_write_operations 之后

REST 适配器配置:

键 默认值 含义
adapters[].config.base_url — 所配置端点相对的基础 URL
adapters[].config.auth {type: none} none、{type: basic, username, password_env} 或 {type: bearer, token_env}
adapters[].config.allow_write_operations false 暴露标记为 write: true 的端点
adapters[].config.timeout_seconds 15 每次调用的 HTTP 超时
adapters[].config.endpoints[].name / .method / .path / .description — 一个加入允许列表的端点;path 使用 {param} 占位符表示路径参数
adapters[].config.endpoints[].params[].name / .location / .type / .required — location 为 path、query 或 body
adapters[].config.endpoints[].write false 将此端点置于 allow_write_operations 之后

安全配置:

键 默认值 含义
security.audit.enabled true 写入审计日志
security.audit.path ./legacy2mcp-audit.log 审计日志位置

路线图

版本 范围 状态
v0.1 SOAP/WSDL 适配器、schema 生成、验证、默认只读、审计日志、run + inspect CLI、基本认证、stdio ✅ 已发布
v0.2 数据库适配器(Postgres / SQL Server / Oracle;表/操作允许列表 + 命名参数化查询,绝不使用任意 SQL) ✅ 已发布
v0.2.x REST 适配器(显式端点允许列表);legacy2mcp init -- 扫描仓库(SOAP 客户端 / ORM 模型 / Flask/FastAPI 路由)并生成起始配置,可选使用兼容 OpenAI 的 LLM 辅助处理模糊部分 ✅ 已发布
v0.2.x HTTP/SSE 传输、角色→工具授权、SOAP 的 OAuth2 计划中
v0.3+ 队列适配器(Kafka/RabbitMQ/SQS)、带审批门的工作流编排、OpenTelemetry 导出 想法

完整细节见 docs/roadmap.md。BaseAdapter 接口(discover_tools() + invoke())是扩展点——服务器核心为任何适配器处理验证、分发和审计。

开发

pip install -e ".[dev]"
pytest tests/ -v

测试针对进程内模拟 SOAP 服务和进程内基于 SQLite 的 DB 适配器替身运行——无需网络,无需真实的 Postgres/SQL Server/Oracle。 DB 适配器的 Postgres/SQL Server/Oracle 方言(db_dialects.py) 其 SQL 转换逻辑由无驱动单元测试覆盖;连接每个的真实实例 不属于默认测试套件的一部分。

CI 在 Python 3.10–3.12 上运行测试套件(ci.yml)。发布到 PyPI 和 MCP Registry 由标签触发——见 docs/releasing.md。演示 GIF 使用 vhs demo/demo.tape(demo/)重新生成。

贡献

针对新遗留系统的适配器是最有价值的贡献——实现 BaseAdapter,核心会处理其余部分。欢迎提交 issue 和 PR。

许可证

Apache 2.0

来源:Hacker News · github.com