DEV Community

Building a Remote MCP Server with OAuth Authorization Using Amazon API Gateway and Cognito

Introduction

In the specification of MCP server published at 2025-03-25, a new authorization specification for MCP servers using HTTP transport was proposed. This specification enables access using temporary user credentials via the OAuth 2.1 authorization code flow, allowing AI agents to access the server without embedding long-lived, highly privileged API keys.

Furthermore, the specification published at 2025-06-18 clarified that MCP server should be treated as a resource server, logically separating the authorization server and MCP server.

https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization

However, developers who want to implement a remote MCP server with OAuth authorization still need to provide an OAuth 2.1-compliant authorization server. Specifically, in AWS environments, Amazon Cognito does not support standard protocols or endpoints for dynamic client registration and authorization server metadata. Thus, it is difficult to comply with standard MCP authorization protocol which is required to communicate with general MCP clients like MCP Inspector or VSCode.

Additionally, managing extra compute resources for the authorization server should be undesired for the MCP server developers. Therefore, in this article, I will demonstrate how to implement a remote MCP server with OAuth authorization that works with MCP Inspector and VSCode, leveraging the request/response manipulating features of Amazon API Gateway.

Overview of Authorized MCP Server

The MCP server authorization specification is based on the following four RFCs (including drafts):

  1. [Draft] OAuth 2.1
  2. RFC 8414 Authorization Server Metadata
  3. RFC 7591 Dynamic Client Registration (DCR)
  4. RFC 9728 Protected Resource Server Metadata

For OAuth 2.1, the authorization code flow is sufficient for this use case, and Cognito's standard features can handle it, so details are omitted here.

Protected Resource Server Metadata is used by the MCP server (as a resource server) to indicate the URL of the authorization server it relies on. In RFC 9728, the authorization_servers field is OPTIONAL, but in the MCP specification, it is mandatory to include information about one or more authorization servers. Additionally, when the MCP server responds with HTTP status 401, it must include a WWW-Authenticate header with a value like Bearer resource_metadata="https://resource.example.com/.well-known/oauth-protected-resource", indicating the URL for the resource metadata. This allows MCP clients to follow the resource metadata to obtain the authorization server URL.

Authorization Server Metadata and Dynamic Client Registration are required for MCP clients to interact with the authorization server. Authorization Server Metadata is mandatory, while DCR is optional. If DCR is not supported, some alternative method for registering MCP clients must be provided. However, since MCP Inspector assumes DCR for client registration, DCR is supported in this implementation to ensure standard operation with MCP Inspector.

The overall flow using these specifications is described in the MCP specification:
https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-flow-steps

Implementing an Authorized MCP Server with AWS Managed Services

To implement an authorized MCP server using AWS managed services, Amazon Cognito is used as the authorization server. Cognito supports the authorization code grant with PKCE and the client credentials grant, making it compatible with the MCP server's authorization flow.

However, Cognito does not support authorization server metadata endpoints or DCR. To address this, API Gateway is utilized to supplement the missing specifications.

The conceptual architecture is shown below. API Gateway routes requests to various endpoints. The HTTP API Gateway in the front is a workaround to place well-known endpoints (e.g., https://example.com/.well-known/oauth-protected-resource) directly under the host, as required by MCP clients like MCP Inspector. The REST API Gateway is the main resource for this implementation. Cognito is used as the authorization server, and API Gateway is integrated with AWS services to support DCR. Lambda is used for the MCP server tool implementation via proxy integration. Metadata endpoints are implemented using API Gateway's Mock integration.

conceptual architecture of the proposed remote MCP server

By default, API Gateway REST APIs add a stage name to the root path. However, MCP clients like MCP Inspector access the /.well-known/ path directly under the host, as specified in RFC 8414, making it impossible to use REST API mode as-is. HTTP API mode allows defining the stage as $default to place paths directly under the host, but lacks features like Mock integration. Therefore, this architecture is used. In production, only REST API mode with a custom domain is required to set paths directly under the host.

The implementation is available in the following repository:
https://github.com/manaty226/remote-mcp-based-on-aws-managed-services

Implementing Metadata Endpoints with API Gateway Mock Integration

Authorization server and resource server metadata consist of static information such as identifiers and related endpoints. For the MCP server, this information is determined at AWS infrastructure setup time.

API Gateway provides a Mock integration feature for returning fixed values or simple computed results. Using this, you can implement metadata endpoints for the authorization server and resource server.

For example, the minimum required authorization server metadata for the MCP server can be implemented with a mapping template like this. The URLs for the issuer and various endpoints can be statically set using Cognito and API Gateway endpoint information.

{
  "issuer": "<Issuer URL>",
  "authorization_endpoint": "<Authorization Endpoint URL>",
  "token_endpoint": "<Token Endpoint URL>",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code"],
  "code_challenge_methods_supported": ["S256"],
  "registration_endpoint": "<DCR Endpoint URL>"
}
Enter fullscreen mode Exit fullscreen mode

Similarly, resource server metadata can be implemented as follows:

{
  "resource": "<API Gateway URL implementing the resource server>",
  "authorization_servers": ["<Authorization Server URL>"]
}
Enter fullscreen mode Exit fullscreen mode

Supporting Dynamic Client Registration with API Gateway AWS Integration and Mapping Templates

Amazon Cognito provides an API called CreateUserPoolClient for client registration. However, the request and response formats are proprietary, so you cannot directly comply with the DCR specification.

https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_CreateUserPoolClient.html

To address this, you can use API Gateway's mapping template feature to transform request and response data, enabling DCR-compliant client registration with Cognito.

For example, you can implement the transformation from a DCR request to a Cognito client creation API request as follows:

{
  "ClientName": $input.json('$.client_name'),
  "CallbackURLs": $input.json('$.redirect_uris'),
  "AllowedOAuthFlows": $input.json('$.response_types'),
  "AllowedOAuthFlowsUserPoolClient": true,
  "AllowedOAuthScopes": ["openid", "profile"],
  "SupportedIdentityProviders": ["COGNITO"],
  "UserPoolId": "<Cognito User Pool ID>"
}
Enter fullscreen mode Exit fullscreen mode

API Gateway mapping templates use the Velocity Template Language (VTL), allowing you to reference request body values like $input.json('$.client_name'). You can set these as values in the Cognito API request body. Required Cognito-specific settings can be hardcoded in the mapping template.

https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html

You can also transform Cognito's proprietary response format to a DCR-compliant response for the MCP client:

{
  "client_id": $input.json('$.UserPoolClient.ClientId'),
  "client_name": $input.json('$.UserPoolClient.ClientName'),
  "redirect_uris": $input.json('$.UserPoolClient.CallbackURLs'),
  "response_types": $input.json('$.UserPoolClient.AllowedOAuthFlows')
}
Enter fullscreen mode Exit fullscreen mode

MCP Tool Implementation

With all authorization features handled by AWS managed services, the resource server functionality (the MCP tool implementation) can be kept lightweight. The awslabs/mcp repository provides a library called mcp-lambda-handler for running MCP tool functions from the Lambda handler's event object.

https://github.com/awslabs/mcp/tree/8d90be5c403af4829a45d8f03093f830ffed6285/src/mcp-lambda-handler

For example, you can implement an MCP server that simply returns a UUID v4 in just a few lines:

import uuid
from awslabs.mcp_lambda_handler import MCPLambdaHandler

mcp_server = MCPLambdaHandler(name="sample-uuid-mcp", version="1.0.0")

@mcp_server.tool()
def get_uuid() -> str:
    """Generate a new UUID v4"""
    return str(uuid.uuid4())

def handler(event, context):
    return mcp_server.handle_request(event, context)
Enter fullscreen mode Exit fullscreen mode

For API authorization, configure the API Gateway Cognito authorizer and add the WWW-Authenticate header to the gateway response for UNAUTHORIZED errors to route to the resource server metadata.

Verification by MCP Inspector

You can use MCP Inspector to verify that the authorized remote MCP server works as expected. Set the Transport Type in MCP Inspector to Streamable HTTP and enter the MCP endpoint of your API Gateway (e.g., https://<your-api-gw-host>/mcp) in the URL form. Then, click "Open Auth Setting" and select either "Quick OAuth Flow" or "Guided OAuth Flow" to start the authorization flow with your remote MCP server.

MCP Inspector settings

If successful, you will be able to retrieve metadata, perform dynamic client registration, and execute the authorization code flow to obtain an access token.

MCP Inspector OAuth testing results

You can also connect and invoke tools from the left menu in MCP Inspector, or from AI agents such as VSCode.

Conclusion

In this article, I demonstrated how to implement a remote MCP server with OAuth authorization using AWS API Gateway and Cognito. By using API Gateway mapping templates to bridge protocol and AWS specification gaps, you can implement authorization without additional compute resources.

For simplicity, both the resource server and authorization server were implemented on the same API Gateway, but you can also separate the authorization server functionality using only API Gateway and Cognito, making the MCP server implementation even simpler.

Top comments (0)