feat: Adds MCP_SSE to be filled from frontend (#7594)

* add mcp sse to copy field

* add MCP_SSE from frontend

* [autofix.ci] apply automated fixes

* refactor: reorder imports in mcp_component.py for consistency

* ✅ (mcp-server.spec.ts): refactor code to use consistent selector for sse_url textarea element in tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
Edwin Jose 2025-04-17 14:02:37 -04:00 • committed by GitHub
commit 0ba8f6559c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 31 additions and 30 deletions

View file

@ -15,7 +15,7 @@ from langflow.base.mcp.util import (
from langflow.custom import Component
from langflow.inputs import DropdownInput
from langflow.inputs.inputs import InputTypes
from langflow.io import MessageTextInput, Output, TabInput
from langflow.io import MessageTextInput, MultilineInput, Output, TabInput
from langflow.io.schema import schema_to_langflow_inputs
from langflow.logging import logger
from langflow.schema import Message
@ -52,7 +52,7 @@ class MCPToolsComponent(Component):
show=True,
refresh_button=True,
),
MessageTextInput(
MultilineInput(
name="sse_url",
display_name="MCP SSE URL",
info="URL for MCP SSE connection",
@ -147,9 +147,7 @@ class MCPToolsComponent(Component):
elif field_value == "SSE":
build_config["command"]["show"] = False
build_config["sse_url"]["show"] = True
_, port, _ = await self.find_langflow_instance()
if port:
build_config["sse_url"]["value"] = f"http://localhost:{port}/api/v1/mcp/sse"
build_config["sse_url"]["value"] = "MCP_SSE"
return build_config
if field_name in ("command", "sse_url", "mode"):
try:

View file

@ -8,7 +8,9 @@ import { Input } from "../../../../ui/input";
import { InputProps, TextAreaComponentType } from "../../types";
const BACKEND_URL = "BACKEND_URL";
const MCP_SSE_VALUE = "MCP_SSE";
const URL_WEBHOOK = `${window.location.protocol}//${window.location.host}/api/v1/webhook/`;
const URL_MCP_SSE = `${window.location.protocol}//${window.location.host}/api/v1/mcp/sse`;
const inputClasses = {
base: ({ isFocused }: { isFocused: boolean }) =>
@ -60,15 +62,16 @@ export default function CopyFieldAreaComponent({
const [isFocused, setIsFocused] = useState(false);
const [isCopied, setIsCopied] = useState(false);
const isValueToReplace = value === BACKEND_URL;
const isValueToReplace = value === BACKEND_URL || value === MCP_SSE_VALUE;
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const currentFlow = useFlowStore((state) => state.currentFlow);
const endpointName = currentFlow?.endpoint_name ?? "";
const valueToRender = useMemo(() => {
if (isValueToReplace) {
const urlWebhook = `${URL_WEBHOOK}${endpointName}`;
return isValueToReplace ? urlWebhook : value;
if (value === BACKEND_URL) {
return `${URL_WEBHOOK}${endpointName}`;
} else if (value === MCP_SSE_VALUE) {
return `${URL_MCP_SSE}`;
}
return value;
}, [value, endpointName]);

View file

@ -20,6 +20,8 @@ const inputClasses = {
};
const WEBHOOK_VALUE = "CURL_WEBHOOK";
const MCP_SSE_VALUE = "MCP_SSE";
const URL_MCP_SSE = `${window.location.protocol}//${window.location.host}/api/v1/mcp/sse`;
const externalLinkIconClasses = {
gradient: ({
@ -75,6 +77,11 @@ export default function TextAreaComponent({
[nodeInformationMetadata?.nodeType],
);
const isMCPSSE = useMemo(
() => nodeInformationMetadata?.nodeType === "mcp_sse",
[nodeInformationMetadata?.nodeType],
);
useEffect(() => {
if (isWebhook && value === WEBHOOK_VALUE) {
const curlWebhookCode = getCurlWebhookCode({
@ -84,8 +91,11 @@ export default function TextAreaComponent({
format: "singleline",
});
handleOnNewValue({ value: curlWebhookCode });
} else if (value === MCP_SSE_VALUE) {
const mcpSSEUrl = `${URL_MCP_SSE}`;
handleOnNewValue({ value: mcpSSEUrl });
}
}, [isWebhook]);
}, [isWebhook, value, nodeInformationMetadata, handleOnNewValue]);
const getInputClassName = () => {
return cn(

View file

@ -68,17 +68,12 @@ test(
await page.getByTestId("tab_1_sse").click();
await page.waitForSelector(
'[data-testid="anchor-popover-anchor-input-sse_url"]',
{
state: "visible",
timeout: 30000,
},
);
await page.waitForSelector('[data-testid="textarea_str_sse_url"]', {
state: "visible",
timeout: 30000,
});
let sseURLCount = await page
.getByTestId("anchor-popover-anchor-input-sse_url")
.count();
let sseURLCount = await page.getByTestId("textarea_str_sse_url").count();
expect(sseURLCount).toBeGreaterThan(0);
@ -122,17 +117,12 @@ test(
await page.getByTestId("tab_1_sse").click();
await page.waitForSelector(
'[data-testid="anchor-popover-anchor-input-sse_url"]',
{
state: "visible",
timeout: 30000,
},
);
await page.waitForSelector('[data-testid="textarea_str_sse_url"]', {
state: "visible",
timeout: 30000,
});
sseURLCount = await page
.getByTestId("anchor-popover-anchor-input-sse_url")
.count();
sseURLCount = await page.getByTestId("textarea_str_sse_url").count();
expect(sseURLCount).toBeGreaterThan(0);