Skip to content

mcp_transcoder: add response streaming option.#45076

Merged
tyxia merged 8 commits into
envoyproxy:mainfrom
guoyilin42:tcrs
May 27, 2026
Merged

mcp_transcoder: add response streaming option.#45076
tyxia merged 8 commits into
envoyproxy:mainfrom
guoyilin42:tcrs

Conversation

@guoyilin42

Copy link
Copy Markdown
Contributor

Commit Message: mcp_transcoder: add response streaming option.
Additional Description:
This PR adds a text_content_streaming_enabled configuration option to the mcp_json_rest_bridge HTTP filter.

Currently, the transcoder buffers the entire response before sending it to the client. With this option enabled, the filter supports streaming unstructured text responses directly to the client chunk-by-chunk.

Implementation details:

  • In encodeHeaders, the filter strips the Content-Length header and pre-builds a JSON-RPC envelope (prefix and suffix).
  • In encodeData, it JSON-escapes each arriving text chunk on-the-fly and streams it to the client wrapped inside the pre-built JSON-RPC envelope.
  • Added comprehensive unit and integration tests to verify streaming behavior, error handling, and JSON escaping of special characters.

Risk Level: Low
Testing: Unit and Integration tests.
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A

Signed-off-by: Yilin Guo <guoyilin@google.com>
@repokitteh-read-only

Copy link
Copy Markdown

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to (api/envoy/|docs/root/api-docs/).
envoyproxy/api-shepherds assignee is @markdroth
CC @envoyproxy/api-watchers: FYI only for changes made to (api/envoy/|docs/root/api-docs/).

🐱

Caused by: #45076 was opened by guoyilin42.

see: more, trace.

@guoyilin42

Copy link
Copy Markdown
Contributor Author

/gemini review

cc @paulhong01

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements streaming transcoding for unstructured text responses in the MCP JSON-REST bridge filter, allowing responses to be delivered in JSON-escaped chunks without full buffering. The implementation includes updates to the filter's header and data encoding logic, along with comprehensive unit and integration tests. Review feedback suggests adding error handling when locating the JSON-RPC envelope split point, enforcing the maximum response body size limit during streaming to prevent resource exhaustion, and optimizing the escaping process to minimize data copies.

@tyxia tyxia self-assigned this May 15, 2026

@tyxia tyxia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for the work! Let's align on the API first

Signed-off-by: Yilin Guo <guoyilin@google.com>
Signed-off-by: Yilin Guo <guoyilin@google.com>
Signed-off-by: Yilin Guo <guoyilin@google.com>
@guoyilin42

Copy link
Copy Markdown
Contributor Author

#44146

@tyxia

tyxia commented May 22, 2026

Copy link
Copy Markdown
Member

/gemini review

API LGTM, i am reviewing the implementation now

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces streaming transcoding for unstructured text responses within the MCP JSON-REST bridge filter, controlled by a new text_content_streaming_enabled configuration field. The implementation allows for JSON-escaping and wrapping response chunks on-the-fly, bypassing full body buffering. Review feedback suggests adding a safety check for the string search operation used to build the JSON-RPC envelope and evaluating whether a total response size limit should be enforced during streaming to mitigate potential resource exhaustion.

@tyxia tyxia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for contribution! Nice work @guoyilin42

API LGTM, and first pass on implementation

Signed-off-by: Yilin Guo <guoyilin@google.com>
Signed-off-by: Yilin Guo <guoyilin@google.com>

@tyxia tyxia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, module comments below.

Thanks!

Signed-off-by: Yilin Guo <guoyilin@google.com>
Signed-off-by: Yilin Guo <guoyilin@google.com>
// output: [prefix+escaped_chunk1] → [escaped_chunk2] → [escaped_chunk3+suffix]
//
// Disabled by default.
bool text_content_streaming_enabled = 4;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to use google.protobuf.BoolValue here, so that data planes can have this be enabled by default if they want to? Or do we expect that the data plane would always want to have this disabled by default?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We had a discussion before with @tyxia and thought we'd like to have this disabled by default for future structured content support which needs buffering anyway. Also could you elaborate more about how would google.protobuf.BoolValue make it enabled by default?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mark meant that: BoolValue is wrapper message for bool can be set to True by default in the future, while bool is only false by default.

For this case, we won't need to set it to True by default even in the future, so bool is fine here.

@tyxia tyxia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM Thanks!

// buffering the full response body.
if (!streaming_json_prefix_.empty()) {
uint64_t len = data.length();
// Note: An empty chunk can arrive when the upstream uses the body + trailer pattern (end_stream

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You probably meant a TODO, as encodeTrailer is not handled yet.
No need to address this in this PR.

@markdroth

Copy link
Copy Markdown
Contributor

/lgtm api

@tyxia

tyxia commented May 27, 2026

Copy link
Copy Markdown
Member

/retest

@tyxia tyxia merged commit a7db8ba into envoyproxy:main May 27, 2026
29 of 30 checks passed
nezdolik pushed a commit to nezdolik/envoy that referenced this pull request Jun 16, 2026
Commit Message: mcp_transcoder: add response streaming option.
Additional Description:
This PR adds a `text_content_streaming_enabled` configuration option to
the `mcp_json_rest_bridge` HTTP filter.

Currently, the transcoder buffers the entire response before sending it
to the client. With this option enabled, the filter supports streaming
unstructured text responses directly to the client chunk-by-chunk.

Implementation details:
* In `encodeHeaders`, the filter strips the `Content-Length` header and
pre-builds a JSON-RPC envelope (prefix and suffix).
* In `encodeData`, it JSON-escapes each arriving text chunk on-the-fly
and streams it to the client wrapped inside the pre-built JSON-RPC
envelope.
* Added comprehensive unit and integration tests to verify streaming
behavior, error handling, and JSON escaping of special characters.

Risk Level: Low
Testing: Unit and Integration tests.
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A

---------

Signed-off-by: Yilin Guo <guoyilin@google.com>
@guoyilin42 guoyilin42 deleted the tcrs branch June 25, 2026 20:33
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.

4 participants