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
- List Tools
- 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
| Approach | Comments |
| #1 Framework-based | Traditional way of developing with existing MCP libs |
| #2 Dynamic Runtime Wrapper for API Services | Needs to integrate dynamic tooling as part of existing API backend |
| #3 Code-generation from OpenAPI to MCP Server Stubs | Needs to implement every tools to invoke API backend |
| #4 Gateway MCP server | MCP 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.
- AWS
- Microsoft / Azure
- 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?
- Open https://us-east-1.console.aws.amazon.com/bedrock-agentcore/toolsAndGateways/create?region=us-east-1
- 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
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)
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.