mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
feat(ui): grey out client-forwarded and OBO MCP servers on the gateway connect flow
On the aggregate gateway connect flow the client holds only an identity-only session bearer, and upstream credentials are resolved server-side from the per-user vault, which is only populated by interactive authorization_code (oauth2). The client-forwarded modes (true_passthrough, oauth_delegate) need the caller to present the upstream Authorization per call, and oauth2_token_exchange (OBO) needs the caller's own IdP token as the exchange subject; the session bearer is neither, so a tool call to those servers can never complete on this connection. Rather than let them look connectable and then 401, the grid greys those servers and labels them "Not supported on this connection" when rendered in connect mode. Outside the connect flow the normal integrations page is unchanged, since the client forwards its own token there and those modes work. The classification lives in a shared isUnsupportedOnGatewayConnect helper next to isClientForwardedTokenMode so the UI gate and the auth-mode taxonomy cannot drift.
This commit is contained in:
parent
5ab7a855c3
commit
6c74e9b988
3 changed files with 42 additions and 2 deletions
|
|
@ -13,7 +13,7 @@ import {
|
|||
getMCPOAuthUserCredentialStatus,
|
||||
listMCPTools,
|
||||
} from "../networking";
|
||||
import { AUTH_TYPE, MCPServer, MCPTool, handleTransport } from "../mcp_tools/types";
|
||||
import { AUTH_TYPE, MCPServer, MCPTool, handleTransport, isUnsupportedOnGatewayConnect } from "../mcp_tools/types";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { useUserMcpOAuthFlow } from "@/hooks/useUserMcpOAuthFlow";
|
||||
|
||||
|
|
@ -242,6 +242,13 @@ const MCPAppsPanel: React.FC<Props> = ({ accessToken, selectedServers, onChange,
|
|||
};
|
||||
|
||||
const renderConnectionIndicator = (server: MCPServer) => {
|
||||
if (connectMode && isUnsupportedOnGatewayConnect(server.auth_type)) {
|
||||
return (
|
||||
<span className="text-[11px] text-muted-foreground shrink-0 whitespace-nowrap">
|
||||
Not supported on this connection
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (server.auth_type === AUTH_TYPE.OAUTH2) {
|
||||
if (oauthConnected.has(server.server_id)) {
|
||||
return <CheckCircle className="h-3.5 w-3.5 text-emerald-600 shrink-0" />;
|
||||
|
|
@ -507,6 +514,7 @@ const MCPAppsPanel: React.FC<Props> = ({ accessToken, selectedServers, onChange,
|
|||
const color = getAvatarColor(name);
|
||||
const isLeftCol = idx % 2 === 0;
|
||||
const count = toolCounts[name];
|
||||
const unsupported = !!connectMode && isUnsupportedOnGatewayConnect(server.auth_type);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -514,7 +522,9 @@ const MCPAppsPanel: React.FC<Props> = ({ accessToken, selectedServers, onChange,
|
|||
onClick={() => setDetailServer(server)}
|
||||
className={`flex items-center gap-3 p-4 bg-card cursor-pointer transition-colors hover:bg-accent/30 min-w-0 ${
|
||||
isLeftCol ? "border-r" : ""
|
||||
} ${Math.floor(idx / 2) < Math.floor((filtered.length - 1) / 2) ? "border-b" : ""}`}
|
||||
} ${Math.floor(idx / 2) < Math.floor((filtered.length - 1) / 2) ? "border-b" : ""} ${
|
||||
unsupported ? "opacity-50" : ""
|
||||
}`}
|
||||
>
|
||||
{server.mcp_info?.logo_url ? (
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
preservedDeclaredAppCredentials,
|
||||
withoutMintedTokenCredentials,
|
||||
credentialAuthClass,
|
||||
isUnsupportedOnGatewayConnect,
|
||||
} from "./types";
|
||||
|
||||
describe("getOAuthAuthorizationIdentity", () => {
|
||||
|
|
@ -231,3 +232,23 @@ describe("credentialAuthClass", () => {
|
|||
expect(credentialAuthClass(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("isUnsupportedOnGatewayConnect", () => {
|
||||
it("flags the modes that need a caller-supplied upstream token or subject", () => {
|
||||
// client-forwarded: caller presents the upstream Authorization per call
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.TRUE_PASSTHROUGH)).toBe(true);
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.OAUTH_DELEGATE)).toBe(true);
|
||||
// OBO: caller's own IdP token is the exchange subject, which the session bearer is not
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.OAUTH2_TOKEN_EXCHANGE)).toBe(true);
|
||||
});
|
||||
|
||||
it("does not flag modes the gateway can serve from server-side state or interactive vaulting", () => {
|
||||
// interactive authorization_code is the one mode the connect grid vaults per user
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.OAUTH2)).toBe(false);
|
||||
// server-configured credentials need no per-user connect
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.API_KEY)).toBe(false);
|
||||
expect(isUnsupportedOnGatewayConnect(AUTH_TYPE.NONE)).toBe(false);
|
||||
expect(isUnsupportedOnGatewayConnect(null)).toBe(false);
|
||||
expect(isUnsupportedOnGatewayConnect(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ export const AUTH_TYPE = {
|
|||
export const isClientForwardedTokenMode = (authType?: string | null): boolean =>
|
||||
authType === AUTH_TYPE.TRUE_PASSTHROUGH || authType === AUTH_TYPE.OAUTH_DELEGATE;
|
||||
|
||||
// Auth modes that cannot be used through the gateway aggregate connect flow, where the client holds
|
||||
// only an identity-only session bearer and upstream credentials are resolved server-side from the
|
||||
// per-user vault. The vault is only populated by interactive authorization_code (oauth2). The
|
||||
// client-forwarded modes need the caller to present the upstream Authorization per call, and
|
||||
// oauth2_token_exchange (OBO) needs the caller's own IdP token as the subject to exchange; the
|
||||
// session bearer is neither, so none of these can complete a tool call on this connection.
|
||||
export const isUnsupportedOnGatewayConnect = (authType?: string | null): boolean =>
|
||||
isClientForwardedTokenMode(authType) || authType === AUTH_TYPE.OAUTH2_TOKEN_EXCHANGE;
|
||||
|
||||
export const OAUTH_FLOW = {
|
||||
INTERACTIVE: "interactive",
|
||||
M2M: "m2m",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue