Examples
The following examples show Python AWS Lambda functions for common interceptor use cases.
Pass-through interceptor
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.
Customize model routing with a request interceptor
For inference targets, the gateway selects a target from the model field using model-based routing. A REQUEST interceptor can rewrite model before the gateway evaluates routing, which lets you support a virtual model ID: a stable alias that does not map to any single configured model. The interceptor resolves the alias to a concrete target-qualified ID in the form {targetName}/{modelId}, so callers use one name while you control model selection centrally.
HTTP interceptor payload
Inference targets use the http interceptor payload, in which the request body is a base64-encoded string. For more information, see Interceptors for HTTP targets.
The following REQUEST interceptor resolves the virtual model ID auto-claude to a specific Anthropic model based on the size of the request input: anthropic/claude-haiku-4-5 for small requests, anthropic/claude-sonnet-4-6 for medium requests, and anthropic/claude-opus-4-7 for large requests. Callers send auto-claude, and the gateway routes each request to the resolved model on the anthropic target. Requests that use any other model ID pass through unchanged and follow normal model-based routing.
import base64 import json import logging logger = logging.getLogger() logger.setLevel(logging.INFO) # The virtual model ID that this interceptor resolves to a concrete # target-qualified ID in the form "{targetName}/{modelId}". VIRTUAL_MODEL = "auto-claude" # Approximate input-size thresholds (in characters) used to choose a model. SONNET_THRESHOLD = 2000 OPUS_THRESHOLD = 8000 def resolve_auto_claude(payload): """Choose an Anthropic model based on the size of the request input.""" input_size = len(json.dumps(payload.get("input", ""))) if input_size >= OPUS_THRESHOLD: return "anthropic/claude-opus-4-7" if input_size >= SONNET_THRESHOLD: return "anthropic/claude-sonnet-4-6" return "anthropic/claude-haiku-4-5" def lambda_handler(event, context): http = event.get("http", {}) request = http.get("gatewayRequest", {}) encoded_body = request.get("body") # Nothing to transform (for example, an empty body) - pass through. if not encoded_body: return {"interceptorOutputVersion": "1.0", "http": {}} try: payload = json.loads(base64.b64decode(encoded_body)) except (ValueError, TypeError) as exc: logger.warning("Passing request through unchanged; could not parse body: %s", exc) return {"interceptorOutputVersion": "1.0", "http": {}} # Only resolve the virtual model ID; let everything else route normally. if not isinstance(payload, dict) or payload.get("model") != VIRTUAL_MODEL: return {"interceptorOutputVersion": "1.0", "http": {}} resolved = resolve_auto_claude(payload) logger.info("Resolved virtual model %s -> %s", VIRTUAL_MODEL, resolved) payload["model"] = resolved encoded = base64.b64encode(json.dumps(payload).encode("utf-8")).decode("utf-8") return { "interceptorOutputVersion": "1.0", "http": { "transformedGatewayRequest": { "body": encoded } }, }
Attach the function to your gateway as a REQUEST interceptor. For instructions, see Configuring interceptors. The gateway service role must also have permission to invoke the function. For more information, see Permissions for interceptors.
To see the resolution, send a request that uses the auto-claude alias. The interceptor rewrites model to a concrete Anthropic model based on the input size, and the gateway routes the request to that model on the anthropic target.
awscurl --service bedrock-agentcore --region us-west-2 -X POST \ "https://GATEWAY_ID.gateway.bedrock-agentcore.us-west-2.amazonaws.com/inference/v1/responses" \ -H "Content-Type: application/json" \ -d '{"model": "auto-claude", "input": "Hello!", "max_output_tokens": 50}'