From eec81770ba2467a62755dff3fb09cc7d0e2f863c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 18 Oct 2026 10:27:46 +0000 Subject: [PATCH 1/2] [Automated] Draft docs (agentgateway): respect idle timeout on MCP path Signed-off-by: GitHub Action --- assets/agw-docs/pages/resiliency/timeouts/request.md | 4 ++++ .../kubernetes/main/release-notes/release-notes.md | 2 ++ .../documentation/configuration/resiliency/timeouts.md | 10 ++++++---- .../standalone/main/release-notes/release-notes.md | 2 ++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/assets/agw-docs/pages/resiliency/timeouts/request.md b/assets/agw-docs/pages/resiliency/timeouts/request.md index 98a906ccd..42027c294 100644 --- a/assets/agw-docs/pages/resiliency/timeouts/request.md +++ b/assets/agw-docs/pages/resiliency/timeouts/request.md @@ -1,5 +1,9 @@ Set the route-level timeout with an HTTPRoute or {{< reuse "agw-docs/snippets/policy.md" >}}. To ensure that your apps are available even if they are temporarily unavailable, you can use timeouts alongside [Retries]({{< link-hextra path="/documentation/resiliency/retry/" >}}). +{{< version exclude-if="1.0.x,1.1.x,1.2.x,1.3.x,1.4.x,1.5.x,1.6.x,2.2.x" >}} +For MCP routes, use a response idle timeout to end an upstream response body that stops sending frames. Set `spec.traffic.timeouts.responseIdle` in an {{< reuse "agw-docs/snippets/policy.md" >}} that attaches to the route, route rule, or Gateway listener. This timeout does not apply to responses that switch protocols. +{{< /version >}} + {{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} ## Set up timeouts {#timeouts} diff --git a/content/docs/kubernetes/main/release-notes/release-notes.md b/content/docs/kubernetes/main/release-notes/release-notes.md index 9e5547e0c..03ef0deb5 100644 --- a/content/docs/kubernetes/main/release-notes/release-notes.md +++ b/content/docs/kubernetes/main/release-notes/release-notes.md @@ -37,6 +37,7 @@ Review the release notes for agentgateway on Kubernetes. + **Traffic management** @@ -55,6 +56,7 @@ Review the release notes for agentgateway on Kubernetes. - The controller creates the session key Secret of each Gateway only when the Secret is missing, and no longer overwrites the key on reconcile, which could invalidate stateful MCP sessions. If the Secret cannot be created, the Gateway reports `Programmed: False` with the reason `DeploymentFailed`. - Stateful MCP requests with a session ID that the proxy cannot decode, such as after the session key changes, now return `404` instead of `400`, so clients start a new session. - MCP streamable HTTP `POST` requests can list `application/json` and `text/event-stream` in separate `Accept` header lines instead of one comma-separated value. +- MCP upstream responses now honor response idle timeout settings, so `responseIdleTimeout` or `spec.traffic.timeouts.responseIdle` ends a response when the MCP server stops sending body frames. **Security** diff --git a/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md b/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md index ccd74db63..14b8c0688 100644 --- a/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md +++ b/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md @@ -29,7 +29,7 @@ You can configure these types of timeouts on a route. |-|-| |`requestTimeout`|The time budget for an incoming request, including the total time across retries, before agentgateway returns response headers to the client. It does not limit the duration of a response body streamed to the client.| |`backendRequestTimeout`|The time budget for each request to a backend, including receiving response headers and any response body that agentgateway buffers. With retries, each attempt has its own budget. Receiving headers does not reset or end the deadline for buffered body reads. The deadline does not limit a response body that agentgateway streams without buffering.| -|`responseIdleTimeout`|The maximum time to wait for the next frame from the upstream response body. Time spent processing the response body, buffering response guardrails, transforming the response, or waiting for the client to receive data does not count. Use this setting to terminate a backend that stalls mid-stream, without capping how long a legitimately long upstream response might run. The timeout is disabled when the field is unset or set to zero, and it never applies to responses that switch protocols, so upgraded WebSocket and CONNECT tunnels are not terminated by it.| +|`responseIdleTimeout`|The maximum time to wait for the next frame from the upstream response body. Time spent processing the response body, buffering response guardrails, transforming the response, or waiting for the client to receive data does not count. For MCP traffic, the timeout watches the response stream from the selected MCP target. Use this setting to terminate a backend that stalls mid-stream, without capping how long a legitimately long upstream response might run. The timeout is disabled when the field is unset or set to zero, and it never applies to responses that switch protocols, so upgraded WebSocket and CONNECT tunnels are not terminated by it.| The `backendRequestTimeout` deadline also applies when agentgateway buffers a body for processing, such as CEL body inspection, non-SSE MCP responses, A2A responses, or external authorization response processing. For example, if the timeout is `5s` and the response headers arrive after `1s`, buffered body reads have only the remaining `4s`. @@ -44,6 +44,7 @@ mcp: policies: timeout: requestTimeout: 1s + responseIdleTimeout: 5s targets: - name: everything stdio: @@ -86,9 +87,9 @@ routes: # * The route-level timeout policy is accepted by agentgateway in all three # forms the page shows: routing-based (gateways), simplified MCP # (mcp.policies) and simplified LLM (llm.policies). -# * That `responseIdleTimeout` is a real field on both the routing-based and -# the simplified LLM forms. It is the newest of the three timeouts, so a -# rename upstream would otherwise reach the page as prose nobody can run. +# * That `responseIdleTimeout` is a real field on all three forms. It is the +# newest of the three timeouts, so a rename upstream would otherwise reach +# the page as prose nobody can run. # WHAT THIS TEST DOES NOT VALIDATE (and why): # * That requests actually time out at runtime — requires a slow backend the # page omits to exceed the configured deadline. @@ -117,6 +118,7 @@ mcp: policies: timeout: requestTimeout: 1s + responseIdleTimeout: 5s targets: - name: everything stdio: diff --git a/content/docs/standalone/main/release-notes/release-notes.md b/content/docs/standalone/main/release-notes/release-notes.md index 4645e06f4..f23442df5 100644 --- a/content/docs/standalone/main/release-notes/release-notes.md +++ b/content/docs/standalone/main/release-notes/release-notes.md @@ -32,6 +32,7 @@ Review the release notes for agentgateway standalone. + **Traffic management** @@ -50,6 +51,7 @@ Review the release notes for agentgateway standalone. - Stateful MCP requests with a session ID that the proxy cannot decode, such as after the session key changes, now return `404` instead of `400`, so clients start a new session. - MCP authentication derives JWKS URLs without a double slash when an issuer value ends with `/`. - MCP streamable HTTP `POST` requests can list `application/json` and `text/event-stream` in separate `Accept` header lines instead of one comma-separated value. +- MCP upstream responses now honor response idle timeout settings, so `responseIdleTimeout` or `spec.traffic.timeouts.responseIdle` ends a response when the MCP server stops sending body frames. **Operations** From da5c033564e1828db1b477531fe474e64fd8e03f Mon Sep 17 00:00:00 2001 From: Art Berger Date: Tue, 20 Oct 2026 09:41:54 -0400 Subject: [PATCH 2/2] revise per review Signed-off-by: Art Berger --- assets/agw-docs/pages/resiliency/timeouts/request.md | 4 ---- .../kubernetes/main/release-notes/release-notes.md | 2 +- .../documentation/configuration/resiliency/timeouts.md | 10 ++++------ .../standalone/main/release-notes/release-notes.md | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/assets/agw-docs/pages/resiliency/timeouts/request.md b/assets/agw-docs/pages/resiliency/timeouts/request.md index 42027c294..98a906ccd 100644 --- a/assets/agw-docs/pages/resiliency/timeouts/request.md +++ b/assets/agw-docs/pages/resiliency/timeouts/request.md @@ -1,9 +1,5 @@ Set the route-level timeout with an HTTPRoute or {{< reuse "agw-docs/snippets/policy.md" >}}. To ensure that your apps are available even if they are temporarily unavailable, you can use timeouts alongside [Retries]({{< link-hextra path="/documentation/resiliency/retry/" >}}). -{{< version exclude-if="1.0.x,1.1.x,1.2.x,1.3.x,1.4.x,1.5.x,1.6.x,2.2.x" >}} -For MCP routes, use a response idle timeout to end an upstream response body that stops sending frames. Set `spec.traffic.timeouts.responseIdle` in an {{< reuse "agw-docs/snippets/policy.md" >}} that attaches to the route, route rule, or Gateway listener. This timeout does not apply to responses that switch protocols. -{{< /version >}} - {{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} ## Set up timeouts {#timeouts} diff --git a/content/docs/kubernetes/main/release-notes/release-notes.md b/content/docs/kubernetes/main/release-notes/release-notes.md index 03ef0deb5..abf83f874 100644 --- a/content/docs/kubernetes/main/release-notes/release-notes.md +++ b/content/docs/kubernetes/main/release-notes/release-notes.md @@ -56,7 +56,7 @@ Review the release notes for agentgateway on Kubernetes. - The controller creates the session key Secret of each Gateway only when the Secret is missing, and no longer overwrites the key on reconcile, which could invalidate stateful MCP sessions. If the Secret cannot be created, the Gateway reports `Programmed: False` with the reason `DeploymentFailed`. - Stateful MCP requests with a session ID that the proxy cannot decode, such as after the session key changes, now return `404` instead of `400`, so clients start a new session. - MCP streamable HTTP `POST` requests can list `application/json` and `text/event-stream` in separate `Accept` header lines instead of one comma-separated value. -- MCP upstream responses now honor response idle timeout settings, so `responseIdleTimeout` or `spec.traffic.timeouts.responseIdle` ends a response when the MCP server stops sending body frames. +- MCP responses now honor the `spec.traffic.timeouts.responseIdle` response idle timeout, which previously did not apply on MCP routes. A response, including an SSE stream, that sends no data for longer than the timeout is now ended. **Security** diff --git a/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md b/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md index 14b8c0688..ccd74db63 100644 --- a/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md +++ b/content/docs/standalone/main/documentation/configuration/resiliency/timeouts.md @@ -29,7 +29,7 @@ You can configure these types of timeouts on a route. |-|-| |`requestTimeout`|The time budget for an incoming request, including the total time across retries, before agentgateway returns response headers to the client. It does not limit the duration of a response body streamed to the client.| |`backendRequestTimeout`|The time budget for each request to a backend, including receiving response headers and any response body that agentgateway buffers. With retries, each attempt has its own budget. Receiving headers does not reset or end the deadline for buffered body reads. The deadline does not limit a response body that agentgateway streams without buffering.| -|`responseIdleTimeout`|The maximum time to wait for the next frame from the upstream response body. Time spent processing the response body, buffering response guardrails, transforming the response, or waiting for the client to receive data does not count. For MCP traffic, the timeout watches the response stream from the selected MCP target. Use this setting to terminate a backend that stalls mid-stream, without capping how long a legitimately long upstream response might run. The timeout is disabled when the field is unset or set to zero, and it never applies to responses that switch protocols, so upgraded WebSocket and CONNECT tunnels are not terminated by it.| +|`responseIdleTimeout`|The maximum time to wait for the next frame from the upstream response body. Time spent processing the response body, buffering response guardrails, transforming the response, or waiting for the client to receive data does not count. Use this setting to terminate a backend that stalls mid-stream, without capping how long a legitimately long upstream response might run. The timeout is disabled when the field is unset or set to zero, and it never applies to responses that switch protocols, so upgraded WebSocket and CONNECT tunnels are not terminated by it.| The `backendRequestTimeout` deadline also applies when agentgateway buffers a body for processing, such as CEL body inspection, non-SSE MCP responses, A2A responses, or external authorization response processing. For example, if the timeout is `5s` and the response headers arrive after `1s`, buffered body reads have only the remaining `4s`. @@ -44,7 +44,6 @@ mcp: policies: timeout: requestTimeout: 1s - responseIdleTimeout: 5s targets: - name: everything stdio: @@ -87,9 +86,9 @@ routes: # * The route-level timeout policy is accepted by agentgateway in all three # forms the page shows: routing-based (gateways), simplified MCP # (mcp.policies) and simplified LLM (llm.policies). -# * That `responseIdleTimeout` is a real field on all three forms. It is the -# newest of the three timeouts, so a rename upstream would otherwise reach -# the page as prose nobody can run. +# * That `responseIdleTimeout` is a real field on both the routing-based and +# the simplified LLM forms. It is the newest of the three timeouts, so a +# rename upstream would otherwise reach the page as prose nobody can run. # WHAT THIS TEST DOES NOT VALIDATE (and why): # * That requests actually time out at runtime — requires a slow backend the # page omits to exceed the configured deadline. @@ -118,7 +117,6 @@ mcp: policies: timeout: requestTimeout: 1s - responseIdleTimeout: 5s targets: - name: everything stdio: diff --git a/content/docs/standalone/main/release-notes/release-notes.md b/content/docs/standalone/main/release-notes/release-notes.md index f23442df5..ba9fc4b85 100644 --- a/content/docs/standalone/main/release-notes/release-notes.md +++ b/content/docs/standalone/main/release-notes/release-notes.md @@ -51,7 +51,7 @@ Review the release notes for agentgateway standalone. - Stateful MCP requests with a session ID that the proxy cannot decode, such as after the session key changes, now return `404` instead of `400`, so clients start a new session. - MCP authentication derives JWKS URLs without a double slash when an issuer value ends with `/`. - MCP streamable HTTP `POST` requests can list `application/json` and `text/event-stream` in separate `Accept` header lines instead of one comma-separated value. -- MCP upstream responses now honor response idle timeout settings, so `responseIdleTimeout` or `spec.traffic.timeouts.responseIdle` ends a response when the MCP server stops sending body frames. +- MCP responses now honor the `responseIdleTimeout` response idle timeout, which previously did not apply on MCP routes. A response, including an SSE stream, that sends no data for longer than the timeout is now ended. **Operations**