refactor: Update MCP composer auth to just api and oauth (#9487)
* Update auth to just api and oauth * schema push * pr comment fix
This commit is contained in:
parent
59937ee9e7
commit
fd9f8c5711
5 changed files with 6 additions and 209 deletions
|
|
@ -39,7 +39,6 @@ from langflow.base.mcp.constants import MAX_MCP_SERVER_NAME_LENGTH
|
||||||
from langflow.base.mcp.util import sanitize_mcp_name
|
from langflow.base.mcp.util import sanitize_mcp_name
|
||||||
from langflow.services.database.models import Flow, Folder
|
from langflow.services.database.models import Flow, Folder
|
||||||
from langflow.services.deps import get_settings_service, session_scope
|
from langflow.services.deps import get_settings_service, session_scope
|
||||||
from langflow.services.settings.feature_flags import FEATURE_FLAGS
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -368,7 +367,6 @@ async def install_mcp_config(
|
||||||
# Determine command and args based on operating system
|
# Determine command and args based on operating system
|
||||||
os_type = platform.system()
|
os_type = platform.system()
|
||||||
command = "uvx"
|
command = "uvx"
|
||||||
mcp_tool = "mcp-composer" if FEATURE_FLAGS.mcp_composer else "mcp-proxy"
|
|
||||||
|
|
||||||
# Check if running on WSL (will appear as Linux but with Microsoft in release info)
|
# Check if running on WSL (will appear as Linux but with Microsoft in release info)
|
||||||
is_wsl = os_type == "Linux" and "microsoft" in platform.uname().release.lower()
|
is_wsl = os_type == "Linux" and "microsoft" in platform.uname().release.lower()
|
||||||
|
|
@ -398,33 +396,8 @@ async def install_mcp_config(
|
||||||
sse_url = sse_url.replace(f"http://{host}:{port}", f"http://{wsl_ip}:{port}")
|
sse_url = sse_url.replace(f"http://{host}:{port}", f"http://{wsl_ip}:{port}")
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.warning("Failed to get WSL IP address: %s. Using default URL.", str(e))
|
logger.warning("Failed to get WSL IP address: %s. Using default URL.", str(e))
|
||||||
|
|
||||||
# Configure args based on the MCP tool
|
|
||||||
oauth_env = None
|
|
||||||
if FEATURE_FLAGS.mcp_composer:
|
|
||||||
args = [mcp_tool, "--sse-url", sse_url]
|
|
||||||
|
|
||||||
# Check for auth settings and add auth parameters
|
|
||||||
if project.auth_settings:
|
|
||||||
from langflow.api.v1.schemas import AuthSettings
|
|
||||||
|
|
||||||
auth_settings = AuthSettings(**project.auth_settings)
|
|
||||||
args.extend(["--auth_type", auth_settings.auth_type])
|
|
||||||
|
|
||||||
oauth_env = {
|
|
||||||
"OAUTH_HOST": auth_settings.oauth_host,
|
|
||||||
"OAUTH_PORT": auth_settings.oauth_port,
|
|
||||||
"OAUTH_SERVER_URL": auth_settings.oauth_server_url,
|
|
||||||
"OAUTH_CALLBACK_PATH": auth_settings.oauth_callback_path,
|
|
||||||
"OAUTH_CLIENT_ID": auth_settings.oauth_client_id,
|
|
||||||
"OAUTH_CLIENT_SECRET": auth_settings.oauth_client_secret,
|
|
||||||
"OAUTH_AUTH_URL": auth_settings.oauth_auth_url,
|
|
||||||
"OAUTH_TOKEN_URL": auth_settings.oauth_token_url,
|
|
||||||
"OAUTH_MCP_SCOPE": auth_settings.oauth_mcp_scope,
|
|
||||||
"OAUTH_PROVIDER_SCOPE": auth_settings.oauth_provider_scope,
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
args = [mcp_tool, sse_url]
|
args = ["mcp-proxy", sse_url]
|
||||||
|
|
||||||
if os_type == "Windows":
|
if os_type == "Windows":
|
||||||
command = "cmd"
|
command = "cmd"
|
||||||
|
|
@ -439,10 +412,6 @@ async def install_mcp_config(
|
||||||
"args": args,
|
"args": args,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add environment variables if mcp-composer feature flag is enabled and auth settings exist
|
|
||||||
if FEATURE_FLAGS.mcp_composer and oauth_env is not None:
|
|
||||||
server_config["env"] = oauth_env # type: ignore[assignment]
|
|
||||||
|
|
||||||
mcp_config = {
|
mcp_config = {
|
||||||
"mcpServers": {f"lf-{sanitize_mcp_name(name)[: (MAX_MCP_SERVER_NAME_LENGTH - 4)]}": server_config}
|
"mcpServers": {f"lf-{sanitize_mcp_name(name)[: (MAX_MCP_SERVER_NAME_LENGTH - 4)]}": server_config}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -444,12 +444,8 @@ class CancelFlowResponse(BaseModel):
|
||||||
class AuthSettings(BaseModel):
|
class AuthSettings(BaseModel):
|
||||||
"""Model representing authentication settings for MCP."""
|
"""Model representing authentication settings for MCP."""
|
||||||
|
|
||||||
auth_type: Literal["none", "apikey", "basic", "bearer", "iam", "oauth"] = "none"
|
auth_type: Literal["none", "apikey", "oauth"] = "none"
|
||||||
api_key: SecretStr | None = None
|
api_key: SecretStr | None = None
|
||||||
username: str | None = None
|
|
||||||
password: SecretStr | None = None
|
|
||||||
bearer_token: SecretStr | None = None
|
|
||||||
iam_endpoint: str | None = None
|
|
||||||
oauth_host: str | None = None
|
oauth_host: str | None = None
|
||||||
oauth_port: str | None = None
|
oauth_port: str | None = None
|
||||||
oauth_server_url: str | None = None
|
oauth_server_url: str | None = None
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,6 @@ const AuthModal = ({ open, setOpen, authSettings, onSave }: AuthModalProps) => {
|
||||||
);
|
);
|
||||||
const [authFields, setAuthFields] = useState<{
|
const [authFields, setAuthFields] = useState<{
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
iamEndpoint?: string;
|
|
||||||
username?: string;
|
|
||||||
password?: string;
|
|
||||||
bearerToken?: string;
|
|
||||||
oauthHost?: string;
|
oauthHost?: string;
|
||||||
oauthPort?: string;
|
oauthPort?: string;
|
||||||
oauthServerUrl?: string;
|
oauthServerUrl?: string;
|
||||||
|
|
@ -37,10 +33,6 @@ const AuthModal = ({ open, setOpen, authSettings, onSave }: AuthModalProps) => {
|
||||||
oauthProviderScope?: string;
|
oauthProviderScope?: string;
|
||||||
}>({
|
}>({
|
||||||
apiKey: authSettings?.api_key || "",
|
apiKey: authSettings?.api_key || "",
|
||||||
iamEndpoint: authSettings?.iam_endpoint || "",
|
|
||||||
username: authSettings?.username || "",
|
|
||||||
password: authSettings?.password || "",
|
|
||||||
bearerToken: authSettings?.bearer_token || "",
|
|
||||||
oauthHost: authSettings?.oauth_host || "",
|
oauthHost: authSettings?.oauth_host || "",
|
||||||
oauthPort: authSettings?.oauth_port || "",
|
oauthPort: authSettings?.oauth_port || "",
|
||||||
oauthServerUrl: authSettings?.oauth_server_url || "",
|
oauthServerUrl: authSettings?.oauth_server_url || "",
|
||||||
|
|
@ -59,10 +51,6 @@ const AuthModal = ({ open, setOpen, authSettings, onSave }: AuthModalProps) => {
|
||||||
setAuthType(authSettings.auth_type || "none");
|
setAuthType(authSettings.auth_type || "none");
|
||||||
setAuthFields({
|
setAuthFields({
|
||||||
apiKey: authSettings.api_key || "",
|
apiKey: authSettings.api_key || "",
|
||||||
iamEndpoint: authSettings.iam_endpoint || "",
|
|
||||||
username: authSettings.username || "",
|
|
||||||
password: authSettings.password || "",
|
|
||||||
bearerToken: authSettings.bearer_token || "",
|
|
||||||
oauthHost: authSettings.oauth_host || "",
|
oauthHost: authSettings.oauth_host || "",
|
||||||
oauthPort: authSettings.oauth_port || "",
|
oauthPort: authSettings.oauth_port || "",
|
||||||
oauthServerUrl: authSettings.oauth_server_url || "",
|
oauthServerUrl: authSettings.oauth_server_url || "",
|
||||||
|
|
@ -93,15 +81,6 @@ const AuthModal = ({ open, setOpen, authSettings, onSave }: AuthModalProps) => {
|
||||||
const authSettingsToSave: AuthSettingsType = {
|
const authSettingsToSave: AuthSettingsType = {
|
||||||
auth_type: authType,
|
auth_type: authType,
|
||||||
...(authType === "apikey" && { api_key: authFields.apiKey }),
|
...(authType === "apikey" && { api_key: authFields.apiKey }),
|
||||||
...(authType === "basic" && {
|
|
||||||
username: authFields.username,
|
|
||||||
password: authFields.password,
|
|
||||||
}),
|
|
||||||
...(authType === "iam" && {
|
|
||||||
iam_endpoint: authFields.iamEndpoint,
|
|
||||||
api_key: authFields.apiKey,
|
|
||||||
}),
|
|
||||||
...(authType === "bearer" && { bearer_token: authFields.bearerToken }),
|
|
||||||
...(authType === "oauth" && {
|
...(authType === "oauth" && {
|
||||||
oauth_host: authFields.oauthHost,
|
oauth_host: authFields.oauthHost,
|
||||||
oauth_port: authFields.oauthPort,
|
oauth_port: authFields.oauthPort,
|
||||||
|
|
@ -188,95 +167,6 @@ const AuthModal = ({ open, setOpen, authSettings, onSave }: AuthModalProps) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{authType === "basic" && (
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label htmlFor="username" className="!text-mmd font-medium">
|
|
||||||
Username
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="username"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter Username"
|
|
||||||
value={authFields.username || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleAuthFieldChange("username", e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label htmlFor="password" className="!text-mmd font-medium">
|
|
||||||
Password
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter Password"
|
|
||||||
value={authFields.password || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleAuthFieldChange("password", e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{authType === "bearer" && (
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label
|
|
||||||
htmlFor="bearer-token"
|
|
||||||
className="!text-mmd font-medium"
|
|
||||||
>
|
|
||||||
Bearer Token
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="bearer-token"
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter Bearer Token"
|
|
||||||
value={authFields.bearerToken || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleAuthFieldChange("bearerToken", e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{authType === "iam" && (
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label
|
|
||||||
htmlFor="iam-endpoint"
|
|
||||||
className="!text-mmd font-medium"
|
|
||||||
>
|
|
||||||
IAM Endpoint
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="iam-endpoint"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter IAM Endpoint"
|
|
||||||
value={authFields.iamEndpoint || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleAuthFieldChange("iamEndpoint", e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label htmlFor="api-key" className="!text-mmd font-medium">
|
|
||||||
API Key or Token
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter API Key or Token"
|
|
||||||
value={authFields.apiKey || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleAuthFieldChange("apiKey", e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{authType === "oauth" && (
|
{authType === "oauth" && (
|
||||||
<div className="flex flex-col gap-4 h-full">
|
<div className="flex flex-col gap-4 h-full">
|
||||||
<span className="text-mmd font-medium text-muted-foreground">
|
<span className="text-mmd font-medium text-muted-foreground">
|
||||||
|
|
|
||||||
|
|
@ -240,62 +240,13 @@ const McpServerTab = ({ folderName }: { folderName: string }) => {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (currentAuthSettings.auth_type) {
|
if (currentAuthSettings.auth_type === "apikey") {
|
||||||
case "apikey":
|
return `
|
||||||
return `
|
|
||||||
"--headers",
|
"--headers",
|
||||||
"x-api-key",
|
"x-api-key",
|
||||||
"${currentAuthSettings.api_key || "YOUR_API_KEY"}",`;
|
"${currentAuthSettings.api_key || "YOUR_API_KEY"}",`;
|
||||||
case "basic":
|
|
||||||
return `
|
|
||||||
"--headers",
|
|
||||||
"Authorization",
|
|
||||||
"Basic ${btoa(
|
|
||||||
`${currentAuthSettings.username || "USERNAME"}:${
|
|
||||||
currentAuthSettings.password || "PASSWORD"
|
|
||||||
}`,
|
|
||||||
)}",`;
|
|
||||||
case "bearer":
|
|
||||||
return `
|
|
||||||
"--headers",
|
|
||||||
"Authorization",
|
|
||||||
"Bearer ${currentAuthSettings.bearer_token || "YOUR_BEARER_TOKEN"}",`;
|
|
||||||
case "iam":
|
|
||||||
return `
|
|
||||||
"--headers",
|
|
||||||
"x-api-key",
|
|
||||||
"${currentAuthSettings.api_key || "YOUR_IAM_TOKEN"}",
|
|
||||||
"--headers",
|
|
||||||
"x-iam-endpoint",
|
|
||||||
"${currentAuthSettings.iam_endpoint || "YOUR_IAM_ENDPOINT"}",`;
|
|
||||||
case "oauth":
|
|
||||||
return `
|
|
||||||
"--auth_type",
|
|
||||||
"oauth",
|
|
||||||
"--sse-url",`;
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const getEnvVars = () => {
|
|
||||||
if (!ENABLE_MCP_COMPOSER || currentAuthSettings?.auth_type === "none")
|
|
||||||
return "";
|
|
||||||
if (currentAuthSettings?.auth_type === "oauth") {
|
|
||||||
return `
|
|
||||||
"env": {
|
|
||||||
"OAUTH_HOST": "${currentAuthSettings.oauth_host || "YOUR_OAUTH_HOST"}",
|
|
||||||
"OAUTH_PORT": "${currentAuthSettings.oauth_port || "YOUR_OAUTH_PORT"}",
|
|
||||||
"OAUTH_SERVER_URL": "${currentAuthSettings.oauth_server_url || "YOUR_OAUTH_SERVER_URL"}",
|
|
||||||
"OAUTH_CALLBACK_PATH": "${currentAuthSettings.oauth_callback_path || "YOUR_OAUTH_CALLBACK_PATH"}",
|
|
||||||
"OAUTH_CLIENT_ID": "${currentAuthSettings.oauth_client_id || "YOUR_OAUTH_CLIENT_ID"}",
|
|
||||||
"OAUTH_CLIENT_SECRET": "${currentAuthSettings.oauth_client_secret || "YOUR_OAUTH_CLIENT_SECRET"}",
|
|
||||||
"OAUTH_AUTH_URL": "${currentAuthSettings.oauth_auth_url || "YOUR_OAUTH_AUTH_URL"}",
|
|
||||||
"OAUTH_TOKEN_URL": "${currentAuthSettings.oauth_token_url || "YOUR_OAUTH_TOKEN_URL"}",
|
|
||||||
"OAUTH_MCP_SCOPE": "${currentAuthSettings.oauth_mcp_scope || "YOUR_OAUTH_MCP_SCOPE"}",
|
|
||||||
"OAUTH_PROVIDER_SCOPE": "${currentAuthSettings.oauth_provider_scope || "YOUR_OAUTH_PROVIDER_SCOPE"}"
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -323,9 +274,9 @@ const McpServerTab = ({ folderName }: { folderName: string }) => {
|
||||||
? `"uvx",
|
? `"uvx",
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}"${ENABLE_MCP_COMPOSER ? "mcp-composer" : "mcp-proxy"}",${getAuthHeaders()}
|
}"mcp-proxy",${getAuthHeaders()}
|
||||||
"${apiUrl}"
|
"${apiUrl}"
|
||||||
]${ENABLE_MCP_COMPOSER && currentAuthSettings?.auth_type === "oauth" ? `,` : ""}${getEnvVars()}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,12 @@ import { MCPServerType } from "@/types/mcp";
|
||||||
export enum AuthMethodId {
|
export enum AuthMethodId {
|
||||||
NONE = "none",
|
NONE = "none",
|
||||||
API_KEY = "apikey",
|
API_KEY = "apikey",
|
||||||
BASIC = "basic",
|
|
||||||
BEARER = "bearer",
|
|
||||||
IAM = "iam",
|
|
||||||
OAUTH = "oauth",
|
OAUTH = "oauth",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AUTH_METHODS = {
|
export const AUTH_METHODS = {
|
||||||
[AuthMethodId.NONE]: { id: AuthMethodId.NONE, label: "None" },
|
[AuthMethodId.NONE]: { id: AuthMethodId.NONE, label: "None" },
|
||||||
[AuthMethodId.API_KEY]: { id: AuthMethodId.API_KEY, label: "API Key" },
|
[AuthMethodId.API_KEY]: { id: AuthMethodId.API_KEY, label: "API Key" },
|
||||||
[AuthMethodId.BASIC]: {
|
|
||||||
id: AuthMethodId.BASIC,
|
|
||||||
label: "Basic",
|
|
||||||
},
|
|
||||||
[AuthMethodId.BEARER]: { id: AuthMethodId.BEARER, label: "Bearer Token" },
|
|
||||||
[AuthMethodId.IAM]: { id: AuthMethodId.IAM, label: "IAM" },
|
|
||||||
[AuthMethodId.OAUTH]: { id: AuthMethodId.OAUTH, label: "OAuth" },
|
[AuthMethodId.OAUTH]: { id: AuthMethodId.OAUTH, label: "OAuth" },
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue