MCP is great, really need to build MCP from scratch?

GenAI is driving businesses to rethink how they build agentic AI applications. These agents and LLMs need to interact with various systems to access and process data. MCP is on the rise as an open standard for enabling this data access and processing. This raises an important question: do we need to build MCP servers from scratch?

The short answer is: not always. The idea is to set up a common MCP server that acts as a gateway for interacting with any existing API backend already built for traditional applications.


How do we convert APIs to MCP?

Imagine a common MCP that can translate Model Context Protocol calls to REST API calls. That's right, let's get into details.

MCP Servers primarily requires to implement the below protocol messages

  1. List Tools
  2. Tools Call

Let's see the spec below for both protocol messages

List Tools - Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
  }
}

Expected Response

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": {
        "tools": [
            {
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        //TBD
                    },
                    "required": [
                       //TBD
                    ]
                },
                "name": "addPet",
                "description": "Add a new pet to the store."
            },
            {
                "inputSchema": {
                    "type": "object",
                    "properties": {
                     //TBD
                    },
                    "required": [
                     //TBD
                    ]
                },
                "name": "getPetById",
                "description": "Returns a single pet."
            }
        ]
    }
}

Tools Call - Request:

{
    "jsonrpc": "2.0",
    "id": "2",
    "method": "tools/call",
    "params": {
        "name": "getPetById",
        "arguments": {
            //TBD
        }
    }
}

Expected Response:

{
    "jsonrpc": "2.0",
    "id": "2",
    "result": {
        "isError": false,
        "content": [
            {
                "type": "text",
                "text": "//Response text from backend server"
            }
        ]
    }
}


Sound good, do I need to implement the above spec with my code?

You may do so, but not needed. There are multiple ways to do so, as illustrated below

ApproachComments
#1 Framework-basedTraditional way of developing with existing MCP libs
#2 Dynamic Runtime Wrapper for API ServicesNeeds to integrate dynamic tooling as part of existing API backend
#3 Code-generation from OpenAPI to MCP Server StubsNeeds to implement every tools to invoke API backend
#4 Gateway MCP serverMCP server doesn’t need every tool implementation, but proxy to backend with reference to API spec

While the first 3 options are possible, but requires 1-1 MCP server implementation for each API backend. So, it defeats the purpose of our initial idea for common MCP server. So, let's explore the option #4 with sequence diagram

Great, now should we implement option #4?

You may do so, but not needed. The below major cloud providers started offering pre built common MCP server as gateway.

  1. AWS
  2. Microsoft / Azure
  3. IBM


AWS has launched (OCT 2025) Bedrock AgentCore to provide AI infrastructure for building agents & tools etc. Let's explore the AgentCore Gateway to expose the existing API backend with low-code / no-code fashion. It helps securing the MCP Server Gateway with OAuth (AWS Cognito) or IAM based security mechanism as well.


See the below sequence diagram including the OAuth Cognito flow

Excellent, how do we create the MCP Gateway w/ AWS?

  1. Open https://us-east-1.console.aws.amazon.com/bedrock-agentcore/toolsAndGateways/create?region=us-east-1
  2. Create the gateway with below details
Gateway name: vnid-mcp-demo
Inbound Auth configurations - Default to Use JSON Web Tokens (JWT)
Create Service role name: vnid-mcp-demo-role
Target name: vnid-mcp-demo-target
Target type: Select REST API
REST API type - OpenAPI schema
OpenAPI schema: Define an inline schema

Enter the below spec inline

Note: Alternatively upload the spec file to s3 bucket and reference the file path w/ Define with an S3 resource option

{
  "openapi": "3.0.4",
  "info": {
    "title": "Swagger Petstore - OpenAPI 3.0",
    "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification.  You can find out more about\nSwagger at [https://swagger.io](https://swagger.io).",
    "termsOfService": "https://swagger.io/terms/",
    "contact": {
      "email": "[email protected]"
    },
    "license": {
      "name": "Apache 2.0",
      "url": "https://www.apache.org/licenses/LICENSE-2.0.html"
    },
    "version": "1.0.27"
  },
  "externalDocs": {
    "description": "Find out more about Swagger",
    "url": "https://swagger.io"
  },
  "servers": [
    {
      "url": "https://petstore3.swagger.io/api/v3"
    }
  ],
  "tags": [
    {
      "name": "pet",
      "description": "Everything about your Pets",
      "externalDocs": {
        "description": "Find out more",
        "url": "https://swagger.io"
      }
    },
    {
      "name": "store",
      "description": "Access to Petstore orders",
      "externalDocs": {
        "description": "Find out more about our store",
        "url": "https://swagger.io"
      }
    },
    {
      "name": "user",
      "description": "Operations about user"
    }
  ],
  "paths": {
    "/pet": {
      "post": {
        "tags": [
          "pet"
        ],
        "summary": "Add a new pet to the store.",
        "description": "Add a new pet to the store.",
        "operationId": "addPet",
        "requestBody": {
          "description": "Create a new pet in the store",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Pet"
              }
            },
            "application/xml": {
              "schema": {
                "$ref": "#/components/schemas/Pet"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Pet"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pet"
                }
              },
              "application/xml": {
                "schema": {
                  "$ref": "#/components/schemas/Pet"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "422": {
            "description": "Validation exception"
          },
          "default": {
            "description": "Unexpected error"
          }
        },
        "security": [
          {
            "petstore_auth": [
              "write:pets",
              "read:pets"
            ]
          }
        ]
      }
    },
    "/pet/{petId}": {
      "get": {
        "tags": [
          "pet"
        ],
        "summary": "Find pet by ID.",
        "description": "Returns a single pet.",
        "operationId": "getPetById",
        "parameters": [
          {
            "name": "petId",
            "in": "path",
            "description": "ID of pet to return",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pet"
                }
              },
              "application/xml": {
                "schema": {
                  "$ref": "#/components/schemas/Pet"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID supplied"
          },
          "404": {
            "description": "Pet not found"
          },
          "default": {
            "description": "Unexpected error"
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "petstore_auth": [
              "write:pets",
              "read:pets"
            ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "example": 1
          },
          "name": {
            "type": "string",
            "example": "Dogs"
          }
        },
        "xml": {
          "name": "category"
        }
      },
      "Tag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          }
        },
        "xml": {
          "name": "tag"
        }
      },
      "Pet": {
        "required": [
          "name",
          "photoUrls"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "example": 10
          },
          "name": {
            "type": "string",
            "example": "doggie"
          },
          "category": {
            "$ref": "#/components/schemas/Category"
          },
          "photoUrls": {
            "type": "array",
            "xml": {
              "wrapped": true
            },
            "items": {
              "type": "string",
              "xml": {
                "name": "photoUrl"
              }
            }
          },
          "tags": {
            "type": "array",
            "xml": {
              "wrapped": true
            },
            "items": {
              "$ref": "#/components/schemas/Tag"
            }
          },
          "status": {
            "type": "string",
            "description": "pet status in the store",
            "enum": [
              "available",
              "pending",
              "sold"
            ]
          }
        },
        "xml": {
          "name": "pet"
        }
      },
      "ApiResponse": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32"
          },
          "type": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "xml": {
          "name": "##default"
        }
      }
    },
    "requestBodies": {
      "Pet": {
        "description": "Pet object that needs to be added to the store",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Pet"
            }
          },
          "application/xml": {
            "schema": {
              "$ref": "#/components/schemas/Pet"
            }
          }
        }
      }
    }
  }
}
Outbound Auth configurations: Select API key

Create New API key https://us-east-1.console.aws.amazon.com/bedrock-agentcore/identity?region=us-east-1 (in new tab)

Add OAuthClient / API Key: Add API Key
Name: vnid-mcp-demo-apikey
API key: vnid-mcp-demo-dummykey

Add & go back previous tab to continue creating the gateway

Select the API key, just created & Click Create Gateway


By now, you should see the gateway along with cognito created successfully.

Congratulations, your MCP Server gateway is ready now!!!!!


Fantastic, how do we verify the gateway now?

Let's view gateway details to find the resource URL from console. You should see something like below. Keep a note of same

Gateway resource URL: https://vnid-mcp-demo-xxxxxxxx.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp


Now, that you have MCP gateway details. Let's find the cognitio user pool & domain to create the OAuth tokens


First, find the discovery URL, you should see something like below. Keep a note of same

Discovery URL: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXX/.well-known/openid-configuration


So, the user pool should have been created with name us-east-1-XXXXXXXXX


Now, go cognito service to find the user pool or replace the above user pool in the below url for direct link


https://us-east-1.console.aws.amazon.com/cognito/v2/idp/user-pools/us-east-1-xxxxxxxxx/branding/domain?region=us-east-1


Let's find the domain url. You should see something like below. Keep a note of same

Domain: https://my-domain-zzzzzzzzz.auth.us-east-1.amazoncognito.com


Now, let's find the client credentials.


Click App clients (on left menu) or go the below direct link (don't forget to replace your pool id in below url)

https://us-east-1.console.aws.amazon.com/cognito/v2/idp/user-pools/us-east-1_xxxxxxxxx/applications/app-clients?region=us-east-1


Open the listed App client name (my-client-sssssssss) & copy the below details from console. Keep a note of same

Client ID: wwwwwwwwwwwww
Client secret: ************************


Now that, you have domain url, client id & client secret. Lets construct the below curl by replacing the domain & base 64 client id & client secret authorization header and run the curl

Request:

curl --location 'https://my-domain-zzzzzzzzz.auth.us-east-1.amazoncognito.com/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic <Base64(clientid:clientSecret)>' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'vnid-mcp-demo/genesis-gateway:invoke'

Response:

{
    "access_token": "<xxxxxx-issued-access-token-xxxxxxx>",
    "expires_in": 3600,
    "token_type": "Bearer"
}


Perfect, Now what?

Now you have the bearer token ready to interact with the tools endpoint.

Run the below url with your generated gateway resource url and Bearer token.

List Tools - Request:

curl --location 'https://vnid-mcp-demo-xxxxxxxx.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <xxxxxx-issued-access-token-xxxxxxx>' \
--data '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "tools/list",
    "params": {}
}'

Response:

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": {
        "tools": [
            {
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "photoUrls": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        },
                        "name": {
                            "examples": [
                                "doggie"
                            ],
                            "type": "string"
                        },
                        "id": {
                            "examples": [
                                10
                            ],
                            "format": "int64",
                            "type": "integer"
                        },
                        "category": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "examples": [
                                        "Dogs"
                                    ],
                                    "type": "string"
                                },
                                "id": {
                                    "examples": [
                                        1
                                    ],
                                    "format": "int64",
                                    "type": "integer"
                                }
                            }
                        },
                        "tags": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string"
                                    },
                                    "id": {
                                        "format": "int64",
                                        "type": "integer"
                                    }
                                }
                            }
                        },
                        "status": {
                            "description": "pet status in the store",
                            "type": "string",
                            "enum": [
                                "available",
                                "pending",
                                "sold"
                            ]
                        }
                    },
                    "required": [
                        "name",
                        "photoUrls"
                    ]
                },
                "name": "vnid-mcp-demo-target___addPet",
                "description": "Add a new pet to the store."
            },
            {
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "petId": {
                            "format": "int64",
                            "description": "ID of pet to return",
                            "type": "integer"
                        }
                    },
                    "required": [
                        "petId"
                    ]
                },
                "name": "vnid-mcp-demo-target___getPetById",
                "description": "Returns a single pet."
            }
        ]
    }
}


Cool, how to invoke the tools now?

Run the below url with your generated gateway resource url and Bearer token (don't forget to replace resource url and Bearer token)

Tools Call - Request

curl --location 'https://vnid-mcp-demo-xxxxxxxx.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <xxxxxx-issued-access-token-xxxxxxx>' \
--data '{
    "jsonrpc": "2.0",
    "id": "2",
    "method": "tools/call",
    "params": {
        "name": "vnid-mcp-demo-target___getPetById",
        "arguments": {
            "petId": 8
        }
    }
}'

Response:

{
    "jsonrpc": "2.0",
    "id": "2",
    "result": {
        "isError": false,
        "content": [
            {
                "type": "text",
                "text": "{\"id\":8,\"category\":{\"id\":1,\"name\":\"hamster\"},\"name\":\"Baxter\",\"photoUrls\":[\"string\"],\"tags\":[{\"id\":9,\"name\":\"string\"},{\"id\":6,\"name\":\"Cat Names\"}],\"status\":\"available\"}"
            }
        ]
    }
}


Awesome, the MCP gateway works!!!!


Disclaimer:

The information in this blog is provided for general informational purposes only and may contain errors or become outdated. It should not be considered professional advice. All opinions are my own. Readers should verify details independently before making decisions. I am not responsible for any actions taken based on this content.