Skip to content

fix: correctly pass headers in mcp stdio connections#11804

Merged
jordanrfrazier merged 10 commits intomainfrom
cherry-pick-mcp-header
Feb 19, 2026
Merged

fix: correctly pass headers in mcp stdio connections#11804
jordanrfrazier merged 10 commits intomainfrom
cherry-pick-mcp-header

Conversation

@jordanrfrazier
Copy link
Collaborator

@jordanrfrazier jordanrfrazier commented Feb 18, 2026

Cherry-picked from release branch to build a nightly with this fix.

ceda88f
6e6ace1
4ff340d

Summary by CodeRabbit

Release Notes

  • New Features

    • Improved dictionary field configuration with enhanced support for dict-type values in various input formats
    • Added header parameter injection capability for MCP stdio mode operations
  • Improvements

    • Enhanced command parsing and assembly for MCP stdio operations across Windows and Linux platforms
    • Refined parameter handling for dictionary field configuration when receiving list-formatted inputs

jordanrfrazier and others added 3 commits February 18, 2026 10:02
* Correctly parse dicts from tweaks

* Add test

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* Fix dict handling of different formats

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* cmp index

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request extends dict-type field handling in apply_tweaks logic across both backend and LFX packages, with special unwrapping for {"value": ...} wrappers. The parameter handler is updated to detect and convert lists with "key"/"value" patterns into flat dicts. MCP utilities are enhanced with improved command parsing using shlex and logic to inject headers into stdio commands with intelligent placement.

Changes

Cohort / File(s) Summary
Apply tweaks dict handling
src/backend/base/langflow/processing/process.py, src/lfx/src/lfx/processing/process.py
Extended apply_tweaks to handle dict-type tweaks by assigning dict values directly to template fields, with special unwrapping for {"value": ...} wrappers that complements existing NestedDict and generic dict/list handling.
Parameter handler dict conversion
src/lfx/src/lfx/graph/vertex/param_handler.py
Enhanced _handle_dict_field to detect lists containing items with both "key" and "value" keys, converting them into flat dicts; maintains fallback behavior for generic dict lists.
MCP stdio command enhancements
src/lfx/src/lfx/base/mcp/util.py
Improved command parsing with shlex.split/shlex.join for safer quoting and Windows path handling. Added header injection logic into stdio commands with intelligent placement (before existing --headers flag, before last positional arg, or appended).
Test coverage
src/backend/tests/unit/base/mcp/test_mcp_util.py, src/backend/tests/unit/test_process.py, src/lfx/tests/unit/graph/vertex/test_param_handler.py
New test suites validating dict field handling in apply_tweaks, MCP header injection behavior with multiple scenarios (existing headers, multiple headers, appending), and parameter handler dict-to-list conversions including {"key"/"value"} pattern recognition.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❓ Inconclusive Test files mentioned in summary (test_mcp_util.py, test_process.py, test_param_handler.py) could not be located in repository despite comprehensive search across all branches and git objects. Verify PR branch loads all commits and test files exist; if missing, re-evaluate actual test coverage for the code modifications.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately reflects the primary objective: fixing header passing in MCP stdio connections, which is the main feature change across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 91.30% which is sufficient. The required threshold is 80.00%.
Test Quality And Coverage ✅ Passed The pull request includes comprehensive, behavior-driven tests for dict-tweak logic in apply_tweaks and async update_tools function, with proper coverage of plain, wrapped, and default-override cases.
Test File Naming And Structure ✅ Passed Test files follow correct naming patterns (test_*.py) with proper test class names using camelCase and Test prefix. File organization in unit test directories is appropriate.
Excessive Mock Usage Warning ✅ Passed Test files use mocks appropriately for external dependencies while testing real behavior of core logic with focused assertions.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cherry-pick-mcp-header

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
Copy link
Collaborator

@Adam-Aghili Adam-Aghili left a comment

Choose a reason for hiding this comment

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

LGTM!

@github-actions github-actions bot added lgtm This PR has been approved by a maintainer bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 14.58333% with 41 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.19%. Comparing base (74a9197) to head (ced8ecc).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/lfx/src/lfx/base/mcp/util.py 0.00% 25 Missing ⚠️
src/lfx/src/lfx/processing/process.py 0.00% 13 Missing ⚠️
src/lfx/src/lfx/graph/vertex/param_handler.py 50.00% 3 Missing ⚠️

❌ Your patch status has failed because the patch coverage (14.58%) is below the target coverage (40.00%). You can increase the patch coverage or adjust the target coverage.
❌ Your project status has failed because the head coverage (42.03%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #11804      +/-   ##
==========================================
- Coverage   35.21%   35.19%   -0.02%     
==========================================
  Files        1521     1521              
  Lines       72967    73008      +41     
  Branches    10938    10951      +13     
==========================================
+ Hits        25695    25698       +3     
- Misses      45876    45914      +38     
  Partials     1396     1396              
Flag Coverage Δ
backend 55.67% <100.00%> (-0.01%) ⬇️
lfx 42.03% <6.81%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/backend/base/langflow/processing/process.py 78.37% <100.00%> (+1.74%) ⬆️
src/lfx/src/lfx/graph/vertex/param_handler.py 83.68% <50.00%> (-1.19%) ⬇️
src/lfx/src/lfx/processing/process.py 17.07% <0.00%> (-1.52%) ⬇️
src/lfx/src/lfx/base/mcp/util.py 0.00% <0.00%> (ø)

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/backend/base/langflow/processing/process.py`:
- Around line 161-168: The current unwrapping logic in process.py (inside the
field_type == "dict" branch where template_data[tweak_name]["value"] is set)
naively unwraps any single-key dict {"value": ...}, which will incorrectly
transform a user-intended dict that legitimately has "value" as a key; update
the logic to only unwrap when the inner value is clearly a template-wrapped
scalar/sequence (e.g., if tweak_value == {"value": v} and v is not a dict —
i.e., isinstance(v, (str, int, float, list, bool, None)) — or when a specific
template marker/flag is present), otherwise preserve the original dict intact;
change the code around tweak_value/tweak_name assignment to test the type of
tweak_value["value"] (or check for an explicit marker) before assigning
template_data[tweak_name]["value"] = tweak_value["value"], otherwise assign the
whole tweak_value.

In `@src/lfx/src/lfx/base/mcp/util.py`:
- Around line 1591-1612: The positional-arg heuristic is fragile: instead of
checking not args[-1].startswith("-"), scan the args list treating tokens that
start with "-" as flags and skipping their immediate value (so flag+value are
kept together) to locate the true last positional argument index; then insert
extra_args before that positional token (or append if none). Update the logic
around the args handling block that builds extra_args and the branch that
currently checks args and not args[-1].startswith("-") (the code that mutates
args and later calls shlex.join) to compute a safe insert index by iterating
over args and skipping values after flags before performing the insertion.

Comment on lines +161 to +168
elif field_type == "dict" and isinstance(tweak_value, dict):
# Dict fields: set the dict directly as the value.
# If the tweak is wrapped in {"value": <actual>}, unwrap it
# to support the template-format style (e.g. from UI exports).
if len(tweak_value) == 1 and "value" in tweak_value:
template_data[tweak_name]["value"] = tweak_value["value"]
else:
template_data[tweak_name]["value"] = tweak_value
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Ambiguous unwrapping of {"value": ...} for dict fields.

If a user legitimately wants to set a dict-type field to {"value": "some_actual_data"} (where "value" is a real key in their data), this heuristic will unwrap it and assign "some_actual_data" instead. This edge case may be unlikely for headers but could surprise users with generic dict fields.

Consider documenting this trade-off or adding a more explicit signal (e.g., checking if the inner value is a list of key/value pairs) to disambiguate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/base/langflow/processing/process.py` around lines 161 - 168, The
current unwrapping logic in process.py (inside the field_type == "dict" branch
where template_data[tweak_name]["value"] is set) naively unwraps any single-key
dict {"value": ...}, which will incorrectly transform a user-intended dict that
legitimately has "value" as a key; update the logic to only unwrap when the
inner value is clearly a template-wrapped scalar/sequence (e.g., if tweak_value
== {"value": v} and v is not a dict — i.e., isinstance(v, (str, int, float,
list, bool, None)) — or when a specific template marker/flag is present),
otherwise preserve the original dict intact; change the code around
tweak_value/tweak_name assignment to test the type of tweak_value["value"] (or
check for an explicit marker) before assigning
template_data[tweak_name]["value"] = tweak_value["value"], otherwise assign the
whole tweak_value.

Comment on lines +1591 to +1612
args = list(server_config.get("args", []))
env = server_config.get("env", {})
full_command = " ".join([command, *args])
# For stdio mode, inject component headers as --headers CLI args.
# This enables passing headers through proxy tools like mcp-proxy
# that forward them to the upstream HTTP server.
if headers:
extra_args = []
for key, value in headers.items():
extra_args.extend(["--headers", key, str(value)])
if "--headers" in args:
# Insert before the existing --headers flag so all header
# flags are grouped together
idx = args.index("--headers")
for i, arg in enumerate(extra_args):
args.insert(idx + i, arg)
elif args and not args[-1].startswith("-"):
# No existing --headers flag; insert before the last positional arg
# (typically the URL in mcp-proxy commands)
args = args[:-1] + extra_args + [args[-1]]
else:
args.extend(extra_args)
full_command = shlex.join([command, *args])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Heuristic for positional arg detection at line 1606 is fragile.

The check not args[-1].startswith("-") assumes the last non-flag arg is a positional argument (like a URL). However, flag values such as "streamablehttp" (from --transport streamablehttp) also don't start with "-". If the args list ends with a flag value rather than a positional URL, headers would be injected in the wrong position, breaking the preceding flag.

This works for the known mcp-proxy pattern (... <URL> at the end), but a more robust approach could inspect pairs of flag + value to avoid misidentifying flag values as positional args.

Example of the failure case
# args = ["mcp-proxy", "--transport", "streamablehttp"]
# Last arg "streamablehttp" doesn't start with "-"
# Code would insert headers before "streamablehttp", producing:
# ["mcp-proxy", "--transport", "--headers", "auth", "token", "streamablehttp"]
# This breaks the --transport flag
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lfx/src/lfx/base/mcp/util.py` around lines 1591 - 1612, The
positional-arg heuristic is fragile: instead of checking not
args[-1].startswith("-"), scan the args list treating tokens that start with "-"
as flags and skipping their immediate value (so flag+value are kept together) to
locate the true last positional argument index; then insert extra_args before
that positional token (or append if none). Update the logic around the args
handling block that builds extra_args and the branch that currently checks args
and not args[-1].startswith("-") (the code that mutates args and later calls
shlex.join) to compute a safe insert index by iterating over args and skipping
values after flags before performing the insertion.

@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 18, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 18, 2026
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Feb 18, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 19, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 19, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 19, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 19, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 19, 2026
@jordanrfrazier jordanrfrazier added this pull request to the merge queue Feb 19, 2026
Merged via the queue into main with commit 38082c1 Feb 19, 2026
37 of 39 checks passed
@jordanrfrazier jordanrfrazier deleted the cherry-pick-mcp-header branch February 19, 2026 04:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants