

# Using interceptors with Gateway
<a name="gateway-interceptors"></a>

Configuring interceptors on your gateway allows you to run custom code during each invocation of your gateway. This section provides guidance on implementing and configuring interceptors for your Gateway.

**Topics**
+ [Overview](#gateway-interceptors-overview)
+ [Security best practices](#gateway-interceptors-security)
+ [Permissions for interceptors](gateway-interceptors-permissions.md)
+ [Types of interceptors](gateway-interceptors-types.md)
+ [Configuration](gateway-interceptors-configuration.md)
+ [Examples](gateway-interceptors-examples.md)

## Overview
<a name="gateway-interceptors-overview"></a>

Configuring interceptors on your gateway allows you to run custom code during each invocation of your gateway. This is useful for the following use cases:
+ Implementing fine-grained access control over tools or MCP operations
+ Transforming the target request and gateway response
+ Implementing custom authorization logic

There are two types of interceptors that can be configured on your gateway:
+  **REQUEST interceptors** – Execute before the gateway makes a call to the target. These are useful for request validation, transformation, or custom authorization.
+  **RESPONSE interceptors** – Execute after the target responds but before the gateway sends the response back to the caller. These are useful for response transformation, filtering, or adding custom headers.

A gateway can have at most one REQUEST interceptor and at most one RESPONSE interceptor configured. You can configure both types on the same gateway, but you cannot have multiple interceptors of the same type.

Currently, interceptors can only be configured with Lambda functions.

## Security best practices
<a name="gateway-interceptors-security"></a>

When implementing interceptors, it’s important to follow security best practices to protect sensitive information and maintain proper access control.

1. By default, request headers will not be passed to an interceptor unless the `passRequestHeaders` field is set to true. Be careful when using this field as request headers can contain sensitive information such as authentication tokens and credentials. Be sure to verify your interceptor is not logging this sensitive information.

1. Be sure to restrict your gateway execution role to have permissions only to invoke the specific lambda functions you are using as interceptors and not to give wild card lambda permissions to your execution role.

1. Implement idempotent Lambda functions for your interceptors. The gateway may retry requests to interceptor Lambda functions in case of failures or timeouts. Ensure your interceptor logic can handle duplicate invocations safely by implementing idempotency keys, tracking processed requests, or designing stateless operations that produce consistent results when executed multiple times with the same input.

# Permissions for interceptors
<a name="gateway-interceptors-permissions"></a>

When configuring interceptors, your gateway service role must have the lambda:InvokeFunction IAM permissions to invoke the Lambda functions that serve as interceptors. The service role needs specific permissions to execute interceptor functions during request and response processing.

For detailed information about configuring the required permissions for your gateway service role, including permissions to Lambda, see [Gateway service role permissions](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-prerequisites-permissions.html#gateway-service-role-permissions).

# Types of interceptors
<a name="gateway-interceptors-types"></a>

There are two types of interceptors that can be configured on your gateway:

## Request interceptors
<a name="gateway-interceptors-types-request"></a>

The REQUEST interceptor gets invoked before gateway makes a call to the target configured on the gateway. You can use the REQUEST interceptor to perform any custom validations or authorizations.

### Request interceptor input payload example
<a name="gateway-interceptors-types-request-payload"></a>

The following example shows the input payload structure that a request interceptor Lambda function receives:

```
{
  "interceptorInputVersion": "1.0",
  "mcp": {
    "rawGatewayRequest": {
        "body": "<raw_request_body>"
    },
    "gatewayRequest" : {
        "path": "/mcp",
        "httpMethod": "POST",
        "headers": {
            "Accept": "application/json",
            "Authorization": "<bearer_token>",
            "SomeHeader": "headerValue1",
            "Mcp-Session-Id": "<session_id>",
            "User-Agent": "<client_user_agent>"
        },
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/list"
        }
    }
  }
}
```

**Note**  
The `headers` field is only included if `passRequestHeaders` is set to `true` in the interceptor configuration. The `gatewayResponse` field is not present for request interceptors since the response has not been generated yet.

### Request interceptor output payload example
<a name="gateway-interceptors-types-request-output"></a>

The following example shows the output payload structure that a request interceptor Lambda function should return:

```
{
  "interceptorOutputVersion": "1.0",
  "mcp": {
    "transformedGatewayRequest" : {
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/list"
        }
    },
    "transformedGatewayResponse" : {
        "statusCode": 200,
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {
                "<result_content>": "<result_value>"
            }
        }
    }
  }
}
```

**Important**  
Important notes about request interceptor output: \$1 The `interceptorOutputVersion` must be set to `"1.0"` . \$1 If the interceptor output contains a `transformedGatewayResponse` , the gateway will respond with that content immediately, even if `transformedGatewayRequest` is also provided. \$1 If both REQUEST and RESPONSE interceptors are configured and the REQUEST interceptor output contains a `transformedGatewayResponse` , the RESPONSE interceptor will still be invoked.

## Response interceptors
<a name="gateway-interceptors-types-response"></a>

The RESPONSE interceptor gets invoked before the gateway responds to the caller. You can use the RESPONSE interceptor to perform any custom redactions or additions to the response back to the caller.

### Response interceptor input payload example
<a name="gateway-interceptors-types-response-payload"></a>

The following example shows the input payload structure that a response interceptor Lambda function receives:

```
{
  "interceptorInputVersion": "1.0",
  "mcp": {
    "rawGatewayRequest": {
        "body": "<raw_request_body>"
    },
    "gatewayRequest" : {
        "path": "/mcp",
        "httpMethod": "POST",
        "headers": {
            "Accept": "application/json",
            "Authorization": "<bearer_token>",
            "SomeHeader": "headerValue1",
            "Mcp-Session-Id": "<session_id>",
            "User-Agent": "<client_user_agent>"
        },
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/list"
        }
    },
    "gatewayResponse" : {
        "statusCode": 200,
        "headers": {
            "SomeHeader": "headerValue1",
            "Mcp-Session-Id": "<session_id>"
        },
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {
                "tools": []
            }
        }
    }
  }
}
```

**Note**  
The `headers` field in `gatewayRequest` is only included if `passRequestHeaders` is set to `true` in the interceptor configuration. Response interceptors receive both the original request and the gateway’s response, allowing you to modify the response before it’s returned to the caller.

### Response interceptor output payload example
<a name="gateway-interceptors-types-response-output"></a>

The following example shows the output payload structure that a response interceptor Lambda function should return:

```
{
  "interceptorOutputVersion": "1.0",
  "mcp": {
    "transformedGatewayRequest" : {
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/list"
        }
    },
    "transformedGatewayResponse" : {
        "statusCode": 200,
        "body": {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {
                "<result_content>": "<result_value>"
            }
        }
    }
  }
}
```

**Important**  
Important notes about response interceptor output: \$1 The `interceptorOutputVersion` must be set to `"1.0"` . \$1 If the interceptor output contains a `transformedGatewayResponse` , the gateway will respond with that content immediately, even if `transformedGatewayRequest` is also provided. \$1 If both REQUEST and RESPONSE interceptors are configured and the REQUEST interceptor output contains a `transformedGatewayResponse` , the RESPONSE interceptor will still be invoked.

# Configuration
<a name="gateway-interceptors-configuration"></a>

Interceptors can be configured with an input parameter called `passRequestHeaders` 

## Interceptor input configuration
<a name="gateway-interceptors-configuration-input"></a>

When configuring interceptors, you can specify whether request headers should be passed to the interceptor function using the `passRequestHeaders` field:

 **passRequestHeaders**   
A boolean value that determines whether request headers are included in the interceptor input payload. When set to `true` , all request headers will be passed to your interceptor Lambda function. When set to `false` (default), headers are not included.  
Use caution when setting this to `true` as request headers may contain sensitive information such as authentication tokens and credentials.

## Configuring interceptors during gateway creation
<a name="gateway-interceptors-configuration-creation"></a>

The following examples show how to create a gateway with interceptors that have `passRequestHeaders` set to `true` :

**Example**  

1. With the AgentCore CLI, first create and deploy the gateway, then configure interceptors using the AWS CLI or AWS Python SDK (Boto3).

   Create the gateway:

   ```
   agentcore add gateway \
     --name my-gateway-with-headers \
     --authorizer-type CUSTOM_JWT \
     --discovery-url "https://cognito-idp.us-west-2.amazonaws.com/some-user-pool/.well-known/openid-configuration" \
     --allowed-audience "api.example.com"
   agentcore deploy
   ```

   After deployment, configure interceptors on the gateway using the AWS CLI `update-gateway` command or the AWS Python SDK (Boto3) as shown in the other tabs.

1. Use the following AWS CLI command to create a gateway with interceptors configured to pass request headers:

   ```
   aws bedrock-agentcore-control create-gateway \
     --name my-gateway-with-headers \
     --role-arn arn:aws:iam::123456789012:role/my-gateway-service-role \
     --protocol-type MCP \
     --authorizer-type CUSTOM_JWT \
     --authorizer-configuration '{
       "customJWTAuthorizer": {
         "discoveryUrl": "https://cognito-idp.us-west-2.amazonaws.com/some-user-pool/.well-known/openid-configuration",
         "allowedClients": ["clientId"]
       }
     }' \
     --interceptor-configurations '[{
         "interceptor": {
             "lambda": {
               "arn":"arn:aws:lambda:us-west-2:123456789012:function:my-interceptor-lambda"
             }
         },
         "interceptionPoints": ["REQUEST", "RESPONSE"],
         "inputConfiguration": {
           "passRequestHeaders": true
         }
     }]'
   ```

1. Use the following Python code with the AWS Python SDK (Boto3) to create a gateway with interceptors configured to pass request headers:

   ```
   import boto3
   
   # Initialize the AgentCore client
   client = boto3.client('bedrock-agentcore-control')
   
   # Create a gateway
   gateway = client.create_gateway(
     name="my-gateway-with-headers",
     roleArn="arn:aws:iam::123456789012:role/my-gateway-service-role",
     protocolType="MCP",
     authorizerType="CUSTOM_JWT",
     authorizerConfiguration={
         "customJWTAuthorizer": {
             "discoveryUrl": "https://cognito-idp.us-west-2.amazonaws.com/some-user-pool/.well-known/openid-configuration",
             "allowedClients": ["clientId"]
         }
     },
     interceptorConfigurations=[{
         "interceptor": {
             "lambda": {
               "arn":"arn:aws:lambda:us-west-2:123456789012:function:my-interceptor-lambda"
             }
         },
         "interceptionPoints": ["REQUEST", "RESPONSE"],
         "inputConfiguration": {
           "passRequestHeaders": True
         }
     }]
   )
   
   print(f"MCP Endpoint: {gateway['gatewayUrl']}")
   ```

## Updating interceptor configurations
<a name="gateway-interceptors-configuration-update"></a>

You can update existing gateway interceptor configurations to modify the `passRequestHeaders` setting or other parameters using the update gateway API operations.

# Examples
<a name="gateway-interceptors-examples"></a>

The following example shows a Python Lambda function that can handle both REQUEST and RESPONSE interceptor types. The REQUEST interceptor logs the MCP method and passes the request through unchanged, while the RESPONSE interceptor passes the response through unchanged.

## Pass-through interceptor
<a name="gateway-interceptors-examples-passthrough"></a>

This example demonstrates a simple interceptor that logs the MCP method for REQUEST interceptors and passes all requests and responses through unchanged:

```
import json
import logging

# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    """
    Lambda function that handles both REQUEST and RESPONSE interceptor types.

    For REQUEST interceptors: logs the MCP method and passes request through unchanged
    For RESPONSE interceptors: passes response through unchanged
    """
    # Extract the MCP data from the event
    mcp_data = event.get('mcp', {})

    # Check if this is a REQUEST or RESPONSE interceptor based on presence of gatewayResponse
    if 'gatewayResponse' in mcp_data and mcp_data['gatewayResponse'] != None:
        # This is a RESPONSE interceptor
        logger.info("Processing RESPONSE interceptor - passing through unchanged")

        # Pass through the original request and response unchanged
        response = {
          "interceptorOutputVersion": "1.0",
          "mcp": {
              "transformedGatewayResponse": {
                  "body": mcp_data.get('gatewayResponse', {}).get('body', {}),
                  "statusCode": mcp_data.get('gatewayResponse', {}).get('statusCode', 200)
              }
          }
        }
    else:
        # This is a REQUEST interceptor
        gateway_request = mcp_data.get('gatewayRequest', {})
        request_body = gateway_request.get('body', {})
        mcp_method = request_body.get('method', 'unknown')

        # Log the MCP method
        logger.info(f"Processing REQUEST interceptor - MCP method: {mcp_method}")

        # Pass through the original request unchanged
        response = {
          "interceptorOutputVersion": "1.0",
          "mcp": {
              "transformedGatewayRequest": {
                  "body": request_body,
              }
          }
        }

    return response
```

This Lambda function can be configured as both a REQUEST and RESPONSE interceptor. When configured as a REQUEST interceptor, it will log the MCP method from the incoming request. When configured as a RESPONSE interceptor, it will simply pass the response through unchanged. Both interceptor types return the original data without modification, making this a "pass-through" interceptor.