

# Use MCP sessions with your AgentCore gateway
<a name="gateway-sessions"></a>

MCP sessions enable stateful interactions between clients and your AgentCore gateway. When sessions are enabled, the gateway generates a unique session identifier during initialization and maintains state across multiple requests, enabling advanced MCP features such as elicitation and sampling.

## Benefits of using sessions
<a name="gateway-sessions-benefits"></a>

 **Stateful MCP server target interactions**   
The gateway stores the MCP server target’s session ID and reuses it on subsequent tool calls. This avoids re-initialization on every request and enables targets to maintain context across calls.

 **Faster responses with AgentCore Runtime targets**   
When the target’s session is reused, AgentCore Runtime doesn’t need to cold-start a new MCP server connection on each request, resulting in faster response times.

 **Enables advanced MCP features**   
Sessions are a prerequisite for [elicitation](gateway-mcp-elicitation.md) and [sampling](gateway-mcp-sampling.md), which require tracking state across multiple requests.

 **User-scoped security (authenticated gateways)**   
For gateways with inbound authentication, sessions are bound to the verified user identity, preventing session hijacking.

## Enable sessions on your gateway
<a name="gateway-sessions-enable"></a>

To enable sessions, specify a `sessionConfiguration` in the `protocolConfiguration.mcp` field when creating or updating your gateway.

```
{
  "protocolConfiguration": {
    "mcp": {
      "sessionConfiguration": {
        "sessionTimeoutInSeconds": 3600
      }
    }
  }
}
```

The `sessionTimeoutInSeconds` parameter is optional. If omitted, the default timeout is 3600 seconds (1 hour). Valid range is 900 (15 minutes) to 28800 (8 hours). The timeout is absolute, calculated from the first `initialize` request.

To also enable features that depend on sessions such as elicitation and sampling, you must additionally enable response streaming:

```
{
  "protocolConfiguration": {
    "mcp": {
      "sessionConfiguration": {
        "sessionTimeoutInSeconds": 3600
      },
      "streamingConfiguration": {
        "enableResponseStreaming": true
      }
    }
  }
}
```

**Note**  
When sessions are enabled on a gateway, you cannot include `Mcp-Session-Id` in the `metadataConfiguration` of a gateway target’s header propagation settings. The gateway manages session IDs internally. Attempting to do so returns an HTTP 400 Bad Request error.

## Session lifecycle
<a name="gateway-sessions-lifecycle"></a>

The session lifecycle follows the MCP protocol’s initialization flow:

1. The client sends an `initialize` request to the gateway.

1. The gateway creates a session, stores session metadata, and returns a unique `Mcp-Session-Id` in the response header.

1. The client includes the `Mcp-Session-Id` header in all subsequent requests.

1. The gateway validates the session existence, expiry, and user identity (for authenticated gateways) on each request.

1. When the session times out or the client disconnects, the session expires.

On the first tool call to an MCP server target within a session, the gateway initializes a connection with the target and stores the target’s session ID. Subsequent tool calls to the same target reuse this stored session ID, avoiding repeated initialization.

## User identity and session scoping
<a name="gateway-sessions-user-scoping"></a>

Sessions are scoped to the authenticated user identity to prevent session hijacking. The gateway derives the user identity differently depending on the inbound authentication method configured on your gateway:


| Authentication method | User identifier | Behavior | 
| --- | --- | --- | 
| OAuth / OIDC |  `sub` claim from the JWT token | Fully scoped. Only the user who created the session can use it. The `sub` claim is required by the OIDC specification, is locally unique within the issuer, case-sensitive, and never reassigned. | 
|  AWS IAM (SigV4) | Principal ARN | Fully scoped. Only the IAM principal who created the session can use it. The Principal ARN is globally unique across AWS, immutable for the IAM entity’s lifetime. Example: `arn:aws:iam::123456789012:user/john-doe`  | 
| No authentication | None |  **No user scoping.** Sessions are available but not bound to any identity. Anyone with the session ID can interact with the session. | 

**Important**  
For gateways with no inbound authentication, sessions carry a **session hijacking risk** as described in the [MCP specification security considerations](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation#security-considerations). If a session ID is leaked or guessed, another party can resume the session. Use unauthenticated sessions only for development and testing, not for production workloads handling sensitive data.

For authenticated gateways, if a different user attempts to use an existing session ID, the gateway returns HTTP 404 Not Found — the session is invisible to other users.

## Session timeout and expiry
<a name="gateway-sessions-timeout"></a>

The session timeout is calculated from the first `initialize` request. After the timeout period, the session expires and cannot be used.
+  **Default timeout**: 3600 seconds (1 hour)
+  **Configurable range**: 900 seconds (15 minutes) to 28800 seconds (8 hours)

If an MCP server target’s session expires before the gateway session timeout, the gateway transparently re-initializes with the target and updates the stored target session ID. The gateway session remains active.

## Error handling
<a name="gateway-sessions-errors"></a>


| Scenario | HTTP status | Description | 
| --- | --- | --- | 
| Missing `Mcp-Session-Id` header on a session-enabled gateway | 400 Bad Request | All requests after `initialize` must include the session header. | 
| Invalid or expired session ID | 404 Not Found | The session does not exist or has timed out. | 
| Different user attempts to use another user’s session (authenticated gateways) | 404 Not Found | The session is invisible to other users. | 
|  `Mcp-Session-Id` in target `metadataConfiguration` when sessions are enabled | 400 Bad Request | Returned at control plane when creating or updating a target. | 

## Code samples
<a name="gateway-sessions-examples"></a>

**Example**  

1. Send an `initialize` request to start a session:

   ```
   curl -X POST \
     https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     -d '{
       "jsonrpc": "2.0",
       "id": "init-request",
       "method": "initialize",
       "params": {
         "protocolVersion": "2025-06-18",
         "capabilities": {},
         "clientInfo": {
           "name": "my-agent",
           "version": "1.0.0"
         }
       }
   }'
   ```

   The response includes the `Mcp-Session-Id` header:

   ```
   HTTP/1.1 200 OK
   Mcp-Session-Id: session-abc123def456
   Content-Type: application/json
   
   {
     "jsonrpc": "2.0",
     "id": "init-request",
     "result": {
       "protocolVersion": "2025-06-18",
       "capabilities": {
         "tools": { "listChanged": true }
       },
       "serverInfo": {
         "name": "agentcore-gateway",
         "version": "1.0.0"
       }
     }
   }
   ```

1. Include the session ID in subsequent requests:

   ```
   curl -X POST \
     https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     -H "Mcp-Session-Id: session-abc123def456" \
     -d '{
       "jsonrpc": "2.0",
       "id": "call-tool-request",
       "method": "tools/call",
       "params": {
         "name": "searchProducts",
         "arguments": {
           "query": "wireless headphones"
         }
       }
   }'
   ```

1. 

   ```
   import requests
   import json
   
   gateway_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp"
   headers = {
       "Content-Type": "application/json",
       "Accept": "application/json",
       "Authorization": "Bearer YOUR_ACCESS_TOKEN"
   }
   
   # Step 1: Initialize and get session ID
   init_response = requests.post(gateway_url, headers=headers, json={
       "jsonrpc": "2.0",
       "id": "init-request",
       "method": "initialize",
       "params": {
           "protocolVersion": "2025-06-18",
           "capabilities": {},
           "clientInfo": {"name": "my-agent", "version": "1.0.0"}
       }
   })
   
   session_id = init_response.headers["Mcp-Session-Id"]
   print(f"Session ID: {session_id}")
   
   # Step 2: Use session ID in subsequent requests
   headers["Mcp-Session-Id"] = session_id
   
   tool_response = requests.post(gateway_url, headers=headers, json={
       "jsonrpc": "2.0",
       "id": "call-tool-request",
       "method": "tools/call",
       "params": {
           "name": "searchProducts",
           "arguments": {"query": "wireless headphones"}
       }
   })
   
   print(json.dumps(tool_response.json(), indent=2))
   ```

1. 

   ```
   from mcp import ClientSession
   from mcp.client.streamable_http import streamablehttp_client
   import asyncio
   
   async def use_session(url, token):
       headers = {"Authorization": f"Bearer {token}"}
   
       async with streamablehttp_client(url=url, headers=headers) as (
           read_stream, write_stream, _
       ):
           async with ClientSession(read_stream, write_stream) as session:
               # Initialize - session ID is managed automatically by the MCP client
               init_response = await session.initialize()
               print(f"Initialized: {init_response}")
   
               # Subsequent calls reuse the session automatically
               tool_response = await session.call_tool(
                   name="searchProducts",
                   arguments={"query": "wireless headphones"}
               )
               print(f"Tool response: {tool_response}")
               return tool_response
   
   asyncio.run(use_session(
       url="https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp",
       token="YOUR_ACCESS_TOKEN"
   ))
   ```

1. 

   ```
   from mcp.client.streamable_http import streamablehttp_client
   from strands import Agent
   from strands.tools.mcp import MCPClient
   
   mcp_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp"
   access_token = "YOUR_ACCESS_TOKEN"
   
   mcp_client = MCPClient(
       lambda: streamablehttp_client(
           mcp_url, headers={"Authorization": f"Bearer {access_token}"}
       )
   )
   
   # Strands MCP client handles session management automatically
   with mcp_client:
       agent = Agent(tools=mcp_client.list_tools_sync())
       response = agent("Search for wireless headphones")
       print(response)
   ```