A practical tool is here to convert existing OpenAPIs into MCP Servers in batches

Written by
Silas Grey
Updated on:July-02nd-2025
Recommendation

Higress's latest open source capabilities help build MCP Server efficiently.

Core content:
1. OpenAPI-related concepts and application scenarios
2. Conventional practices for converting existing OpenAPIs to MCP Servers
3. Use and configuration debugging of Higress batch conversion tools

Yang Fangxian
Founder of 53AI/Most Valuable Expert of Tencent Cloud (TVP)

Converting existing OpenAPIs into MCP Servers in batches is Higress's latest open source capability, helping developers build MCP Servers efficiently. [1]


Table of contents 

01. OpenAPI related concepts

02.  Common practices for converting existing OpenAPI to MCP Server

03. Batch conversion of OpenAPI to MCP Server

04.  Debug the MCP Server configuration

05. Conclusion

01. 

OpenAPI related concepts

OpenAPI is written in YAML or JSON, and defines a language-independent HTTP API interface, providing a unified way of information transmission for each stage of the API life cycle. API allows developers to discover and use corresponding services without accessing the source code.


If a social APP wants to obtain the geographic location information of both parties, it does not need to build an Amap by itself, nor does it need to obtain the Amap source code. Instead, it can obtain the geographic location information through the Amap API interface.


Classic Internet applications, such as Amap and Alipay, all provide API services to the outside world in the form of open platforms; public cloud services, such as Alibaba Cloud, provide API services to users through OpenAPI Explorer, allowing developers to manage cloud resources, data, and services through these application programming interfaces. Another example is artificial intelligence big models. Tongyi, DeepSeek, and OpenAI all provide big model call services to the outside world in the form of APIs. These APIs all follow the OpenAPI specification. With specifications, collaboration will be efficient.


02. 

Common practices for converting existing OpenAPI to MCP Server

MCP allows LLM to access external resources, data, and services in a standardized way. Converting existing OpenAPI to MCP Server is a reuse strategy. As a path to ensure economic benefits, the purpose is to enable its own services to be called by external AI applications, thereby increasing the value of existing services. Still taking Amap as an example, Amap provides the ability to convert existing OpenAPI services, such as IP positioning and geocoding, into MCP Server, which allows external applications to call Amap, thereby increasing the activity of the service.


Although MCP has greatly reduced the complexity of large model applications accessing and calling external resources, data, and services, if you use the reuse of existing infrastructure as an MCP development strategy, you will face a new problem, that is, converting existing OpenAPIs into MCP Servers is a "repetitive manual labor" and requires daily maintenance, including interface updates and server stability assurance.


MCP provides SDK toolkits such as TypeScript and Java for developing MCP Server, exposing the existing  Open API as a common HTTP service through the MCP protocol. This process includes: [2]


  • Read and parse the existing OpenAPI documents and extract key information, such as API path, request method, request parameters, response format, etc.
  • According to  the MCP protocol specification, it is converted into a new description, including the tool's function description and tool parameter description, and returned to the client as tool/list result.
  • When the MCP Client wants to call the MCP Server , it parses the Json RPC request of tool/call, generates the HTTP call request of the backend through the configured parameter mapping information, Path, backend address and other information, and makes the call. After the call is completed, the backend call result is packaged for the standard tool/call interface to return the result.

03. 

Batch conversion of OpenAPI to MCP Server

3.1 Installation

go install github.com/higress-group/openapi-to-mcpserver/cmd/openapi-to-mcp@latest


3.2 Use

openapi-to-mcp --input path/to/openapi.json --output path/to/mcp-config.yaml

illustrate

  • --input: The path to the OpenAPI specification file (JSON or YAML format), required.
  • --output: The path to the output MCP configuration file (in YAML format), required.
  • --server-name: The name of the MCP server. The default value is "openapi-server".
  • --tool-prefix: The prefix of the tool name, the default value is empty.
  • --format: Output format (yaml or json), the default value is "yaml".
  • --validate: Whether to validate the OpenAPI specification, the default value is false.
  • --template: Path to template file used to patch output, default value is empty.


3.3 Examples

openapi-to-mcp --input petstore.json --output petstore-mcp.yaml --server-name petstore

This example will petstore.json Convert files to petstore-mcp.yaml file and set the name of the MCP server to petstore.


Here is the complete example.


a.  Start with an OpenAPI specification (petstore.json):

{ "openapi": "3.0.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification" }, "servers": [ { "url": "http://petstore.swagger.io/v1" } ], "paths": { "/pets": { "get": { "summary": "List all pets", "operationId": "listPets", "parameters": [ { "name": "limit", "in": "query", "description": "How many items to return at one time (max 100)", "required": false, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "A paged array of pets", "content": { "application/json": {                "schema": { "type": "object", "properties": { "pets": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string", "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } }, "nextPage": { "type": "string", "description": "URL to get the next page of pets" } } } } } } }, "post": { "summary": "Create a pet", "operationId": "createPets", "requestBody": { "description": "Pet to add to the store", "required": true, "content":{ "application/json": { "schema": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } }, "responses": { "201": { "description": "Null response" } } } }, "/pets/{petId}": { "get": { "summary": "Info for a specific pet", "operationId": "showPetById", "parameters": [ { "name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Expected response to a valid request", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string", "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } } } } } }}"Tag of the pet" } } } } } }, "responses": { "201": { "description": "Null response" } } } }, "/pets/{petId}": { "get": { "summary": "Info for a specific pet", "operationId": "showPetById", "parameters": [ { "name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Expected response to a valid request", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string",                      "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } } } } } }}"Tag of the pet" } } } } } }, "responses": { "201": { "description": "Null response" } } } }, "/pets/{petId}": { "get": { "summary": "Info for a specific pet", "operationId": "showPetById", "parameters": [ { "name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Expected response to a valid request", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string",                      "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } } } } } }}{ "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string", "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } } } } } }}{ "id": { "type": "integer", "description": "Unique identifier for the pet" }, "name": { "type": "string", "description": "Name of the pet" }, "tag": { "type": "string", "description": "Tag of the pet" } } } } } } } } } }}


b.  Convert it to Higress REST-to-MCP configuration:

openapi-to-mcp --input petstore.json --output petstore-mcp.yaml --server-name petstore


c.  Generate petstore-mcp.yaml file:

server:  name: petstoretools:  - name: showPetById    description: Info  for  a specific pet    args:      - name: petId        description: The id of the pet to retrieve        type:  string        required:  true        position: path    requestTemplate:      url: /pets/{petId}      method: GET    responseTemplate:      prependBody: |        # API Response Information
        Below is the response  from  an API call. To help you understand the data, I 've provided:
        1. A detailed description of all fields in the response structure        2. The complete API response
        ## Response Structure
        > Content-Type: application/json
        - **id**: Unique identifier for the pet (Type: integer)        - **name**: Name of the pet (Type: string)        - **tag**: Tag of the pet (Type: string)
        ## Original Response
  - name: createPets    description: Create a pet    args:      - name: name        description: Name of the pet        type: string        required: true        position: body      - name: tag        description: Tag of the pet        type: string        position: body    requestTemplate:      url: /pets      method: POST      headers:        - key: Content-Type          value: application/json    responseTemplate: {}
  - name: listPets    description: List all pets    args:      - name: limit        description: How many items to return at one time (max 100)        type: integer        position: query    requestTemplate:      url: /pets      method: GET    responseTemplate:      prependBody: |        # API Response Information
        Below is the response from an API call. To help you understand the data, I've provided:
        1. A detailed description of all fields in the response structure        2. The complete API response
        ## Response Structure
        > Content-Type: application/json
        - **pets**: (Type:  array )          - **pets[].id**: Unique identifier  for  the  pet  ( Typeinteger )          - **pets[].name**: Name of the  pet  ( Typestring )          - **pets[].tag**: Tag of the  pet  ( Typestring )        - **nextPage**: URL to get the next page of  pets  ( Typestring )
        ## Original Response

Note that the tool automatically sets the position field for each parameter based on its position in the OpenAPI spec:

  • petId The parameters are set to position: path(location: path), because in the OpenAPI specification it is defined as in: path(located in the path).
  • limit The parameters are set to position: query(Location: Query Parameters), because in the OpenAPI specification it is defined as in: query(in the query parameters).
  • Request body attributes (name and tag) is set to position: body(Location: request body).


The MCP server automatically handles these parameters in the correct place when making API requests. For more information on how to use this configuration with Higress REST-to-MCP, refer to the Higress REST-to-MCP documentation. [2]


3.4 Functionality

  • Convert OpenAPI paths to MCP tools.
  • Supports OpenAPI specifications in JSON and YAML formats.
  • Generates an MCP configuration containing server and tool definitions.
  • Preserve parameter descriptions and types.
  • Automatically set parameter locations (path, query, header, cookie, request body) based on OpenAPI parameter locations.
  • Handles path, query, header, cookie, and body parameters.
  • Generate response templates with field descriptions and improved formatting for Large Language Model (LLM) to understand.
  • Optional OpenAPI specification validation (disabled by default).


3.5 Configuring the MCP Server Plug-in

Next, we import the generated files into  the Higress console, add the MCP Server plugin and configure it so that it can be used with Higress.


Plugin configuration example:

server: name: "random-user-server" tools:- description: "Get random user information" name: "get-user" requestTemplate: method: "GET" url: "https://randomuser.me/api/" responseTemplate: body: |- # User Information {{- with (index .results 0) }} - **Name**: {{.name.first}} {{.name.last}} - **Email**: {{.email}} - **Location**: {{.location.city}}, {{.location.country}} - **Phone**: {{.phone}} {{- end }}

Note:  For the 2025-03-26  MCP streamable HTTP protocol, this plugin can be used directly without the need for a global ConfigMap configuration.


3.6 Calling MCP Server

Configure the SSE connection of MCP Server in AI Agent, taking Cursor as an example:

  • MCP Server of database type: Use the path + sse_path_suffix configured in ConfigMap
  • MCP Server of REST API type: Use the route path + sse_path_suffix configured in the console

"mcpServers": { "postgres": { "url": "http://your-higress-address/postgres/sse" }, "rest-api": { "url": "http://your-higress-address/user/sse" }

Configuration is completed in Cursor  :


With MCP Server, you can quickly add support for various data sources for AI Agents to improve development efficiency. Any REST API can be converted to MCP Server through simple configuration without writing additional code.


04. 

Tuning the MCP Server configuration

In the previous chapters, we have learned how the OpenAPI to MCP tool can help us quickly convert existing APIs into tools that can be called by AI assistants. This automated conversion greatly improves development efficiency, allowing us to complete work that would have taken hours or even days in just a few minutes.


However, although the automatically generated configuration is functionally complete, it is often not sophisticated enough. Especially when the API returns complex data structures, if the configuration is not manually tuned, it may cause the large language model (LLM) to understand the data inaccurately, thus affecting the user experience.


4.1 Why do we need to tune the MCP configuration?

The automatically generated MCP configuration will usually contain all the fields returned by the API, presented in a flat manner. This may be sufficient when dealing with simple APIs, but for complex APIs that return a lot of nested data, it will bring several problems:

? Information overload : LLM has a limited context window, and too much irrelevant information will dilute the important content.

? Unclear structure : Complex nested relationships are easily lost in flat descriptions.

? Lack of semantics : Technical codes and professional terms are not converted and difficult to be correctly understood by LLM.

? Hallucination risk : LLM may make incorrect inferences when faced with unfamiliar data structures.


By manually tuning the MCP configuration, we can significantly improve LLM's ability to understand the data returned by the API, reduce misinterpretations and hallucinations, and thus provide users with more accurate and valuable answers.


4.2 Tuning Case: E-commerce Product Search API

Let's use a specific example to illustrate the importance of MCP configuration tuning. Suppose we have a product search API for an e-commerce platform that returns complex product information containing a lot of technical details.


4.2.1 Automatically generated basic configuration

The configuration automatically generated using the OpenAPI to MCP tool might look like this:

server:  name: ecommerce-apitools:  - name: searchProducts    description: "Search for products in the e-commerce platform"    args:      - name: query        description: "Search query string"        type: string        required: true      - name: category        description: "Product category"        type: string        required: false      - name: limit        description: "Maximum number of results to return"        type: integer        default: 10    requestTemplate:      url: "https://api.example.com/products/search"      method: GET      argsToUrlParam: true    responseTemplate:      prependBody: |        # Search Results
        Below is the API response with these fields:
        -  **success** : Boolean indicating if the request was successful        -  **total** : Total number of matching products        -  **page** : Current page number        -  **pageSize** : Number of items per page        -  **products** : Array of product objects with the following fields:          -  **id** : Product unique identifier          -  **name** : Product name          -  **description** : Product description           -  **price** : Product price          -  **compareAtPrice** : Original price before discount          -  **currency** : Currency code (eg, USD, EUR)          -  **availability** : Product availability status          -  **metadata** : Technical metadata          -  **attributes** : Product attributes          -  **variants** : Product variations          -  **images** : Product images          -  **categories** : Categories the product belongs to          -  **tags** : Product tags          -  **brand** : Product brand information          -  **shipping** : Shipping information          -  **ratings** : Product ratings and reviews
        Original response:


When LLM receives an API response in this configuration, it faces the following challenges:


? Data structure confusion : Unable to clearly understand the internal structure of nested objects (such as metadata, attributes).

? Field meaning is unknown : The possible values ​​of the "availability" field and their meaning are unknown.

? Unclear information priority : It is difficult to determine which information is most important to users.

Context window occupancy : A large amount of raw JSON occupies the context window of LLM, squeezing out other important information.


These issues may lead to the following misunderstandings of LLM:


? Confusing the main product with variant information: "This watch is available in black, silver, and rose gold, and the prices are 899 yuan, 899 yuan, and 949 yuan respectively." (mistaking variant information for the main information)

? Incorrectly associated technical details: "The warranty period for this TechFit Pro smartwatch is TF-SW-P10." (Confusing SKU with warranty period)

? Generating hallucinations based on incomplete information: "This watch is available in all electronics stores." (based on incorrect inference of shipping.locations)


4.2.2 Manually tuned configuration

Higress supports combining Go template and Gjson expressions to perform fine-grained processing on request and response templates (for detailed capabilities, please refer to the document: https://higress.cn/en/ai/mcp-server ). Through careful tuning, we can optimize the configuration as follows:

server:  name: ecommerce -apitools:  - name: searchProducts    description:  "Search for products on the e-commerce platform and return a list of products that match the search criteria, including basic product information, price, inventory status, ratings, etc."    args:      - name: query        description:  "Search keywords, which can be product names, brands, models or keywords in descriptions"        type : string        required: true      - name: category        description:  "Product category, such as 'electronics', 'clothing', 'home', etc."        type : string        required: false      - name: limit        description:  "The number of results returned, range 1-50"        type : integer        minimum:  1        maximum:  50        default:  10    requestTemplate:      url:  "https://api.example.com/products/search"      method: GET      argsToUrlParam: true    responseTemplate:      body: |        # Product Search Results
        Found {{.total}} products matching "{{.query}}" . Here are the most relevant {{len .products}} results:
        {{range  $index$product  := .products}}        ## {{add $index 1}}. {{$product.name}}
        **Price**: {{ if  $product .onSale}}~~{{ $product .compareAtPrice}} {{ $product .currency}}~~ **{{ $product .price}} {{ $product .currency}}** (Save {{percentage  $product .compareAtPrice  $product .price}}%){{ else }}{{ $product .price}} {{ $product .currency}}{{ end }}
        **Brand**: {{ $product .brand.name}}
        **Stock status**: {{ if  eq  $product .availability  "in_stock" }}In stock{{ else  if  eq  $product .availability  "low_stock" }}Low stock{{ else }}Out of stock{{ end }}
        {{ if  gt (len  $product .ratings.reviews)  0 }}**Rating**: {{ $product .ratings.averageRating}} / 5  ({{ $product .ratings.reviewCount}} reviews){{ end }}
        {{ $product .description | truncate  200  "..." }}
        {{ if  gt (len  $product .highlights)  0 }}**Product Features**:        {{range  $highlight  :=  $product .highlights}}        - {{ $highlight }}        {{ end }}{{ end }}
        {{ end }}
        {{ if  gt .total (len .products)}}        There are more results not shown. You can get more accurate matches by adjusting the search conditions.        {{ end }}



Here’s an actual response example from a smartwatch product, showing how the fine-tuned template handles raw data:

# Product Search Results
Found 128 products matching "smart watch", here are the most relevant 10 results:
## 1. TechFit Pro Smartwatch
**Price** : ~~1299 CNY~~  **899 CNY**  (save 30.8%)
**Brand** : TechFit
**Stock Status** : In stock
**Rating** : 4.7/5 (342 reviews)
The TechFit Pro smartwatch is equipped with a high-definition color touch screen, supports heart rate monitoring, blood oxygen detection, multiple sports mode tracking and sleep analysis. It is waterproof and has a battery life of up to 7 days.
**Features** :-HD  AMOLED touch screen-  7 days of long battery life-Heart  rate and blood oxygen monitoring-  30 sports modes-  5ATM waterproof
2. FitBit Versa 3
**Price** : 1499 CNY
**Brand** : FitBit
**Stock Status** : In stock
**Rating** : 4.5/5 (287 reviews)
The FitBit Versa 3 smartwatch has integrated GPS positioning, supports 24/7 heart rate monitoring, has a built-in voice assistant, can answer calls, and has a battery life of up to 6 days.
**Features** :-Built  -in GPS-  Voice assistant function-Answering  calls-  20+ sports modes-Waterproof  50 meters
## 3. Apple Watch Series 7
**Price** : 2999 CNY
**Brand** : Apple
**Stock Status** : Limited stock
**Rating** : 4.9/5 (1243 reviews)
The Apple Watch Series 7 has a larger display, faster charging, IP6X dust resistance, swimming-grade waterproofing, and 24/7 blood oxygen monitoring and electrocardiogram functions.
**Features** :-  Retina-grade OLED display-  Fast charging-ECG  and blood oxygen monitoring-  Fall detection and emergency SOS-Support  Apple Pay
There are more results not shown. You can get more accurate matches by adjusting the search conditions.

With this structured response format, LLM can clearly understand the key information of each product without being bogged down by a lot of technical details and raw JSON structure.


4.3 How Tuning Improves LLM Understanding

A well-tuned configuration can significantly improve LLM’s understanding of the data:


4.3.1 Understanding of LLM before tuning

? Structural confusion : Failure to distinguish between the main product and variants, and the possibility of incorrectly describing variant attributes as main product features.

? Focusing on the wrong things : You may focus too much on details (such as SKUs, barcodes) rather than product features that users care about.

? Misunderstanding of field meaning : Failure to correctly understand professional terms and coded values.

? Hallucinations : Being influenced by irrelevant content such as product details, leading to hallucinations


4.3.2 Improved Understanding of LLM after Tuning

? Clear structure : Accurately understand the basic information, price, brand, inventory status and ratings of each product.

? Focus : Able to identify key information such as price discounts, product descriptions and features.

? Clear semantics : The meaning of inventory status is correctly understood without ambiguity.

? Contextual completeness : Get the full picture of the search results ("Found 128 matches, showing 10")


4.4 Tuning Strategy Summary

Based on the above cases, we can extract the following MCP configuration tuning strategies:

? Identify and extract core fields : Analyze the information that users really need and remove technical details and internal data.

? Convert technical codes and technical terms into descriptions that are easy for LLM to understand.

? Add contextual information : Help LLM understand the completeness and scope of the data.

? Structure key information : Use a hierarchical structure to make the importance and relationship of information clear at a glance


05. 

Conclusion

The OpenAPI to MCP tool provides us with the ability to quickly convert APIs into AI tools, and manual tuning is a key step in improving AI understanding and user experience. Through carefully designed response templates, we can guide LLM to more accurately understand the data returned by the API, reduce misinterpretations and illusions, and provide users with more valuable services.


In actual applications, it is recommended to use the OpenAPI to MCP tool to generate basic configurations first, and then perform targeted tuning based on the complexity of the API and user needs. For simple APIs, the automatically generated configuration may be sufficient; for complex APIs, especially those that return a large amount of nested data, manual tuning will significantly improve the experience.


It should be emphasized that high-quality MCP configuration tuning often relies on data feedback and iterative optimization. A single configuration is difficult to meet all user scenarios at one time, so it is necessary to build a closed loop of evaluation feedback and continuous optimization based on grayscale testing of multiple configuration versions. Higress will combine the powerful capabilities of the Nacos Configuration Center to provide MCP server maintainers with more sophisticated configuration management functions, including version control, grayscale release, configuration rollback, and effect analysis, so that configuration tuning is no longer a one-time job, but a data-driven continuous optimization process.


Through the complete link of "automatic conversion + manual tuning + data feedback", we can not only enjoy the efficiency improvement brought by automation, but also ensure that the AI ​​assistant provides a high-quality user experience. At the same time, we can continuously optimize the configuration based on actual usage data, so that the capabilities of the AI ​​assistant continue to evolve with use.


Preview: Higress will launch the MCP Marketplace based on API Gateway next week, including 50 commonly used API services to quickly call MCP Server.