Skip to content

Make proxy request read timeout configurable - #6285

Merged
ChrisJBurns merged 3 commits into
stacklok:mainfrom
Sanskarzz:configurable-1
Sep 9, 2026
Merged

Make proxy request read timeout configurable#6285
ChrisJBurns merged 3 commits into
stacklok:mainfrom
Sanskarzz:configurable-1

Conversation

@Sanskarzz

Copy link
Copy Markdown
Contributor

Summary

Proxy HTTP servers currently protect against slow or stalled request uploads with a fixed 30-second read timeout.
That secure default cannot be adjusted for workloads that legitimately need more time, or tightened for environments that
want a shorter limit.

  • Add thv run --proxy-read-timeout and MCPServer spec.proxyReadTimeout configuration.
  • Persist the setting as a unit-explicit Go duration string in RunConfig and validate malformed or negative values before the proxy starts.
  • Thread positive overrides through the existing transport factory to the streamable, SSE, and transparent proxy implementations.
  • Preserve the existing 30-second default when the value is omitted or zero, so zero never disables slow-upload protection.
  • Update the architecture documentation, generated CLI/API references, OpenAPI output, and operator CRD manifests.

Fixes #5503

Type of change

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe):

Test plan

  • Unit tests (task test)
  • E2E tests (task test-e2e)
  • Linting (task lint-fix)
  • Manual testing (describe below)
  • Build (task build)
  • Operator integration tests (task operator-test-integration)

Generated artifacts were refreshed with task operator-generate,
task operator-manifests, task crdref-gen, and task docs.

API Compatibility

  • This PR does not break the v1beta1 API, OR the api-break-allowed label is applied and the migration guidance is described above.

The MCPServer API change is additive and optional. Existing resources continue to use the proxy's 30-second default.

Changes

File Change
cmd/thv/app/run_flags.go Add the --proxy-read-timeout CLI flag and RunConfig builder wiring.
pkg/runner/{config.go,config_builder.go,runner.go} Add the RunConfig field, normalize builder input, and validate persisted duration strings.
pkg/transport/{types/transport.go,factory.go,stdio.go,http.go} Carry the timeout through the existing transport configuration and proxy option paths.
cmd/thv-operator/api/v1beta1/mcpserver_types.go Add the optional duration field and reject negative values at admission.
cmd/thv-operator/controllers/mcpserver_runconfig.go Translate the MCPServer field into the shared RunConfig contract.
Tests Cover builder/runtime validation, transport propagation, CRD translation, and Kubernetes admission behavior.
Docs and generated artifacts Document the setting and refresh CLI, OpenAPI, CRD, deepcopy, and operator API output.

Does this introduce a user-facing change?

Yes. CLI users can set thv run --proxy-read-timeout <duration>, and Kubernetes users can set MCPServer.spec.proxyReadTimeout.
Both accept Go duration values such as 45s or 2m. Omitting the setting or specifying zero retains the 30-second default.

Implementation plan

Approved implementation plan
  1. Add CLI and MCPServer API inputs using existing duration conventions.
  2. Store the value in the shared RunConfig contract as a Go duration string.
  3. Validate and parse the value at the builder, Kubernetes admission, and
    persisted RunConfig boundaries.
  4. Pass the resolved duration through transport.Config and the existing
    transport factory to each proxy's typed WithReadTimeout option.
  5. Keep proxy constructors as the source of the 30-second default and omit the
    option for zero values.
  6. Add focused unit and operator integration coverage, then regenerate the
    affected documentation and CRD/OpenAPI artifacts.

Special notes for reviewers

  • This builds on the proxy-level read timeout protection added by Add request timeouts to MCP proxy servers #5501; it configures the existing implementation rather than adding another timeout
    mechanism.
  • The setting bounds reading request headers and bodies. It does not configure WriteTimeout, so long-lived SSE responses remain unaffected.
  • Zero intentionally means "use the secure default," not "disable the timeout."
  • The management API and thv mcp serve retain their independent timeout behavior.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.72727% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 78.82%. Comparing base (0972fb8) to head (b7f0221).

Files with missing lines Patch % Lines
pkg/runner/runner.go 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6285      +/-   ##
==========================================
+ Coverage   78.77%   78.82%   +0.04%     
==========================================
  Files         778      778              
  Lines       77490    77534      +44     
==========================================
+ Hits        61042    61114      +72     
+ Misses      16443    16415      -28     
  Partials        5        5              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Sanskarzz
Sanskarzz force-pushed the configurable-1 branch 9 times, most recently from f6df8b5 to 2f34254 Compare August 16, 2026 17:22
@Sanskarzz
Sanskarzz force-pushed the configurable-1 branch 2 times, most recently from 13af64e to 25c0bcd Compare August 25, 2026 14:59
@Sanskarzz
Sanskarzz force-pushed the configurable-1 branch 5 times, most recently from 5502064 to 3f617bc Compare September 4, 2026 17:01

@ChrisJBurns ChrisJBurns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Agent Consensus Review

Agents consulted: general-quality, transport-architecture, kubernetes-api-tests, codex

Consensus Summary

# Finding Consensus Severity Action
1 Workload upgrades drop the configured proxy read timeout 10/10 HIGH Fix
2 Transport factory silently accepts negative read timeouts 9/10 LOW Fix
3 Tests do not prove the new configuration changes live proxy behavior 9/10 MEDIUM Fix
4 Transport documentation needs timeout wording cleanup 7/10 LOW Fix

Overall

This feature uses the right shape: a unit-explicit optional RunConfig field, API-level Kubernetes validation, runtime validation for persisted configs, and typed propagation through all three proxy implementations while preserving the secure 30-second default. The v1beta1 change is additive, generated artifacts are synchronized, and the implementation does not introduce a WriteTimeout that could disrupt SSE responses.

One correctness issue should be fixed before merge: ToolHive's workload-upgrade rebuild path does not preserve the new field, so thv upgrade silently resets a custom timeout to 30 seconds. The remaining findings concern fail-loud validation at the exported transport factory boundary and coverage that currently verifies stored values rather than the live proxy behavior required by the new CLI and operator entry points.

Documentation

docs/arch/03-transport-architecture.md should qualify the preceding statement about transparent proxies having no request timeout so it clearly refers to the MCP response-correlation timeout, and remove the trailing whitespace on the new paragraph.


Generated with Claude Code

Comment thread pkg/runner/config.go
Comment thread pkg/transport/factory.go
Comment thread pkg/transport/stdio_test.go
Comment thread docs/arch/03-transport-architecture.md
@ChrisJBurns

Copy link
Copy Markdown
Collaborator

@Sanskarzz I merged one of your PR's and there's a couple conflicts on this PR - you'll need to rebase with main and I can re-review

@ChrisJBurns ChrisJBurns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Agent Consensus Review

Agents consulted: code-reviewer, toolhive-expert, kubernetes-expert, codex

Consensus Summary

# Finding Consensus Severity Action
1 Workloads API update drops the configured proxy read timeout 10/10 MEDIUM Follow up
2 Public configuration entry points lack live E2E coverage 9/10 MEDIUM Follow up

Overall

This PR adds a per-workload proxy request read timeout across the CLI, persisted RunConfig, transport factory, all proxy implementations, and the MCPServer CRD. The timeout semantics, validation, upgrade preservation, generated API artifacts, and top-level CRD placement are coherent and backward-compatible; resourceOverrides.proxyDeployment is infrastructure-only and is not a better home for this behavioral setting.

Two non-blocking integration gaps remain. Workloads API edits can silently discard a timeout configured through the CLI, and the public CLI and MCPServer configuration paths do not yet have live end-to-end coverage. The focused transport and operator tests otherwise provide strong confidence in the implementation.


Generated with Claude Code

Comment thread pkg/runner/config.go
Comment thread pkg/transport/read_timeout_test.go
@Sanskarzz
Sanskarzz force-pushed the configurable-1 branch 2 times, most recently from c654d91 to 63870b9 Compare September 8, 2026 05:20
ChrisJBurns
ChrisJBurns previously approved these changes Sep 8, 2026

@ChrisJBurns ChrisJBurns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Agent Consensus Review

Agents consulted: code-reviewer, toolhive-expert, kubernetes-expert, codex

Consensus Summary

No actionable findings.

Overall

This PR consistently threads a per-workload proxy request read timeout through the CLI, persisted RunConfig, Workloads API, transport factory, all proxy implementations, and the MCPServer CRD. The top-level spec.proxyReadTimeout placement is appropriate for workload behavior, and omission or zero retains the 30-second default while negative values are rejected.

The Workloads API preserves the timeout across GET-to-edit round trips, and both public configuration paths have live coverage. The slow-upload operator test exercises the configured inbound deadline because ToolHive consumes the request body before contacting the backend.

The branch currently conflicts with main in pkg/api/v1/workload_service.go; the rebase should retain both this PR's timeout parsing and main's OIDC client-secret preservation. The failing operator integration check is an unrelated timeout in existing MCPRemoteProxy/OIDC deletion coverage.


Generated with Claude Code

@ChrisJBurns

Copy link
Copy Markdown
Collaborator

@Sanskarzz Have approved but you've got a couple more conflicts to resolve

Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
@ChrisJBurns
ChrisJBurns merged commit 649b242 into stacklok:main Sep 9, 2026
43 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 10, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make proxy request read timeout configurable

2 participants